Skip to content

Commit 458fd89

Browse files
committed
refactored tests, all passing
1 parent 14f4c8c commit 458fd89

File tree

7 files changed

+413
-193
lines changed

7 files changed

+413
-193
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.23;
3+
4+
import {Test} from "forge-std/Test.sol";
5+
import {EIP7702Proxy} from "../../src/EIP7702Proxy.sol";
6+
import {CoinbaseSmartWallet} from "../../lib/smart-wallet/src/CoinbaseSmartWallet.sol";
7+
import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
8+
9+
contract CoinbaseImplementationTest is Test {
10+
uint256 constant _EOA_PRIVATE_KEY = 0xA11CE;
11+
address payable _eoa;
12+
13+
uint256 constant _NEW_OWNER_PRIVATE_KEY = 0xB0B;
14+
address payable _newOwner;
15+
16+
CoinbaseSmartWallet wallet;
17+
CoinbaseSmartWallet implementation;
18+
EIP7702Proxy proxy;
19+
bytes4 initSelector;
20+
21+
bytes4 constant ERC1271_MAGIC_VALUE = 0x1626ba7e;
22+
bytes4 constant ERC1271_FAIL_VALUE = 0xffffffff;
23+
24+
function setUp() public {
25+
// Set up test accounts
26+
_eoa = payable(vm.addr(_EOA_PRIVATE_KEY));
27+
_newOwner = payable(vm.addr(_NEW_OWNER_PRIVATE_KEY));
28+
29+
// Deploy Coinbase implementation
30+
implementation = new CoinbaseSmartWallet();
31+
initSelector = CoinbaseSmartWallet.initialize.selector;
32+
33+
// Deploy and setup proxy
34+
proxy = new EIP7702Proxy(address(implementation), initSelector);
35+
bytes memory proxyCode = address(proxy).code;
36+
vm.etch(_eoa, proxyCode);
37+
38+
// Initialize with Coinbase implementation
39+
bytes memory initArgs = _createInitArgs(_newOwner);
40+
bytes memory signature = _signInitData(_EOA_PRIVATE_KEY, initArgs);
41+
EIP7702Proxy(_eoa).initialize(initArgs, signature);
42+
43+
wallet = CoinbaseSmartWallet(payable(_eoa));
44+
}
45+
46+
// ======== Utility Functions ========
47+
function _createInitArgs(
48+
address owner
49+
) internal pure returns (bytes memory) {
50+
bytes[] memory owners = new bytes[](1);
51+
owners[0] = abi.encode(owner);
52+
return abi.encode(owners);
53+
}
54+
55+
function _signInitData(
56+
uint256 signerPk,
57+
bytes memory initArgs
58+
) internal view returns (bytes memory) {
59+
// Use the EOA address in the hash since that's where our proxy lives
60+
bytes32 initHash = keccak256(abi.encode(proxy, initArgs));
61+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, initHash);
62+
return abi.encodePacked(r, s, v);
63+
}
64+
65+
function testCoinbaseInitializeSetsOwner() public {
66+
assertTrue(
67+
wallet.isOwnerAddress(_newOwner),
68+
"New owner should be owner after initialization"
69+
);
70+
}
71+
72+
function testCoinbaseOwnerSignatureValidation() public {
73+
bytes32 testHash = keccak256("test message");
74+
assertTrue(
75+
wallet.isOwnerAddress(_newOwner),
76+
"New owner should be set after initialization"
77+
);
78+
assertEq(
79+
wallet.ownerAtIndex(0),
80+
abi.encode(_newOwner),
81+
"Owner at index 0 should be new owner"
82+
);
83+
84+
bytes memory signature = _createOwnerSignature(
85+
testHash,
86+
address(wallet),
87+
_NEW_OWNER_PRIVATE_KEY,
88+
0 // First owner
89+
);
90+
91+
bytes4 result = wallet.isValidSignature(testHash, signature);
92+
assertEq(
93+
result,
94+
ERC1271_MAGIC_VALUE,
95+
"Should accept valid contract owner signature"
96+
);
97+
}
98+
99+
function testCoinbaseExecuteFunction() public {
100+
address recipient = address(0xBEEF);
101+
uint256 amount = 1 ether;
102+
103+
vm.deal(address(_eoa), amount);
104+
105+
vm.prank(_newOwner);
106+
wallet.execute(
107+
payable(recipient),
108+
amount,
109+
"" // empty calldata for simple transfer
110+
);
111+
112+
assertEq(
113+
recipient.balance,
114+
amount,
115+
"Coinbase wallet execute should transfer ETH"
116+
);
117+
}
118+
119+
function testCoinbaseUpgradeAccess() public {
120+
address newImpl = address(new CoinbaseSmartWallet());
121+
122+
vm.prank(address(0xBAD));
123+
vm.expectRevert(); // Coinbase wallet specific access control
124+
wallet.upgradeToAndCall(newImpl, "");
125+
}
126+
127+
function testCanOnlyBeCalledOnce() public {
128+
bytes memory initArgs = _createInitArgs(_newOwner);
129+
bytes memory signature = _signInitData(_EOA_PRIVATE_KEY, initArgs);
130+
131+
// Try to initialize again
132+
vm.expectRevert(CoinbaseSmartWallet.Initialized.selector);
133+
EIP7702Proxy(_eoa).initialize(initArgs, signature);
134+
}
135+
136+
// ======== Utility Functions ========
137+
138+
function _sign(
139+
uint256 pk,
140+
bytes32 hash
141+
) internal pure returns (bytes memory signature) {
142+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, hash);
143+
return abi.encodePacked(r, s, v);
144+
}
145+
146+
function _createOwnerSignature(
147+
bytes32 message,
148+
address smartWallet,
149+
uint256 ownerPk,
150+
uint256 ownerIndex
151+
) internal view returns (bytes memory) {
152+
bytes32 replaySafeHash = CoinbaseSmartWallet(payable(smartWallet))
153+
.replaySafeHash(message);
154+
bytes memory signature = _sign(ownerPk, replaySafeHash);
155+
return _applySignatureWrapper(ownerIndex, signature);
156+
}
157+
158+
function _applySignatureWrapper(
159+
uint256 ownerIndex,
160+
bytes memory signatureData
161+
) internal pure returns (bytes memory) {
162+
return
163+
abi.encode(
164+
CoinbaseSmartWallet.SignatureWrapper(ownerIndex, signatureData)
165+
);
166+
}
167+
}

