Skip to content

Commit 10061d0

Browse files
committed
potential separate contract approach for setting 1967 slot
1 parent 9c0c9e6 commit 10061d0

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

src/EIP7702Proxy.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,6 @@ contract EIP7702Proxy is Proxy {
144144
function _implementation() internal view override returns (address) {
145145
return ERC1967Utils.getImplementation();
146146
}
147+
148+
receive() external payable {}
147149
}

src/Implementation1967Resetter.sol

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.23;
3+
4+
import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
5+
import {ERC1967Utils} from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Utils.sol";
6+
7+
/**
8+
* @title Implementation1967Resetter
9+
* @notice Contract deployed via EIP-7702 to reset the ERC-1967 implementation slot
10+
* @dev This contract is meant to be deployed temporarily at an EOA's address to reset
11+
* the implementation slot before re-delegating back to the EIP7702Proxy
12+
*/
13+
contract Implementation1967Resetter {
14+
/// @notice Emitted when signature verification fails
15+
error InvalidSignature();
16+
17+
/// @notice Emitted when nonce has already been used
18+
error NonceAlreadyUsed();
19+
20+
/// @notice Address of the global nonce tracker contract
21+
address public immutable nonceTracker;
22+
23+
constructor(address _nonceTracker) {
24+
if (_nonceTracker == address(0)) revert("Zero address");
25+
nonceTracker = _nonceTracker;
26+
}
27+
28+
/**
29+
* @notice Resets the ERC-1967 implementation slot after signature verification
30+
* @param newImplementation The implementation address to set
31+
* @param nonce The nonce for this operation (verified against NonceTracker)
32+
* @param signature The EOA signature authorizing this change
33+
*/
34+
function resetImplementation(
35+
address newImplementation,
36+
uint256 nonce,
37+
bytes calldata signature
38+
) external {
39+
// Verify nonce hasn't been used
40+
if (
41+
!INonceTracker(nonceTracker).verifyAndUseNonce(address(this), nonce)
42+
) {
43+
revert NonceAlreadyUsed();
44+
}
45+
46+
// Only need to sign over the implementation and nonce
47+
bytes32 hash = keccak256(abi.encode(newImplementation, nonce));
48+
49+
// Verify signature is from this address (the EOA)
50+
address recovered = ECDSA.recover(hash, signature);
51+
if (recovered != address(this)) {
52+
revert InvalidSignature();
53+
}
54+
55+
// Update the implementation slot
56+
ERC1967Utils.upgradeToAndCall(
57+
newImplementation,
58+
"" // No initialization needed
59+
);
60+
}
61+
}
62+
63+
/**
64+
* @title INonceTracker
65+
* @notice Interface for the nonce tracking contract
66+
*/
67+
interface INonceTracker {
68+
function verifyAndUseNonce(
69+
address account,
70+
uint256 nonce
71+
) external returns (bool);
72+
}

src/NonceTracker.sol

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.23;
3+
4+
/**
5+
* @title NonceTracker
6+
* @notice Global nonce tracking for EIP-7702 accounts
7+
* @dev Maintains nonce state separately from mutable EOA storage
8+
*/
9+
contract NonceTracker {
10+
/// @notice Mapping of account => highest used nonce
11+
mapping(address => uint256) private highestNonce;
12+
13+
/**
14+
* @notice Verifies and marks a nonce as used for an account
15+
* @param account The account using the nonce
16+
* @param nonce The nonce to verify and use
17+
* @return success True if nonce was valid and is now used
18+
*/
19+
function verifyAndUseNonce(
20+
address account,
21+
uint256 nonce
22+
) external returns (bool) {
23+
uint256 current = highestNonce[account];
24+
25+
// Nonce must be strictly increasing
26+
if (nonce <= current) {
27+
return false;
28+
}
29+
30+
highestNonce[account] = nonce;
31+
return true;
32+
}
33+
34+
/**
35+
* @notice Gets the highest used nonce for an account
36+
* @param account The account to check
37+
* @return The highest nonce used so far
38+
*/
39+
function getHighestNonce(address account) external view returns (uint256) {
40+
return highestNonce[account];
41+
}
42+
}

0 commit comments

Comments
 (0)