-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: revert to custom initializable, make changes from oz 5.0
- Loading branch information
Showing
6 changed files
with
126 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.25; | ||
|
||
import {AccountStorage, getAccountStorage} from "./AccountStorage.sol"; | ||
|
||
abstract contract AccountStorageInitializable { | ||
/** | ||
* @dev The contract is already initialized. | ||
*/ | ||
error InvalidInitialization(); | ||
|
||
/** | ||
* @dev The contract is not initializing. | ||
*/ | ||
error NotInitializing(); | ||
|
||
/** | ||
* @dev Triggered when the contract has been initialized or reinitialized. | ||
*/ | ||
event Initialized(uint64 version); | ||
|
||
/// @notice Modifier to put on function intended to be called only once per implementation | ||
/// @dev Reverts if the contract has already been initialized | ||
modifier initializer() { | ||
AccountStorage storage $ = getAccountStorage(); | ||
|
||
// Cache values to avoid duplicated sloads | ||
bool isTopLevelCall = !$.initializing; | ||
uint64 initialized = $.initialized; | ||
|
||
// Allowed calls: | ||
// - initialSetup: the contract is not in the initializing state and no previous version was | ||
// initialized | ||
// - construction: the contract is initialized at version 1 (no reininitialization) and the | ||
// current contract is just being deployed | ||
bool initialSetup = initialized == 0 && isTopLevelCall; | ||
bool construction = initialized == 1 && address(this).code.length == 0; | ||
|
||
if (!initialSetup && !construction) { | ||
revert InvalidInitialization(); | ||
} | ||
$.initialized = 1; | ||
if (isTopLevelCall) { | ||
$.initializing = true; | ||
} | ||
_; | ||
if (isTopLevelCall) { | ||
$.initializing = false; | ||
emit Initialized(1); | ||
} | ||
} | ||
|
||
/// @notice Internal function to disable calls to initialization functions | ||
/// @dev Reverts if the contract has already been initialized | ||
function _disableInitializers() internal virtual { | ||
AccountStorage storage $ = getAccountStorage(); | ||
if ($.initializing) { | ||
revert InvalidInitialization(); | ||
} | ||
if ($.initialized != type(uint8).max) { | ||
$.initialized = type(uint8).max; | ||
emit Initialized(type(uint8).max); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.19; | ||
|
||
import {Test} from "forge-std/Test.sol"; | ||
import {_ACCOUNT_STORAGE_SLOT, getPermittedCallKey} from "../../src/account/AccountStorage.sol"; | ||
import {AccountStorageInitializable} from "../../src/account/AccountStorageInitializable.sol"; | ||
import {MockDiamondStorageContract} from "../mocks/MockDiamondStorageContract.sol"; | ||
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; | ||
|
||
// Test implementation of AccountStorageInitializable which is contained in UpgradeableModularAccount | ||
contract AccountStorageTest is Test { | ||
MockDiamondStorageContract public impl; | ||
address public proxy; | ||
|
||
function setUp() external { | ||
impl = new MockDiamondStorageContract(); | ||
proxy = address(new ERC1967Proxy(address(impl), "")); | ||
} | ||
|
||
function test_storageSlotImpl() external { | ||
// disable initializers sets value to uint8(max) | ||
assertEq(uint256(vm.load(address(impl), _ACCOUNT_STORAGE_SLOT)), type(uint8).max); | ||
|
||
// should revert if we try to initialize again | ||
vm.expectRevert(AccountStorageInitializable.InvalidInitialization.selector); | ||
impl.initialize(); | ||
} | ||
|
||
function test_storageSlotProxy() external { | ||
// before init, proxy's slot should be empty | ||
assertEq(uint256(vm.load(proxy, _ACCOUNT_STORAGE_SLOT)), uint256(0)); | ||
|
||
MockDiamondStorageContract(proxy).initialize(); | ||
// post init slot should contains: packed(uint8 initialized = 1, bool initializing = 0) | ||
assertEq(uint256(vm.load(proxy, _ACCOUNT_STORAGE_SLOT)), uint256(1)); | ||
} | ||
|
||
function testFuzz_permittedCallKey(address addr, bytes4 selector) public { | ||
bytes24 key = getPermittedCallKey(addr, selector); | ||
assertEq(bytes20(addr), bytes20(key)); | ||
assertEq(bytes4(selector), bytes4(key << 160)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.19; | ||
|
||
import {AccountStorageInitializable} from "../../src/account/AccountStorageInitializable.sol"; | ||
|
||
contract MockDiamondStorageContract is AccountStorageInitializable { | ||
constructor() { | ||
_disableInitializers(); | ||
} | ||
|
||
// solhint-disable-next-line no-empty-blocks | ||
function initialize() external initializer {} | ||
} |