diff --git a/CHANGELOG.md b/CHANGELOG.md index 1afa0ddf822..ffa4c02be71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased + * `Initializable`: add internal functions `_getInitializedVersion` and `_isInitializing` ([#3598](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3598)) * `ERC165Checker`: add `supportsERC165InterfaceUnchecked` for consulting individual interfaces without the full ERC165 protocol. ([#3339](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3339)) * `Address`: optimize `functionCall` by calling `functionCallWithValue` directly. ([#3468](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3468)) * `Address`: optimize `functionCall` functions by checking contract size only if there is no returned data. ([#3469](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3469)) diff --git a/contracts/mocks/InitializableMock.sol b/contracts/mocks/InitializableMock.sol index b24db08911c..34040b6e52c 100644 --- a/contracts/mocks/InitializableMock.sol +++ b/contracts/mocks/InitializableMock.sol @@ -13,6 +13,10 @@ contract InitializableMock is Initializable { bool public onlyInitializingRan; uint256 public x; + function isInitializing() public view returns (bool) { + return _isInitializing(); + } + function initialize() public initializer { initializerRan = true; } @@ -75,6 +79,10 @@ contract ChildConstructorInitializableMock is ConstructorInitializableMock { contract ReinitializerMock is Initializable { uint256 public counter; + function getInitializedVersion() public view returns (uint8) { + return _getInitializedVersion(); + } + function initialize() public initializer { doStuff(); } diff --git a/contracts/proxy/utils/Initializable.sol b/contracts/proxy/utils/Initializable.sol index 67a81e0a5f3..81683f002e0 100644 --- a/contracts/proxy/utils/Initializable.sol +++ b/contracts/proxy/utils/Initializable.sol @@ -135,4 +135,18 @@ abstract contract Initializable { emit Initialized(type(uint8).max); } } + + /** + * @dev Internal function that returns the initialized version. Returns `_initialized` + */ + function _getInitializedVersion() internal view returns (uint8) { + return _initialized; + } + + /** + * @dev Internal function that returns the initialized version. Returns `_initializing` + */ + function _isInitializing() internal view returns (bool) { + return _initializing; + } } diff --git a/test/proxy/utils/Initializable.test.js b/test/proxy/utils/Initializable.test.js index 664bd899d24..2c0c147f651 100644 --- a/test/proxy/utils/Initializable.test.js +++ b/test/proxy/utils/Initializable.test.js @@ -20,6 +20,10 @@ contract('Initializable', function (accounts) { it('initializer has not run', async function () { expect(await this.contract.initializerRan()).to.equal(false); }); + + it('_initializing returns false before initialization', async function () { + expect(await this.contract.isInitializing()).to.equal(false); + }); }); describe('after initialize', function () { @@ -31,6 +35,10 @@ contract('Initializable', function (accounts) { expect(await this.contract.initializerRan()).to.equal(true); }); + it('_initializing returns false after initialization', async function () { + expect(await this.contract.isInitializing()).to.equal(false); + }); + it('initializer does not run again', async function () { await expectRevert(this.contract.initialize(), 'Initializable: contract is already initialized'); }); @@ -101,6 +109,13 @@ contract('Initializable', function (accounts) { expect(await this.contract.counter()).to.be.bignumber.equal('2'); }); + it('_getInitializedVersion returns right version', async function () { + await this.contract.initialize(); + expect(await this.contract.getInitializedVersion()).to.be.bignumber.equal('1'); + await this.contract.reinitialize(12); + expect(await this.contract.getInitializedVersion()).to.be.bignumber.equal('12'); + }); + describe('contract locking', function () { it('prevents initialization', async function () { await this.contract.disableInitializers();