Skip to content

Commit 2b9b797

Browse files
committed
WIP: Orders fuzz tests
1 parent fe0dbae commit 2b9b797

File tree

2 files changed

+217
-0
lines changed

2 files changed

+217
-0
lines changed

test/fuzz-rollup/OrdersFuzz.t.sol

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
pragma solidity 0.8.26;
3+
4+
// test contracts
5+
import {IOrders} from "../../src/orders/IOrders.sol";
6+
import {RollupOrders} from "../../src/orders/RollupOrders.sol";
7+
import {OrderOrigin} from "../../src/orders/OrderOrigin.sol";
8+
// utils
9+
import {TestERC20} from "../Helpers.t.sol";
10+
import {SignetStdTest} from "../SignetStdTest.t.sol";
11+
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
12+
import {Test, console2} from "forge-std/Test.sol";
13+
14+
contract OrdersFuzzTest is SignetStdTest {
15+
RollupOrders public target;
16+
17+
event Filled(IOrders.Output[] outputs);
18+
19+
event Order(uint256 deadline, IOrders.Input[] inputs, IOrders.Output[] outputs);
20+
21+
event Sweep(address indexed recipient, address indexed token, uint256 amount);
22+
23+
function setUp() public virtual {
24+
target = ROLLUP_ORDERS;
25+
}
26+
27+
// input ERC20
28+
function test_initiate(uint256 deadline, IOrders.Input memory input, IOrders.Output memory output) public {
29+
vm.assume(deadline >= block.timestamp);
30+
31+
uint256 ethAmount = 0;
32+
33+
if (input.token == address(0)) {
34+
ethAmount += input.amount;
35+
} else {
36+
vm.mockCall(
37+
input.token,
38+
abi.encodeWithSelector(ERC20.transferFrom.selector, address(this), address(target), input.amount),
39+
abi.encode(true)
40+
);
41+
vm.expectCall(
42+
input.token,
43+
abi.encodeWithSelector(ERC20.transferFrom.selector, address(this), address(target), input.amount)
44+
);
45+
}
46+
47+
IOrders.Input[] memory inputs = new IOrders.Input[](1);
48+
inputs[0] = input;
49+
IOrders.Output[] memory outputs = new IOrders.Output[](1);
50+
outputs[0] = output;
51+
52+
vm.deal(address(this), ethAmount + 1 ether); // give contract some ETH
53+
54+
vm.expectEmit();
55+
emit Order(deadline, inputs, outputs);
56+
target.initiate{value: ethAmount}(deadline, inputs, outputs);
57+
58+
assertEq(address(target).balance, ethAmount);
59+
}
60+
61+
function test_underflowETH(uint256 deadline, uint256 amount, IOrders.Output memory output) public {
62+
vm.assume(deadline >= block.timestamp);
63+
vm.assume(amount < type(uint256).max); // prevent overflow in vm.deal
64+
vm.deal(address(this), amount); // give contract some ETH
65+
66+
IOrders.Input[] memory inputs = new IOrders.Input[](2);
67+
inputs[0] = IOrders.Input(address(0), amount);
68+
inputs[1] = IOrders.Input(address(0), 1);
69+
70+
IOrders.Output[] memory outputs = new IOrders.Output[](1);
71+
outputs[0] = output;
72+
73+
// total ETH inputs should be amount + 1; function should underflow only sending amount
74+
vm.expectRevert();
75+
target.initiate{value: amount}(deadline, inputs, outputs);
76+
}
77+
78+
function test_orderExpired(uint256 deadline) public {
79+
vm.assume(deadline < block.timestamp);
80+
81+
IOrders.Input[] memory inputs = new IOrders.Input[](0);
82+
IOrders.Output[] memory outputs = new IOrders.Output[](0);
83+
84+
vm.expectRevert(OrderOrigin.OrderExpired.selector);
85+
target.initiate(deadline, inputs, outputs);
86+
}
87+
88+
function test_sweepETH(uint256 deadline, uint256 amount, address recipient, IOrders.Output memory output) public {
89+
vm.assume(deadline >= block.timestamp);
90+
vm.assume(amount < type(uint256).max - 1000 ether); // prevent overflow in vm.deal
91+
vm.assume(recipient.code.length == 0 && uint160(recipient) > 0x09); // recipient is non-precompile EOA
92+
vm.assume(address(recipient).balance == 0); // recipient starts with zero balance
93+
vm.deal(address(this), amount); // give contract some ETH
94+
95+
// initiate an ETH order
96+
IOrders.Input[] memory inputs = new IOrders.Input[](1);
97+
inputs[0] = IOrders.Input(address(0), amount);
98+
IOrders.Output[] memory outputs = new IOrders.Output[](1);
99+
outputs[0] = output;
100+
target.initiate{value: amount}(deadline, inputs, outputs);
101+
102+
assertEq(address(target).balance, amount);
103+
104+
// sweep ETH
105+
vm.expectEmit();
106+
emit Sweep(recipient, address(0), amount);
107+
target.sweep(recipient, address(0), amount);
108+
109+
assertEq(address(target).balance, 0);
110+
assertEq(recipient.balance, amount);
111+
}
112+
113+
function test_sweepERC20(address recipient, address token, uint256 amount) public {
114+
vm.assume(token != address(0));
115+
116+
vm.mockCall(
117+
token, abi.encodeWithSelector(ERC20.transfer.selector, address(recipient), amount), abi.encode(true)
118+
);
119+
vm.expectCall(token, abi.encodeWithSelector(ERC20.transfer.selector, recipient, amount));
120+
121+
// sweep ERC20
122+
vm.expectEmit();
123+
emit Sweep(recipient, token, amount);
124+
target.sweep(recipient, token, amount);
125+
}
126+
127+
function test_fill(IOrders.Output memory output) public {
128+
vm.assume(output.amount < type(uint256).max - 1000 ether); // prevent overflow in vm.deal
129+
vm.assume(output.recipient.code.length == 0 && uint160(output.recipient) > 0x09); // recipient is non-precompile EOA
130+
vm.deal(address(this), output.amount); // give contract some ETH
131+
132+
uint256 ethAmount = 0;
133+
if (output.token == address(0)) {
134+
ethAmount += output.amount;
135+
} else {
136+
vm.mockCall(
137+
output.token,
138+
abi.encodeWithSelector(ERC20.transferFrom.selector, address(this), address(output.recipient), output.amount),
139+
abi.encode(true)
140+
);
141+
vm.expectCall(
142+
output.token,
143+
abi.encodeWithSelector(ERC20.transferFrom.selector, address(this), address(output.recipient), output.amount)
144+
);
145+
}
146+
147+
IOrders.Output[] memory outputs = new IOrders.Output[](1);
148+
outputs[0] = output;
149+
150+
vm.expectEmit();
151+
emit Filled(outputs);
152+
target.fill{value: ethAmount}(outputs);
153+
154+
// ETH is transferred to recipient
155+
assertEq(output.recipient.balance, ethAmount);
156+
}
157+
158+
function test_fill_underflowETH(uint256 amount, address recipient, uint32 chainId) public {
159+
vm.assume(amount > 0 && amount < type(uint256).max - 1000 ether); // prevent overflow in vm.deal
160+
vm.assume(recipient.code.length == 0 && uint160(recipient) > 0x09); // recipient is non-precompile EOA
161+
vm.deal(address(this), amount); // give contract some ETH
162+
163+
IOrders.Output[] memory outputs = new IOrders.Output[](2);
164+
outputs[0] = IOrders.Output(address(0), amount, recipient, chainId);
165+
outputs[1] = IOrders.Output(address(0), 1, recipient, chainId);
166+
167+
// total ETH outputs should be `amount` + 1; function should underflow only sending `amount`
168+
vm.expectRevert();
169+
target.fill{value: amount}(outputs);
170+
}
171+
172+
function test_fill_zeroETH(address recipient, uint32 chainId) public {
173+
vm.assume(recipient.code.length == 0 && uint160(recipient) > 0x09); // recipient is non-precompile EOA
174+
175+
IOrders.Output memory output = IOrders.Output(address(0), 0, recipient, chainId);
176+
IOrders.Output[] memory outputs = new IOrders.Output[](1);
177+
outputs[0] = output;
178+
179+
vm.expectEmit();
180+
emit Filled(outputs);
181+
target.fill(outputs);
182+
}
183+
}

