-
Notifications
You must be signed in to change notification settings - Fork 12.4k
Allow Initializable versions greater than 256 #4460
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8bcba2f
6df8c25
2a6e2f6
54395da
eabc9c0
9777b62
aa0bb6f
bae9786
1b27b49
f106f03
eb76991
4d6d205
5935090
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| 'openzeppelin-solidity': major | ||
| --- | ||
|
|
||
| `Initializable`: Use the namespaced storage pattern to avoid putting critical variables in slot 0. Allow reinitializer versions greater than 256. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol) | ||
|
|
||
| pragma solidity ^0.8.19; | ||
| pragma solidity ^0.8.20; | ||
|
|
||
| /** | ||
| * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed | ||
|
|
@@ -55,14 +55,27 @@ pragma solidity ^0.8.19; | |
| */ | ||
| abstract contract Initializable { | ||
| /** | ||
| * @dev Indicates that the contract has been initialized. | ||
| * @dev Storage of the initializable contract. | ||
| * | ||
| * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions | ||
| * when using with upgradeable contracts. | ||
| * | ||
| * @custom:storage-location erc7201:openzeppelin.storage.Initializable | ||
| */ | ||
| uint8 private _initialized; | ||
| struct InitializableStorage { | ||
| /** | ||
| * @dev Indicates that the contract has been initialized. | ||
| */ | ||
| uint64 _initialized; | ||
| /** | ||
| * @dev Indicates that the contract is in the process of being initialized. | ||
| */ | ||
| bool _initializing; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Indicates that the contract is in the process of being initialized. | ||
| */ | ||
| bool private _initializing; | ||
| // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) | ||
| bytes32 private constant _INITIALIZABLE_STORAGE = | ||
| 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0e; | ||
Amxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * @dev The contract is already initialized. | ||
|
|
@@ -77,7 +90,7 @@ abstract contract Initializable { | |
| /** | ||
| * @dev Triggered when the contract has been initialized or reinitialized. | ||
| */ | ||
| event Initialized(uint8 version); | ||
| event Initialized(uint64 version); | ||
|
|
||
| /** | ||
| * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, | ||
|
|
@@ -89,17 +102,20 @@ abstract contract Initializable { | |
| * Emits an {Initialized} event. | ||
| */ | ||
| modifier initializer() { | ||
| bool isTopLevelCall = !_initializing; | ||
| if (!(isTopLevelCall && _initialized < 1) && !(address(this).code.length == 0 && _initialized == 1)) { | ||
| // solhint-disable-next-line var-name-mixedcase | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are several of these for the |
||
| InitializableStorage storage $ = _getInitializableStorage(); | ||
|
|
||
| bool isTopLevelCall = !$._initializing; | ||
| if (!(isTopLevelCall && $._initialized < 1) && !(address(this).code.length == 0 && $._initialized == 1)) { | ||
| revert AlreadyInitialized(); | ||
| } | ||
| _initialized = 1; | ||
| $._initialized = 1; | ||
| if (isTopLevelCall) { | ||
| _initializing = true; | ||
| $._initializing = true; | ||
| } | ||
| _; | ||
| if (isTopLevelCall) { | ||
| _initializing = false; | ||
| $._initializing = false; | ||
| emit Initialized(1); | ||
| } | ||
| } | ||
|
|
@@ -122,14 +138,17 @@ abstract contract Initializable { | |
| * | ||
| * Emits an {Initialized} event. | ||
| */ | ||
| modifier reinitializer(uint8 version) { | ||
| if (_initializing || _initialized >= version) { | ||
| modifier reinitializer(uint64 version) { | ||
| // solhint-disable-next-line var-name-mixedcase | ||
| InitializableStorage storage $ = _getInitializableStorage(); | ||
|
|
||
| if ($._initializing || $._initialized >= version) { | ||
| revert AlreadyInitialized(); | ||
| } | ||
| _initialized = version; | ||
| _initializing = true; | ||
| $._initialized = version; | ||
| $._initializing = true; | ||
| _; | ||
| _initializing = false; | ||
| $._initializing = false; | ||
| emit Initialized(version); | ||
| } | ||
|
|
||
|
|
@@ -146,7 +165,7 @@ abstract contract Initializable { | |
| * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. | ||
| */ | ||
| function _checkInitializing() internal view virtual { | ||
| if (!_initializing) { | ||
| if (!_isInitializing()) { | ||
| revert NotInitializing(); | ||
| } | ||
| } | ||
|
|
@@ -160,26 +179,39 @@ abstract contract Initializable { | |
| * Emits an {Initialized} event the first time it is successfully executed. | ||
| */ | ||
| function _disableInitializers() internal virtual { | ||
| if (_initializing) { | ||
| // solhint-disable-next-line var-name-mixedcase | ||
| InitializableStorage storage $ = _getInitializableStorage(); | ||
|
|
||
| if ($._initializing) { | ||
| revert AlreadyInitialized(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is not changed by this PR, but I'm wondering if that is the right error to trigger.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with you but we may decide on this once we decide to tackle #3950 as well |
||
| } | ||
| if (_initialized != type(uint8).max) { | ||
| _initialized = type(uint8).max; | ||
| emit Initialized(type(uint8).max); | ||
| if ($._initialized != type(uint64).max) { | ||
| $._initialized = type(uint64).max; | ||
| emit Initialized(type(uint64).max); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @dev Returns the highest version that has been initialized. See {reinitializer}. | ||
| */ | ||
| function _getInitializedVersion() internal view returns (uint8) { | ||
| return _initialized; | ||
| function _getInitializedVersion() internal view returns (uint64) { | ||
| return _getInitializableStorage()._initialized; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. | ||
| */ | ||
| function _isInitializing() internal view returns (bool) { | ||
| return _initializing; | ||
| return _getInitializableStorage()._initializing; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Returns a pointer to the storage namespace. | ||
| */ | ||
| // solhint-disable-next-line var-name-mixedcase | ||
Amxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| function _getInitializableStorage() private pure returns (InitializableStorage storage $) { | ||
| assembly { | ||
| $.slot := _INITIALIZABLE_STORAGE | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| const MAX_UINT48 = web3.utils.toBN(1).shln(48).subn(1).toString(); | ||
| const MAX_UINT64 = web3.utils.toBN(1).shln(64).subn(1).toString(); | ||
|
|
||
| module.exports = { | ||
| MAX_UINT48, | ||
| MAX_UINT64, | ||
| }; |
Uh oh!
There was an error while loading. Please reload this page.