test/EIP7702Proxy/delegate.t.sol

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,39 @@ pragma solidity ^0.8.23;
33

44
import {EIP7702ProxyBase} from "../base/EIP7702ProxyBase.sol";
55
import {EIP7702Proxy} from "../../src/EIP7702Proxy.sol";
6-
import {CoinbaseSmartWallet} from "../../lib/smart-wallet/src/CoinbaseSmartWallet.sol";
6+
import {MockImplementation} from "../mocks/MockImplementation.sol";
77

88
contract DelegateTest is EIP7702ProxyBase {
99
function setUp() public override {
1010
super.setUp();
11-
12-
// Initialize the proxy for delegation tests
11+
12+
// Initialize the proxy
1313
bytes memory initArgs = _createInitArgs(_newOwner);
1414
bytes memory signature = _signInitData(_EOA_PRIVATE_KEY, initArgs);
15-
vm.prank(_eoa);
1615
EIP7702Proxy(_eoa).initialize(initArgs, signature);
1716
}
1817

1918
function testBlocksGuardedInitializer() public {
2019
bytes memory initData = abi.encodeWithSelector(
21-
CoinbaseSmartWallet.initialize.selector,
22-
_createInitArgs(_newOwner)
20+
MockImplementation.initialize.selector,
21+
_newOwner
2322
);
2423

2524
vm.expectRevert(EIP7702Proxy.InvalidInitializer.selector);
2625
address(_eoa).call(initData);
2726
}
2827

2928
function testDelegatesReadCall() public {
30-
assertTrue(
31-
CoinbaseSmartWallet(payable(_eoa)).isOwnerAddress(_newOwner),
29+
assertEq(
30+
MockImplementation(payable(_eoa)).owner(),
31+
_newOwner,
3232
"Delegated read call should succeed"
3333
);
3434
}
3535

3636
function testDelegatesWriteCall() public {
37-
// Test a state-changing call
38-
address recipient = address(0xBEEF);
39-
uint256 amount = 1 ether;
40-
41-
// Fund the proxy
42-
vm.deal(address(_eoa), amount);
43-
4437
vm.prank(_newOwner);
45-
CoinbaseSmartWallet(payable(_eoa)).execute(
46-
payable(recipient),
47-
amount,
48-
"" // empty calldata for simple transfer
49-
);
50-
51-
assertEq(
52-
recipient.balance,
53-
amount,
54-
"Delegated write call should transfer ETH"
55-
);
38+
MockImplementation(payable(_eoa)).mockFunction();
39+
// Success is just completing the call without revert
5640
}
57-
}
41+
}