test/rollup/Orders.t.sol

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,40 @@ contract OrdersTest is SignetStdTest {
5555
deadline = block.timestamp;
5656
}
5757

58+
// zero inputs or outptus
59+
function test_initiate_empty() public {
60+
IOrders.Input[] memory emptyInputs = new IOrders.Input[](0);
61+
IOrders.Output[] memory emptyOutputs = new IOrders.Output[](0);
62+
63+
// expect Order event is initiated, ERC20 is transferred
64+
vm.expectEmit();
65+
emit Order(deadline, emptyInputs, emptyOutputs);
66+
target.initiate(deadline, emptyInputs, emptyOutputs);
67+
}
68+
69+
// empty inputs, non-empty outputs
70+
function test_initiate_emptyInputs() public {
71+
IOrders.Input[] memory emptyInputs = new IOrders.Input[](0);
72+
73+
// expect Order event is initiated, ERC20 is transferred
74+
vm.expectEmit();
75+
emit Order(deadline, emptyInputs, outputs);
76+
target.initiate(deadline, emptyInputs, outputs);
77+
}
78+
79+
// empty outputs, non-empty inputs
80+
function test_initiate_emptyOutputs() public {
81+
IOrders.Output[] memory emptyOutputs = new IOrders.Output[](0);
82+
83+
// expect Order event is initiated, ERC20 is transferred
84+
vm.expectEmit();
85+
emit Order(deadline, inputs, emptyOutputs);
86+
vm.expectCall(
87+
token, abi.encodeWithSelector(ERC20.transferFrom.selector, address(this), address(target), amount)
88+
);
89+
target.initiate(deadline, inputs, emptyOutputs);
90+
}
91+
5892
// input ERC20
5993
function test_initiate_ERC20() public {
6094
// expect Order event is initiated, ERC20 is transferred

0 commit comments

Comments
 (0)