From 79d6c020fa381b872fa421523bb3b7917f3cf418 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Mon, 10 Mar 2025 20:18:47 +0530 Subject: [PATCH 01/11] misc: fix _swapChecked for cyclic swap, update encoder swap path validation, add cyclic sequesntial swap integration test --- config/executor_addresses.json | 4 +- foundry/src/TychoRouter.sol | 6 +- foundry/test/TychoRouter.t.sol | 27 +++++- .../evm/strategy_encoder/strategy_encoders.rs | 96 ++++++++++++++++++- .../strategy_encoder/strategy_validators.rs | 53 +++++++++- 5 files changed, 178 insertions(+), 8 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index da05b18..96f1ac2 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -1,7 +1,7 @@ { "ethereum": { "uniswap_v2": "0xf6c5be66FFf9DC69962d73da0A617a827c382329", - "uniswap_v3": "0xdD8559c917393FC8DD2b4dD289c52Ff445fDE1B0", + "uniswap_v3": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91" }, @@ -19,4 +19,4 @@ "tenderly_base": { "uniswap_v3": "0x7c7E06d7317e620a185078e236879D2a87fC8d22" } -} \ No newline at end of file +} diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 32fb7f5..7a26ff7 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -14,6 +14,7 @@ import "@permit2/src/interfaces/IAllowanceTransfer.sol"; import "./Dispatcher.sol"; import {LibSwap} from "../lib/LibSwap.sol"; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import "forge-std/console.sol"; // ✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷ // ✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷ @@ -259,7 +260,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { : IERC20(tokenIn).balanceOf(address(this)); amountOut = _swap(amountIn, nTokens, swaps); - uint256 currentBalance = tokenIn == address(0) ? address(this).balance : IERC20(tokenIn).balanceOf(address(this)); @@ -323,6 +323,10 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { revert TychoRouter__EmptySwaps(); } + console.log("amountIn", amountIn); + console.log("nTokens", nTokens); + console.logBytes(swaps_); + uint256 currentAmountIn; uint256 currentAmountOut; uint8 tokenInIndex = 0; diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index e86b622..c760122 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1192,7 +1192,7 @@ contract TychoRouterTest is TychoRouterTestSetup { assertEq(IERC20(WBTC_ADDR).balanceOf(tychoRouterAddr), 102718); } - function testCyclicSequentialSwap() public { + function testCyclicSequentialSwap1() public { // This test has start and end tokens that are the same // The flow is: // USDC -> WETH -> USDC using two pools @@ -1332,6 +1332,23 @@ contract TychoRouterTest is TychoRouterTestSetup { assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99525908); } + function testCyclicSequentialSwapIntegration() public { + deal(USDC_ADDR, ALICE, 100 * 10 ** 6); + + // Approve permit2 + vm.startPrank(ALICE); + IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); + // Encoded solution generated using `test_cyclic_sequential_swap` + // but manually replacing the executor address + // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` with the one in this test + // `2e234DAe75C793f67A35089C9d99245E1C58470b` + (bool success,) = tychoRouterAddr.call( + hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f67a8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cef493000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c07077fc73bb0f5129006061288fa0583c101631307377281d6b8f3feb50aa2d564f9948c92e0e4abc3771d592bd2f22ebb18ccf21b270459b05f272251ce1c71b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000" + ); + + vm.stopPrank(); + } + // Base Network Tests // Make sure to set the RPC_URL to base network function testSwapSingleBase() public { @@ -1354,3 +1371,11 @@ contract TychoRouterTest is TychoRouterTestSetup { assertGt(IERC20(BASE_MAG7).balanceOf(tychoRouterAddr), 1379830606); } } + +//amountIn 100000000 +//nTokens 2 +//0x006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d800 + +//amountIn 100000000 +//nTokens 2 +//0x006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d800 diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index ea2b191..336bd04 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -206,6 +206,11 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { } let encoded_swaps = self.ple_encode(swaps); + let tokens_len = if &solution.given_token == &solution.checked_token { + tokens.len() - 1 + } else { + tokens.len() + }; let method_calldata = if let Some(permit2) = self.permit2.clone() { let (permit, signature) = permit2.get_permit( &solution.router_address, @@ -220,7 +225,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { biguint_to_u256(&min_amount_out), wrap, unwrap, - U256::from(tokens.len()), + U256::from(tokens_len), bytes_to_address(&solution.receiver)?, permit, signature.as_bytes().to_vec(), @@ -235,7 +240,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { biguint_to_u256(&min_amount_out), wrap, unwrap, - U256::from(tokens.len()), + U256::from(tokens_len), bytes_to_address(&solution.receiver)?, encoded_swaps, ) @@ -1212,4 +1217,91 @@ mod tests { let hex_calldata = encode(&calldata); println!("{}", hex_calldata); } + + #[test] + fn test_cyclic_sequential_swap() { + // This test has start and end tokens that are the same + // The flow is: + // USDC -> WETH -> USDC using two pools + + // Set up a mock private key for signing (Alice's pk in our router tests) + let private_key = + "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); + + 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, + }; + + // 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, + }; + + let swap_encoder_registry = get_swap_encoder_registry(); + let encoder = + SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) + .unwrap(); + + 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(), + expected_amount: None, + checked_amount: Some(BigUint::from_str("99889294").unwrap()), /* Expected output from + * test */ + slippage: None, + swaps: vec![swap_usdc_weth, swap_weth_usdc], + router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + ..Default::default() + }; + + let (calldata, _) = encoder + .encode_strategy(solution) + .unwrap(); + + println!("{}", hex::encode(&calldata)); + } + + #[test] + } diff --git a/src/encoding/evm/strategy_encoder/strategy_validators.rs b/src/encoding/evm/strategy_encoder/strategy_validators.rs index 266769d..8ac5628 100644 --- a/src/encoding/evm/strategy_encoder/strategy_validators.rs +++ b/src/encoding/evm/strategy_encoder/strategy_validators.rs @@ -150,6 +150,13 @@ impl SplitSwapValidator { .insert(&swap.token_out); } + // Collect all unique tokens from the swaps + let mut all_tokens = HashSet::new(); + for swap in swaps { + all_tokens.insert(&swap.token_in); + all_tokens.insert(&swap.token_out); + } + // BFS from validation_given let mut visited = HashSet::new(); let mut queue = VecDeque::new(); @@ -160,8 +167,8 @@ impl SplitSwapValidator { continue; } - // Early success check - if token == checked_token && visited.len() == graph.len() + 1 { + // Early success check - if we've reached the checked token and visited all tokens + if token == checked_token && visited.len() == all_tokens.len() { return Ok(()); } @@ -174,6 +181,13 @@ impl SplitSwapValidator { } } + // After BFS completes, check if both conditions are met: + // 1. The checked token is in the visited set + // 2. All unique tokens from the swaps are visited + if visited.contains(checked_token) && visited.len() == all_tokens.len() { + return Ok(()); + } + // If we get here, either checked_token wasn't reached or not all tokens were visited if !visited.contains(checked_token) { Err(EncodingError::InvalidInput( @@ -291,6 +305,41 @@ mod tests { )); } + #[test] + fn test_validate_path_cyclic_swap() { + let validator = SplitSwapValidator; + let eth = Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(); + let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); + let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + let cyclic_swaps = vec![ + Swap { + component: ProtocolComponent { + id: "pool1".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: usdc.clone(), + token_out: weth.clone(), + split: 0.5, + }, + Swap { + component: ProtocolComponent { + id: "pool2".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth.clone(), + token_out: usdc.clone(), + split: 0.5, + }, + ]; + + // Test with USDC as both given token and checked token + let result = validator.validate_swap_path(&cyclic_swaps, &usdc, &usdc, &None, ð, &weth); + assert_eq!(result, Ok(())); + } + #[test] fn test_validate_path_unreachable_checked_token() { let validator = SplitSwapValidator; From 0a0101cc4d64af8ea7becb3bab3bdbec6dc1374c Mon Sep 17 00:00:00 2001 From: royvardhan Date: Mon, 10 Mar 2025 21:52:14 +0530 Subject: [PATCH 02/11] test: add testCyclicSplitSwapIntegration --- foundry/test/TychoRouter.t.sol | 17 +++ .../evm/strategy_encoder/strategy_encoders.rs | 110 +++++++++++++++++- 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index c760122..b53d088 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1349,6 +1349,23 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } + function testCyclicSplitSwapIntegration() public { + deal(USDC_ADDR, ALICE, 100000 * 10 ** 6); + + // Approve permit2 + vm.startPrank(ALICE); + IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); + // Encoded solution generated using `test_cyclic_split_swap` + // but manually replacing the executor address + // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` with the one in this test + // `2e234DAe75C793f67A35089C9d99245E1C58470b` + (bool success,) = tychoRouterAddr.call( + hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ebe468000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f69de900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf17f1000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f521cbb1d97ae14209faec0337cdb5b2e3691acd29ac2ef86ad2ac2c60350d9f5c39bb688be6305bd03288b69ba05d8bc066bd12e8585190f047c67f06b4ae4c1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014d006d00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d800006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100000000000000000000000000000000000000" + ); + + vm.stopPrank(); + } + // Base Network Tests // Make sure to set the RPC_URL to base network function testSwapSingleBase() public { diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 336bd04..072a655 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1303,5 +1303,113 @@ mod tests { } #[test] - + fn test_split_input_cyclic_swap() { + // This test has start and end tokens that are the same + // The flow is: + // ┌─── WETH (USV3 Pool 1) ───┐ + // │ │ + // USDC (60% split) ─┤ ├─> USDC + // │ │ + // └─── WETH (USV3 Pool 2) ───┘ + + // Set up a mock private key for signing (Alice's pk in our router tests) + let private_key = + "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); + + 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 + }; + + // 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, // Remaining 40% + }; + + // WETH -> USDC (Pool 2) + let swap_weth_usdc_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, + }; + + let swap_encoder_registry = get_swap_encoder_registry(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key.clone()), + ) + .unwrap(); + + 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(), + expected_amount: None, + checked_amount: Some(BigUint::from_str("99345512").unwrap()), /* Expected output from + * test */ + router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + slippage: None, + swaps: vec![swap_usdc_weth_pool1, swap_weth_usdc_pool2, swap_usdc_weth_pool2], + ..Default::default() + }; + + let (calldata, _) = encoder + .encode_strategy(solution) + .unwrap(); + + println!("{}", hex::encode(&calldata)); + } } From 3a6e933ce92a3d84c011e8adc4d301d904546d59 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Mon, 10 Mar 2025 23:57:42 +0530 Subject: [PATCH 03/11] test: fix test_split_input_cyclic_swap and testCyclicSplitSwapIntegration --- config/executor_addresses.json | 2 +- foundry/src/TychoRouter.sol | 5 ----- foundry/test/TychoRouter.t.sol | 20 ++++++++----------- .../evm/strategy_encoder/strategy_encoders.rs | 4 ++-- 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 96f1ac2..1aed562 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -1,6 +1,6 @@ { "ethereum": { - "uniswap_v2": "0xf6c5be66FFf9DC69962d73da0A617a827c382329", + "uniswap_v2": "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", "uniswap_v3": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91" diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 7a26ff7..4b01e5b 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -14,7 +14,6 @@ import "@permit2/src/interfaces/IAllowanceTransfer.sol"; import "./Dispatcher.sol"; import {LibSwap} from "../lib/LibSwap.sol"; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; -import "forge-std/console.sol"; // ✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷ // ✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷✷ @@ -323,10 +322,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { revert TychoRouter__EmptySwaps(); } - console.log("amountIn", amountIn); - console.log("nTokens", nTokens); - console.logBytes(swaps_); - uint256 currentAmountIn; uint256 currentAmountOut; uint8 tokenInIndex = 0; diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index b53d088..1006da6 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1338,7 +1338,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); - // Encoded solution generated using `test_cyclic_sequential_swap` + // Encoded solution generated using `test_cyclic_sequential_swap` // but manually replacing the executor address // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` with the one in this test // `2e234DAe75C793f67A35089C9d99245E1C58470b` @@ -1346,23 +1346,27 @@ contract TychoRouterTest is TychoRouterTestSetup { hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f67a8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cef493000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c07077fc73bb0f5129006061288fa0583c101631307377281d6b8f3feb50aa2d564f9948c92e0e4abc3771d592bd2f22ebb18ccf21b270459b05f272251ce1c71b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000" ); + assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99889294); + vm.stopPrank(); } function testCyclicSplitSwapIntegration() public { - deal(USDC_ADDR, ALICE, 100000 * 10 ** 6); + deal(USDC_ADDR, ALICE, 100 * 10 ** 6); // Approve permit2 vm.startPrank(ALICE); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); - // Encoded solution generated using `test_cyclic_split_swap` + // Encoded solution generated using `test_cyclic_split_swap` // but manually replacing the executor address // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` with the one in this test // `2e234DAe75C793f67A35089C9d99245E1C58470b` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ebe468000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f69de900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf17f1000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f521cbb1d97ae14209faec0337cdb5b2e3691acd29ac2ef86ad2ac2c60350d9f5c39bb688be6305bd03288b69ba05d8bc066bd12e8585190f047c67f06b4ae4c1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014d006d00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d800006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100000000000000000000000000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ebe468000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f6bb0c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf3514000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041593a2ab4137d6b2657b5d6ece56457ac5ddbc1846d18e331d1a48ef3921ae66d49769f33aff5e18e4e2c13ceb3b408903ba1e78c88bae53151046710cd441b651b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014d006d00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d801006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000000000000000000000000000000000000000" ); + assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99693951); + vm.stopPrank(); } @@ -1388,11 +1392,3 @@ contract TychoRouterTest is TychoRouterTestSetup { assertGt(IERC20(BASE_MAG7).balanceOf(tychoRouterAddr), 1379830606); } } - -//amountIn 100000000 -//nTokens 2 -//0x006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d800 - -//amountIn 100000000 -//nTokens 2 -//0x006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d800 diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 072a655..7ebd8d2 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1396,13 +1396,13 @@ mod tests { given_amount: BigUint::from_str("100000000").unwrap(), // 100 USDC (6 decimals) checked_token: usdc.clone(), expected_amount: None, - checked_amount: Some(BigUint::from_str("99345512").unwrap()), /* Expected output from + checked_amount: Some(BigUint::from_str("99693951").unwrap()), /* Expected output from * test */ router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), slippage: None, - swaps: vec![swap_usdc_weth_pool1, swap_weth_usdc_pool2, swap_usdc_weth_pool2], + swaps: vec![swap_usdc_weth_pool1, swap_usdc_weth_pool2, swap_weth_usdc_pool2], ..Default::default() }; From 0edc201f060c81b036519acec1c3dd9dcf56d319 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Tue, 11 Mar 2025 00:11:16 +0530 Subject: [PATCH 04/11] test: add test_split_output_cyclic_swap in encoder, and testCyclicSplitOutputSwapIntegration integration test --- foundry/test/TychoRouter.t.sol | 22 +++- .../evm/strategy_encoder/strategy_encoders.rs | 107 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 1006da6..9740454 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1350,8 +1350,9 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } + - function testCyclicSplitSwapIntegration() public { + function testCyclicSplitInputSwapIntegration() public { deal(USDC_ADDR, ALICE, 100 * 10 ** 6); // Approve permit2 @@ -1370,6 +1371,25 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } + function testCyclicSplitOutputSwapIntegration() public { + deal(USDC_ADDR, ALICE, 100 * 10 ** 6); + + // Approve permit2 + vm.startPrank(ALICE); + IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); + // Encoded solution generated using `test_cyclic_split_swap` + // but manually replacing the executor address + // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` with the one in this test + // `2e234DAe75C793f67A35089C9d99245E1C58470b` + (bool success,) = tychoRouterAddr.call( + hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f6be9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf389c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c02ad8eceede50085f35ce8e8313ebbac9b379396c6e72a35bb4df0970cbdaaa1a91e6f787641af55b13b926199c844df42fdd2ae7bb287db7e5cc2a8bc1d7f51b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d0139501006d01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564000006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000000000000000000000" + ); + + assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99525908); + + vm.stopPrank(); + } + // Base Network Tests // Make sure to set the RPC_URL to base network function testSwapSingleBase() public { diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 7ebd8d2..2566a8c 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1412,4 +1412,111 @@ mod tests { println!("{}", hex::encode(&calldata)); } + + #[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 + // │ │ + // └─── WETH (USV3 Pool 2)───┘ + + // Set up a mock private key for signing (Alice's pk in our router tests) + let private_key = + "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); + + 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, + }; + + 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, + }; + + 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, + }; + + let swap_encoder_registry = get_swap_encoder_registry(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key.clone()), + ) + .unwrap(); + + 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(), + expected_amount: None, + checked_amount: Some(BigUint::from_str("99525908").unwrap()), /* Expected output from + * test */ + router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + slippage: None, + swaps: vec![swap_usdc_weth_v2, swap_weth_usdc_v3_pool1, swap_weth_usdc_v3_pool2], + ..Default::default() + }; + + let (calldata, _) = encoder + .encode_strategy(solution) + .unwrap(); + + println!("{}", hex::encode(&calldata)); + } } From bd504a8fede8b22b41270d4d209cfcd6c8752389 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Tue, 11 Mar 2025 00:18:38 +0530 Subject: [PATCH 05/11] test: update test_split_input_cyclic_swap to use usv2 in the last swap --- foundry/test/TychoRouter.t.sol | 5 ++--- src/encoding/evm/strategy_encoder/strategy_encoders.rs | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 9740454..9e435d4 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1350,7 +1350,6 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } - function testCyclicSplitInputSwapIntegration() public { deal(USDC_ADDR, ALICE, 100 * 10 ** 6); @@ -1363,10 +1362,10 @@ contract TychoRouterTest is TychoRouterTestSetup { // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` with the one in this test // `2e234DAe75C793f67A35089C9d99245E1C58470b` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ebe468000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f6bb0c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf3514000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041593a2ab4137d6b2657b5d6ece56457ac5ddbc1846d18e331d1a48ef3921ae66d49769f33aff5e18e4e2c13ceb3b408903ba1e78c88bae53151046710cd441b651b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014d006d00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d801006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000000000000000000000000000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f6c08700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf3a8f000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f248bfa39e6801b4173cd4d61e5e5d0c31942eb3c194785f964a82b2c3e05b4b302bccc0924fa4c4ef90854e42865db11f458d3b6a62afddee833f3eb069cd521b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136006d00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d801005601000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000" ); - assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99693951); + assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99574171); vm.stopPrank(); } diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 2566a8c..22d078f 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1364,9 +1364,9 @@ mod tests { // WETH -> USDC (Pool 2) let swap_weth_usdc_pool2 = Swap { component: ProtocolComponent { - id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), /* USDC-WETH USV3 + id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), /* USDC-WETH USV2 * Pool 2 */ - protocol_system: "uniswap_v3".to_string(), + protocol_system: "uniswap_v2".to_string(), static_attributes: { let mut attrs = HashMap::new(); attrs.insert( @@ -1396,7 +1396,7 @@ mod tests { given_amount: BigUint::from_str("100000000").unwrap(), // 100 USDC (6 decimals) checked_token: usdc.clone(), expected_amount: None, - checked_amount: Some(BigUint::from_str("99693951").unwrap()), /* Expected output from + checked_amount: Some(BigUint::from_str("99574171").unwrap()), /* Expected output from * test */ router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), From 525560ea163de78b5ff1bf9fe0417407a48bedf5 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Tue, 11 Mar 2025 00:51:39 +0530 Subject: [PATCH 06/11] chore: cleanup --- config/executor_addresses.json | 4 ++-- foundry/test/TychoRouter.t.sol | 14 +++++++------- .../evm/strategy_encoder/strategy_encoders.rs | 12 ++++++------ .../evm/strategy_encoder/strategy_validators.rs | 10 +++------- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/config/executor_addresses.json b/config/executor_addresses.json index 1aed562..02e98e6 100644 --- a/config/executor_addresses.json +++ b/config/executor_addresses.json @@ -1,7 +1,7 @@ { "ethereum": { - "uniswap_v2": "0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", - "uniswap_v3": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "uniswap_v2": "0xf6c5be66FFf9DC69962d73da0A617a827c382329", + "uniswap_v3": "0xdD8559c917393FC8DD2b4dD289c52Ff445fDE1B0", "uniswap_v4": "0x042C0ebBEAb9d9987c2f64Ee05f2B3aeB86eAf70", "vm:balancer_v2": "0x00BE8EfAE40219Ff76287b0F9b9e497942f5BC91" }, diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 9e435d4..239cda8 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1192,7 +1192,7 @@ contract TychoRouterTest is TychoRouterTestSetup { assertEq(IERC20(WBTC_ADDR).balanceOf(tychoRouterAddr), 102718); } - function testCyclicSequentialSwap1() public { + function testCyclicSequentialSwap() public { // This test has start and end tokens that are the same // The flow is: // USDC -> WETH -> USDC using two pools @@ -1358,9 +1358,9 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_cyclic_split_swap` - // but manually replacing the executor address - // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` with the one in this test - // `2e234DAe75C793f67A35089C9d99245E1C58470b` + // but manually replacing the executor addresses with the ones in this test + // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` to `2e234DAe75C793f67A35089C9d99245E1C58470b` + // `f6c5be66FFf9DC69962d73da0A617a827c382329` to `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f6c08700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf3a8f000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f248bfa39e6801b4173cd4d61e5e5d0c31942eb3c194785f964a82b2c3e05b4b302bccc0924fa4c4ef90854e42865db11f458d3b6a62afddee833f3eb069cd521b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136006d00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d801005601000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000" ); @@ -1377,9 +1377,9 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_cyclic_split_swap` - // but manually replacing the executor address - // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` with the one in this test - // `2e234DAe75C793f67A35089C9d99245E1C58470b` + // but manually replacing the executor addresses with the ones in this test + // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` to `2e234DAe75C793f67A35089C9d99245E1C58470b` + // `f6c5be66FFf9DC69962d73da0A617a827c382329` to `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f6be9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf389c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c02ad8eceede50085f35ce8e8313ebbac9b379396c6e72a35bb4df0970cbdaaa1a91e6f787641af55b13b926199c844df42fdd2ae7bb287db7e5cc2a8bc1d7f51b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d0139501006d01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564000006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000000000000000000000" ); diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 22d078f..91db180 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -206,7 +206,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { } let encoded_swaps = self.ple_encode(swaps); - let tokens_len = if &solution.given_token == &solution.checked_token { + let tokens_len = if solution.given_token == solution.checked_token { tokens.len() - 1 } else { tokens.len() @@ -1306,11 +1306,11 @@ mod tests { fn test_split_input_cyclic_swap() { // This test has start and end tokens that are the same // The flow is: - // ┌─── WETH (USV3 Pool 1) ───┐ - // │ │ - // USDC (60% split) ─┤ ├─> USDC - // │ │ - // └─── WETH (USV3 Pool 2) ───┘ + // ┌─ (USV3, 60% split) ──> WETH ─┐ + // │ │ + // USDC ──────┤ ├──(USV2)──> USDC + // │ │ + // └─ (USV3, 40% split) ──> WETH ─┘ // Set up a mock private key for signing (Alice's pk in our router tests) let private_key = diff --git a/src/encoding/evm/strategy_encoder/strategy_validators.rs b/src/encoding/evm/strategy_encoder/strategy_validators.rs index 8ac5628..d4bf22f 100644 --- a/src/encoding/evm/strategy_encoder/strategy_validators.rs +++ b/src/encoding/evm/strategy_encoder/strategy_validators.rs @@ -143,16 +143,12 @@ impl SplitSwapValidator { // Build directed graph of token flows let mut graph: HashMap<&Bytes, HashSet<&Bytes>> = HashMap::new(); + let mut all_tokens = HashSet::new(); for swap in swaps { graph .entry(&swap.token_in) .or_default() .insert(&swap.token_out); - } - - // Collect all unique tokens from the swaps - let mut all_tokens = HashSet::new(); - for swap in swaps { all_tokens.insert(&swap.token_in); all_tokens.insert(&swap.token_out); } @@ -321,7 +317,7 @@ mod tests { }, token_in: usdc.clone(), token_out: weth.clone(), - split: 0.5, + split: 0f64, }, Swap { component: ProtocolComponent { @@ -331,7 +327,7 @@ mod tests { }, token_in: weth.clone(), token_out: usdc.clone(), - split: 0.5, + split: 0f64, }, ]; From 6c319067339e41d8031d6891d5018e3cff9e46a4 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Tue, 11 Mar 2025 00:56:39 +0530 Subject: [PATCH 07/11] chore: cleanup --- foundry/test/TychoRouter.t.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 239cda8..8d2285b 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1351,13 +1351,13 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } - function testCyclicSplitInputSwapIntegration() public { + function testSplitInputCyclicSwapIntegration() public { deal(USDC_ADDR, ALICE, 100 * 10 ** 6); // Approve permit2 vm.startPrank(ALICE); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); - // Encoded solution generated using `test_cyclic_split_swap` + // Encoded solution generated using `test_split_input_cyclic_swap` // but manually replacing the executor addresses with the ones in this test // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` to `2e234DAe75C793f67A35089C9d99245E1C58470b` // `f6c5be66FFf9DC69962d73da0A617a827c382329` to `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` @@ -1370,13 +1370,13 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } - function testCyclicSplitOutputSwapIntegration() public { + function testSplitOutputCyclicSwapIntegration() public { deal(USDC_ADDR, ALICE, 100 * 10 ** 6); // Approve permit2 vm.startPrank(ALICE); IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); - // Encoded solution generated using `test_cyclic_split_swap` + // Encoded solution generated using `test_split_output_cyclic_swap` // but manually replacing the executor addresses with the ones in this test // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` to `2e234DAe75C793f67A35089C9d99245E1C58470b` // `f6c5be66FFf9DC69962d73da0A617a827c382329` to `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` From b7c1619796c0ce46946fa25e969729047845be5e Mon Sep 17 00:00:00 2001 From: royvardhan Date: Tue, 11 Mar 2025 18:06:20 +0530 Subject: [PATCH 08/11] docs: make cyclic test ref addresses lowercase --- foundry/test/TychoRouter.t.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 8d2285b..3801a2e 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1340,8 +1340,8 @@ contract TychoRouterTest is TychoRouterTestSetup { IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_cyclic_sequential_swap` // but manually replacing the executor address - // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` with the one in this test - // `2e234DAe75C793f67A35089C9d99245E1C58470b` + // `dd8559c917393fc8dd2b4dd289c52ff445fde1b0` with the one in this test + // `2e234dae75c793f67a35089c9d99245e1c58470b` (bool success,) = tychoRouterAddr.call( hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f67a8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cef493000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c07077fc73bb0f5129006061288fa0583c101631307377281d6b8f3feb50aa2d564f9948c92e0e4abc3771d592bd2f22ebb18ccf21b270459b05f272251ce1c71b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000" ); @@ -1359,8 +1359,8 @@ contract TychoRouterTest is TychoRouterTestSetup { IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_input_cyclic_swap` // but manually replacing the executor addresses with the ones in this test - // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` to `2e234DAe75C793f67A35089C9d99245E1C58470b` - // `f6c5be66FFf9DC69962d73da0A617a827c382329` to `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` + // `dd8559c917393fc8dd2b4dd289c52ff445fde1b0` to `2e234dae75c793f67a35089c9d99245e1c58470b` + // `f6c5be66fff9dc69962d73da0a617a827c382329` to `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f6c08700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf3a8f000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f248bfa39e6801b4173cd4d61e5e5d0c31942eb3c194785f964a82b2c3e05b4b302bccc0924fa4c4ef90854e42865db11f458d3b6a62afddee833f3eb069cd521b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136006d00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d801005601000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000" ); @@ -1378,8 +1378,8 @@ contract TychoRouterTest is TychoRouterTestSetup { IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_output_cyclic_swap` // but manually replacing the executor addresses with the ones in this test - // `dD8559c917393FC8DD2b4dD289c52Ff445fDE1B0` to `2e234DAe75C793f67A35089C9d99245E1C58470b` - // `f6c5be66FFf9DC69962d73da0A617a827c382329` to `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` + // `dd8559c917393fc8dd2b4dd289c52ff445fde1b0` to `2e234dae75c793f67a35089c9d99245e1c58470b` + // `f6c5be66fff9dc69962d73da0a617a827c382329` to `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( hex"d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000067f6be9400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067cf389c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c02ad8eceede50085f35ce8e8313ebbac9b379396c6e72a35bb4df0970cbdaaa1a91e6f787641af55b13b926199c844df42fdd2ae7bb287db7e5cc2a8bc1d7f51b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000136005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d0139501006d01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564000006d01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000000000000000000000" ); From eb4ce40f6e0e2e1807201bc90e68f32f87157a7e Mon Sep 17 00:00:00 2001 From: royvardhan Date: Tue, 11 Mar 2025 20:31:37 +0530 Subject: [PATCH 09/11] test: add testCyclicSwapUnwrapOutputIntegration in encoder and testCyclicSwapUnwrapOutputIntegration test --- foundry/test/TychoRouter.t.sol | 19 ++++ .../evm/strategy_encoder/strategy_encoders.rs | 90 ++++++++++++++++++- 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 3801a2e..718e4b5 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1389,6 +1389,25 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } + + function testCyclicSwapUnwrapOutputIntegration() public { + vm.deal(tychoRouterAddr, 1 ether); + + vm.startPrank(ALICE); + + // Encoded solution generated using `test_cylic_swap_unwrap_output` + // but manually replacing the executor addresses with the ones in this test + // `dd8559c917393fc8dd2b4dd289c52ff445fde1b0` to `2e234dae75c793f67a35089c9d99245e1c58470b` + // `042c0ebbeab9d9987c2f64ee05f2b3aeb86eaf70` to `f62849f9a0b5bf2913b396098f7c7019b51a820a` + (bool success,) = tychoRouterAddr.call( + hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067f7d35e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067d04d66000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041043569db60af05e3bbc0e4fcff373ffeeacc0fa5bbbe322f171becf366be7f1c4d31d26e0b2952b4d7bf09ddc9902bbb2758870ce36b737286f76a2006fcfc861b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e100700001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59901f62849f9a0b5bf2913b396098f7c7019b51a820a2260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c006d01020000002e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed0100000000000000000000000000000000000000000000000000000000000000" + ); + + assertEq(ALICE.balance, 993164318934741987); // 0.993164318934741987 ETH + + vm.stopPrank(); + } + // Base Network Tests // Make sure to set the RPC_URL to base network function testSwapSingleBase() public { diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 91db180..1c1fca7 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1421,7 +1421,7 @@ mod tests { // │ │ // USDC ──(USV2) ── WETH──| ├─> USDC // │ │ - // └─── WETH (USV3 Pool 2)───┘ + // └─── (USV3, 40% split) ───┘ // Set up a mock private key for signing (Alice's pk in our router tests) let private_key = @@ -1519,4 +1519,92 @@ mod tests { println!("{}", hex::encode(&calldata)); } + + #[test] + fn test_cylic_swap_unwrap_output() { + // This test has start and end tokens that are the same + // The flow is: + // ETH -> WBTC -> WETH(unwrap operation) -> ETH + + // Set up a mock private key for signing (Alice's pk in our router tests) + let private_key = + "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); + + let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); + let wbtc = Bytes::from_str("0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599").unwrap(); + + // ETH -> WBTC (Uniswap V4) + let swap_eth_wbtc = Swap { + component: ProtocolComponent { + id: "0x54c72c46df32f2cc455e84e41e191b26ed73a29452cdd3d82f511097af9f427e" + .to_string(), + protocol_system: "uniswap_v4".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "key_lp_fee".to_string(), + Bytes::from(BigInt::from(3000).to_signed_bytes_be()), + ); + attrs.insert( + "tick_spacing".to_string(), + Bytes::from(BigInt::from(60).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: eth(), + token_out: wbtc.clone(), + split: 0f64, + }; + + // WBTC -> WETH (Uniswap V3) + let swap_wbtc_weth = 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: wbtc.clone(), + token_out: weth.clone(), + split: 0f64, + }; + + let swap_encoder_registry = get_swap_encoder_registry(); + let encoder = + SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) + .unwrap(); + + let solution = Solution { + exact_out: false, + given_token: eth(), + given_amount: BigUint::from_str("1000000000000000000").unwrap(), // 1 WETH + checked_token: eth(), + expected_amount: None, + checked_amount: Some(BigUint::from_str("993164318934741987").unwrap()), /* Expected output + * from + * test */ + slippage: None, + swaps: vec![swap_eth_wbtc, swap_wbtc_weth], + router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + native_action: Some(NativeAction::Unwrap), + ..Default::default() + }; + + let (calldata, _) = encoder + .encode_strategy(solution) + .unwrap(); + + println!("{}", hex::encode(&calldata)); + } } From ec26890d00bec8ce34f1dcdeea7e47a25370d212 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Tue, 11 Mar 2025 21:33:51 +0530 Subject: [PATCH 10/11] test: add assert in cyclic swap encoding tests --- foundry/test/TychoRouter.t.sol | 1 - .../evm/strategy_encoder/strategy_encoders.rs | 30 +++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 718e4b5..129e903 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1389,7 +1389,6 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } - function testCyclicSwapUnwrapOutputIntegration() public { vm.deal(tychoRouterAddr, 1 ether); diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 1c1fca7..988af9a 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1299,7 +1299,12 @@ mod tests { .encode_strategy(solution) .unwrap(); - println!("{}", hex::encode(&calldata)); + let hex_calldata = encode(&calldata); + let encoded_swap_header = "d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2".to_string(); + let encoded_swaps = "00000000000000000000000000000000000000000000000000000000000000de006d0001000000dd8559c917393fc8dd2b4dd289c52ff445fde1b0a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d0100000000dd8559c917393fc8dd2b4dd289c52ff445fde1b0c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000".to_string(); + assert_eq!(hex_calldata[..520], encoded_swap_header); + assert_eq!(hex_calldata[1288..], encoded_swaps); + println!("{}", hex_calldata); } #[test] @@ -1410,7 +1415,12 @@ mod tests { .encode_strategy(solution) .unwrap(); - println!("{}", hex::encode(&calldata)); + let hex_calldata = hex::encode(&calldata); + let encoded_swap_header = "d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2".to_string(); + let encoded_swaps = "0000000000000000000000000000000000000000000000000000000000000136006d0001999999dd8559c917393fc8dd2b4dd289c52ff445fde1b0a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d0001000000dd8559c917393fc8dd2b4dd289c52ff445fde1b0a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100560100000000f6c5be66fff9dc69962d73da0a617a827c382329c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000".to_string(); + assert_eq!(hex_calldata[..520], encoded_swap_header); + assert_eq!(hex_calldata[1288..], encoded_swaps); + println!("{}", hex_calldata); } #[test] @@ -1517,7 +1527,12 @@ mod tests { .encode_strategy(solution) .unwrap(); - println!("{}", hex::encode(&calldata)); + let hex_calldata = hex::encode(&calldata); + let encoded_swap_header = "d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2".to_string(); + let encoded_swaps = "000000000000000000000000000000000000000000000000000000000000013600560001000000f6c5be66fff9dc69962d73da0a617a827c382329a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d0139501006d0100999999dd8559c917393fc8dd2b4dd289c52ff445fde1b0c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564000006d0100000000dd8559c917393fc8dd2b4dd289c52ff445fde1b0c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000000000000000000000".to_string(); + assert_eq!(hex_calldata[..520], encoded_swap_header); + assert_eq!(hex_calldata[1288..], encoded_swaps); + println!("{}", hex_calldata); } #[test] @@ -1598,13 +1613,18 @@ mod tests { sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), native_action: Some(NativeAction::Unwrap), - ..Default::default() }; let (calldata, _) = encoder .encode_strategy(solution) .unwrap(); - println!("{}", hex::encode(&calldata)); + let hex_calldata = hex::encode(&calldata); + + let expected_input_swap_header = "d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc86dafa1b2b3e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2".to_string(); + let expected_input_swaps = "00000000000000000000000000000000000000000000000000000000000000e100700001000000042c0ebbeab9d9987c2f64ee05f2b3aeb86eaf7000000000000000000000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59901042c0ebbeab9d9987c2f64ee05f2b3aeb86eaf702260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c006d0102000000dd8559c917393fc8dd2b4dd289c52ff445fde1b02260fac5e5542a773aa44fbcfedf7c193bc2c599c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed0100000000000000000000000000000000000000000000000000000000000000".to_string(); + assert_eq!(hex_calldata[..520], expected_input_swap_header); + assert_eq!(hex_calldata[1288..], expected_input_swaps); + println!("{}", hex_calldata); } } From b0e0c6d49082bb777839edd950d3cba9b53d75f0 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Wed, 12 Mar 2025 00:49:19 +0530 Subject: [PATCH 11/11] test: update cyclic swap encoding test asserts --- .../evm/strategy_encoder/strategy_encoders.rs | 209 ++++++++++++++++-- 1 file changed, 189 insertions(+), 20 deletions(-) diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 988af9a..144b5d5 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1298,13 +1298,48 @@ mod tests { let (calldata, _) = encoder .encode_strategy(solution) .unwrap(); + let hex_calldata = hex::encode(&calldata); + let expected_input = [ + "d499aa88", // selector + "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token + "0000000000000000000000000000000000000000000000000000000005f4308e", // min amount out + "0000000000000000000000000000000000000000000000000000000000000000", // wrap action + "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action + "0000000000000000000000000000000000000000000000000000000000000002", // tokens length + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + ] + .join(""); - let hex_calldata = encode(&calldata); - let encoded_swap_header = "d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2".to_string(); - let encoded_swaps = "00000000000000000000000000000000000000000000000000000000000000de006d0001000000dd8559c917393fc8dd2b4dd289c52ff445fde1b0a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d0100000000dd8559c917393fc8dd2b4dd289c52ff445fde1b0c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000".to_string(); - assert_eq!(hex_calldata[..520], encoded_swap_header); - assert_eq!(hex_calldata[1288..], encoded_swaps); - println!("{}", hex_calldata); + let expected_swaps = [ + "00000000000000000000000000000000000000000000000000000000000000de", // length of ple encoded swaps without padding + "006d", // ple encoded swaps + "00", // token in index + "01", // token out index + "000000", // split + "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out + "0001f4", // pool fee + "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address + "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id + "01", // zero2one + "006d", // ple encoded swaps + "01", // token in index + "00000000", // split + "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out + "000bb8", // pool fee + "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address + "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id + "000000", // zero2one + ] + .join(""); + + assert_eq!(hex_calldata[..520], expected_input); + assert_eq!(hex_calldata[1288..], expected_swaps); } #[test] @@ -1416,11 +1451,56 @@ mod tests { .unwrap(); let hex_calldata = hex::encode(&calldata); - let encoded_swap_header = "d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2".to_string(); - let encoded_swaps = "0000000000000000000000000000000000000000000000000000000000000136006d0001999999dd8559c917393fc8dd2b4dd289c52ff445fde1b0a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564001006d0001000000dd8559c917393fc8dd2b4dd289c52ff445fde1b0a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100560100000000f6c5be66fff9dc69962d73da0a617a827c382329c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000".to_string(); - assert_eq!(hex_calldata[..520], encoded_swap_header); - assert_eq!(hex_calldata[1288..], encoded_swaps); - println!("{}", hex_calldata); + let expected_input = [ + "d499aa88", // 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 = [ + "0000000000000000000000000000000000000000000000000000000000000136", // length of ple encoded swaps without padding + "006d", // ple encoded swaps + "00", // token in index + "01", // token out index + "999999", // split + "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out + "0001f4", // pool fee + "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address + "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id + "01", // zero2one + "006d", // ple encoded swaps + "00", // token in index + "01", // token out index + "000000", // split + "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out + "000bb8", // pool fee + "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address + "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id + "01", // zero2one + "0056", // ple encoded swaps + "01", // token in index + "00", // token out index + "000000", // split + "f6c5be66fff9dc69962d73da0a617a827c382329", // executor address, + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id, + "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address + "00", // zero2one + "00000000000000000000" // padding + ] + .join(""); + assert_eq!(hex_calldata[..520], expected_input); + assert_eq!(hex_calldata[1288..], expected_swaps); } #[test] @@ -1528,11 +1608,58 @@ mod tests { .unwrap(); let hex_calldata = hex::encode(&calldata); - let encoded_swap_header = "d499aa880000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2".to_string(); - let encoded_swaps = "000000000000000000000000000000000000000000000000000000000000013600560001000000f6c5be66fff9dc69962d73da0a617a827c382329a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d0139501006d0100999999dd8559c917393fc8dd2b4dd289c52ff445fde1b0c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f564000006d0100000000dd8559c917393fc8dd2b4dd289c52ff445fde1b0c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000000000000000000000".to_string(); - assert_eq!(hex_calldata[..520], encoded_swap_header); - assert_eq!(hex_calldata[1288..], encoded_swaps); - println!("{}", hex_calldata); + let expected_input = [ + "d499aa88", // selector + "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token + "0000000000000000000000000000000000000000000000000000000005eea514", // min amount out + "0000000000000000000000000000000000000000000000000000000000000000", // wrap action + "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action + "0000000000000000000000000000000000000000000000000000000000000002", // tokens length + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + ] + .join(""); + + let expected_swaps = [ + "0000000000000000000000000000000000000000000000000000000000000136", // length of ple encoded swaps without padding + "0056", // ple encoded swaps + "00", // token in index + "01", // token out index + "000000", // split + "f6c5be66fff9dc69962d73da0a617a827c382329", // executor address + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in + "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id + "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address + "01", // zero2one + "006d", // ple encoded swaps + "01", // token in index + "00", // token out index + "999999", // split + "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out + "0001f4", // pool fee + "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address + "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id + "00", // zero2one + "006d", // ple encoded swaps + "01", // token in index + "00", // token out index + "000000", // split + "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out + "000bb8", // pool fee + "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address + "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id + "00", // zero2one + "00000000000000000000" // padding + ] + .join(""); + + assert_eq!(hex_calldata[..520], expected_input); + assert_eq!(hex_calldata[1288..], expected_swaps); } #[test] @@ -1621,10 +1748,52 @@ mod tests { let hex_calldata = hex::encode(&calldata); - let expected_input_swap_header = "d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc86dafa1b2b3e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2".to_string(); - let expected_input_swaps = "00000000000000000000000000000000000000000000000000000000000000e100700001000000042c0ebbeab9d9987c2f64ee05f2b3aeb86eaf7000000000000000000000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59901042c0ebbeab9d9987c2f64ee05f2b3aeb86eaf702260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c006d0102000000dd8559c917393fc8dd2b4dd289c52ff445fde1b02260fac5e5542a773aa44fbcfedf7c193bc2c599c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed0100000000000000000000000000000000000000000000000000000000000000".to_string(); - assert_eq!(hex_calldata[..520], expected_input_swap_header); - assert_eq!(hex_calldata[1288..], expected_input_swaps); + let expected_input = [ + "d499aa88", // selector + "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // given amount + "0000000000000000000000000000000000000000000000000000000000000000", // given token + "0000000000000000000000000000000000000000000000000000000000000000", // checked token + "0000000000000000000000000000000000000000000000000dc86dafa1b2b3e3", // min amount out + "0000000000000000000000000000000000000000000000000000000000000000", // wrap action + "0000000000000000000000000000000000000000000000000000000000000001", // unwrap action + "0000000000000000000000000000000000000000000000000000000000000003", // tokens length + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + ] + .join(""); + + let expected_swaps = [ + "00000000000000000000000000000000000000000000000000000000000000e1", // length of ple encoded swaps without padding + "0070", // ple encoded swaps + "00", // token in index + "01", // token out index + "000000", // split + "042c0ebbeab9d9987c2f64ee05f2b3aeb86eaf70", // executor address + "0000000000000000000000000000000000000000", // token in + "2260fac5e5542a773aa44fbcfedf7c193bc2c599", // intermediary token + "01", // zero2one + "042c0ebbeab9d9987c2f64ee05f2b3aeb86eaf70", // executor address + // Pool params + "2260fac5e5542a773aa44fbcfedf7c193bc2c599", // intermediary token + "000bb8", // pool fee + "00003c", // tick spacing + // Next swap + "006d", // ple encoded swaps + "01", // token in index + "02", // token out index + "000000", // split + "dd8559c917393fc8dd2b4dd289c52ff445fde1b0", // executor address + "2260fac5e5542a773aa44fbcfedf7c193bc2c599", // token in + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out + "000bb8", // pool fee + "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address + "cbcdf9626bc03e24f779434178a73a0b4bad62ed", // component id + "01", // zero2one + "00000000000000000000000000000000000000000000000000000000000000" // padding + ] + .join(""); + + assert_eq!(hex_calldata[..520], expected_input); + assert_eq!(hex_calldata[1288..], expected_swaps); println!("{}", hex_calldata); } }