Skip to content

Commit 67c6ddd

Browse files
committed
draft approach to resetImplementation directly on eip7702Proxy
1 parent 10061d0 commit 67c6ddd

File tree

1 file changed

+61
-1
lines changed

1 file changed

+61
-1
lines changed

src/EIP7702Proxy.sol

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,33 @@ contract EIP7702Proxy is Proxy {
4242
/// @notice Emitted when constructor arguments are zero
4343
error ZeroValueConstructorArguments();
4444

45+
/// @notice Emitted when nonce has already been used
46+
error NonceAlreadyUsed();
47+
48+
/// @notice Address of the global nonce tracker for reset operations
49+
address public immutable nonceTracker;
50+
51+
/// @notice Emitted when the implementation is reset
52+
event ImplementationReset(address newImplementation);
53+
4554
/// @notice Initializes the proxy with an initial implementation and guarded initializer
4655
/// @param implementation The initial implementation address
4756
/// @param initializer The selector of the `guardedInitializer` function
48-
constructor(address implementation, bytes4 initializer) {
57+
/// @param _nonceTracker The address of the global nonce tracker for reset operations
58+
constructor(
59+
address implementation,
60+
bytes4 initializer,
61+
address _nonceTracker
62+
) {
4963
if (implementation == address(0))
5064
revert ZeroValueConstructorArguments();
5165
if (initializer == bytes4(0)) revert ZeroValueConstructorArguments();
66+
if (_nonceTracker == address(0)) revert ZeroValueConstructorArguments();
5267

5368
proxy = address(this);
5469
initialImplementation = implementation;
5570
guardedInitializer = initializer;
71+
nonceTracker = _nonceTracker;
5672
}
5773

5874
/// @dev Checks if proxy has been initialized by checking the initialized flag
@@ -146,4 +162,48 @@ contract EIP7702Proxy is Proxy {
146162
}
147163

148164
receive() external payable {}
165+
166+
/**
167+
* @notice Resets the ERC-1967 implementation slot after signature verification
168+
* @dev Uses raw hash (no Ethereum signed message prefix) to prevent phishing
169+
* @param newImplementation The implementation address to set
170+
* @param nonce The nonce for this operation (verified against NonceTracker)
171+
* @param signature The EOA signature authorizing this change
172+
*/
173+
function resetImplementation(
174+
address newImplementation,
175+
uint256 nonce,
176+
bytes calldata signature
177+
) external {
178+
// Verify nonce hasn't been used
179+
if (
180+
!INonceTracker(nonceTracker).verifyAndUseNonce(address(this), nonce)
181+
) {
182+
revert NonceAlreadyUsed();
183+
}
184+
185+
// Raw hash without Ethereum signed message prefix
186+
bytes32 hash = keccak256(abi.encode(newImplementation, nonce));
187+
188+
// Verify signature is from this address (the EOA)
189+
address recovered = ECDSA.recover(hash, signature);
190+
if (recovered != address(this)) {
191+
revert InvalidSignature();
192+
}
193+
194+
// Reset the implementation slot
195+
ERC1967Utils.upgradeToAndCall(
196+
newImplementation,
197+
"" // No initialization needed
198+
);
199+
200+
emit ImplementationReset(newImplementation);
201+
}
202+
}
203+
204+
interface INonceTracker {
205+
function verifyAndUseNonce(
206+
address account,
207+
uint256 nonce
208+
) external returns (bool);
149209
}

0 commit comments

Comments
 (0)