test/EIP7702Proxy/initialize.t.sol

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ pragma solidity ^0.8.23;
33

44
import {EIP7702ProxyBase} from "../base/EIP7702ProxyBase.sol";
55
import {EIP7702Proxy} from "../../src/EIP7702Proxy.sol";
6-
import {CoinbaseSmartWallet} from "../../lib/smart-wallet/src/CoinbaseSmartWallet.sol";
6+
import {MockImplementation, RevertingInitializerMockImplementation} from "../mocks/MockImplementation.sol";
77
import {ECDSA} from "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
8-
import {ERC1967Utils} from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Utils.sol";
98

109
contract InitializeTest is EIP7702ProxyBase {
1110
function testSucceedsWithValidSignature() public {
@@ -14,43 +13,41 @@ contract InitializeTest is EIP7702ProxyBase {
1413

1514
EIP7702Proxy(_eoa).initialize(initArgs, signature);
1615

17-
// Verify initialization through implementation at the EOA's address
18-
CoinbaseSmartWallet wallet = CoinbaseSmartWallet(payable(_eoa));
19-
assertTrue(
20-
wallet.isOwnerAddress(_newOwner),
21-
"New owner should be owner after initialization"
16+
// Verify initialization through implementation
17+
assertEq(
18+
MockImplementation(payable(_eoa)).owner(),
19+
_newOwner,
20+
"Owner should be set after initialization"
2221
);
2322
}
2423

25-
function testRevertsWithInvalidSignature() public {
24+
function testRevertsWithInvalidSignatureLength() public {
2625
bytes memory initArgs = _createInitArgs(_newOwner);
27-
bytes memory signature = hex"deadbeef"; // Invalid signature
26+
bytes memory signature = hex"deadbeef"; // Too short to be valid ECDSA signature
2827

29-
vm.expectRevert(); // Should revert with signature verification error
28+
vm.expectRevert(
29+
abi.encodeWithSignature("ECDSAInvalidSignatureLength(uint256)", 4)
30+
);
3031
EIP7702Proxy(_eoa).initialize(initArgs, signature);
3132
}
3233

33-
function testRevertsWithWrongSigner() public {
34-
// Create signature with different private key
35-
uint256 wrongPk = 0xC0FFEE; // Using a different key than either EOA or new owner
36-
34+
function testRevertsWithInvalidSignature() public {
3735
bytes memory initArgs = _createInitArgs(_newOwner);
38-
bytes32 initHash = keccak256(abi.encode(_eoa, initArgs));
39-
(uint8 v, bytes32 r, bytes32 s) = vm.sign(wrongPk, initHash);
40-
bytes memory signature = abi.encodePacked(r, s, v);
36+
// 65 bytes of invalid signature data
37+
bytes memory signature = new bytes(65);
4138

42-
vm.expectRevert(); // Should revert with signature verification error
39+
vm.expectRevert(abi.encodeWithSignature("ECDSAInvalidSignature()"));
4340
EIP7702Proxy(_eoa).initialize(initArgs, signature);
4441
}
4542

46-
function testCanOnlyBeCalledOnce() public {
43+
function testRevertsWithWrongSigner() public {
44+
uint256 wrongPk = 0xC0FFEE;
4745
bytes memory initArgs = _createInitArgs(_newOwner);
48-
bytes memory signature = _signInitData(_EOA_PRIVATE_KEY, initArgs);
49-
50-
EIP7702Proxy(_eoa).initialize(initArgs, signature);
46+
bytes32 initHash = keccak256(abi.encode(_eoa, initArgs));
47+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(wrongPk, initHash);
48+
bytes memory signature = abi.encodePacked(r, s, v);
5149

52-
// Try to initialize again
53-
vm.expectRevert(CoinbaseSmartWallet.Initialized.selector);
50+
vm.expectRevert(EIP7702Proxy.InvalidSignature.selector);
5451
EIP7702Proxy(_eoa).initialize(initArgs, signature);
5552
}
5653

0 commit comments

Comments
 (0)