From 79b26212bc7de4b274d4b02e82bef3973534132f Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 7 Dec 2021 16:46:38 +0100 Subject: [PATCH 1/9] update initializer contract to prevent reentry during initialization --- CHANGELOG.md | 7 ++++--- contracts/proxy/utils/Initializable.sol | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cee4746924..7a498826d64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,13 @@ ## Unreleased -* `GovernorTimelockControl`: improve the `state()` function to have it reflect cases where a proposal has been canceled directly on the timelock. ([#2977](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2977)) -* `Math`: add a `abs(int256)` method that returns the unsigned absolute value of a signed value. ([#2984](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2984)) -* Preset contracts are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com). ([#2986](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2986)) + * `GovernorTimelockControl`: improve the `state()` function to have it reflect cases where a proposal has been canceled directly on the timelock. ([#2977](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2977)) + * `Math`: add a `abs(int256)` method that returns the unsigned absolute value of a signed value. ([#2984](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2984)) + * Preset contracts are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com). ([#2986](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2986)) * `Governor`: add a relay function to help recover assets sent to a governor that is not its own executor (e.g. when using a timelock). ([#2926](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2926)) * `GovernorExtendedVoting`: add new module to ensure a minimum voting duration is available after the quorum is reached. ([#2973](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2973)) + ## 4.4.0 (2021-11-25) * `Ownable`: add an internal `_transferOwnership(address)`. ([#2568](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2568)) diff --git a/contracts/proxy/utils/Initializable.sol b/contracts/proxy/utils/Initializable.sol index 84e99f791ee..55e98d54df5 100644 --- a/contracts/proxy/utils/Initializable.sol +++ b/contracts/proxy/utils/Initializable.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.0; +import "../../utils/Address.sol"; + /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an @@ -41,11 +43,21 @@ abstract contract Initializable { */ bool private _initializing; + /** + * @dev Modifier to protect (internal) functions that should only be executed as subcalls of an initializing + * function (protected with the {initializer} modifier). This modifier is designed to be used in upgradeable + * contracts for the constructor-replacing internal functions. + */ + modifier onlyInitializing() { + require(_initializing, "Initializable: contract is not initializing"); + _; + } + /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { - require(_initializing || !_initialized, "Initializable: contract is already initialized"); + require(_initializing ? isConstructor() : !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { @@ -59,4 +71,8 @@ abstract contract Initializable { _initializing = false; } } + + function isConstructor() private view returns(bool) { + return !Address.isContract(address(this)); + } } From 970325c059771c319906ea4808939e297ccb2a6c Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 7 Dec 2021 16:58:00 +0100 Subject: [PATCH 2/9] add changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a498826d64..a56a0f0a757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ * Preset contracts are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com). ([#2986](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2986)) * `Governor`: add a relay function to help recover assets sent to a governor that is not its own executor (e.g. when using a timelock). ([#2926](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2926)) * `GovernorExtendedVoting`: add new module to ensure a minimum voting duration is available after the quorum is reached. ([#2973](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2973)) - + * `Initializable`: change the existing `initialize()` modifier and add a new `onlyInitializing()` modifier to prevent some re-entrancy risks. ([#3006](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3006)) ## 4.4.0 (2021-11-25) From 382b11f7be58ec0d1e66cd39e2742c603aa81508 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 7 Dec 2021 17:00:55 +0100 Subject: [PATCH 3/9] fix linting --- contracts/proxy/utils/Initializable.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/proxy/utils/Initializable.sol b/contracts/proxy/utils/Initializable.sol index 55e98d54df5..698f14b7b6b 100644 --- a/contracts/proxy/utils/Initializable.sol +++ b/contracts/proxy/utils/Initializable.sol @@ -57,7 +57,7 @@ abstract contract Initializable { * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { - require(_initializing ? isConstructor() : !_initialized, "Initializable: contract is already initialized"); + require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { @@ -72,7 +72,7 @@ abstract contract Initializable { } } - function isConstructor() private view returns(bool) { + function _isConstructor() private view returns (bool) { return !Address.isContract(address(this)); } } From 1f95f56e39b5127d55139d83542ceaa9d06a18fc Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 7 Dec 2021 18:29:56 +0100 Subject: [PATCH 4/9] update tests --- contracts/mocks/InitializableMock.sol | 27 +++++++++ .../MultipleInheritanceInitializableMocks.sol | 55 +++++++++++++++++-- test/proxy/utils/Initializable.test.js | 32 +++++++++-- 3 files changed, 105 insertions(+), 9 deletions(-) diff --git a/contracts/mocks/InitializableMock.sol b/contracts/mocks/InitializableMock.sol index 0d3e77dfa1f..76e93cadd52 100644 --- a/contracts/mocks/InitializableMock.sol +++ b/contracts/mocks/InitializableMock.sol @@ -10,16 +10,25 @@ import "../proxy/utils/Initializable.sol"; */ contract InitializableMock is Initializable { bool public initializerRan; + bool public initializerRan2; uint256 public x; function initialize() public initializer { initializerRan = true; } + function initialize2() public onlyInitializing { + initializerRan2 = true; + } + function initializeNested() public initializer { initialize(); } + function initializeNested2() public initializer { + initialize2(); + } + function initializeWithX(uint256 _x) public payable initializer { x = _x; } @@ -32,3 +41,21 @@ contract InitializableMock is Initializable { require(false, "InitializableMock forced failure"); } } + +contract ConstructorInitializableMock is Initializable { + bool public initializerRan; + bool public initializerRan2; + + constructor() initializer { + initialize(); + initialize2(); + } + + function initialize() public initializer { + initializerRan = true; + } + + function initialize2() public onlyInitializing { + initializerRan2 = true; + } +} \ No newline at end of file diff --git a/contracts/mocks/MultipleInheritanceInitializableMocks.sol b/contracts/mocks/MultipleInheritanceInitializableMocks.sol index 1a008e8d8c1..367690d6f63 100644 --- a/contracts/mocks/MultipleInheritanceInitializableMocks.sol +++ b/contracts/mocks/MultipleInheritanceInitializableMocks.sol @@ -22,6 +22,14 @@ contract SampleHuman is Initializable { bool public isHuman; function initialize() public initializer { + __SampleHuman_init(); + } + + function __SampleHuman_init() internal onlyInitializing() { + __SampleHuman_init_unchained(); + } + + function __SampleHuman_init_unchained() internal onlyInitializing() { isHuman = true; } } @@ -33,7 +41,15 @@ contract SampleMother is Initializable, SampleHuman { uint256 public mother; function initialize(uint256 value) public virtual initializer { - SampleHuman.initialize(); + __SampleMother_init(value); + } + + function __SampleMother_init(uint256 value) internal onlyInitializing { + __SampleHuman_init(); + __SampleMother_init_unchained(value); + } + + function __SampleMother_init_unchained(uint256 value) internal onlyInitializing { mother = value; } } @@ -45,7 +61,15 @@ contract SampleGramps is Initializable, SampleHuman { string public gramps; function initialize(string memory value) public virtual initializer { - SampleHuman.initialize(); + __SampleGramps_init(value); + } + + function __SampleGramps_init(string memory value) internal onlyInitializing { + __SampleHuman_init(); + __SampleGramps_init_unchained(value); + } + + function __SampleGramps_init_unchained(string memory value) internal onlyInitializing { gramps = value; } } @@ -57,7 +81,15 @@ contract SampleFather is Initializable, SampleGramps { uint256 public father; function initialize(string memory _gramps, uint256 _father) public initializer { - SampleGramps.initialize(_gramps); + __SampleFather_init(_gramps, _father); + } + + function __SampleFather_init(string memory _gramps, uint256 _father) internal onlyInitializing { + __SampleGramps_init(_gramps); + __SampleFather_init_unchained(_father); + } + + function __SampleFather_init_unchained(uint256 _father) internal onlyInitializing { father = _father; } } @@ -74,8 +106,21 @@ contract SampleChild is Initializable, SampleMother, SampleFather { uint256 _father, uint256 _child ) public initializer { - SampleMother.initialize(_mother); - SampleFather.initialize(_gramps, _father); + __SampleChild_init(_mother, _gramps, _father, _child); + } + + function __SampleChild_init( + uint256 _mother, + string memory _gramps, + uint256 _father, + uint256 _child + ) internal onlyInitializing { + __SampleMother_init(_mother); + __SampleFather_init(_gramps, _father); + __SampleChild_init_unchained(_child); + } + + function __SampleChild_init_unchained(uint256 _child) internal onlyInitializing { child = _child; } } diff --git a/test/proxy/utils/Initializable.test.js b/test/proxy/utils/Initializable.test.js index 7581a37d056..aa23203a104 100644 --- a/test/proxy/utils/Initializable.test.js +++ b/test/proxy/utils/Initializable.test.js @@ -3,6 +3,7 @@ const { expectRevert } = require('@openzeppelin/test-helpers'); const { assert } = require('chai'); const InitializableMock = artifacts.require('InitializableMock'); +const ConstructorInitializableMock = artifacts.require('ConstructorInitializableMock'); const SampleChild = artifacts.require('SampleChild'); contract('Initializable', function (accounts) { @@ -32,14 +33,37 @@ contract('Initializable', function (accounts) { }); context('after nested initialize', function () { - beforeEach('initializing', async function () { - await this.contract.initializeNested(); + context('with initializer methods', function () { + it('nested initializer reverts', async function () { + await expectRevert(this.contract.initializeNested(), 'Initializable: contract is already initialized'); + }); }); - it('initializer has run', async function () { - assert.isTrue(await this.contract.initializerRan()); + context('with onlyInitializing methods', function () { + beforeEach('initializing', async function () { + await this.contract.initializeNested2(); + }); + + it('nested initializer has run', async function () { + assert.isTrue(await this.contract.initializerRan2()); + }); }); }); + + it('cannot call onlyInitializable function outside the scope of an initializable function', async function () { + await expectRevert(this.contract.initialize2(), 'Initializable: contract is not initializing'); + }); + }); + + describe('initialization during construction', function () { + beforeEach('initializing', async function () { + this.contract2 = await ConstructorInitializableMock.new(); + }); + + it('nested initializer can run during construction', async function () { + assert.isTrue(await this.contract2.initializerRan()); + assert.isTrue(await this.contract2.initializerRan2()); + }); }); describe('complex testing with inheritance', function () { From 9eb1a842bb04b25564e96ccb8822084f2eec83f9 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 7 Dec 2021 18:32:12 +0100 Subject: [PATCH 5/9] fix lint --- contracts/mocks/InitializableMock.sol | 2 +- .../MultipleInheritanceInitializableMocks.sol | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/contracts/mocks/InitializableMock.sol b/contracts/mocks/InitializableMock.sol index 76e93cadd52..5e42c43013a 100644 --- a/contracts/mocks/InitializableMock.sol +++ b/contracts/mocks/InitializableMock.sol @@ -58,4 +58,4 @@ contract ConstructorInitializableMock is Initializable { function initialize2() public onlyInitializing { initializerRan2 = true; } -} \ No newline at end of file +} diff --git a/contracts/mocks/MultipleInheritanceInitializableMocks.sol b/contracts/mocks/MultipleInheritanceInitializableMocks.sol index 367690d6f63..f8b6e465e5f 100644 --- a/contracts/mocks/MultipleInheritanceInitializableMocks.sol +++ b/contracts/mocks/MultipleInheritanceInitializableMocks.sol @@ -25,11 +25,13 @@ contract SampleHuman is Initializable { __SampleHuman_init(); } - function __SampleHuman_init() internal onlyInitializing() { + // solhint-disable-next-line func-name-mixedcase + function __SampleHuman_init() internal onlyInitializing { __SampleHuman_init_unchained(); } - function __SampleHuman_init_unchained() internal onlyInitializing() { + // solhint-disable-next-line func-name-mixedcase + function __SampleHuman_init_unchained() internal onlyInitializing { isHuman = true; } } @@ -44,11 +46,13 @@ contract SampleMother is Initializable, SampleHuman { __SampleMother_init(value); } + // solhint-disable-next-line func-name-mixedcase function __SampleMother_init(uint256 value) internal onlyInitializing { __SampleHuman_init(); __SampleMother_init_unchained(value); } + // solhint-disable-next-line func-name-mixedcase function __SampleMother_init_unchained(uint256 value) internal onlyInitializing { mother = value; } @@ -64,11 +68,13 @@ contract SampleGramps is Initializable, SampleHuman { __SampleGramps_init(value); } + // solhint-disable-next-line func-name-mixedcase function __SampleGramps_init(string memory value) internal onlyInitializing { __SampleHuman_init(); __SampleGramps_init_unchained(value); } + // solhint-disable-next-line func-name-mixedcase function __SampleGramps_init_unchained(string memory value) internal onlyInitializing { gramps = value; } @@ -84,11 +90,13 @@ contract SampleFather is Initializable, SampleGramps { __SampleFather_init(_gramps, _father); } + // solhint-disable-next-line func-name-mixedcase function __SampleFather_init(string memory _gramps, uint256 _father) internal onlyInitializing { __SampleGramps_init(_gramps); __SampleFather_init_unchained(_father); } + // solhint-disable-next-line func-name-mixedcase function __SampleFather_init_unchained(uint256 _father) internal onlyInitializing { father = _father; } @@ -109,6 +117,7 @@ contract SampleChild is Initializable, SampleMother, SampleFather { __SampleChild_init(_mother, _gramps, _father, _child); } + // solhint-disable-next-line func-name-mixedcase function __SampleChild_init( uint256 _mother, string memory _gramps, @@ -120,6 +129,7 @@ contract SampleChild is Initializable, SampleMother, SampleFather { __SampleChild_init_unchained(_child); } + // solhint-disable-next-line func-name-mixedcase function __SampleChild_init_unchained(uint256 _child) internal onlyInitializing { child = _child; } From 4417ca2d09ece7548593f5c8fce6bbfc42b74b8e Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Tue, 7 Dec 2021 16:21:39 -0300 Subject: [PATCH 6/9] add breaking changes section in changelog --- CHANGELOG.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a56a0f0a757..6d45e4fb8ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,23 @@ * Preset contracts are now deprecated in favor of [Contracts Wizard](https://wizard.openzeppelin.com). ([#2986](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2986)) * `Governor`: add a relay function to help recover assets sent to a governor that is not its own executor (e.g. when using a timelock). ([#2926](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2926)) * `GovernorExtendedVoting`: add new module to ensure a minimum voting duration is available after the quorum is reached. ([#2973](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2973)) - * `Initializable`: change the existing `initialize()` modifier and add a new `onlyInitializing()` modifier to prevent some re-entrancy risks. ([#3006](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3006)) + * `Initializable`: change the existing `initializer` modifier and add a new `onlyInitializing` modifier to prevent reentrancy risk. ([#3006](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3006)) + +### Breaking changes + +It is no longer possible to call an `initializer`-protected function from within another `initializer` function outside the context of a constructor. Projects using OpenZeppelin upgradeable proxies should continue to work as is, since in the common case the initializer is invoked in the constructor directly. If this is not the case for you, the suggested change is to use the new `onlyInitializing` modifier in the following way: + +```diff + contract A { +- function initialize() public initializer { ... } ++ function initialize() internal onlyInitializing { ... } + } + contract B is A { + function initialize() public initializer { + A.initialize(); + } + } +``` ## 4.4.0 (2021-11-25) From 48eebbc45a2f3f3e9b914d52a01c6ecdd4be1f09 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Tue, 7 Dec 2021 23:28:51 -0300 Subject: [PATCH 7/9] docs --- contracts/proxy/utils/Initializable.sol | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/contracts/proxy/utils/Initializable.sol b/contracts/proxy/utils/Initializable.sol index 698f14b7b6b..e14ad93cbac 100644 --- a/contracts/proxy/utils/Initializable.sol +++ b/contracts/proxy/utils/Initializable.sol @@ -43,20 +43,13 @@ abstract contract Initializable { */ bool private _initializing; - /** - * @dev Modifier to protect (internal) functions that should only be executed as subcalls of an initializing - * function (protected with the {initializer} modifier). This modifier is designed to be used in upgradeable - * contracts for the constructor-replacing internal functions. - */ - modifier onlyInitializing() { - require(_initializing, "Initializable: contract is not initializing"); - _; - } - /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { + // If the contract is initializing we ignore whether _initialized is set in order to support multiple + // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the + // contract may have been reentered. require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; @@ -72,6 +65,15 @@ abstract contract Initializable { } } + /** + * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the + * {initializer} modifier, directly or indirectly. + */ + modifier onlyInitializing() { + require(_initializing, "Initializable: contract is not initializing"); + _; + } + function _isConstructor() private view returns (bool) { return !Address.isContract(address(this)); } From 4ae014f6b4eb66f6ea96617ee4f399a2ef0f9f13 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 10 Dec 2021 12:18:24 -0300 Subject: [PATCH 8/9] simplify test structure --- test/proxy/utils/Initializable.test.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/test/proxy/utils/Initializable.test.js b/test/proxy/utils/Initializable.test.js index aa23203a104..cf327f47e0c 100644 --- a/test/proxy/utils/Initializable.test.js +++ b/test/proxy/utils/Initializable.test.js @@ -55,15 +55,10 @@ contract('Initializable', function (accounts) { }); }); - describe('initialization during construction', function () { - beforeEach('initializing', async function () { - this.contract2 = await ConstructorInitializableMock.new(); - }); - - it('nested initializer can run during construction', async function () { - assert.isTrue(await this.contract2.initializerRan()); - assert.isTrue(await this.contract2.initializerRan2()); - }); + it('nested initializer can run during construction', async function () { + const contract2 = await ConstructorInitializableMock.new(); + assert.isTrue(await contract2.initializerRan()); + assert.isTrue(await contract2.initializerRan2()); }); describe('complex testing with inheritance', function () { From 089f3d5b28b96e6593908ef727274631452f31f0 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 10 Dec 2021 12:44:13 -0300 Subject: [PATCH 9/9] rename test functions --- contracts/mocks/InitializableMock.sol | 20 ++++++++++---------- test/proxy/utils/Initializable.test.js | 24 ++++++++---------------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/contracts/mocks/InitializableMock.sol b/contracts/mocks/InitializableMock.sol index 5e42c43013a..630e8bbfad6 100644 --- a/contracts/mocks/InitializableMock.sol +++ b/contracts/mocks/InitializableMock.sol @@ -10,23 +10,23 @@ import "../proxy/utils/Initializable.sol"; */ contract InitializableMock is Initializable { bool public initializerRan; - bool public initializerRan2; + bool public onlyInitializingRan; uint256 public x; function initialize() public initializer { initializerRan = true; } - function initialize2() public onlyInitializing { - initializerRan2 = true; + function initializeOnlyInitializing() public onlyInitializing { + onlyInitializingRan = true; } - function initializeNested() public initializer { + function initializerNested() public initializer { initialize(); } - function initializeNested2() public initializer { - initialize2(); + function onlyInitializingNested() public initializer { + initializeOnlyInitializing(); } function initializeWithX(uint256 _x) public payable initializer { @@ -44,18 +44,18 @@ contract InitializableMock is Initializable { contract ConstructorInitializableMock is Initializable { bool public initializerRan; - bool public initializerRan2; + bool public onlyInitializingRan; constructor() initializer { initialize(); - initialize2(); + initializeOnlyInitializing(); } function initialize() public initializer { initializerRan = true; } - function initialize2() public onlyInitializing { - initializerRan2 = true; + function initializeOnlyInitializing() public onlyInitializing { + onlyInitializingRan = true; } } diff --git a/test/proxy/utils/Initializable.test.js b/test/proxy/utils/Initializable.test.js index cf327f47e0c..04884a1d45e 100644 --- a/test/proxy/utils/Initializable.test.js +++ b/test/proxy/utils/Initializable.test.js @@ -1,5 +1,4 @@ const { expectRevert } = require('@openzeppelin/test-helpers'); - const { assert } = require('chai'); const InitializableMock = artifacts.require('InitializableMock'); @@ -32,33 +31,26 @@ contract('Initializable', function (accounts) { }); }); - context('after nested initialize', function () { - context('with initializer methods', function () { - it('nested initializer reverts', async function () { - await expectRevert(this.contract.initializeNested(), 'Initializable: contract is already initialized'); - }); + context('nested under an initializer', function () { + it('initializer modifier reverts', async function () { + await expectRevert(this.contract.initializerNested(), 'Initializable: contract is already initialized'); }); - context('with onlyInitializing methods', function () { - beforeEach('initializing', async function () { - await this.contract.initializeNested2(); - }); - - it('nested initializer has run', async function () { - assert.isTrue(await this.contract.initializerRan2()); - }); + it('onlyInitializing modifier succeeds', async function () { + await this.contract.onlyInitializingNested(); + assert.isTrue(await this.contract.onlyInitializingRan()); }); }); it('cannot call onlyInitializable function outside the scope of an initializable function', async function () { - await expectRevert(this.contract.initialize2(), 'Initializable: contract is not initializing'); + await expectRevert(this.contract.initializeOnlyInitializing(), 'Initializable: contract is not initializing'); }); }); it('nested initializer can run during construction', async function () { const contract2 = await ConstructorInitializableMock.new(); assert.isTrue(await contract2.initializerRan()); - assert.isTrue(await contract2.initializerRan2()); + assert.isTrue(await contract2.onlyInitializingRan()); }); describe('complex testing with inheritance', function () {