1+ // SPDX-License-Identifier: UNLICENSED
2+ pragma solidity ^ 0.8.13 ;
3+
4+ import { SetupHook } from "./SetupHook.t.sol " ;
5+ import { AsyncOrder } from "@async-swap/types/AsyncOrder.sol " ;
6+ import { Currency } from "v4-core/interfaces/IPoolManager.sol " ;
7+ import { CurrencyLibrary } from "v4-core/types/Currency.sol " ;
8+
9+ contract IntegrationTest is SetupHook {
10+
11+ using CurrencyLibrary for Currency;
12+
13+ address alice = makeAddr ("alice " );
14+ address bob = makeAddr ("bob " );
15+ address charlie = makeAddr ("charlie " );
16+
17+ function setUp () public override {
18+ super .setUp ();
19+ topUp (alice, 50 ether);
20+ topUp (bob, 50 ether);
21+ topUp (charlie, 50 ether);
22+ }
23+
24+ function topUp (address _user , uint256 amount ) public ownerAction {
25+ token0.transfer (_user, amount);
26+ token1.transfer (_user, amount);
27+ }
28+
29+ function testCompleteSwapAndFillWorkflow () public {
30+ uint256 swapAmount = 2 ether ;
31+
32+ // Alice creates async swap order
33+ vm.startPrank (alice);
34+ token0.approve (address (router), swapAmount);
35+
36+ AsyncOrder memory aliceOrder = AsyncOrder ({
37+ key: key,
38+ owner: alice,
39+ zeroForOne: true ,
40+ amountIn: swapAmount,
41+ sqrtPrice: 2 ** 96
42+ });
43+
44+ router.swap (aliceOrder, abi.encode (alice, address (router)));
45+ vm.stopPrank ();
46+
47+ // Verify Alice's order
48+ assertEq (hook.asyncOrder (poolId, alice, true ), swapAmount);
49+ assertTrue (hook.isExecutor (poolId, alice, address (router)));
50+
51+ // Bob fills Alice's order
52+ vm.startPrank (bob);
53+ token1.approve (address (router), swapAmount);
54+ router.fillOrder (aliceOrder, "" );
55+ vm.stopPrank ();
56+
57+ // Verify order completion
58+ assertEq (hook.asyncOrder (poolId, alice, true ), 0 );
59+ assertEq (manager.balanceOf (alice, currency0.toId ()), swapAmount);
60+ }
61+
62+ function testMultipleUsersMultipleOrders () public {
63+ uint256 aliceAmount = 1 ether ;
64+ uint256 bobAmount = 1.5 ether ;
65+
66+ // Alice creates order (zeroForOne = true)
67+ vm.startPrank (alice);
68+ token0.approve (address (router), aliceAmount);
69+
70+ AsyncOrder memory aliceOrder = AsyncOrder ({
71+ key: key,
72+ owner: alice,
73+ zeroForOne: true ,
74+ amountIn: aliceAmount,
75+ sqrtPrice: 2 ** 96
76+ });
77+
78+ router.swap (aliceOrder, abi.encode (alice, address (router)));
79+ vm.stopPrank ();
80+
81+ // Bob creates order (zeroForOne = false)
82+ vm.startPrank (bob);
83+ token1.approve (address (router), bobAmount);
84+
85+ AsyncOrder memory bobOrder = AsyncOrder ({
86+ key: key,
87+ owner: bob,
88+ zeroForOne: false ,
89+ amountIn: bobAmount,
90+ sqrtPrice: 2 ** 96
91+ });
92+
93+ router.swap (bobOrder, abi.encode (bob, address (router)));
94+ vm.stopPrank ();
95+
96+ // Verify both orders exist
97+ assertEq (hook.asyncOrder (poolId, alice, true ), aliceAmount);
98+ assertEq (hook.asyncOrder (poolId, bob, false ), bobAmount);
99+
100+ // Charlie fills Alice's order
101+ vm.startPrank (charlie);
102+ token1.approve (address (router), aliceAmount);
103+ router.fillOrder (aliceOrder, "" );
104+ vm.stopPrank ();
105+
106+ // Charlie fills Bob's order
107+ vm.startPrank (charlie);
108+ token0.approve (address (router), bobAmount);
109+
110+ AsyncOrder memory bobFillOrder = AsyncOrder ({
111+ key: key,
112+ owner: bob,
113+ zeroForOne: false ,
114+ amountIn: bobAmount,
115+ sqrtPrice: 2 ** 96
116+ });
117+
118+ router.fillOrder (bobFillOrder, "" );
119+ vm.stopPrank ();
120+
121+ // Verify all orders filled
122+ assertEq (hook.asyncOrder (poolId, alice, true ), 0 );
123+ assertEq (hook.asyncOrder (poolId, bob, false ), 0 );
124+ }
125+
126+ function testPartialFillsAndAccumulation () public {
127+ uint256 totalAmount = 3 ether ;
128+ uint256 firstSwap = 1 ether ;
129+ uint256 secondSwap = 2 ether ;
130+ uint256 firstFill = 1.5 ether ;
131+ uint256 secondFill = 1.5 ether ;
132+
133+ // Alice creates first order
134+ vm.startPrank (alice);
135+ token0.approve (address (router), firstSwap);
136+
137+ AsyncOrder memory aliceOrder1 = AsyncOrder ({
138+ key: key,
139+ owner: alice,
140+ zeroForOne: true ,
141+ amountIn: firstSwap,
142+ sqrtPrice: 2 ** 96
143+ });
144+
145+ router.swap (aliceOrder1, abi.encode (alice, address (router)));
146+ assertEq (hook.asyncOrder (poolId, alice, true ), firstSwap);
147+
148+ // Alice creates second order (accumulates)
149+ token0.approve (address (router), secondSwap);
150+
151+ AsyncOrder memory aliceOrder2 = AsyncOrder ({
152+ key: key,
153+ owner: alice,
154+ zeroForOne: true ,
155+ amountIn: secondSwap,
156+ sqrtPrice: 2 ** 96
157+ });
158+
159+ router.swap (aliceOrder2, abi.encode (alice, address (router)));
160+ vm.stopPrank ();
161+
162+ // Verify accumulated amount
163+ assertEq (hook.asyncOrder (poolId, alice, true ), totalAmount);
164+
165+ // Bob partially fills
166+ vm.startPrank (bob);
167+ token1.approve (address (router), firstFill);
168+
169+ AsyncOrder memory fillOrder1 = AsyncOrder ({
170+ key: key,
171+ owner: alice,
172+ zeroForOne: true ,
173+ amountIn: firstFill,
174+ sqrtPrice: 2 ** 96
175+ });
176+
177+ router.fillOrder (fillOrder1, "" );
178+ vm.stopPrank ();
179+
180+ // Verify partial fill
181+ assertEq (hook.asyncOrder (poolId, alice, true ), totalAmount - firstFill);
182+
183+ // Charlie fills remainder
184+ vm.startPrank (charlie);
185+ token1.approve (address (router), secondFill);
186+
187+ AsyncOrder memory fillOrder2 = AsyncOrder ({
188+ key: key,
189+ owner: alice,
190+ zeroForOne: true ,
191+ amountIn: secondFill,
192+ sqrtPrice: 2 ** 96
193+ });
194+
195+ router.fillOrder (fillOrder2, "" );
196+ vm.stopPrank ();
197+
198+ // Verify complete fill
199+ assertEq (hook.asyncOrder (poolId, alice, true ), 0 );
200+ }
201+
202+ function testBatchOrderExecution () public {
203+ uint256 orderCount = 5 ;
204+ uint256 amountPerOrder = 0.5 ether ;
205+
206+ // Alice creates multiple orders
207+ vm.startPrank (alice);
208+ for (uint i = 0 ; i < orderCount; i++ ) {
209+ token0.approve (address (router), amountPerOrder);
210+
211+ AsyncOrder memory order = AsyncOrder ({
212+ key: key,
213+ owner: alice,
214+ zeroForOne: true ,
215+ amountIn: amountPerOrder,
216+ sqrtPrice: 2 ** 96
217+ });
218+
219+ router.swap (order, abi.encode (alice, address (router)));
220+ }
221+ vm.stopPrank ();
222+
223+ // Verify total accumulated
224+ assertEq (hook.asyncOrder (poolId, alice, true ), orderCount * amountPerOrder);
225+
226+ // Execute orders one by one using router (since alice set router as executor)
227+ for (uint i = 0 ; i < orderCount; i++ ) {
228+ AsyncOrder memory fillOrder = AsyncOrder ({
229+ key: key,
230+ owner: alice,
231+ zeroForOne: true ,
232+ amountIn: amountPerOrder,
233+ sqrtPrice: 2 ** 96
234+ });
235+
236+ vm.startPrank (bob);
237+ token1.approve (address (router), amountPerOrder);
238+ router.fillOrder (fillOrder, abi.encode (address (router)));
239+ vm.stopPrank ();
240+ }
241+
242+ // Verify all orders executed
243+ assertEq (hook.asyncOrder (poolId, alice, true ), 0 );
244+ }
245+
246+ function testCrossDirectionalOrders () public {
247+ uint256 amount = 1 ether ;
248+
249+ // Alice: token0 -> token1 (zeroForOne = true)
250+ vm.startPrank (alice);
251+ token0.approve (address (router), amount);
252+
253+ AsyncOrder memory aliceOrder = AsyncOrder ({
254+ key: key,
255+ owner: alice,
256+ zeroForOne: true ,
257+ amountIn: amount,
258+ sqrtPrice: 2 ** 96
259+ });
260+
261+ router.swap (aliceOrder, abi.encode (alice, address (router)));
262+ vm.stopPrank ();
263+
264+ // Bob: token1 -> token0 (zeroForOne = false)
265+ vm.startPrank (bob);
266+ token1.approve (address (router), amount);
267+
268+ AsyncOrder memory bobOrder = AsyncOrder ({
269+ key: key,
270+ owner: bob,
271+ zeroForOne: false ,
272+ amountIn: amount,
273+ sqrtPrice: 2 ** 96
274+ });
275+
276+ router.swap (bobOrder, abi.encode (bob, address (router)));
277+ vm.stopPrank ();
278+
279+ // Charlie can fill both orders
280+ vm.startPrank (charlie);
281+
282+ // Fill Alice's order (needs token1)
283+ token1.approve (address (router), amount);
284+ router.fillOrder (aliceOrder, "" );
285+
286+ // Fill Bob's order (needs token0)
287+ token0.approve (address (router), amount);
288+ router.fillOrder (bobOrder, "" );
289+
290+ vm.stopPrank ();
291+
292+ // Verify both filled
293+ assertEq (hook.asyncOrder (poolId, alice, true ), 0 );
294+ assertEq (hook.asyncOrder (poolId, bob, false ), 0 );
295+ }
296+
297+ function testAlgorithmIntegration () public {
298+ uint256 amount = 1 ether ;
299+
300+ // Verify algorithm is set correctly
301+ address algorithmAddress = address (hook.ALGORITHM ());
302+ assertTrue (algorithmAddress != address (0 ));
303+
304+ // Create order to trigger algorithm
305+ vm.startPrank (alice);
306+ token0.approve (address (router), amount);
307+
308+ AsyncOrder memory order = AsyncOrder ({
309+ key: key,
310+ owner: alice,
311+ zeroForOne: true ,
312+ amountIn: amount,
313+ sqrtPrice: 2 ** 96
314+ });
315+
316+ router.swap (order, abi.encode (alice, address (router)));
317+ vm.stopPrank ();
318+
319+ // The swap should succeed, meaning algorithm was called successfully
320+ assertEq (hook.asyncOrder (poolId, alice, true ), amount);
321+ }
322+
323+ function testLargeVolumeStressTest () public {
324+ uint256 userCount = 10 ;
325+ uint256 amountPerUser = 0.1 ether ;
326+
327+ // Multiple users create orders
328+ for (uint i = 0 ; i < userCount; i++ ) {
329+ address user = address (uint160 (i + 1000 )); // Generate unique addresses
330+ topUp (user, amountPerUser);
331+
332+ vm.startPrank (user);
333+ token0.approve (address (router), amountPerUser);
334+
335+ AsyncOrder memory order = AsyncOrder ({
336+ key: key,
337+ owner: user,
338+ zeroForOne: true ,
339+ amountIn: amountPerUser,
340+ sqrtPrice: 2 ** 96
341+ });
342+
343+ router.swap (order, abi.encode (user, address (router)));
344+ vm.stopPrank ();
345+
346+ // Verify each order
347+ assertEq (hook.asyncOrder (poolId, user, true ), amountPerUser);
348+ }
349+
350+ // Single filler fills all orders
351+ vm.startPrank (charlie);
352+ token1.approve (address (router), userCount * amountPerUser);
353+
354+ for (uint i = 0 ; i < userCount; i++ ) {
355+ address user = address (uint160 (i + 1000 ));
356+
357+ AsyncOrder memory fillOrder = AsyncOrder ({
358+ key: key,
359+ owner: user,
360+ zeroForOne: true ,
361+ amountIn: amountPerUser,
362+ sqrtPrice: 2 ** 96
363+ });
364+
365+ router.fillOrder (fillOrder, "" );
366+ assertEq (hook.asyncOrder (poolId, user, true ), 0 );
367+ }
368+ vm.stopPrank ();
369+ }
370+
371+ }
0 commit comments