From 01ab5d22b182072a65221f620df7fade3ab6fe7a Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Tue, 24 Jun 2025 10:39:58 +0100 Subject: [PATCH] fix: Move Bebop tests according to new setup Encoding: integration tests are now separate and inside their own test folder Execution: the final integration test should be inside of the protocol test file now and not in TychoRouterProtocolIntegration.t.sol. For this I had to move the BebopExecutionHarness.t.sol outside of the Bebop test file (because of imports) Took 24 minutes # Commit time for manual adjustment: # Took 2 minutes --- .../test/TychoRouterProtocolIntegration.t.sol | 77 - foundry/test/TychoRouterTestSetup.sol | 13 +- foundry/test/assets/calldata.txt | 29 +- .../{BebopExecutor.t.sol => Bebop.t.sol} | 280 +- .../protocols/BebopExecutionHarness.t.sol | 182 ++ .../evm/swap_encoder/swap_encoders.rs | 14 +- src/encoding/evm/tycho_encoders.rs | 2361 ----------------- tests/common/mod.rs | 99 +- .../optimized_transfers_integration_tests.rs | 142 +- tests/protocol_integration_tests.rs | 244 +- 10 files changed, 771 insertions(+), 2670 deletions(-) rename foundry/test/protocols/{BebopExecutor.t.sol => Bebop.t.sol} (85%) create mode 100644 foundry/test/protocols/BebopExecutionHarness.t.sol diff --git a/foundry/test/TychoRouterProtocolIntegration.t.sol b/foundry/test/TychoRouterProtocolIntegration.t.sol index f3a51d1..1a85475 100644 --- a/foundry/test/TychoRouterProtocolIntegration.t.sol +++ b/foundry/test/TychoRouterProtocolIntegration.t.sol @@ -49,81 +49,4 @@ contract TychoRouterTestProtocolIntegration is TychoRouterTestSetup { assertTrue(success, "Call Failed"); assertEq(balanceAfter - balanceBefore, 235610487387677804636755778); } - - function testSingleBebopIntegration() public { - // The calldata swaps 200 USDC for ONDO - // The receiver in the order is 0xc5564C13A157E6240659fb81882A28091add8670 - address orderTaker = 0xc5564C13A157E6240659fb81882A28091add8670; - address maker = 0xCe79b081c0c924cb67848723ed3057234d10FC6b; - deal(USDC_ADDR, orderTaker, 200 * 10 ** 6); // 200 USDC - uint256 expAmountOut = 237212396774431060000; // Expected ONDO amount from calldata - - // Fund the maker with ONDO and approve settlement - deal(ONDO_ADDR, maker, expAmountOut); - vm.prank(maker); - IERC20(ONDO_ADDR).approve(BEBOP_SETTLEMENT, expAmountOut); - - uint256 ondoBefore = IERC20(ONDO_ADDR).balanceOf(orderTaker); - - vm.startPrank(orderTaker); - IERC20(USDC_ADDR).approve(tychoRouterAddr, type(uint256).max); - - // Load calldata from file - bytes memory callData = - loadCallDataFromFile("test_single_encoding_strategy_bebop"); - - (bool success,) = tychoRouterAddr.call(callData); - - // Check the receiver's balance (not ALICE, since the order specifies a different receiver) - uint256 ondoReceived = - IERC20(ONDO_ADDR).balanceOf(orderTaker) - ondoBefore; - assertTrue(success, "Call Failed"); - assertEq(ondoReceived, expAmountOut); - assertEq( - IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), - 0, - "USDC left in router" - ); - - vm.stopPrank(); - } - - function testBebopAggregateIntegration() public { - // Based on real transaction: https://etherscan.io/tx/0xec88410136c287280da87d0a37c1cb745f320406ca3ae55c678dec11996c1b1c - address orderTaker = 0x7078B12Ca5B294d95e9aC16D90B7D38238d8F4E6; // This is both taker and receiver in the order - uint256 ethAmount = 9850000000000000; // 0.00985 WETH - uint256 expAmountOut = 17969561; // 17.969561 USDC expected output - - // Fund the two makers from the real transaction with USDC - address maker1 = 0x67336Cec42645F55059EfF241Cb02eA5cC52fF86; - address maker2 = 0xBF19CbF0256f19f39A016a86Ff3551ecC6f2aAFE; - - deal(USDC_ADDR, maker1, 10607211); // Maker 1 provides 10.607211 USDC - deal(USDC_ADDR, maker2, 7362350); // Maker 2 provides 7.362350 USDC - - // Makers approve settlement contract - vm.prank(maker1); - IERC20(USDC_ADDR).approve(BEBOP_SETTLEMENT, type(uint256).max); - vm.prank(maker2); - IERC20(USDC_ADDR).approve(BEBOP_SETTLEMENT, type(uint256).max); - - // Fund ALICE with ETH as it will send the transaction - vm.deal(ALICE, ethAmount); - vm.startPrank(ALICE); - - // Load calldata from file - bytes memory callData = loadCallDataFromFile( - "test_single_encoding_strategy_bebop_aggregate" - ); - - // Execute the swap - (bool success,) = tychoRouterAddr.call{value: ethAmount}(callData); - uint256 finalBalance = IERC20(USDC_ADDR).balanceOf(orderTaker); - - assertTrue(success, "Call Failed"); - assertEq(finalBalance, expAmountOut); - assertEq(address(tychoRouterAddr).balance, 0, "ETH left in router"); - - vm.stopPrank(); - } } diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index ae52739..24a001f 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -13,16 +13,17 @@ import { IUniswapV3Pool } from "../src/executors/UniswapV3Executor.sol"; import {UniswapV4Executor} from "../src/executors/UniswapV4Executor.sol"; -import {BebopExecutorHarness} from "./executors/BebopExecutor.t.sol"; +import {BebopExecutorHarness} from "./protocols/BebopExecutionHarness.t.sol"; // Test utilities and mocks -import "./Constants.sol"; -import "./TestUtils.sol"; -import {Permit2TestHelper} from "./Permit2TestHelper.sol"; +import {EkuboExecutor} from "../src/executors/EkuboExecutor.sol"; +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {MaverickV2Executor} from "../src/executors/MaverickV2Executor.sol"; // Core contracts and interfaces -import "@src/TychoRouter.sol"; -import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {Permit2TestHelper} from "./Permit2TestHelper.sol"; +import {UniswapV2Executor} from "../src/executors/UniswapV2Executor.sol"; +import {UniswapV4Executor} from "../src/executors/UniswapV4Executor.sol"; contract TychoRouterExposed is TychoRouter { constructor(address _permit2, address weth) TychoRouter(_permit2, weth) {} diff --git a/foundry/test/assets/calldata.txt b/foundry/test/assets/calldata.txt index 3aecf03..976b41e 100644 --- a/foundry/test/assets/calldata.txt +++ b/foundry/test/assets/calldata.txt @@ -3,25 +3,25 @@ test_single_encoding_strategy_ekubo:5c4b639c000000000000000000000000000000000000 test_uniswap_v3_uniswap_v3:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010100000000000000000000 test_balancer_v2_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_sequential_swap_strategy_encoder_no_permit2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 -test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006880f27600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c7e00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041509fa14e28ad71fed11dae329c5d17b4cc634b74379cef3b5c5bfe445d5a929d0e139ad79e8ec09bc6368b9d1a5ad350d98e467e05e453d47431333ee985fd4d1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 -test_single_encoding_strategy_usv4_eth_out:30ace1b100000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006880f27600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c7e00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041df4f662a218a599150507f1089ea781771903aa5ed41e5f24d57483074b469c031a18bb247df38ecf866faf5ff86cec389e3410124260218fb99dc9e01b788b01c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c0000000000000000000000000000000000000000 -test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f27600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c7e00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041176632aaa7a353a547b128b56c490c4da926ad7ba12aeace54629f750e937ab704fa1f9b9149872d39553d2d987ec882840b73a44c036d476ea0e75439346c2f1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006881fc2f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763700000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041710d440f3d3221a9c23f3e0fe25a2e4890afc2b86f09373c37dfca9d657879b7014c0fe433da84c2edbb86c7e467fc5aae630010ee38316fc06864ea54ec0df21b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_usv4_eth_out:30ace1b100000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006881fc2f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763700000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410f1bdf3501ee258a4886ba4a07dc32c6735b05621974ce9556450cc6a0c77ebd581e9a38d99aa55519239cb7d64e8de344206ecec9fce95f0405df7d05040b031c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c0000000000000000000000000000000000000000 +test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006881fc3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763800000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410357af5509f681eacdb89ef8cafac506340b9454bb6626fba2455f9202380edf30409621855d4225dbdc2e1e2b0327e3f1c746fbd1a478134e1b33ecca804b031c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_single_swap_strategy_encoder_no_permit2:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_swap_strategy_encoder_no_transfer_in:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 -test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f27600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c7e00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004128c50a468f1b4bb63b28f8dd8c33c89b065effdf718ce87632e7d16f3a22a8c661e05ccd2d9fb65719f6b3123a40e25a0b5ef1e21f939344e2b92ce0d97805b91c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 -test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006880f27600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c7e00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004110cf1125cc7207bf96734e97da8e1a7e101e8e9a5886128a6e607e8b3d1fb9775b00f4290b2cc85692273a9622388475ba1a62771d1a834d529af36562139e3e1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 +test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006881fc2f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763700000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000412ef11fb37c24f2937ab95153ed8145f28dc436254b9e26276859c723737d535d3b4189638c62332c2abb968e4d5cb1beb9343f171bdda276629464327a9feaeb1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 +test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006881fc3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763800000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041c367c23b4a4e055488442b804cc21a8b596fbf1181b1b2fdb9c3aac90a9581cb25fbb5eed915837b08ecefa9c8689f75c29e26ff4cf86a633e0597a7387563e41c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 test_single_encoding_strategy_curve_st_eth:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 test_single test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c 6d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006869398600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006841b38e00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041752ca399237fc5086ef89d5f6dabecfb4b43c0753ecfb7020a6a86045db423fd3be9565f79b511fe93f55f76f61b1ac8d786b04051110ca6cbe10bbf69901b871c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_encoding_strategy_curve:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 -test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006880f27700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c7f00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041ef8669fa0cfea3980202c2526db6e383b14dec72f5b0423432a6e4783a6bc56d51a24d3cf7da4852cd95bb8ad37827877e38bf4b4716111e7113b8af17f6de3d1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000 -test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f27700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c7f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004143ab32167d0352fc20dda7b7c0c3fd76452dc2bd6bd675ad3b4ffbcc908ae6401bb7df63b661cb43cab3f8397ea8db0ba510c5c1d939688e4674a108cf46180c1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 -test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006880f27800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c80000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041cf6d57a1ab0d7cba6d5c8739142ad0f0a1ea07d50b7f82a644ec5d48125aa3f35a64e1abd5c0007274005e8f5e0031334da2e96a06b2935267f947f8e856f6e11b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000 -test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006880f27800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c80000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041cf6d57a1ab0d7cba6d5c8739142ad0f0a1ea07d50b7f82a644ec5d48125aa3f35a64e1abd5c0007274005e8f5e0031334da2e96a06b2935267f947f8e856f6e11b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000 -test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f27800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c80000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041a0be3470609c83fed0b58f0171e2e929ed46ddc952b8ccd76a54e6eb686f9ad961e9ec6a7f89423584fd0b56336640cf62b44c658b4616a2cf79b0cdf0c8980e1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000 +test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006881fc3100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763900000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004193ec68786c1079d44165d38e04f0a2803c477ff8f110571927b6958be7a8d24332910568a5f0a49dccfee00a16c3d6dcd97abd20df76079f957064f4cc4af5c81b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000 +test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006881fc3100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763900000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041b3f81fd19b92c4a35a937bc55b6384b447c7213d7097323a4ec7e373e28a2b601c58d5ca02c2762a111a4a3e68ddf00b67c774897fd70a2d73d6018ad324a9021c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 +test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006881fc3200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763a00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004116f1e2e32d3f74da25c7ea0e8496015d83196892ff5764fea33c9579d747dd62693aed4e9df3bec11f6dc3d726f052878d35a5ba798631febb1e9da63ee17e931c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000 +test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006881fc3200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763a00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004116f1e2e32d3f74da25c7ea0e8496015d83196892ff5764fea33c9579d747dd62693aed4e9df3bec11f6dc3d726f052878d35a5ba798631febb1e9da63ee17e931c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000 +test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006881fc3200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763a0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000419230b1e10d81bd25989b9bdca62288c5dca4c201a93dda4151c5248871b77b9033ac3daff2db697fba3c098735257acf083481113dae6a1542e73905512bc2b91b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000 test_uniswap_v3_curve:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000 -test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf00000000000000000000000000000000000000000000000000000000000006880f27500000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c7d00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041ddbcb13f2381def6b19a68af409abaefa505ddbc282c9db0a48bf53135726b7e603b248a314f46822406c9f406793673ff0e775fa584d41c3c08283f958a59d31c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 +test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf00000000000000000000000000000000000000000000000000000000000006881fc2f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763700000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004114d8f73bb8445a24689cb78618d20f4805be4427f9760c1c9f317929c4af95947f79b3886503ba3032e6920d1a74fb4441c6b7fa0c84d7a6798bd9167a8be4021c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0102 test_ekubo_encode_swap_multi:01ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032 test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c @@ -32,4 +32,9 @@ test_encode_uniswap_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e test_encode_balancer_v3:7bc3485026ac48b6cf9baf0a377477fff5703af8c71ea051a5f82c67adcf634c36ffe6334793d24c85b2b559bc2d21104c4defdd6efca8a20343361d011d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e test_single_encoding_strategy_balancer_v3:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000097ffedb80d4b2ca6105a07a4d90eb739c45a66600000000000000000000000030881baa943777f92dc934d53d3bfdf33382cab300000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000006503a6a84cd762d9707a21605b548aaab891562aab097ffedb80d4b2ca6105a07a4d90eb739c45a66630881baa943777f92dc934d53d3bfdf33382cab3f028ac624074d6793c36dc8a06ecec0f5a39a71800cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000 test_uniswap_v3_balancer_v3:e21dd0d3000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000004a220e6096b25eadb88358cb44068a324825467500000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d200692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed0000006503a6a84cd762d9707a21605b548aaab891562aab2260fac5e5542a773aa44fbcfedf7c193bc2c5994a220e6096b25eadb88358cb44068a3248254675571bea0e99e139cd0b6b7d9352ca872dfe0d72dd01cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000 -test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006880f27700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000068596c7f00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000411d60a13b5e993ef8088cb2cb7e16281f47fbc329bd0ec2a48165e7898542a7ed12fe7ffdec713b6d94e99ddf0a384674617b6190b8534491d525ecb090c8e4881c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 +test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006881fc3100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000685a763900000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041b665f58635b1444259d652b2a35e26ce28145c3e5bbce2d156ec7fd77699d0671abc9b9ce78cf95e9c65d704a8e453e06875cb354f033de7d2bb9b0e18ec7acd1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 +test_encode_bebop_aggregate:0000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480101000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000681773350000000000000000000000007078b12ca5b294d95e9ac16d90b7d38238d8f4e6000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000004c00000000000000000000000007078b12ca5b294d95e9ac16d90b7d38238d8f4e600000000000000000000000000000000000000000000000000000000000005a0d3bb6e37a886dc243affa93ce81c8a4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000067336cec42645f55059eff241cb02ea5cc52ff86000000000000000000000000bf19cbf0256f19f39a016a86ff3551ecc6f2aafe0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000001969b98b07c0000000000000000000000000000000000000000000000000000000000ebe7000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000014a614797ce1520000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000e58715d8cbeae00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000a1da6b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000070572e00000000000000000000000000000000000000000000000000000000000000040004000400000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041d5abb425f9bac1f44d48705f41a8ab9cae207517be8553d2c03b06a88995f2f351ab8ce7627a87048178d539dd64fd2380245531a0c8e43fdc614652b1f32fc71c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f38c698e48a3eac48f184bc324fef0b135ee13705ab38cc0bbf5a792f21002f051e445b9e7d57cf24c35e17629ea35b3263591c4abf8ca87ffa44b41301b89c41b0000000000000000000000000000000000000000000000000000000000000000 +test_encode_bebop_single:a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48faba6f8e4a5e8ab82f62fe7c39859fa577269be301000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000068470140000000000000000000000000c5564c13a157e6240659fb81882a28091add8670000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b000000000000000000000000000000000000000000000000000637256e698be1000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be3000000000000000000000000000000000000000000000000000000000bebc20000000000000000000000000000000000000000000000000cdbfbba0faafaf020000000000000000000000000c5564c13a157e6240659fb81882a28091add8670000000000000000000000000000000000000000000000000000000000000000072c75365fbad713c3dbdcac257c435540000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041eb5419631614978da217532a40f02a8f2ece37d8cfb94aaa602baabbdefb56b474f4c2048a0f56502caff4ea7411d99eed6027cd67dc1088aaf4181dcb0df7051c0000000000000000000000000000000000000000000000000000000000000001 +test_uniswap_v3_bebop:e21dd0d3000000000000000000000000000000000000000000000000015fb7f9b8c38000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be300000000000000000000000000000000000000000000000cdbfbba0faafaf02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c5564c13a157e6240659fb81882a28091add867000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000035400692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640000002e7d6bbde9174b1cdaa358d2cf4d57d1a9f7178fbffa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48faba6f8e4a5e8ab82f62fe7c39859fa577269be301000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000068470140000000000000000000000000c5564c13a157e6240659fb81882a28091add8670000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b000000000000000000000000000000000000000000000000000637256e698be1000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be3000000000000000000000000000000000000000000000000000000000bebc20000000000000000000000000000000000000000000000000cdbfbba0faafaf020000000000000000000000000c5564c13a157e6240659fb81882a28091add8670000000000000000000000000000000000000000000000000000000000000000072c75365fbad713c3dbdcac257c435540000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041eb5419631614978da217532a40f02a8f2ece37d8cfb94aaa602baabbdefb56b474f4c2048a0f56502caff4ea7411d99eed6027cd67dc1088aaf4181dcb0df7051c0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000 +test_single_encoding_strategy_bebop_aggregate:5c4b639c0000000000000000000000000000000000000000000000000022fe85d709a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000001123199000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007078b12ca5b294d95e9ac16d90b7d38238d8f4e6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000867d6bbde9174b1cdaa358d2cf4d57d1a9f7178fbff0000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480201000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000681773350000000000000000000000007078b12ca5b294d95e9ac16d90b7d38238d8f4e6000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000004c00000000000000000000000007078b12ca5b294d95e9ac16d90b7d38238d8f4e600000000000000000000000000000000000000000000000000000000000005a0d3bb6e37a886dc243affa93ce81c8a4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000067336cec42645f55059eff241cb02ea5cc52ff86000000000000000000000000bf19cbf0256f19f39a016a86ff3551ecc6f2aafe0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000001969b98b07c0000000000000000000000000000000000000000000000000000000000ebe7000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000014a614797ce1520000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000e58715d8cbeae00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000a1da6b0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000070572e00000000000000000000000000000000000000000000000000000000000000040004000400000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041d5abb425f9bac1f44d48705f41a8ab9cae207517be8553d2c03b06a88995f2f351ab8ce7627a87048178d539dd64fd2380245531a0c8e43fdc614652b1f32fc71c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f38c698e48a3eac48f184bc324fef0b135ee13705ab38cc0bbf5a792f21002f051e445b9e7d57cf24c35e17629ea35b3263591c4abf8ca87ffa44b41301b89c41b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_bebop:5c4b639c000000000000000000000000000000000000000000000000000000000bebc200000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be300000000000000000000000000000000000000000000000cdbfbba0faafaf02000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c5564c13a157e6240659fb81882a28091add86700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002e7d6bbde9174b1cdaa358d2cf4d57d1a9f7178fbffa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48faba6f8e4a5e8ab82f62fe7c39859fa577269be300000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000068470140000000000000000000000000c5564c13a157e6240659fb81882a28091add8670000000000000000000000000ce79b081c0c924cb67848723ed3057234d10fc6b000000000000000000000000000000000000000000000000000637256e698be1000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000faba6f8e4a5e8ab82f62fe7c39859fa577269be3000000000000000000000000000000000000000000000000000000000bebc20000000000000000000000000000000000000000000000000cdbfbba0faafaf020000000000000000000000000c5564c13a157e6240659fb81882a28091add8670000000000000000000000000000000000000000000000000000000000000000072c75365fbad713c3dbdcac257c435540000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041eb5419631614978da217532a40f02a8f2ece37d8cfb94aaa602baabbdefb56b474f4c2048a0f56502caff4ea7411d99eed6027cd67dc1088aaf4181dcb0df7051c000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000 diff --git a/foundry/test/protocols/BebopExecutor.t.sol b/foundry/test/protocols/Bebop.t.sol similarity index 85% rename from foundry/test/protocols/BebopExecutor.t.sol rename to foundry/test/protocols/Bebop.t.sol index 3958359..dd9c71d 100644 --- a/foundry/test/protocols/BebopExecutor.t.sol +++ b/foundry/test/protocols/Bebop.t.sol @@ -2,210 +2,15 @@ pragma solidity ^0.8.26; import "../TestUtils.sol"; +import "../TychoRouterTestSetup.sol"; +import "./BebopExecutionHarness.t.sol"; import "@src/executors/BebopExecutor.sol"; import {Constants} from "../Constants.sol"; -import {Permit2TestHelper} from "../Permit2TestHelper.sol"; -import {Test, console} from "forge-std/Test.sol"; -import {StdCheats} from "forge-std/StdCheats.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {Permit2TestHelper} from "../Permit2TestHelper.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -contract MockToken is ERC20 { - uint8 private _decimals; - - constructor(string memory name_, string memory symbol_, uint8 decimals_) - ERC20(name_, symbol_) - { - _decimals = decimals_; - } - - function mint(address to, uint256 amount) external { - _mint(to, amount); - } - - function decimals() public view virtual override returns (uint8) { - return _decimals; - } -} - -contract BebopExecutorHarness is BebopExecutor, Test { - using SafeERC20 for IERC20; - - constructor(address _bebopSettlement, address _permit2) - BebopExecutor(_bebopSettlement, _permit2) - {} - - // Expose the internal decodeData function for testing - function decodeParams(bytes calldata data) - external - pure - returns ( - address tokenIn, - address tokenOut, - RestrictTransferFrom.TransferType transferType, - BebopExecutor.OrderType orderType, - uint256 filledTakerAmount, - bytes memory quoteData, - bytes memory makerSignaturesData, - bool // approvalNeeded - unused in test harness - ) - { - return _decodeData(data); - } - - // Expose the internal getActualFilledTakerAmount function for testing - function exposed_getActualFilledTakerAmount( - uint256 givenAmount, - uint256 orderTakerAmount, - uint256 filledTakerAmount - ) external pure returns (uint256 actualFilledTakerAmount) { - return _getActualFilledTakerAmount( - givenAmount, orderTakerAmount, filledTakerAmount - ); - } - - // Override to prank the taker address before calling the real settlement - function _executeSingleRFQ( - address tokenIn, - address tokenOut, - TransferType transferType, - uint256 givenAmount, - uint256 filledTakerAmount, - bytes memory quoteData, - bytes memory makerSignaturesData, - bool approvalNeeded - ) internal virtual override returns (uint256 amountOut) { - // Decode the order from quoteData - IBebopSettlement.Single memory order = - abi.decode(quoteData, (IBebopSettlement.Single)); - - uint256 actualFilledTakerAmount = _getActualFilledTakerAmount( - givenAmount, order.taker_amount, filledTakerAmount - ); - - // For testing: transfer tokens from executor to taker address - // This simulates the taker having the tokens with approval - if (tokenIn != address(0)) { - _transfer( - address(this), transferType, tokenIn, actualFilledTakerAmount - ); - IERC20(tokenIn).safeTransfer( - order.taker_address, actualFilledTakerAmount - ); - - // Approve settlement from taker's perspective - // Stop any existing prank first - vm.stopPrank(); - vm.startPrank(order.taker_address); - IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max); - vm.stopPrank(); - } else { - vm.stopPrank(); - // For native ETH, send it to the taker address - payable(order.taker_address).transfer(actualFilledTakerAmount); - } - - // IMPORTANT: Prank as the taker address to pass the settlement validation - vm.stopPrank(); - vm.startPrank(order.taker_address); - - // Set block timestamp to ensure order is valid regardless of fork block - uint256 currentTimestamp = block.timestamp; - vm.warp(order.expiry - 1); // Set timestamp to just before expiry - - // Execute the single swap, let's test the actual settlement logic - amountOut = super._executeSingleRFQ( - tokenIn, - tokenOut, - TransferType.None, // We set transfer type to none for testing in order to keep the taker's balance unchanged as it will execute the swap - givenAmount, - filledTakerAmount, - quoteData, - makerSignaturesData, - approvalNeeded - ); - - // Restore original timestamp - vm.warp(currentTimestamp); - vm.stopPrank(); - } - - // Override to execute aggregate orders through the real settlement - function _executeAggregateRFQ( - address tokenIn, - address tokenOut, - TransferType transferType, - uint256 givenAmount, - uint256 filledTakerAmount, - bytes memory quoteData, - bytes memory makerSignaturesData, - bool approvalNeeded - ) internal virtual override returns (uint256 amountOut) { - // Decode the Aggregate order - IBebopSettlement.Aggregate memory order = - abi.decode(quoteData, (IBebopSettlement.Aggregate)); - - // For aggregate orders, calculate total taker amount across all amounts of the 2D array - uint256 totalTakerAmount = 0; - for (uint256 i = 0; i < order.taker_amounts.length; i++) { - for (uint256 j = 0; j < order.taker_amounts[i].length; j++) { - totalTakerAmount += order.taker_amounts[i][j]; - } - } - - uint256 actualFilledTakerAmount = _getActualFilledTakerAmount( - givenAmount, totalTakerAmount, filledTakerAmount - ); - - // For testing: transfer tokens from executor to taker address - // This simulates the taker having the tokens with approval - if (tokenIn != address(0)) { - _transfer( - address(this), transferType, tokenIn, actualFilledTakerAmount - ); - IERC20(tokenIn).safeTransfer( - order.taker_address, actualFilledTakerAmount - ); - - // Approve settlement from taker's perspective - // Stop any existing prank first - vm.stopPrank(); - vm.startPrank(order.taker_address); - IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max); - vm.stopPrank(); - } else { - vm.stopPrank(); - // For native ETH, send it to the taker address - payable(order.taker_address).transfer(actualFilledTakerAmount); - } - - // IMPORTANT: Prank as the taker address to pass the settlement validation - vm.stopPrank(); - vm.startPrank(order.taker_address); - - // Set block timestamp to ensure order is valid regardless of fork block - uint256 currentTimestamp = block.timestamp; - vm.warp(order.expiry - 1); // Set timestamp to just before expiry - - // Execute the aggregate swap, let's test the actual settlement logic - amountOut = super._executeAggregateRFQ( - tokenIn, - tokenOut, - TransferType.None, // We set transfer type to none for testing in order to keep the taker's balance unchanged as it will execute the swap - givenAmount, - filledTakerAmount, - quoteData, - makerSignaturesData, - approvalNeeded - ); - - // Restore original timestamp - vm.warp(currentTimestamp); - vm.stopPrank(); - } -} - contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils { using SafeERC20 for IERC20; @@ -1301,3 +1106,82 @@ contract BebopExecutorTest is Constants, Permit2TestHelper, TestUtils { } } } + +contract TychoRouterForBebopTest is TychoRouterTestSetup { + function testSingleBebopIntegration() public { + // The calldata swaps 200 USDC for ONDO + // The receiver in the order is 0xc5564C13A157E6240659fb81882A28091add8670 + address orderTaker = 0xc5564C13A157E6240659fb81882A28091add8670; + address maker = 0xCe79b081c0c924cb67848723ed3057234d10FC6b; + deal(USDC_ADDR, orderTaker, 200 * 10 ** 6); // 200 USDC + uint256 expAmountOut = 237212396774431060000; // Expected ONDO amount from calldata + + // Fund the maker with ONDO and approve settlement + deal(ONDO_ADDR, maker, expAmountOut); + vm.prank(maker); + IERC20(ONDO_ADDR).approve(BEBOP_SETTLEMENT, expAmountOut); + + uint256 ondoBefore = IERC20(ONDO_ADDR).balanceOf(orderTaker); + + vm.startPrank(orderTaker); + IERC20(USDC_ADDR).approve(tychoRouterAddr, type(uint256).max); + + // Load calldata from file + bytes memory callData = + loadCallDataFromFile("test_single_encoding_strategy_bebop"); + + (bool success,) = tychoRouterAddr.call(callData); + + // Check the receiver's balance (not ALICE, since the order specifies a different receiver) + uint256 ondoReceived = + IERC20(ONDO_ADDR).balanceOf(orderTaker) - ondoBefore; + assertTrue(success, "Call Failed"); + assertEq(ondoReceived, expAmountOut); + assertEq( + IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), + 0, + "USDC left in router" + ); + + vm.stopPrank(); + } + + function testBebopAggregateIntegration() public { + // Based on real transaction: https://etherscan.io/tx/0xec88410136c287280da87d0a37c1cb745f320406ca3ae55c678dec11996c1b1c + address orderTaker = 0x7078B12Ca5B294d95e9aC16D90B7D38238d8F4E6; // This is both taker and receiver in the order + uint256 ethAmount = 9850000000000000; // 0.00985 WETH + uint256 expAmountOut = 17969561; // 17.969561 USDC expected output + + // Fund the two makers from the real transaction with USDC + address maker1 = 0x67336Cec42645F55059EfF241Cb02eA5cC52fF86; + address maker2 = 0xBF19CbF0256f19f39A016a86Ff3551ecC6f2aAFE; + + deal(USDC_ADDR, maker1, 10607211); // Maker 1 provides 10.607211 USDC + deal(USDC_ADDR, maker2, 7362350); // Maker 2 provides 7.362350 USDC + + // Makers approve settlement contract + vm.prank(maker1); + IERC20(USDC_ADDR).approve(BEBOP_SETTLEMENT, type(uint256).max); + vm.prank(maker2); + IERC20(USDC_ADDR).approve(BEBOP_SETTLEMENT, type(uint256).max); + + // Fund ALICE with ETH as it will send the transaction + vm.deal(ALICE, ethAmount); + vm.startPrank(ALICE); + + // Load calldata from file + bytes memory callData = loadCallDataFromFile( + "test_single_encoding_strategy_bebop_aggregate" + ); + + // Execute the swap + (bool success,) = tychoRouterAddr.call{value: ethAmount}(callData); + uint256 finalBalance = IERC20(USDC_ADDR).balanceOf(orderTaker); + + assertTrue(success, "Call Failed"); + assertEq(finalBalance, expAmountOut); + assertEq(address(tychoRouterAddr).balance, 0, "ETH left in router"); + + vm.stopPrank(); + } +} diff --git a/foundry/test/protocols/BebopExecutionHarness.t.sol b/foundry/test/protocols/BebopExecutionHarness.t.sol new file mode 100644 index 0000000..17a89ae --- /dev/null +++ b/foundry/test/protocols/BebopExecutionHarness.t.sol @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.10; + +import "../../src/executors/BebopExecutor.sol"; +import {Test, console} from "forge-std/Test.sol"; + +contract BebopExecutorHarness is BebopExecutor, Test { + using SafeERC20 for IERC20; + + constructor(address _bebopSettlement, address _permit2) + BebopExecutor(_bebopSettlement, _permit2) + {} + + // Expose the internal decodeData function for testing + function decodeParams(bytes calldata data) + external + pure + returns ( + address tokenIn, + address tokenOut, + RestrictTransferFrom.TransferType transferType, + BebopExecutor.OrderType orderType, + uint256 filledTakerAmount, + bytes memory quoteData, + bytes memory makerSignaturesData, + bool // approvalNeeded - unused in test harness + ) + { + return _decodeData(data); + } + + // Expose the internal getActualFilledTakerAmount function for testing + function exposed_getActualFilledTakerAmount( + uint256 givenAmount, + uint256 orderTakerAmount, + uint256 filledTakerAmount + ) external pure returns (uint256 actualFilledTakerAmount) { + return _getActualFilledTakerAmount( + givenAmount, orderTakerAmount, filledTakerAmount + ); + } + + // Override to prank the taker address before calling the real settlement + function _executeSingleRFQ( + address tokenIn, + address tokenOut, + TransferType transferType, + uint256 givenAmount, + uint256 filledTakerAmount, + bytes memory quoteData, + bytes memory makerSignaturesData, + bool approvalNeeded + ) internal virtual override returns (uint256 amountOut) { + // Decode the order from quoteData + IBebopSettlement.Single memory order = + abi.decode(quoteData, (IBebopSettlement.Single)); + + uint256 actualFilledTakerAmount = _getActualFilledTakerAmount( + givenAmount, order.taker_amount, filledTakerAmount + ); + + // For testing: transfer tokens from executor to taker address + // This simulates the taker having the tokens with approval + if (tokenIn != address(0)) { + _transfer( + address(this), transferType, tokenIn, actualFilledTakerAmount + ); + IERC20(tokenIn).safeTransfer( + order.taker_address, actualFilledTakerAmount + ); + + // Approve settlement from taker's perspective + // Stop any existing prank first + vm.stopPrank(); + vm.startPrank(order.taker_address); + IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max); + vm.stopPrank(); + } else { + vm.stopPrank(); + // For native ETH, send it to the taker address + payable(order.taker_address).transfer(actualFilledTakerAmount); + } + + // IMPORTANT: Prank as the taker address to pass the settlement validation + vm.stopPrank(); + vm.startPrank(order.taker_address); + + // Set block timestamp to ensure order is valid regardless of fork block + uint256 currentTimestamp = block.timestamp; + vm.warp(order.expiry - 1); // Set timestamp to just before expiry + + // Execute the single swap, let's test the actual settlement logic + amountOut = super._executeSingleRFQ( + tokenIn, + tokenOut, + TransferType.None, // We set transfer type to none for testing in order to keep the taker's balance unchanged as it will execute the swap + givenAmount, + filledTakerAmount, + quoteData, + makerSignaturesData, + approvalNeeded + ); + + // Restore original timestamp + vm.warp(currentTimestamp); + vm.stopPrank(); + } + + // Override to execute aggregate orders through the real settlement + function _executeAggregateRFQ( + address tokenIn, + address tokenOut, + TransferType transferType, + uint256 givenAmount, + uint256 filledTakerAmount, + bytes memory quoteData, + bytes memory makerSignaturesData, + bool approvalNeeded + ) internal virtual override returns (uint256 amountOut) { + // Decode the Aggregate order + IBebopSettlement.Aggregate memory order = + abi.decode(quoteData, (IBebopSettlement.Aggregate)); + + // For aggregate orders, calculate total taker amount across all amounts of the 2D array + uint256 totalTakerAmount = 0; + for (uint256 i = 0; i < order.taker_amounts.length; i++) { + for (uint256 j = 0; j < order.taker_amounts[i].length; j++) { + totalTakerAmount += order.taker_amounts[i][j]; + } + } + + uint256 actualFilledTakerAmount = _getActualFilledTakerAmount( + givenAmount, totalTakerAmount, filledTakerAmount + ); + + // For testing: transfer tokens from executor to taker address + // This simulates the taker having the tokens with approval + if (tokenIn != address(0)) { + _transfer( + address(this), transferType, tokenIn, actualFilledTakerAmount + ); + IERC20(tokenIn).safeTransfer( + order.taker_address, actualFilledTakerAmount + ); + + // Approve settlement from taker's perspective + // Stop any existing prank first + vm.stopPrank(); + vm.startPrank(order.taker_address); + IERC20(tokenIn).forceApprove(bebopSettlement, type(uint256).max); + vm.stopPrank(); + } else { + vm.stopPrank(); + // For native ETH, send it to the taker address + payable(order.taker_address).transfer(actualFilledTakerAmount); + } + + // IMPORTANT: Prank as the taker address to pass the settlement validation + vm.stopPrank(); + vm.startPrank(order.taker_address); + + // Set block timestamp to ensure order is valid regardless of fork block + uint256 currentTimestamp = block.timestamp; + vm.warp(order.expiry - 1); // Set timestamp to just before expiry + + // Execute the aggregate swap, let's test the actual settlement logic + amountOut = super._executeAggregateRFQ( + tokenIn, + tokenOut, + TransferType.None, // We set transfer type to none for testing in order to keep the taker's balance unchanged as it will execute the swap + givenAmount, + filledTakerAmount, + quoteData, + makerSignaturesData, + approvalNeeded + ); + + // Restore original timestamp + vm.warp(currentTimestamp); + vm.stopPrank(); + } +} diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 53d6a5b..84440bc 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -776,8 +776,8 @@ impl SwapEncoder for BebopSwapEncoder { fn encode_swap( &self, - swap: Swap, - encoding_context: EncodingContext, + swap: &Swap, + encoding_context: &EncodingContext, ) -> Result, EncodingError> { let token_in = bytes_to_address(&swap.token_in)?; let token_out = bytes_to_address(&swap.token_out)?; @@ -785,8 +785,8 @@ impl SwapEncoder for BebopSwapEncoder { let token_approvals_manager = ProtocolApprovalsManager::new()?; let approval_needed: bool; - if let Some(router_address) = encoding_context.router_address { - let tycho_router_address = bytes_to_address(&router_address)?; + if let Some(router_address) = &encoding_context.router_address { + let tycho_router_address = bytes_to_address(router_address)?; let token_to_approve = token_in.clone(); let settlement_address = Address::from_str(&self.settlement_address) .map_err(|_| EncodingError::FatalError("Invalid settlement address".to_string()))?; @@ -809,7 +809,7 @@ impl SwapEncoder for BebopSwapEncoder { Self::validate_component_id(&swap.component.id)?; // Extract data from user_data (required for Bebop) - let user_data = swap.user_data.ok_or_else(|| { + let user_data = swap.user_data.clone().ok_or_else(|| { EncodingError::InvalidInput( "Bebop swaps require user_data with quote and signature".to_string(), ) @@ -2202,7 +2202,7 @@ mod tests { .unwrap(); let encoded_swap = encoder - .encode_swap(swap, encoding_context) + .encode_swap(&swap, &encoding_context) .unwrap(); let hex_swap = encode(&encoded_swap); @@ -2352,7 +2352,7 @@ mod tests { .unwrap(); let encoded_swap = encoder - .encode_swap(swap, encoding_context) + .encode_swap(&swap, &encoding_context) .unwrap(); let hex_swap = encode(&encoded_swap); diff --git a/src/encoding/evm/tycho_encoders.rs b/src/encoding/evm/tycho_encoders.rs index 75b6bd1..4ecc366 100644 --- a/src/encoding/evm/tycho_encoders.rs +++ b/src/encoding/evm/tycho_encoders.rs @@ -416,10 +416,6 @@ mod tests { Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap() } - fn ondo() -> Bytes { - Bytes::from_str("0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3").unwrap() - } - // Fee and tick spacing information for this test is obtained by querying the // USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e // Using the poolKeys function with the first 25 bytes of the pool id @@ -1273,2361 +1269,4 @@ mod tests { ); } } - - mod integration { - use std::{collections::HashMap, str::FromStr}; - - use alloy::{ - hex::encode, - primitives::{hex, Address, B256, U256}, - signers::local::PrivateKeySigner, - sol_types::SolValue, - }; - use num_bigint::{BigInt, BigUint}; - use tycho_common::{models::protocol::ProtocolComponent, Bytes}; - - use super::*; - use crate::encoding::evm::utils::{biguint_to_u256, write_calldata_to_file}; - - fn get_signer() -> PrivateKeySigner { - // Set up a mock private key for signing (Alice's pk in our contract tests) - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - - let pk = B256::from_str(&private_key).unwrap(); - PrivateKeySigner::from_bytes(&pk).unwrap() - } - - mod single { - use super::*; - - #[test] - fn test_single_swap_strategy_encoder() { - // Performs a single swap from WETH to DAI on a USV2 pool, with no grouping - // optimizations. - let checked_amount = BigUint::from_str("2018817438608734439720").unwrap(); - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); - - let swap = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: dai.clone(), - split: 0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: dai, - checked_amount: checked_amount.clone(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solutions = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solutions[0].clone(), - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - let expected_min_amount_encoded = - encode(U256::abi_encode(&biguint_to_u256(&checked_amount))); - let expected_input = [ - "30ace1b1", // Function selector - "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in - "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out - &expected_min_amount_encoded, // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - ] - .join(""); - - // after this there is the permit and because of the deadlines (that depend on block - // time) it's hard to assert - - let expected_swap = String::from(concat!( - // length of encoded swap without padding - "0000000000000000000000000000000000000000000000000000000000000052", - // Swap data - "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "00", // zero2one - "00", // transfer type TransferFrom - "0000000000000000000000000000", // padding - )); - let hex_calldata = encode(&calldata); - - assert_eq!(hex_calldata[..456], expected_input); - assert_eq!(hex_calldata[1224..], expected_swap); - write_calldata_to_file( - "test_single_swap_strategy_encoder", - &hex_calldata.to_string(), - ); - } - - #[test] - fn test_single_swap_strategy_encoder_no_permit2() { - // Performs a single swap from WETH to DAI on a USV2 pool, without permit2 and no - // grouping optimizations. - - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); - - let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap(); - let expected_min_amount = U256::from_str("1_640_000000000000000000").unwrap(); - - let swap = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: dai.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: dai, - checked_amount, - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - let expected_min_amount_encoded = encode(U256::abi_encode(&expected_min_amount)); - let expected_input = [ - "5c4b639c", // Function selector - "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in - "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out - &expected_min_amount_encoded, // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "0000000000000000000000000000000000000000000000000000000000000001", // transfer from needed - "0000000000000000000000000000000000000000000000000000000000000120", // offset of swap bytes - "0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding - - // Swap data - "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "00", // zero2one - "00", // transfer type TransferFrom - "0000000000000000000000000000", // padding - ] - .join(""); - - let hex_calldata = encode(&calldata); - - assert_eq!(hex_calldata, expected_input); - write_calldata_to_file( - "test_single_swap_strategy_encoder_no_permit2", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_swap_strategy_encoder_no_transfer_in() { - // Performs a single swap from WETH to DAI on a USV2 pool assuming that the tokens - // are already in the router - - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); - - let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap(); - let expected_min_amount = U256::from_str("1_640_000000000000000000").unwrap(); - - let swap = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: dai.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::None); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: dai, - checked_amount, - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::None, - eth(), - None, - ) - .unwrap() - .data; - let expected_min_amount_encoded = encode(U256::abi_encode(&expected_min_amount)); - let expected_input = [ - "5c4b639c", // Function selector - "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in - "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out - &expected_min_amount_encoded, // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "0000000000000000000000000000000000000000000000000000000000000000", // transfer from not needed - "0000000000000000000000000000000000000000000000000000000000000120", // offset of swap bytes - "0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding - - // Swap data - "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "00", // zero2one - "01", // transfer type Transfer - "0000000000000000000000000000", // padding - ] - .join(""); - - let hex_calldata = encode(&calldata); - - assert_eq!(hex_calldata, expected_input); - write_calldata_to_file( - "test_single_swap_strategy_encoder_no_transfer_in", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_swap_strategy_encoder_wrap() { - // Performs a single swap from WETH to DAI on a USV2 pool, wrapping ETH - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); - - let swap = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth(), - token_out: dai.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: eth(), - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: dai, - checked_amount: BigUint::from_str("1659881924818443699787").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap], - native_action: Some(NativeAction::Wrap), - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_swap_strategy_encoder_wrap", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_swap_strategy_encoder_unwrap() { - // Performs a single swap from DAI to WETH on a USV2 pool, unwrapping ETH at the end - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); - - let swap = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: dai.clone(), - token_out: weth(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: dai, - given_amount: BigUint::from_str("3_000_000000000000000000").unwrap(), - checked_token: eth(), - checked_amount: BigUint::from_str("1_000000000000000000").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap], - native_action: Some(NativeAction::Unwrap), - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_swap_strategy_encoder_unwrap", - hex_calldata.as_str(), - ); - } - } - - mod sequential { - use super::*; - - #[test] - fn test_sequential_swap_strategy_encoder() { - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - // - // Performs a sequential swap from WETH to USDC though WBTC using USV2 pools - // - // WETH ───(USV2)──> WBTC ───(USV2)──> USDC - - let weth = weth(); - let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - user_data: None, - }; - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_sequential_swap_strategy_encoder", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_sequential_swap_strategy_encoder_no_permit2() { - // Performs a sequential swap from WETH to USDC though WBTC using USV2 pools - // - // WETH ───(USV2)──> WBTC ───(USV2)──> USDC - - let weth = weth(); - let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - user_data: None, - }; - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - - let expected = String::from(concat!( - "e21dd0d3", /* function selector */ - "0000000000000000000000000000000000000000000000000de0b6b3a7640000", /* amount in */ - "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token ou - "00000000000000000000000000000000000000000000000000000000018f61ec", /* min amount out */ - "0000000000000000000000000000000000000000000000000000000000000000", // wrap - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "0000000000000000000000000000000000000000000000000000000000000001", /* transfer from needed */ - "0000000000000000000000000000000000000000000000000000000000000120", /* length ple - * encode */ - "00000000000000000000000000000000000000000000000000000000000000a8", - // swap 1 - "0052", // swap length - "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "bb2b8038a1640196fbe3e38816f3e67cba72d940", // component id - "004375dff511095cc5a197a54140a24efef3a416", // receiver (next pool) - "00", // zero to one - "00", // transfer type TransferFrom - // swap 2 - "0052", // swap length - "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address - "2260fac5e5542a773aa44fbcfedf7c193bc2c599", // token in - "004375dff511095cc5a197a54140a24efef3a416", // component id - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver (final user) - "01", // zero to one - "02", // transfer type None - "000000000000000000000000000000000000000000000000", // padding - )); - - assert_eq!(hex_calldata, expected); - write_calldata_to_file( - "test_sequential_swap_strategy_encoder_no_permit2", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_sequential_strategy_cyclic_swap() { - // This test has start and end tokens that are the same - // The flow is: - // USDC -> WETH -> USDC using two pools - - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - // Create two Uniswap V3 pools for the cyclic swap - // USDC -> WETH (Pool 1) - let swap_usdc_weth = Swap { - component: ProtocolComponent { - id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* USDC-WETH USV3 - * Pool 1 */ - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(500).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: weth.clone(), - split: 0f64, - user_data: None, - }; - - // WETH -> USDC (Pool 2) - let swap_weth_usdc = Swap { - component: ProtocolComponent { - id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), /* USDC-WETH USV3 - * Pool 2 */ - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: usdc.clone(), - split: 0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: usdc.clone(), - given_amount: BigUint::from_str("100000000").unwrap(), // 100 USDC (6 decimals) - checked_token: usdc.clone(), - checked_amount: BigUint::from_str("99389294").unwrap(), /* Expected output - * from test */ - swaps: vec![swap_usdc_weth, swap_weth_usdc], - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - let hex_calldata = hex::encode(&calldata); - let expected_input = [ - "51bcc7b6", // selector - "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token - "0000000000000000000000000000000000000000000000000000000005ec8f6e", // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap action - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - ] - .join(""); - - let expected_swaps = [ - "00000000000000000000000000000000000000000000000000000000000000d6", // length of ple encoded swaps without padding - "0069", // ple encoded swaps - "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out - "0001f4", // pool fee - "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver - "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id - "01", // zero2one - "00", // transfer type TransferFrom - "0069", // ple encoded swaps - "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out - "000bb8", // pool fee - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id - "00", // zero2one - "01", // transfer type Transfer - "00000000000000000000", // padding - ] - .join(""); - - assert_eq!(hex_calldata[..456], expected_input); - assert_eq!(hex_calldata[1224..], expected_swaps); - write_calldata_to_file( - "test_sequential_strategy_cyclic_swap", - hex_calldata.as_str(), - ); - } - - mod optimized_transfers { - // In this module we test the ability to chain swaps or not. Different protocols are - // tested. The encoded data is used for solidity tests as well - use super::*; - - #[test] - fn test_uniswap_v3_uniswap_v2() { - // Note: This test does not assert anything. It is only used to obtain - // integration test data for our router solidity test. - // - // Performs a sequential swap from WETH to USDC though WBTC using USV3 and USV2 - // pools - // - // WETH ───(USV3)──> WBTC ───(USV2)──> USDC - - let weth = weth(); - let wbtc = - Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = - Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD".to_string(), - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - user_data: None, - }; - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_uniswap_v3_uniswap_v2", hex_calldata.as_str()); - } - - #[test] - fn test_uniswap_v3_uniswap_v3() { - // Note: This test does not assert anything. It is only used to obtain - // integration test data for our router solidity test. - // - // Performs a sequential swap from WETH to USDC though WBTC using USV3 pools - // There is no optimization between the two USV3 pools, this is currently not - // supported. - // - // WETH ───(USV3)──> WBTC ───(USV3)──> USDC - - let weth = weth(); - let wbtc = - Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = - Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD".to_string(), - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - user_data: None, - }; - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x99ac8cA7087fA4A2A1FB6357269965A2014ABc35".to_string(), - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_uniswap_v3_uniswap_v3", hex_calldata.as_str()); - } - - #[test] - fn test_uniswap_v3_curve() { - // Note: This test does not assert anything. It is only used to obtain - // integration test data for our router solidity test. - // - // Performs a sequential swap from WETH to USDT though WBTC using USV3 and curve - // pools - // - // WETH ───(USV3)──> WBTC ───(curve)──> USDT - - let weth = weth(); - let wbtc = - Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdt = - Bytes::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD".to_string(), - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - user_data: None, - }; - - let swap_wbtc_usdt = Swap { - component: ProtocolComponent { - id: String::from("0xD51a44d3FaE010294C616388b506AcdA1bfAAE46"), - protocol_system: String::from("vm:curve"), - static_attributes: { - let mut attrs: HashMap = HashMap::new(); - attrs.insert( - "factory".into(), - Bytes::from( - "0x0000000000000000000000000000000000000000" - .as_bytes() - .to_vec(), - ), - ); - attrs.insert( - "coins".into(), - Bytes::from_str("0x5b22307864616331376639353864326565353233613232303632303639393435393763313364383331656337222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307863303261616133396232323366653864306130653563346632376561643930383363373536636332225d") - .unwrap(), - ); - attrs - }, - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdt.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdt, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdt], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_uniswap_v3_curve", hex_calldata.as_str()); - } - - #[test] - fn test_balancer_v2_uniswap_v2() { - // Note: This test does not assert anything. It is only used to obtain - // integration test data for our router solidity test. - // - // Performs a sequential swap from WETH to USDC though WBTC using balancer and - // USV2 pools - // - // WETH ───(balancer)──> WBTC ───(USV2)──> USDC - - let weth = weth(); - let wbtc = - Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = - Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: - "0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e" - .to_string(), - protocol_system: "vm:balancer_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - user_data: None, - }; - - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_balancer_v2_uniswap_v2", hex_calldata.as_str()); - } - - #[test] - fn test_multi_protocol() { - // Note: This test does not assert anything. It is only used to obtain - // integration test data for our router solidity test. - // - // Performs the following swap: - // - // DAI ─(USV2)-> WETH ─(bal)─> WBTC ─(curve)─> USDT ─(ekubo)─> USDC ─(USV4)─> - // ETH - - let weth = weth(); - let eth = eth(); - let wbtc = - Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = - Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - let usdt = - Bytes::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap(); - let dai = - Bytes::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F").unwrap(); - - let usv2_swap_dai_weth = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: dai.clone(), - token_out: weth.clone(), - split: 0f64, - user_data: None, - }; - - let balancer_swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: - "0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e" - .to_string(), - protocol_system: "vm:balancer_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - user_data: None, - }; - - let curve_swap_wbtc_usdt = Swap { - component: ProtocolComponent { - id: String::from("0xD51a44d3FaE010294C616388b506AcdA1bfAAE46"), - protocol_system: String::from("vm:curve"), - static_attributes: { - let mut attrs: HashMap = HashMap::new(); - attrs.insert( - "factory".into(), - Bytes::from( - "0x0000000000000000000000000000000000000000" - .as_bytes() - .to_vec(), - ), - ); - attrs.insert( - "coins".into(), - Bytes::from_str("0x5b22307864616331376639353864326565353233613232303632303639393435393763313364383331656337222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307863303261616133396232323366653864306130653563346632376561643930383363373536636332225d") - .unwrap(), - ); - attrs - }, - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdt.clone(), - split: 0f64, - user_data: None, - }; - - // Ekubo - - let component = ProtocolComponent { - // All Ekubo swaps go through the core contract - not necessary to specify - // pool id for test - protocol_system: "ekubo_v2".to_string(), - // 0.0025% fee & 0.005% base pool - static_attributes: HashMap::from([ - ("fee".to_string(), Bytes::from(461168601842738_u64)), - ("tick_spacing".to_string(), Bytes::from(50_u32)), - ("extension".to_string(), Bytes::zero(20)), - ]), - ..Default::default() - }; - let ekubo_swap_usdt_usdc = Swap { - component, - token_in: usdt.clone(), - token_out: usdc.clone(), - split: 0f64, - user_data: None, - }; - - // USV4 - // Fee and tick spacing information for this test is obtained by querying the - // USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e - // Using the poolKeys function with the first 25 bytes of the pool id - let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be()); - let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be()); - let mut static_attributes_usdc_eth: HashMap = HashMap::new(); - static_attributes_usdc_eth.insert("key_lp_fee".into(), pool_fee_usdc_eth); - static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth); - - let usv4_swap_usdc_eth = Swap { - component: ProtocolComponent { - id: - "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d" - .to_string(), - protocol_system: "uniswap_v4".to_string(), - static_attributes: static_attributes_usdc_eth, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: eth.clone(), - split: 0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - // Put all components together - let solution = Solution { - exact_out: false, - given_token: dai, - given_amount: BigUint::from_str("1500_000000000000000000").unwrap(), - checked_token: eth.clone(), - checked_amount: BigUint::from_str("732214216964381330").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![ - usv2_swap_dai_weth, - balancer_swap_weth_wbtc, - curve_swap_wbtc_usdt, - ekubo_swap_usdt_usdc, - usv4_swap_usdc_eth, - ], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth, - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_multi_protocol", hex_calldata.as_str()); - } - - #[test] - fn test_uniswap_v3_balancer_v3() { - // Note: This test does not assert anything. It is only used to obtain - // integration test data for our router solidity test. - // - // WETH ───(USV3)──> WBTC ───(balancer v3)──> QNT - - let weth = weth(); - let wbtc = - Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let qnt = - Bytes::from_str("0x4a220e6096b25eadb88358cb44068a3248254675").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD".to_string(), - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - user_data: None, - }; - let swap_wbtc_qnt = Swap { - component: ProtocolComponent { - id: "0x571bea0e99e139cd0b6b7d9352ca872dfe0d72dd".to_string(), - protocol_system: "vm:balancer_v3".to_string(), - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: qnt.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_0000000000000000").unwrap(), - checked_token: qnt, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_qnt], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_uniswap_v3_balancer_v3", hex_calldata.as_str()); - } - } - } - - mod split { - use super::*; - - #[test] - fn test_split_swap_strategy_encoder() { - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - // - // Performs a split swap from WETH to USDC though WBTC and DAI using USV2 pools - // - // ┌──(USV2)──> WBTC ───(USV2)──> USDC - // WETH ─┤ - // └──(USV2)──> DAI ───(USV2)──> USDC - // - - let weth = weth(); - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); - let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_dai = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: dai.clone(), - split: 0.5f64, - user_data: None, - }; - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - // This represents the remaining 50%, but to avoid any rounding errors we set - // this to 0 to signify "the remainder of the WETH value". - // It should still be very close to 50% - split: 0f64, - user_data: None, - }; - let swap_dai_usdc = Swap { - component: ProtocolComponent { - id: "0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: dai.clone(), - token_out: usdc.clone(), - split: 0f64, - user_data: None, - }; - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_split_swap_strategy_encoder", hex_calldata.as_str()); - } - - #[test] - fn test_split_input_cyclic_swap() { - // This test has start and end tokens that are the same - // The flow is: - // ┌─ (USV3, 60% split) ──> WETH ─┐ - // │ │ - // USDC ──────┤ ├──(USV2)──> USDC - // │ │ - // └─ (USV3, 40% split) ──> WETH ─┘ - - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - // USDC -> WETH (Pool 1) - 60% of input - let swap_usdc_weth_pool1 = Swap { - component: ProtocolComponent { - id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* USDC-WETH USV3 - * Pool 1 */ - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(500).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: weth.clone(), - split: 0.6f64, // 60% of input - user_data: None, - }; - - // USDC -> WETH (Pool 2) - 40% of input (remaining) - let swap_usdc_weth_pool2 = Swap { - component: ProtocolComponent { - id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), /* USDC-WETH USV3 - * Pool 2 */ - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: weth.clone(), - split: 0f64, - user_data: None, // Remaining 40% - }; - - // WETH -> USDC (Pool 2) - let swap_weth_usdc_pool2 = Swap { - component: ProtocolComponent { - id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), /* USDC-WETH USV2 - * Pool 2 */ - protocol_system: "uniswap_v2".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: usdc.clone(), - split: 0.0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: usdc.clone(), - given_amount: BigUint::from_str("100000000").unwrap(), // 100 USDC (6 decimals) - checked_token: usdc.clone(), - checked_amount: BigUint::from_str("99574171").unwrap(), /* Expected output - * from - * test */ - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_usdc_weth_pool1, swap_usdc_weth_pool2, swap_weth_usdc_pool2], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = hex::encode(&calldata); - let expected_input = [ - "7c553846", // selector - "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token - "0000000000000000000000000000000000000000000000000000000005ef619b", // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap action - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action - "0000000000000000000000000000000000000000000000000000000000000002", // tokens length - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - ] - .join(""); - let expected_swaps = [ - "0000000000000000000000000000000000000000000000000000000000000139", // length of ple encoded swaps without padding - "006e", // ple encoded swaps - "00", // token in index - "01", // token out index - "999999", // split - "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out - "0001f4", // pool fee - "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver - "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id - "01", // zero2one - "00", // transfer type TransferFrom - "006e", // ple encoded swaps - "00", // token in index - "01", // token out index - "000000", // split - "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out - "000bb8", // pool fee - "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver - "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id - "01", // zero2one - "00", // transfer type TransferFrom - "0057", // ple encoded swaps - "01", // token in index - "00", // token out index - "000000", // split - "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address, - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id, - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "00", // zero2one - "01", // transfer type Transfer - "00000000000000" // padding - ] - .join(""); - assert_eq!(hex_calldata[..520], expected_input); - assert_eq!(hex_calldata[1288..], expected_swaps); - write_calldata_to_file("test_split_input_cyclic_swap", hex_calldata.as_str()); - } - - #[test] - fn test_split_output_cyclic_swap() { - // This test has start and end tokens that are the same - // The flow is: - // ┌─── (USV3, 60% split) ───┐ - // │ │ - // USDC ──(USV2) ── WETH──| ├─> USDC - // │ │ - // └─── (USV3, 40% split) ───┘ - - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_usdc_weth_v2 = Swap { - component: ProtocolComponent { - id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), /* USDC-WETH USV2 */ - protocol_system: "uniswap_v2".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(500).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: weth.clone(), - split: 0.0f64, - user_data: None, - }; - - let swap_weth_usdc_v3_pool1 = Swap { - component: ProtocolComponent { - id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* USDC-WETH USV3 - * Pool 1 */ - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(500).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: usdc.clone(), - split: 0.6f64, - user_data: None, - }; - - let swap_weth_usdc_v3_pool2 = Swap { - component: ProtocolComponent { - id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), /* USDC-WETH USV3 - * Pool 2 */ - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: usdc.clone(), - split: 0.0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: usdc.clone(), - given_amount: BigUint::from_str("100000000").unwrap(), // 100 USDC (6 decimals) - checked_token: usdc.clone(), - checked_amount: BigUint::from_str("99025908").unwrap(), /* Expected output - * from - * test */ - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![ - swap_usdc_weth_v2, - swap_weth_usdc_v3_pool1, - swap_weth_usdc_v3_pool2, - ], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = hex::encode(&calldata); - let expected_input = [ - "7c553846", // selector - "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token - "0000000000000000000000000000000000000000000000000000000005e703f4", // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap action - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action - "0000000000000000000000000000000000000000000000000000000000000002", // tokens length - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - ] - .join(""); - - let expected_swaps = [ - "0000000000000000000000000000000000000000000000000000000000000139", // length of ple encoded swaps without padding - "0057", // ple encoded swaps - "00", // token in index - "01", // token out index - "000000", // split - "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in - "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id - "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver - "01", // zero2one - "00", // transfer type TransferFrom - "006e", // ple encoded swaps - "01", // token in index - "00", // token out index - "999999", // split - "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out - "0001f4", // pool fee - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id - "00", // zero2one - "01", // transfer type Transfer - "006e", // ple encoded swaps - "01", // token in index - "00", // token out index - "000000", // split - "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out - "000bb8", // pool fee - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id - "00", // zero2one - "01", // transfer type Transfer - "00000000000000" // padding - ] - .join(""); - - assert_eq!(hex_calldata[..520], expected_input); - assert_eq!(hex_calldata[1288..], expected_swaps); - write_calldata_to_file("test_split_output_cyclic_swap", hex_calldata.as_str()); - } - } - - mod protocol_integration { - // in this module we test protocol specific logic by creating the calldata that then is - // used in the solidity tests - use super::*; - - #[test] - fn test_single_encoding_strategy_ekubo() { - // ETH ──(EKUBO)──> USDC - - let token_in = Bytes::from(Address::ZERO.as_slice()); - let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC - - let static_attributes = HashMap::from([ - ("fee".to_string(), Bytes::from(0_u64)), - ("tick_spacing".to_string(), Bytes::from(0_u32)), - ( - "extension".to_string(), - Bytes::from("0x51d02a5948496a67827242eabc5725531342527c"), - ), /* Oracle */ - ]); - - let component = ProtocolComponent { - // All Ekubo swaps go through the core contract - not necessary to specify pool - // id for test - protocol_system: "ekubo_v2".to_string(), - static_attributes, - ..Default::default() - }; - - let swap = Swap { - component, - token_in: token_in.clone(), - token_out: token_out.clone(), - split: 0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: token_in, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: token_out, - checked_amount: BigUint::from_str("1000").unwrap(), - // Alice - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_encoding_strategy_ekubo", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_encoding_strategy_maverick() { - // GHO -> (maverick) -> USDC - let maverick_pool = ProtocolComponent { - id: String::from("0x14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67"), - protocol_system: String::from("vm:maverick_v2"), - ..Default::default() - }; - let token_in = Bytes::from("0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f"); - let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); - let swap = Swap { - component: maverick_pool, - token_in: token_in.clone(), - token_out: token_out.clone(), - split: 0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: token_in, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: token_out, - checked_amount: BigUint::from_str("1000").unwrap(), - // Alice - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_encoding_strategy_maverick", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_encoding_strategy_usv4_eth_in() { - // Performs a single swap from ETH to PEPE using a USV4 pool - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - // - // ETH ───(USV4)──> PEPE - // - let eth = eth(); - let pepe = Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap(); - - let pool_fee_eth_pepe = Bytes::from(BigInt::from(25000).to_signed_bytes_be()); - let tick_spacing_eth_pepe = Bytes::from(BigInt::from(500).to_signed_bytes_be()); - let mut static_attributes_eth_pepe: HashMap = HashMap::new(); - static_attributes_eth_pepe.insert("key_lp_fee".into(), pool_fee_eth_pepe); - static_attributes_eth_pepe.insert("tick_spacing".into(), tick_spacing_eth_pepe); - - let swap_eth_pepe = Swap { - component: ProtocolComponent { - id: "0xecd73ecbf77219f21f129c8836d5d686bbc27d264742ddad620500e3e548e2c9" - .to_string(), - protocol_system: "uniswap_v4".to_string(), - static_attributes: static_attributes_eth_pepe, - ..Default::default() - }, - token_in: eth.clone(), - token_out: pepe.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: eth.clone(), - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: pepe, - checked_amount: BigUint::from_str("152373460199848577067005852").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_eth_pepe], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth, - Some(get_signer()), - ) - .unwrap() - .data; - let hex_calldata = encode(&calldata); - - write_calldata_to_file( - "test_single_encoding_strategy_usv4_eth_in", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_encoding_strategy_usv4_eth_out() { - // Performs a single swap from USDC to ETH using a USV4 pool - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - // - // USDC ───(USV4)──> ETH - // - let eth = eth(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - // Fee and tick spacing information for this test is obtained by querying the - // USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e - // Using the poolKeys function with the first 25 bytes of the pool id - let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be()); - let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be()); - let mut static_attributes_usdc_eth: HashMap = HashMap::new(); - static_attributes_usdc_eth.insert("key_lp_fee".into(), pool_fee_usdc_eth); - static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth); - - let swap_usdc_eth = Swap { - component: ProtocolComponent { - id: "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d" - .to_string(), - protocol_system: "uniswap_v4".to_string(), - static_attributes: static_attributes_usdc_eth, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: eth.clone(), - split: 0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: usdc, - given_amount: BigUint::from_str("3000_000000").unwrap(), - checked_token: eth.clone(), - checked_amount: BigUint::from_str("1117254495486192350").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_usdc_eth], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth, - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_encoding_strategy_usv4_eth_out", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_encoding_strategy_usv4_grouped_swap() { - // Performs a sequential swap from USDC to PEPE though ETH using two consecutive - // USV4 pools - // - // USDC ──(USV4)──> ETH ───(USV4)──> PEPE - // - - let eth = eth(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - let pepe = Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap(); - - // Fee and tick spacing information for this test is obtained by querying the - // USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e - // Using the poolKeys function with the first 25 bytes of the pool id - let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be()); - let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be()); - let mut static_attributes_usdc_eth: HashMap = HashMap::new(); - static_attributes_usdc_eth.insert("key_lp_fee".into(), pool_fee_usdc_eth); - static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth); - - let pool_fee_eth_pepe = Bytes::from(BigInt::from(25000).to_signed_bytes_be()); - let tick_spacing_eth_pepe = Bytes::from(BigInt::from(500).to_signed_bytes_be()); - let mut static_attributes_eth_pepe: HashMap = HashMap::new(); - static_attributes_eth_pepe.insert("key_lp_fee".into(), pool_fee_eth_pepe); - static_attributes_eth_pepe.insert("tick_spacing".into(), tick_spacing_eth_pepe); - - let swap_usdc_eth = Swap { - component: ProtocolComponent { - id: "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d" - .to_string(), - protocol_system: "uniswap_v4".to_string(), - static_attributes: static_attributes_usdc_eth, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: eth.clone(), - split: 0f64, - user_data: None, - }; - - let swap_eth_pepe = Swap { - component: ProtocolComponent { - id: "0xecd73ecbf77219f21f129c8836d5d686bbc27d264742ddad620500e3e548e2c9" - .to_string(), - protocol_system: "uniswap_v4".to_string(), - static_attributes: static_attributes_eth_pepe, - ..Default::default() - }, - token_in: eth.clone(), - token_out: pepe.clone(), - split: 0f64, - user_data: None, - }; - let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); - - let solution = Solution { - exact_out: false, - given_token: usdc, - given_amount: BigUint::from_str("1000_000000").unwrap(), - checked_token: pepe, - checked_amount: BigUint::from_str("97191013220606467325121599").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_usdc_eth, swap_eth_pepe], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth, - Some(get_signer()), - ) - .unwrap() - .data; - - let expected_input = [ - "30ace1b1", // Function selector (single swap) - "000000000000000000000000000000000000000000000000000000003b9aca00", // amount in - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in - "0000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933", // token out - "0000000000000000000000000000000000000000005064ff624d54346285543f", // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - ] - .join(""); - - // after this there is the permit and because of the deadlines (that depend on block - // time) it's hard to assert - - let expected_swaps = String::from(concat!( - // length of ple encoded swaps without padding - "0000000000000000000000000000000000000000000000000000000000000086", - // Swap data header - "f62849f9a0b5bf2913b396098f7c7019b51a820a", // executor address - // Protocol data - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // group token in - "6982508145454ce325ddbe47a25d4ec3d2311933", // group token in - "00", // zero2one - "00", // transfer type TransferFrom - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - // First pool params - "0000000000000000000000000000000000000000", // intermediary token (ETH) - "000bb8", // fee - "00003c", // tick spacing - // Second pool params - "6982508145454ce325ddbe47a25d4ec3d2311933", // intermediary token (PEPE) - "0061a8", // fee - "0001f4", // tick spacing - "0000000000000000000000000000000000000000000000000000" // padding - )); - - let hex_calldata = encode(&calldata); - - assert_eq!(hex_calldata[..456], expected_input); - assert_eq!(hex_calldata[1224..], expected_swaps); - write_calldata_to_file( - "test_single_encoding_strategy_usv4_grouped_swap", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_encoding_strategy_curve() { - // UWU ──(curve 2 crypto pool)──> WETH - - let token_in = Bytes::from("0x55C08ca52497e2f1534B59E2917BF524D4765257"); // UWU - let token_out = Bytes::from("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); // WETH - - let static_attributes = HashMap::from([( - "factory".to_string(), - Bytes::from( - "0x98ee851a00abee0d95d08cf4ca2bdce32aeaaf7f" - .as_bytes() - .to_vec(), - )), - ("coins".to_string(), Bytes::from_str("0x5b22307863303261616133396232323366653864306130653563346632376561643930383363373536636332222c22307835356330386361353234393765326631353334623539653239313762663532346434373635323537225d").unwrap()), - ]); - - let component = ProtocolComponent { - id: String::from("0x77146B0a1d08B6844376dF6d9da99bA7F1b19e71"), - protocol_system: String::from("vm:curve"), - static_attributes, - ..Default::default() - }; - - let swap = Swap { - component, - token_in: token_in.clone(), - token_out: token_out.clone(), - split: 0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: token_in, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: token_out, - checked_amount: BigUint::from_str("1").unwrap(), - // Alice - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_encoding_strategy_curve", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_encoding_strategy_curve_st_eth() { - // ETH ──(curve stETH pool)──> STETH - - let token_in = Bytes::from("0x0000000000000000000000000000000000000000"); // ETH - let token_out = Bytes::from("0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"); // STETH - - let static_attributes = HashMap::from([( - "factory".to_string(), - Bytes::from( - "0x0000000000000000000000000000000000000000" - .as_bytes() - .to_vec(), - ), - ), - ("coins".to_string(), Bytes::from_str("0x5b22307865656565656565656565656565656565656565656565656565656565656565656565656565656565222c22307861653761623936353230646533613138653565313131623565616162303935333132643766653834225d").unwrap()),]); - - let component = ProtocolComponent { - id: String::from("0xDC24316b9AE028F1497c275EB9192a3Ea0f67022"), - protocol_system: String::from("vm:curve"), - static_attributes, - ..Default::default() - }; - - let swap = Swap { - component, - token_in: token_in.clone(), - token_out: token_out.clone(), - split: 0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: token_in, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: token_out, - checked_amount: BigUint::from_str("1").unwrap(), - // Alice - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_encoding_strategy_curve_st_eth", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_encoding_strategy_balancer_v3() { - // steakUSDTlite -> (balancer v3) -> steakUSDR - let balancer_pool = ProtocolComponent { - id: String::from("0xf028ac624074d6793c36dc8a06ecec0f5a39a718"), - protocol_system: String::from("vm:balancer_v3"), - ..Default::default() - }; - let token_in = Bytes::from("0x097ffedb80d4b2ca6105a07a4d90eb739c45a666"); - let token_out = Bytes::from("0x30881baa943777f92dc934d53d3bfdf33382cab3"); - let swap = Swap { - component: balancer_pool, - token_in: token_in.clone(), - token_out: token_out.clone(), - split: 0f64, - user_data: None, - }; - - let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); - - let solution = Solution { - exact_out: false, - given_token: token_in, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: token_out, - checked_amount: BigUint::from_str("1000").unwrap(), - // Alice - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_solutions(vec![solution.clone()]) - .unwrap()[0] - .clone(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_encoding_strategy_balancer_v3", - hex_calldata.as_str(), - ); - } - } - } } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index b10ca13..b581288 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -3,11 +3,14 @@ pub mod encoding; use std::str::FromStr; -use alloy::{primitives::B256, signers::local::PrivateKeySigner}; +use alloy::{ + primitives::{B256, U256}, + signers::local::PrivateKeySigner, +}; use tycho_common::{models::Chain as TychoCommonChain, Bytes}; use tycho_execution::encoding::{ evm::encoder_builders::TychoRouterEncoderBuilder, - models::{Chain, UserTransferType}, + models::{BebopOrderType, Chain, UserTransferType}, tycho_encoder::TychoEncoder, }; @@ -43,6 +46,10 @@ pub fn pepe() -> Bytes { Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap() } +pub fn ondo() -> Bytes { + Bytes::from_str("0xfAbA6f8e4a5E8Ab82F62fe7C39859FA577269BE3").unwrap() +} + pub fn get_signer() -> PrivateKeySigner { // Set up a mock private key for signing (Alice's pk in our contract tests) let private_key = @@ -61,3 +68,91 @@ pub fn get_tycho_router_encoder(user_transfer_type: UserTransferType) -> Box, u8)>, // (signature, signature_type) +) -> Bytes { + // ABI encode MakerSignature[] array + // Format: offset_to_array | array_length | [offset_to_struct_i]... | [struct_i_data]... + let mut encoded_maker_sigs = Vec::new(); + + // Calculate total size needed + let array_offset = 32; // offset to array start + let array_length_size = 32; + let struct_offsets_size = 32 * signatures.len(); + let _header_size = array_length_size + struct_offsets_size; + + // Build each struct's data and calculate offsets + let mut struct_data = Vec::new(); + let mut struct_offsets = Vec::new(); + // Offsets are relative to the start of array data, not the absolute position + // Array data starts after array length, so first offset is after all offset values + let mut current_offset = struct_offsets_size; // Just the space for offsets, not including array length + + for (signature, signature_type) in &signatures { + struct_offsets.push(current_offset); + + // Each struct contains: + // - offset to signatureBytes (32 bytes) - always 0x40 (64) + // - flags (32 bytes) + // - signatureBytes length (32 bytes) + // - signatureBytes data (padded to 32 bytes) + let mut struct_bytes = Vec::new(); + + // Offset to signatureBytes within this struct + struct_bytes.extend_from_slice(&U256::from(64).to_be_bytes::<32>()); + + // Flags (contains signature type) - AFTER the offset, not before! + let flags = U256::from(*signature_type); + struct_bytes.extend_from_slice(&flags.to_be_bytes::<32>()); + + // SignatureBytes length + struct_bytes.extend_from_slice(&U256::from(signature.len()).to_be_bytes::<32>()); + + // SignatureBytes data (padded to 32 byte boundary) + struct_bytes.extend_from_slice(signature); + let padding = (32 - (signature.len() % 32)) % 32; + struct_bytes.extend_from_slice(&vec![0u8; padding]); + + current_offset += struct_bytes.len(); + struct_data.push(struct_bytes); + } + + // Build the complete ABI encoded array + // Offset to array (always 0x20 for a single parameter) + encoded_maker_sigs.extend_from_slice(&U256::from(array_offset).to_be_bytes::<32>()); + + // Array length + encoded_maker_sigs.extend_from_slice(&U256::from(signatures.len()).to_be_bytes::<32>()); + + // Struct offsets (relative to start of array data) + for offset in struct_offsets { + encoded_maker_sigs.extend_from_slice(&U256::from(offset).to_be_bytes::<32>()); + } + + // Struct data + for data in struct_data { + encoded_maker_sigs.extend_from_slice(&data); + } + + // Build complete user_data + let mut user_data = Vec::new(); + user_data.push(order_type as u8); + user_data.extend_from_slice(&filled_taker_amount.to_be_bytes::<32>()); + user_data.extend_from_slice(&(quote_data.len() as u32).to_be_bytes()); + user_data.extend_from_slice(quote_data); + user_data.extend_from_slice(&encoded_maker_sigs); + Bytes::from(user_data) +} diff --git a/tests/optimized_transfers_integration_tests.rs b/tests/optimized_transfers_integration_tests.rs index b348d99..e2a43c9 100644 --- a/tests/optimized_transfers_integration_tests.rs +++ b/tests/optimized_transfers_integration_tests.rs @@ -1,15 +1,20 @@ use std::{collections::HashMap, str::FromStr}; -use alloy::hex::encode; +use alloy::{ + hex::encode, + primitives::{Address, U256}, + sol_types::SolValue, +}; use num_bigint::{BigInt, BigUint}; use tycho_common::{models::protocol::ProtocolComponent, Bytes}; use tycho_execution::encoding::{ evm::utils::write_calldata_to_file, - models::{Solution, Swap, UserTransferType}, + models::{BebopOrderType, Solution, Swap, UserTransferType}, }; use crate::common::{ - encoding::encode_tycho_router_call, eth, eth_chain, get_signer, get_tycho_router_encoder, weth, + build_bebop_user_data, encoding::encode_tycho_router_call, eth, eth_chain, get_signer, + get_tycho_router_encoder, ondo, usdc, weth, }; mod common; @@ -578,3 +583,134 @@ fn test_uniswap_v3_balancer_v3() { let hex_calldata = encode(&calldata); write_calldata_to_file("test_uniswap_v3_balancer_v3", hex_calldata.as_str()); } + +#[test] +fn test_uniswap_v3_bebop() { + // Note: This test does not assert anything. It is only used to obtain + // integration test data for our router solidity test. + // + // Performs a sequential swap from WETH to ONDO through USDC using USV3 and + // Bebop RFQ + // + // WETH ───(USV3)──> USDC ───(Bebop RFQ)──> ONDO + + let weth = weth(); + let usdc = usdc(); + let ondo = ondo(); + + // First swap: WETH -> USDC via UniswapV3 + let swap_weth_usdc = Swap { + component: ProtocolComponent { + id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* WETH-USDC USV3 Pool + * 0.05% */ + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs + .insert("fee".to_string(), Bytes::from(BigInt::from(500).to_signed_bytes_be())); + attrs + }, + ..Default::default() + }, + token_in: weth.clone(), + token_out: usdc.clone(), + split: 0f64, + user_data: None, + }; + + // Second swap: USDC -> ONDO via Bebop RFQ using real order data + // Using the same real order from the mainnet transaction at block 22667985 + let expiry = 1749483840u64; // Real expiry from the order + let taker_address = Address::from_str("0xc5564C13A157E6240659fb81882A28091add8670").unwrap(); // Real taker + let maker_address = Address::from_str("0xCe79b081c0c924cb67848723ed3057234d10FC6b").unwrap(); // Real maker + let maker_nonce = 1749483765992417u64; // Real nonce + let taker_token = Address::from_str(&usdc.to_string()).unwrap(); + let maker_token = Address::from_str(&ondo.to_string()).unwrap(); + // Using the real order amounts + let taker_amount = U256::from_str("200000000").unwrap(); // 200 USDC (6 decimals) + let maker_amount = U256::from_str("237212396774431060000").unwrap(); // 237.21 ONDO (18 decimals) + let receiver = Address::from_str("0xc5564C13A157E6240659fb81882A28091add8670").unwrap(); // Real receiver + let packed_commands = U256::ZERO; + let flags = U256::from_str( + "51915842898789398998206002334703507894664330885127600393944965515693155942400", + ) + .unwrap(); // Real flags + + // Encode using standard ABI encoding (not packed) + let quote_data = ( + expiry, + taker_address, + maker_address, + maker_nonce, + taker_token, + maker_token, + taker_amount, + maker_amount, + receiver, + packed_commands, + flags, + ) + .abi_encode(); + + // Real signature from the order + let signature = hex::decode("eb5419631614978da217532a40f02a8f2ece37d8cfb94aaa602baabbdefb56b474f4c2048a0f56502caff4ea7411d99eed6027cd67dc1088aaf4181dcb0df7051c").unwrap(); + + // Build user_data with the quote and signature + let user_data = build_bebop_user_data( + BebopOrderType::Single, + U256::from(0), // 0 means fill entire order + "e_data, + vec![(signature, 0)], // ETH_SIGN signature type (0) + ); + + let bebop_component = ProtocolComponent { + id: String::from("bebop-rfq"), + protocol_system: String::from("rfq:bebop"), + static_attributes: HashMap::new(), // No static attributes needed + ..Default::default() + }; + + let swap_usdc_ondo = Swap { + component: bebop_component, + token_in: usdc.clone(), + token_out: ondo.clone(), + split: 0f64, + user_data: Some(user_data), + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: weth, + // Use ~0.099 WETH to get approximately 200 USDC from UniswapV3 + // This should leave only dust amount in the router after Bebop consumes 200 + // USDC + given_amount: BigUint::from_str("99000000000000000").unwrap(), // 0.099 WETH + checked_token: ondo, + checked_amount: BigUint::from_str("237212396774431060000").unwrap(), /* Expected ONDO from Bebop order */ + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xc5564C13A157E6240659fb81882A28091add8670").unwrap(), /* Using the real order receiver */ + swaps: vec![swap_weth_usdc, swap_usdc_ondo], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + &UserTransferType::TransferFrom, + ð(), + None, + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file("test_uniswap_v3_bebop", hex_calldata.as_str()); +} diff --git a/tests/protocol_integration_tests.rs b/tests/protocol_integration_tests.rs index c0cb2ee..078469a 100644 --- a/tests/protocol_integration_tests.rs +++ b/tests/protocol_integration_tests.rs @@ -1,18 +1,24 @@ mod common; use std::{collections::HashMap, str::FromStr}; -use alloy::hex::encode; +use alloy::{ + hex, + hex::encode, + primitives::{Address, U256}, + sol_types::SolValue, +}; use num_bigint::{BigInt, BigUint}; use tycho_common::{models::protocol::ProtocolComponent, Bytes}; use tycho_execution::encoding::{ evm::utils::write_calldata_to_file, - models::{Solution, Swap, UserTransferType}, + models::{BebopOrderType, Solution, Swap, UserTransferType}, }; use crate::common::{ - encoding::encode_tycho_router_call, eth, eth_chain, get_signer, get_tycho_router_encoder, pepe, - usdc, weth, + build_bebop_user_data, encoding::encode_tycho_router_call, eth, eth_chain, get_signer, + get_tycho_router_encoder, ondo, pepe, usdc, weth, }; + #[test] fn test_single_encoding_strategy_ekubo() { // ETH ──(EKUBO)──> USDC @@ -572,3 +578,233 @@ fn test_single_encoding_strategy_balancer_v3() { let hex_calldata = encode(&calldata); write_calldata_to_file("test_single_encoding_strategy_balancer_v3", hex_calldata.as_str()); } + +#[test] +fn test_single_encoding_strategy_bebop() { + // Use the same mainnet data from Solidity tests + // Transaction: https://etherscan.io/tx/0x6279bc970273b6e526e86d9b69133c2ca1277e697ba25375f5e6fc4df50c0c94 + let token_in = usdc(); + let token_out = ondo(); + let amount_in = BigUint::from_str("200000000").unwrap(); // 200 USDC + let amount_out = BigUint::from_str("237212396774431060000").unwrap(); // 237.21 ONDO + + // Create the exact same order from mainnet + let expiry = 1749483840u64; + let taker_address = Address::from_str("0xc5564C13A157E6240659fb81882A28091add8670").unwrap(); // Order receiver from mainnet + let maker_address = Address::from_str("0xCe79b081c0c924cb67848723ed3057234d10FC6b").unwrap(); + let maker_nonce = 1749483765992417u64; + let taker_token = Address::from_str(&token_in.to_string()).unwrap(); + let maker_token = Address::from_str(&token_out.to_string()).unwrap(); + let taker_amount = U256::from_str(&amount_in.to_string()).unwrap(); + let maker_amount = U256::from_str(&amount_out.to_string()).unwrap(); + let receiver = taker_address; // Same as taker_address in this order + let packed_commands = U256::ZERO; + let flags = U256::from_str( + "51915842898789398998206002334703507894664330885127600393944965515693155942400", + ) + .unwrap(); + + // Encode using standard ABI encoding (not packed) + let quote_data = ( + expiry, + taker_address, + maker_address, + maker_nonce, + taker_token, + maker_token, + taker_amount, + maker_amount, + receiver, + packed_commands, + flags, + ) + .abi_encode(); + + // Real signature from mainnet + let signature = hex::decode("eb5419631614978da217532a40f02a8f2ece37d8cfb94aaa602baabbdefb56b474f4c2048a0f56502caff4ea7411d99eed6027cd67dc1088aaf4181dcb0df7051c").unwrap(); + + // Build user_data with the quote and signature + let user_data = build_bebop_user_data( + BebopOrderType::Single, + U256::ZERO, // 0 means fill entire order + "e_data, + vec![(signature, 0)], // ETH_SIGN signature type + ); + + let bebop_component = ProtocolComponent { + id: String::from("bebop-rfq"), + protocol_system: String::from("rfq:bebop"), + static_attributes: HashMap::new(), // No static attributes needed + ..Default::default() + }; + + let swap = Swap { + component: bebop_component, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + user_data: Some(user_data), + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let taker_address_bytes = Bytes::from_str(&taker_address.to_string()).unwrap(); + + let solution = Solution { + exact_out: false, + given_token: token_in, + given_amount: amount_in, + checked_token: token_out, + checked_amount: amount_out, // Expected output amount + // Use the order's taker address as sender and receiver + sender: taker_address_bytes.clone(), + receiver: taker_address_bytes, + swaps: vec![swap], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + &UserTransferType::TransferFrom, + ð(), + None, + ) + .unwrap() + .data; + let hex_calldata = hex::encode(&calldata); + write_calldata_to_file("test_single_encoding_strategy_bebop", hex_calldata.as_str()); +} + +#[test] +fn test_single_encoding_strategy_bebop_aggregate() { + // Use real mainnet aggregate order data from CLAUDE.md + // Transaction: https://etherscan.io/tx/0xec88410136c287280da87d0a37c1cb745f320406ca3ae55c678dec11996c1b1c + // For testing, use WETH directly to avoid delegatecall + native ETH complexities + let token_in = eth(); + let token_out = usdc(); + let amount_in = BigUint::from_str("9850000000000000").unwrap(); // 0.00985 WETH + let amount_out = BigUint::from_str("17969561").unwrap(); // 17.969561 USDC + + // Create the exact aggregate order from mainnet + let expiry = 1746367285u64; + let taker_address = Address::from_str("0x7078B12Ca5B294d95e9aC16D90B7D38238d8F4E6").unwrap(); + let receiver = Address::from_str("0x7078B12Ca5B294d95e9aC16D90B7D38238d8F4E6").unwrap(); + + // Set up makers + let maker_addresses = vec![ + Address::from_str("0x67336Cec42645F55059EfF241Cb02eA5cC52fF86").unwrap(), + Address::from_str("0xBF19CbF0256f19f39A016a86Ff3551ecC6f2aAFE").unwrap(), + ]; + let maker_nonces = vec![U256::from(1746367197308u64), U256::from(15460096u64)]; + + // 2D arrays for tokens + // We use WETH as a taker token even when handling native ETH + let taker_tokens = vec![vec![Address::from_slice(&weth())], vec![Address::from_slice(&weth())]]; + let maker_tokens = + vec![vec![Address::from_slice(&token_out)], vec![Address::from_slice(&token_out)]]; + + // 2D arrays for amounts + let taker_amounts = vec![ + vec![U256::from_str("5812106401997138").unwrap()], + vec![U256::from_str("4037893598002862").unwrap()], + ]; + let maker_amounts = + vec![vec![U256::from_str("10607211").unwrap()], vec![U256::from_str("7362350").unwrap()]]; + + // Commands and flags from the real transaction + let commands = hex!("00040004").to_vec(); + let flags = U256::from_str( + "95769172144825922628485191511070792431742484643425438763224908097896054784000", + ) + .unwrap(); + + // Encode Aggregate order - must match IBebopSettlement.Aggregate struct exactly + let quote_data = ( + U256::from(expiry), // expiry as U256 + taker_address, + maker_addresses, + maker_nonces, // Array of maker nonces + taker_tokens, // 2D array + maker_tokens, + taker_amounts, // 2D array + maker_amounts, + receiver, + commands, + flags, + ) + .abi_encode(); + + // Use real signatures from the mainnet transaction + let sig1 = hex::decode("d5abb425f9bac1f44d48705f41a8ab9cae207517be8553d2c03b06a88995f2f351ab8ce7627a87048178d539dd64fd2380245531a0c8e43fdc614652b1f32fc71c").unwrap(); + let sig2 = hex::decode("f38c698e48a3eac48f184bc324fef0b135ee13705ab38cc0bbf5a792f21002f051e445b9e7d57cf24c35e17629ea35b3263591c4abf8ca87ffa44b41301b89c41b").unwrap(); + + // Build user_data with ETH_SIGN flag (0) for both signatures + let signatures = vec![ + (sig1, 0u8), // ETH_SIGN for maker 1 + (sig2, 0u8), // ETH_SIGN for maker 2 + ]; + + let user_data = build_bebop_user_data( + BebopOrderType::Aggregate, + U256::from(0), // 0 means fill entire aggregate order + "e_data, + signatures, + ); + + let bebop_component = ProtocolComponent { + id: String::from("bebop-rfq"), + protocol_system: String::from("rfq:bebop"), + static_attributes: HashMap::new(), + ..Default::default() + }; + + let swap = Swap { + component: bebop_component, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + user_data: Some(user_data), + }; + + // Use TransferFrom for WETH token transfer + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: token_in.clone(), + given_amount: amount_in, + checked_token: token_out, + checked_amount: amount_out, + // Use ALICE as sender but order receiver as receiver + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), /* ALICE */ + receiver: Bytes::from_str("0x7078B12Ca5B294d95e9aC16D90B7D38238d8F4E6").unwrap(), /* Order receiver */ + swaps: vec![swap], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + &UserTransferType::None, + ð(), + None, + ) + .unwrap() + .data; + let hex_calldata = hex::encode(&calldata); + + write_calldata_to_file("test_single_encoding_strategy_bebop_aggregate", hex_calldata.as_str()); +}