Skip to content

Commit eb82ffe

Browse files
authored
Add signature expiry (#54)
* add signature expiry * add expiry to typehashes and a few locations in tests * update expiry comparison to >=
1 parent 371a141 commit eb82ffe

File tree

7 files changed

+155
-46
lines changed

7 files changed

+155
-46
lines changed

src/EIP7702Proxy.sol

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ contract EIP7702Proxy is Proxy {
2424

2525
/// @notice Typehash for setting implementation
2626
bytes32 internal constant _IMPLEMENTATION_SET_TYPEHASH = keccak256(
27-
"EIP7702ProxyImplementationSet(uint256 chainId,address proxy,uint256 nonce,address currentImplementation,address newImplementation,bytes callData,address validator)"
27+
"EIP7702ProxyImplementationSet(uint256 chainId,address proxy,uint256 nonce,address currentImplementation,address newImplementation,bytes callData,address validator,uint256 expiry)"
2828
);
2929

3030
/// @notice Address of the global nonce tracker for initialization
@@ -45,6 +45,9 @@ contract EIP7702Proxy is Proxy {
4545
/// @notice Validator did not return ACCOUNT_STATE_VALIDATION_SUCCESS
4646
error InvalidValidation();
4747

48+
/// @notice Signature has expired
49+
error SignatureExpired();
50+
4851
/// @notice Initializes the proxy with a default receiver implementation and an external nonce tracker
4952
///
5053
/// @param nonceTracker_ The address of the nonce tracker contract
@@ -72,9 +75,12 @@ contract EIP7702Proxy is Proxy {
7275
address newImplementation,
7376
bytes calldata callData,
7477
address validator,
78+
uint256 expiry,
7579
bytes calldata signature,
7680
bool allowCrossChainReplay
7781
) external {
82+
if (block.timestamp >= expiry) revert SignatureExpired();
83+
7884
// Construct hash using typehash to prevent signature collisions
7985
bytes32 hash = keccak256(
8086
abi.encode(
@@ -85,7 +91,8 @@ contract EIP7702Proxy is Proxy {
8591
ERC1967Utils.getImplementation(),
8692
newImplementation,
8793
keccak256(callData),
88-
validator
94+
validator,
95+
expiry
8996
)
9097
);
9198

test/CoinbaseSmartWalletValidator.t.sol

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ contract CoinbaseSmartWalletValidatorTest is Test {
2727
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
2828

2929
bytes32 _IMPLEMENTATION_SET_TYPEHASH = keccak256(
30-
"EIP7702ProxyImplementationSet(uint256 chainId,address proxy,uint256 nonce,address currentImplementation,address newImplementation,bytes callData,address validator)"
30+
"EIP7702ProxyImplementationSet(uint256 chainId,address proxy,uint256 nonce,address currentImplementation,address newImplementation,bytes callData,address validator,uint256 expiry)"
3131
);
3232

3333
function setUp() public {
@@ -58,7 +58,9 @@ contract CoinbaseSmartWalletValidatorTest is Test {
5858
_signSetImplementationData(_EOA_PRIVATE_KEY, initArgs, address(_implementation), address(_validator));
5959

6060
// Should not revert
61-
EIP7702Proxy(_eoa).setImplementation(address(_implementation), initArgs, address(_validator), signature, true);
61+
EIP7702Proxy(_eoa).setImplementation(
62+
address(_implementation), initArgs, address(_validator), type(uint256).max, signature, true
63+
);
6264
}
6365

6466
function test_succeeds_whenWalletHasMultipleOwners() public {
@@ -73,7 +75,9 @@ contract CoinbaseSmartWalletValidatorTest is Test {
7375
_signSetImplementationData(_EOA_PRIVATE_KEY, initArgs, address(_implementation), address(_validator));
7476

7577
// Should not revert
76-
EIP7702Proxy(_eoa).setImplementation(address(_implementation), initArgs, address(_validator), signature, true);
78+
EIP7702Proxy(_eoa).setImplementation(
79+
address(_implementation), initArgs, address(_validator), type(uint256).max, signature, true
80+
);
7781
}
7882

7983
function test_reverts_whenWalletHasNoOwners() public {
@@ -84,15 +88,19 @@ contract CoinbaseSmartWalletValidatorTest is Test {
8488
_signSetImplementationData(_EOA_PRIVATE_KEY, initArgs, address(_implementation), address(_validator));
8589

8690
vm.expectRevert(CoinbaseSmartWalletValidator.Unintialized.selector);
87-
EIP7702Proxy(_eoa).setImplementation(address(_implementation), initArgs, address(_validator), signature, true);
91+
EIP7702Proxy(_eoa).setImplementation(
92+
address(_implementation), initArgs, address(_validator), type(uint256).max, signature, true
93+
);
8894
}
8995

9096
function test_succeeds_whenWalletHadOwnersButLastOwnerRemoved() public {
9197
// First initialize the wallet with an owner
9298
bytes memory initArgs = _createInitArgs(_newOwner);
9399
bytes memory signature =
94100
_signSetImplementationData(_EOA_PRIVATE_KEY, initArgs, address(_implementation), address(_validator));
95-
EIP7702Proxy(_eoa).setImplementation(address(_implementation), initArgs, address(_validator), signature, true);
101+
EIP7702Proxy(_eoa).setImplementation(
102+
address(_implementation), initArgs, address(_validator), type(uint256).max, signature, true
103+
);
96104

97105
// Now remove the owner through the wallet interface
98106
vm.prank(_newOwner);
@@ -123,7 +131,9 @@ contract CoinbaseSmartWalletValidatorTest is Test {
123131
vm.expectRevert(
124132
abi.encodeWithSelector(IAccountStateValidator.InvalidImplementation.selector, address(_implementation))
125133
);
126-
EIP7702Proxy(_eoa).setImplementation(address(_implementation), initArgs, address(validator), signature, true);
134+
EIP7702Proxy(_eoa).setImplementation(
135+
address(_implementation), initArgs, address(validator), type(uint256).max, signature, true
136+
);
127137
}
128138

129139
// Helper functions from coinbaseImplementation.t.sol
@@ -158,7 +168,8 @@ contract CoinbaseSmartWalletValidatorTest is Test {
158168
_getERC1967Implementation(address(_eoa)),
159169
address(implementation),
160170
keccak256(initArgs),
161-
address(validator)
171+
address(validator),
172+
type(uint256).max // default to max expiry
162173
)
163174
);
164175
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, initHash);

test/EIP7702Proxy/coinbaseImplementation.t.sol

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ contract CoinbaseImplementationTest is Test {
3838
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
3939

4040
bytes32 _IMPLEMENTATION_SET_TYPEHASH = keccak256(
41-
"EIP7702ProxyImplementationSet(uint256 chainId,address proxy,uint256 nonce,address currentImplementation,address newImplementation,bytes callData,address validator)"
41+
"EIP7702ProxyImplementationSet(uint256 chainId,address proxy,uint256 nonce,address currentImplementation,address newImplementation,bytes callData,address validator,uint256 expiry)"
4242
);
4343

4444
function setUp() public virtual {
@@ -72,6 +72,7 @@ contract CoinbaseImplementationTest is Test {
7272
address(_cbswImplementation),
7373
initArgs,
7474
address(_cbswValidator),
75+
type(uint256).max,
7576
signature,
7677
true // Allow cross-chain replay for tests
7778
);
@@ -107,7 +108,8 @@ contract CoinbaseImplementationTest is Test {
107108
_getERC1967Implementation(address(_eoa)),
108109
address(_cbswImplementation),
109110
keccak256(initArgs),
110-
address(_cbswValidator)
111+
address(_cbswValidator),
112+
type(uint256).max // default to max expiry
111113
)
112114
);
113115
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, initHash);
@@ -233,7 +235,7 @@ contract CoinbaseImplementationTest is Test {
233235

234236
vm.expectRevert(CoinbaseSmartWallet.Initialized.selector);
235237
EIP7702Proxy(_eoa).setImplementation(
236-
address(_cbswImplementation), initArgs, address(_cbswValidator), signature, true
238+
address(_cbswImplementation), initArgs, address(_cbswValidator), type(uint256).max, signature, true
237239
);
238240
}
239241
}

test/EIP7702Proxy/delegate.t.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ contract DelegateTest is EIP7702ProxyBase {
6464
address(newImplementation),
6565
"", // no init data needed
6666
address(newImplementationValidator),
67+
type(uint256).max,
6768
signature,
6869
true
6970
);

test/EIP7702Proxy/isValidSignature.t.sol

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ contract FailingImplementationTest is IsValidSignatureTestBase {
9797
address(_implementation),
9898
initArgs,
9999
address(_validator),
100+
type(uint256).max,
100101
signature,
101102
true // Allow cross-chain replay for tests
102103
);
@@ -196,8 +197,9 @@ contract SucceedingImplementationTest is IsValidSignatureTestBase {
196197
address(_validator)
197198
);
198199

199-
EIP7702Proxy(_eoa).setImplementation(address(_implementation), initArgs, address(_validator), signature, true);
200-
200+
EIP7702Proxy(_eoa).setImplementation(
201+
address(_implementation), initArgs, address(_validator), type(uint256).max, signature, true
202+
);
201203
super.setUp();
202204
}
203205

@@ -240,7 +242,9 @@ contract RevertingImplementationTest is IsValidSignatureTestBase {
240242
address(_validator)
241243
);
242244

243-
EIP7702Proxy(_eoa).setImplementation(address(_implementation), initArgs, address(_validator), signature, true);
245+
EIP7702Proxy(_eoa).setImplementation(
246+
address(_implementation), initArgs, address(_validator), type(uint256).max, signature, true
247+
);
244248

245249
super.setUp();
246250
}
@@ -297,7 +301,9 @@ contract ExtraDataTest is IsValidSignatureTestBase {
297301
address(_validator)
298302
);
299303

300-
EIP7702Proxy(_eoa).setImplementation(address(_implementation), initArgs, address(_validator), signature, true);
304+
EIP7702Proxy(_eoa).setImplementation(
305+
address(_implementation), initArgs, address(_validator), type(uint256).max, signature, true
306+
);
301307

302308
super.setUp();
303309
}

0 commit comments

Comments
 (0)