Skip to content

Commit a87a68f

Browse files
committed
EnsoWalletV2 tests
1 parent 5a12f5b commit a87a68f

File tree

9 files changed

+745
-0
lines changed

9 files changed

+745
-0
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity ^0.8.20;
3+
4+
import { EnsoWalletV2 } from "../../../../../src/wallet/EnsoWalletV2.sol";
5+
import { EnsoWalletV2Factory } from "../../../../../src/wallet/EnsoWalletV2Factory.sol";
6+
7+
import { MockERC1155 } from "../../../../mocks/MockERC1155.sol";
8+
import { MockERC20 } from "../../../../mocks/MockERC20.sol";
9+
import { MockERC721 } from "../../../../mocks/MockERC721.sol";
10+
11+
import { Test } from "forge-std-1.9.7/Test.sol";
12+
import { IERC1155 } from "openzeppelin-contracts/token/ERC1155/IERC1155.sol";
13+
14+
abstract contract EnsoWalletV2_Unit_Concrete_Test is Test {
15+
address payable internal constant EOA_1 = payable(0xE150e171dDf7ef6785e2c6fBBbE9eCd0f2f63682);
16+
bytes32 internal constant EOA_1_PK = 0x74dc97524c0473f102953ebfe8bbec30f0e9cd304703ed7275c708921deaab3b;
17+
18+
address payable internal s_deployer;
19+
address payable internal s_owner;
20+
address payable internal s_user;
21+
address payable internal s_account1;
22+
address payable internal s_account2;
23+
24+
EnsoWalletV2 internal s_walletImplementation;
25+
EnsoWalletV2Factory internal s_walletFactory;
26+
EnsoWalletV2 internal s_wallet;
27+
28+
MockERC20 internal s_erc20;
29+
MockERC721 internal s_erc721;
30+
MockERC1155 internal s_erc1155;
31+
32+
function setUp() public virtual {
33+
s_deployer = payable(vm.addr(1));
34+
vm.deal(s_deployer, 1000 ether);
35+
vm.label(s_deployer, "Deployer");
36+
37+
s_owner = payable(vm.addr(2));
38+
vm.deal(s_owner, 1000 ether);
39+
vm.label(s_owner, "Owner");
40+
41+
s_user = payable(vm.addr(3));
42+
vm.deal(s_user, 1000 ether);
43+
vm.label(s_user, "User");
44+
45+
s_account1 = payable(vm.addr(5));
46+
vm.deal(s_account1, 1000 ether);
47+
vm.label(s_account1, "Account 1");
48+
49+
s_account2 = payable(vm.addr(6));
50+
vm.deal(s_account2, 1000 ether);
51+
vm.label(s_account2, "Account 2");
52+
53+
vm.startPrank(s_deployer);
54+
55+
// Deploy implementation
56+
s_walletImplementation = new EnsoWalletV2();
57+
vm.label(address(s_walletImplementation), "EnsoWalletV2Implementation");
58+
59+
// Deploy factory
60+
s_walletFactory = new EnsoWalletV2Factory(address(s_walletImplementation));
61+
vm.label(address(s_walletFactory), "EnsoWalletV2Factory");
62+
63+
// Deploy mock tokens
64+
s_erc20 = new MockERC20("Mock ERC20", "MERC20");
65+
vm.label(address(s_erc20), "MockERC20");
66+
67+
s_erc721 = new MockERC721("Mock ERC721", "MERC721");
68+
vm.label(address(s_erc721), "MockERC721");
69+
70+
s_erc1155 = new MockERC1155("Mock ERC1155");
71+
vm.label(address(s_erc1155), "MockERC1155");
72+
73+
vm.stopPrank();
74+
}
75+
76+
function _deployWallet(address owner) internal returns (EnsoWalletV2 wallet) {
77+
// vm.startPrank(s_factory);
78+
wallet = EnsoWalletV2(payable(s_walletFactory.deploy(owner)));
79+
// vm.stopPrank();
80+
}
81+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity ^0.8.20;
3+
4+
import { EnsoWalletV2 } from "../../../../../src/wallet/EnsoWalletV2.sol";
5+
import { EnsoWalletV2_Unit_Concrete_Test } from "./EnsoWalletV2.t.sol";
6+
7+
contract Target {
8+
function func() external pure returns (uint256) {
9+
return 42;
10+
}
11+
12+
function functionWithValue(uint256 value) external payable returns (uint256) {
13+
return value;
14+
}
15+
16+
function revert() external pure {
17+
revert("Test revert");
18+
}
19+
}
20+
21+
contract EnsoWalletV2_Execute_Unit_Concrete_Test is EnsoWalletV2_Unit_Concrete_Test {
22+
Target internal s_target;
23+
24+
function setUp() public override {
25+
super.setUp();
26+
27+
s_target = new Target();
28+
vm.label(address(s_target), "Target");
29+
30+
s_wallet = _deployWallet(s_owner);
31+
}
32+
33+
function test_WhenValidCall() external {
34+
// it should execute call successfully
35+
vm.startPrank(s_owner);
36+
bool success = s_wallet.execute(address(s_target), 0, abi.encodeWithSelector(Target.func.selector));
37+
38+
assertTrue(success);
39+
}
40+
41+
function test_WhenCallWithValue() external {
42+
// it should execute call with value
43+
uint256 value = 1 ether;
44+
45+
vm.startPrank(s_owner);
46+
bool success = s_wallet.execute{ value: value }(
47+
address(s_target), value, abi.encodeWithSelector(Target.functionWithValue.selector, value)
48+
);
49+
50+
assertTrue(success);
51+
}
52+
53+
function test_RevertWhen_TargetReverts() external {
54+
// it should revert when target call reverts
55+
vm.startPrank(s_owner);
56+
bool success = s_wallet.execute(address(s_target), 0, abi.encodeWithSelector(Target.revert.selector));
57+
58+
assertFalse(success);
59+
}
60+
61+
function test_RevertWhen_NotOwner() external {
62+
// it should revert when not called by owner
63+
vm.startPrank(s_user);
64+
vm.expectRevert(abi.encodeWithSelector(EnsoWalletV2.InvalidSender.selector, s_user));
65+
s_wallet.execute(address(s_target), 0, "");
66+
}
67+
}
68+
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity ^0.8.20;
3+
4+
import { EnsoWalletV2 } from "../../../../../src/wallet/EnsoWalletV2.sol";
5+
import { WeirollPlanner } from "../../../../utils/WeirollPlanner.sol";
6+
7+
import { EnsoWalletV2_Unit_Concrete_Test } from "./EnsoWalletV2.t.sol";
8+
import { WETH } from "solady/tokens/WETH.sol";
9+
10+
contract Target {
11+
function func() external payable returns (uint256) {
12+
return 42;
13+
}
14+
15+
function revertFunc() external pure {
16+
revert("Test revert");
17+
}
18+
}
19+
20+
contract EnsoWalletV2_ExecuteShortcut_Unit_Concrete_Test is EnsoWalletV2_Unit_Concrete_Test {
21+
bytes32 internal constant ACCOUNT_ID = bytes32("test_account");
22+
bytes32 internal constant REQUEST_ID = bytes32("test_request");
23+
WETH internal weth;
24+
25+
function setUp() public override {
26+
super.setUp();
27+
s_wallet = _deployWallet(s_owner);
28+
weth = new WETH();
29+
}
30+
31+
function test_WhenCalledByOwner() external {
32+
// it should allow owner to execute shortcuts
33+
vm.startPrank(s_owner);
34+
bytes32[] memory commands = new bytes32[](0);
35+
bytes[] memory state = new bytes[](0);
36+
37+
s_wallet.executeShortcut(ACCOUNT_ID, REQUEST_ID, commands, state);
38+
}
39+
40+
function test_WhenCalledByFactory() external {
41+
// it should allow factory to execute shortcuts
42+
vm.startPrank(address(s_walletFactory));
43+
bytes32[] memory commands = new bytes32[](0);
44+
bytes[] memory state = new bytes[](0);
45+
46+
s_wallet.executeShortcut(ACCOUNT_ID, REQUEST_ID, commands, state);
47+
}
48+
49+
function test_RevertWhen_NotOwnerOrFactory() external {
50+
// it should revert when not called by owner or factory
51+
vm.startPrank(s_user);
52+
bytes32[] memory commands = new bytes32[](0);
53+
bytes[] memory state = new bytes[](0);
54+
55+
vm.expectRevert(abi.encodeWithSelector(EnsoWalletV2.InvalidSender.selector, s_user));
56+
s_wallet.executeShortcut(ACCOUNT_ID, REQUEST_ID, commands, state);
57+
}
58+
59+
function test_executeShortcutWithNative() external {
60+
// action:
61+
// wrap 1 ETH to WETH
62+
// transfer 1 WETH to s_account1
63+
64+
uint256 value = 1 ether;
65+
66+
bytes32[] memory commands = new bytes32[](2);
67+
bytes[] memory state = new bytes[](2);
68+
69+
commands[0] = WeirollPlanner.buildCommand(
70+
weth.deposit.selector,
71+
0x03, // call with value
72+
0x00ffffffffff, // 1 input
73+
0xff, // no output
74+
address(weth)
75+
);
76+
77+
commands[1] = WeirollPlanner.buildCommand(
78+
weth.transfer.selector,
79+
0x01, // call
80+
0x0100ffffffff, // 1 input
81+
0xff, // no output
82+
address(weth)
83+
);
84+
85+
state[0] = abi.encode(1 ether);
86+
state[1] = abi.encode(s_account1);
87+
88+
vm.startPrank(s_owner);
89+
vm.deal(address(s_wallet), value);
90+
91+
s_wallet.executeShortcut{ value: value }(ACCOUNT_ID, REQUEST_ID, commands, state);
92+
93+
// it should transfer WETH to account1
94+
assertEq(weth.balanceOf(s_account1), value);
95+
}
96+
97+
function test_executeShortcutWithERC20() external {
98+
// action:
99+
// transfer 1 WETH to s_account1
100+
101+
uint256 value = 1 ether;
102+
103+
bytes32[] memory commands = new bytes32[](1);
104+
bytes[] memory state = new bytes[](2);
105+
106+
commands[0] = WeirollPlanner.buildCommand(
107+
weth.transfer.selector,
108+
0x01, // call
109+
0x0001ffffffff, // 1 input
110+
0xff, // no output
111+
address(weth)
112+
);
113+
114+
state[0] = abi.encode(s_account1);
115+
state[1] = abi.encode(value);
116+
117+
vm.startPrank(s_owner);
118+
119+
// fund wallet with WETH
120+
weth.deposit{ value: value }();
121+
weth.transfer(address(s_wallet), value);
122+
123+
s_wallet.executeShortcut(ACCOUNT_ID, REQUEST_ID, commands, state);
124+
125+
// it should transfer WETH to account1
126+
assertEq(weth.balanceOf(s_account1), value);
127+
}
128+
129+
function test_revert_executeShortcut() external {
130+
// action:
131+
// wrap 1 ETH to WETH
132+
// but try to wrap more than available
133+
134+
uint256 value = 1 ether;
135+
136+
bytes32[] memory commands = new bytes32[](1);
137+
bytes[] memory state = new bytes[](1);
138+
139+
commands[0] = WeirollPlanner.buildCommand(
140+
weth.deposit.selector,
141+
0x03, // call with value
142+
0x00ffffffffff, // 1 input
143+
0xff, // no output
144+
address(weth)
145+
);
146+
147+
// should revert because not enough ETH
148+
state[0] = abi.encode(value + 1);
149+
150+
vm.startPrank(s_owner);
151+
152+
vm.expectRevert();
153+
s_wallet.executeShortcut{ value: value }(ACCOUNT_ID, REQUEST_ID, commands, state);
154+
}
155+
156+
function test_WhenWithValue() external {
157+
// it should handle value transfers
158+
uint256 value = 0.5 ether;
159+
160+
vm.deal(address(s_wallet), value);
161+
vm.startPrank(s_owner);
162+
163+
bytes32[] memory commands = new bytes32[](0);
164+
bytes[] memory state = new bytes[](0);
165+
166+
uint256 balanceBefore = address(s_wallet).balance;
167+
s_wallet.executeShortcut{ value: value }(ACCOUNT_ID, REQUEST_ID, commands, state);
168+
uint256 balanceAfter = address(s_wallet).balance;
169+
170+
// Value should be transferred to the wallet
171+
assertEq(balanceAfter - balanceBefore, value);
172+
}
173+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity ^0.8.20;
3+
4+
import { EnsoWalletV2 } from "../../../../../src/wallet/EnsoWalletV2.sol";
5+
import { EnsoWalletV2_Unit_Concrete_Test } from "./EnsoWalletV2.t.sol";
6+
import { Initializable } from "openzeppelin-contracts/proxy/utils/Initializable.sol";
7+
8+
contract EnsoWalletV2_Initialize_Unit_Concrete_Test is EnsoWalletV2_Unit_Concrete_Test {
9+
function setUp() public override {
10+
super.setUp();
11+
12+
s_wallet = _deployWallet(s_owner);
13+
}
14+
15+
function test_RevertWhen_AlreadyInitialized() external {
16+
// it should revert when trying to initialize again
17+
vm.startPrank(address(s_walletFactory));
18+
vm.expectRevert(abi.encodeWithSelector(Initializable.InvalidInitialization.selector));
19+
s_wallet.initialize(s_owner);
20+
}
21+
22+
function test_WhenNotInitialized() external {
23+
// it should initialize correctly
24+
EnsoWalletV2 wallet = EnsoWalletV2(payable(address(s_walletImplementation)));
25+
26+
vm.startPrank(address(s_walletFactory));
27+
wallet.initialize(s_owner);
28+
29+
// it should set owner
30+
assertEq(wallet.owner(), s_owner);
31+
32+
// it should set factory
33+
assertEq(wallet.factory(), address(s_walletFactory));
34+
}
35+
}
36+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity ^0.8.20;
3+
4+
import { EnsoWalletV2 } from "../../../../../src/wallet/EnsoWalletV2.sol";
5+
import { EnsoWalletV2_Unit_Concrete_Test } from "./EnsoWalletV2.t.sol";
6+
7+
contract EnsoWalletV2_Owner_Unit_Concrete_Test is EnsoWalletV2_Unit_Concrete_Test {
8+
function test_WhenWalletInitialized() external {
9+
// it should return correct owner
10+
s_wallet = _deployWallet(s_owner);
11+
assertEq(s_wallet.owner(), s_owner);
12+
}
13+
14+
function test_WhenCalledByOwner() external {
15+
// it should allow owner-only functions
16+
s_wallet = _deployWallet(s_owner);
17+
18+
vm.startPrank(s_owner);
19+
// This should not revert
20+
s_wallet.execute(s_user, 0, "");
21+
}
22+
23+
function test_RevertWhen_NotOwner() external {
24+
// it should revert when not called by owner
25+
s_wallet = _deployWallet(s_owner);
26+
27+
vm.startPrank(s_user);
28+
vm.expectRevert(abi.encodeWithSelector(EnsoWalletV2.InvalidSender.selector, s_user));
29+
s_wallet.execute(s_user, 0, "");
30+
}
31+
}

0 commit comments

Comments
 (0)