Skip to content

Commit 675654a

Browse files
committed
feat: Orders fuzz tests
1 parent fe0dbae commit 675654a

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed

test/fuzz-rollup/OrdersFuzz.t.sol

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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.assume(output.token != address(vm));
131+
vm.deal(address(this), output.amount); // give contract some ETH
132+
133+
uint256 ethAmount = 0;
134+
if (output.token == address(0)) {
135+
ethAmount += output.amount;
136+
} else {
137+
vm.mockCall(
138+
output.token,
139+
abi.encodeWithSelector(
140+
ERC20.transferFrom.selector, address(this), address(output.recipient), output.amount
141+
),
142+
abi.encode(true)
143+
);
144+
vm.expectCall(
145+
output.token,
146+
abi.encodeWithSelector(
147+
ERC20.transferFrom.selector, address(this), address(output.recipient), output.amount
148+
)
149+
);
150+
}
151+
152+
IOrders.Output[] memory outputs = new IOrders.Output[](1);
153+
outputs[0] = output;
154+
155+
vm.expectEmit();
156+
emit Filled(outputs);
157+
target.fill{value: ethAmount}(outputs);
158+
159+
// ETH is transferred to recipient
160+
assertEq(output.recipient.balance, ethAmount);
161+
}
162+
163+
function test_fill_underflowETH(uint256 amount, address recipient, uint32 chainId) public {
164+
vm.assume(amount > 0 && amount < type(uint256).max - 1000 ether); // prevent overflow in vm.deal
165+
vm.assume(recipient.code.length == 0 && uint160(recipient) > 0x09); // recipient is non-precompile EOA
166+
vm.deal(address(this), amount); // give contract some ETH
167+
168+
IOrders.Output[] memory outputs = new IOrders.Output[](2);
169+
outputs[0] = IOrders.Output(address(0), amount, recipient, chainId);
170+
outputs[1] = IOrders.Output(address(0), 1, recipient, chainId);
171+
172+
// total ETH outputs should be `amount` + 1; function should underflow only sending `amount`
173+
vm.expectRevert();
174+
target.fill{value: amount}(outputs);
175+
}
176+
177+
function test_fill_zeroETH(address recipient, uint32 chainId) public {
178+
vm.assume(recipient.code.length == 0 && uint160(recipient) > 0x09); // recipient is non-precompile EOA
179+
180+
IOrders.Output memory output = IOrders.Output(address(0), 0, recipient, chainId);
181+
IOrders.Output[] memory outputs = new IOrders.Output[](1);
182+
outputs[0] = output;
183+
184+
vm.expectEmit();
185+
emit Filled(outputs);
186+
target.fill(outputs);
187+
}
188+
}

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)