@@ -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