Skip to content

Commit 8552e64

Browse files
committed
have to track initialization separately
1 parent 94382ab commit 8552e64

File tree

3 files changed

+57
-2
lines changed

3 files changed

+57
-2
lines changed

src/EIP7702Proxy.sol

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {Proxy} from "openzeppelin-contracts/contracts/proxy/Proxy.sol";
55
import {ERC1967Utils} from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Utils.sol";
66
import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
77
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";
8+
import {StorageSlot} from "openzeppelin-contracts/contracts/utils/StorageSlot.sol";
89

910
/// @title EIP7702Proxy
1011
/// @notice Proxy contract designed for EIP-7702 smart accounts
@@ -24,6 +25,10 @@ contract EIP7702Proxy is Proxy {
2425
/// @notice Function selector on the implementation that is guarded from direct calls
2526
bytes4 immutable guardedInitializer;
2627

28+
/// @dev Storage slot with the initialized flag
29+
bytes32 internal constant INITIALIZED_SLOT =
30+
bytes32(uint256(keccak256("EIP7702Proxy.initialized")) - 1);
31+
2732
/// @notice Emitted when the implementation is upgraded
2833
event Upgraded(address indexed implementation);
2934

@@ -54,7 +59,7 @@ contract EIP7702Proxy is Proxy {
5459

5560
/// @dev Checks if proxy has been initialized by comparing implementation slot
5661
function _isInitialized() internal view returns (bool) {
57-
return _implementation() == initialImplementation;
62+
return StorageSlot.getBooleanSlot(INITIALIZED_SLOT).value;
5863
}
5964

6065
/// @notice Initializes the proxy and implementation with a signed payload
@@ -72,7 +77,10 @@ contract EIP7702Proxy is Proxy {
7277
address recovered = ECDSA.recover(hash, signature);
7378
if (recovered != address(this)) revert InvalidSignature();
7479

75-
// Set the ERC-1967 implementation slot, emit Upgraded event, call the initializer on the initial implementation
80+
// Set initialized flag before upgrading
81+
StorageSlot.getBooleanSlot(INITIALIZED_SLOT).value = true;
82+
83+
// Set the ERC-1967 implementation slot, emit Upgraded event, call the initializer
7684
ERC1967Utils.upgradeToAndCall(
7785
initialImplementation,
7886
abi.encodePacked(guardedInitializer, args)
@@ -91,6 +99,9 @@ contract EIP7702Proxy is Proxy {
9199
bytes32 hash,
92100
bytes calldata signature
93101
) external returns (bytes4) {
102+
// Check initialization status first
103+
if (!_isInitialized()) revert ProxyNotInitialized();
104+
94105
// First try delegatecall to implementation
95106
(bool success, bytes memory result) = _implementation().delegatecall(
96107
msg.data

test/EIP7702Proxy/delegate.t.sol

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,35 @@ contract DelegateTest is EIP7702ProxyBase {
9393
(bool success, ) = address(uninitProxy).call(arbitraryCalldata);
9494
assertFalse(success, "Low-level call should fail");
9595
}
96+
97+
function test_continues_delegating_afterUpgrade() public {
98+
// Setup will have already initialized the proxy with initial implementation
99+
100+
// Deploy a new implementation
101+
MockImplementation newImplementation = new MockImplementation();
102+
103+
// Upgrade to the new implementation
104+
vm.prank(_newOwner);
105+
MockImplementation(_eoa).upgradeToAndCall(
106+
address(newImplementation),
107+
""
108+
);
109+
110+
// Verify the implementation was changed
111+
assertEq(
112+
_getERC1967Implementation(_eoa),
113+
address(newImplementation),
114+
"Implementation should be updated"
115+
);
116+
117+
// Try to make a call through the proxy - this should work but might fail
118+
vm.prank(_newOwner);
119+
MockImplementation(_eoa).mockFunction();
120+
121+
// Verify the call succeeded
122+
assertTrue(
123+
MockImplementation(_eoa).mockFunctionCalled(),
124+
"Should be able to call through proxy after upgrade"
125+
);
126+
}
96127
}

test/EIP7702Proxy/isValidSignature.t.sol

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,19 @@ contract FailingImplementationTest is IsValidSignatureTestBase {
114114
);
115115
assertEq(result, ERC1271_FAIL_VALUE, "Should reject empty signature");
116116
}
117+
118+
function test_reverts_whenCalledBeforeInitialization() public {
119+
// Deploy a fresh proxy without initializing
120+
address payable uninitProxy = payable(makeAddr("uninitProxy"));
121+
_deployProxy(uninitProxy);
122+
123+
// Try to call isValidSignature
124+
bytes32 hash = keccak256("test message");
125+
bytes memory signature = new bytes(65);
126+
127+
vm.expectRevert(EIP7702Proxy.ProxyNotInitialized.selector);
128+
EIP7702Proxy(uninitProxy).isValidSignature(hash, signature);
129+
}
117130
}
118131

119132
/**

0 commit comments

Comments
 (0)