Allow Initializable versions greater than 256 (#4460)

Co-authored-by: Francisco <fg@frang.io>
This commit is contained in:
Ernesto García
2023-08-07 15:59:19 -06:00
committed by GitHub
parent 54a235f895
commit 70578bbb44
6 changed files with 76 additions and 32 deletions

View File

@ -55,14 +55,27 @@ pragma solidity ^0.8.20;
*/
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;
/**
* @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
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();
}
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
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := _INITIALIZABLE_STORAGE
}
}
}