Merge pull request #203 from propeller-heads/audit/dc/ENG-4536-separate-encoding-swaps-and-encoding-function

feat: Separate encoding swaps from encoding txs
This commit is contained in:
dianacarvalho1
2025-05-22 14:53:27 +01:00
committed by GitHub
16 changed files with 862 additions and 764 deletions

View File

@@ -55,23 +55,23 @@ fn main() {
given_amount: BigUint::from_str("1_000000000000000000").expect("Failed to create amount"), given_amount: BigUint::from_str("1_000000000000000000").expect("Failed to create amount"),
checked_token: usdc.clone(), checked_token: usdc.clone(),
exact_out: false, // it's an exact in solution exact_out: false, // it's an exact in solution
checked_amount: Some(BigUint::from(1u64)), checked_amount: BigUint::from(1u64),
swaps: vec![simple_swap], swaps: vec![simple_swap],
..Default::default() ..Default::default()
}; };
// Encode the solution // Encode the solution
let tx = encoder let encoded_solution = encoder
.encode_calldata(vec![solution.clone()]) .encode_solutions(vec![solution.clone()])
.expect("Failed to encode router calldata")[0] .expect("Failed to encode router calldata")[0]
.clone(); .clone();
println!(" ====== Simple swap WETH -> USDC ======"); println!(" ====== Simple swap WETH -> USDC ======");
println!( println!(
"The simple swap encoded transaction should be sent to address {:?} with the value of {:?} and the \ "The simple swap encoded solution should be sent to address {:?} and selector {:?} and the \
following encoded data: {:?}", following encoded data: {:?}",
tx.to, encoded_solution.interacting_with,
tx.value, encoded_solution.selector,
hex::encode(tx.data) hex::encode(encoded_solution.swaps)
); );
// ------------------- Encode a swap with multiple splits ------------------- // ------------------- Encode a swap with multiple splits -------------------
@@ -134,17 +134,17 @@ fn main() {
complex_solution.swaps = vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc]; complex_solution.swaps = vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc];
// Encode the solution // Encode the solution
let complex_tx = encoder let complex_encoded_solution = encoder
.encode_calldata(vec![complex_solution]) .encode_solutions(vec![complex_solution])
.expect("Failed to encode router calldata")[0] .expect("Failed to encode router calldata")[0]
.clone(); .clone();
println!(" ====== Complex split swap WETH -> USDC ======"); println!(" ====== Complex split swap WETH -> USDC ======");
println!( println!(
"The complex solution encoded transaction should be sent to address {:?} with the value of {:?} and the \ "The complex swaps encoded solution should be sent to address {:?} and selector {:?} and the \
following encoded data: {:?}", following encoded data: {:?}",
complex_tx.to, complex_encoded_solution.interacting_with,
complex_tx.value, complex_encoded_solution.selector,
hex::encode(complex_tx.data) hex::encode(complex_encoded_solution.swaps)
); );
} }

View File

@@ -3,23 +3,23 @@ test_single_encoding_strategy_ekubo:5c4b639c000000000000000000000000000000000000
test_uniswap_v3_uniswap_v3:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010100000000000000000000 test_uniswap_v3_uniswap_v3:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010100000000000000000000
test_balancer_v2_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_balancer_v2_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
test_sequential_swap_strategy_encoder_no_permit2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_sequential_swap_strategy_encoder_no_permit2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c800000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041b70914df20e4b2675adc5a61d26b959e0fd21e7cce6503b884d5ba9e1db2c82164112e4dc362b9a3ef794a03fcfebe0710700028b79abe8b7de525288ac904991c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041972d1d0dd19889db47087a4653c517a455a19a1dc76a0a14a29fd12ae7d2d4f218ba986eada63bfdfacf78e3a7ffdce07c4064fcf11033f185819509f64b6b191b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000
test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c8000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c33e9da2d2a5bea1087e27f42b9255fcaea06efa76558843f6192d571b15c09c5818e1a4b3a111bb6accc4cb8b583c318d2386e0e94cc87b9fcc4065db0d65611c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000 test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e0000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c0bbb5f493c1d4b87c92085902e4488efba2b87740abdcc298a5adb435bfe3b23820a0677d325e34fa06e8c2a91f0d3851b00cf170885176b5f47d40983d64941b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000
test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000419861aedfb04e8f7b9fe147a7d19c41f9b1cf37a06850b09f645fb351c003967d43d365af50b08ef741348898578b7fa1454a8e9cd0390eaf6c2939d7b817d0d31c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder_no_permit2:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_swap_strategy_encoder_no_permit2:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
test_single_swap_strategy_encoder_no_transfer_in:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 test_single_swap_strategy_encoder_no_transfer_in:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000
test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c800000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041f59744bfe3e154be7c72429eb93db7ef2ec00344feeb1376195a2e3f133437f7533c3df955de146e229e4a37fda22acbe11d9393215d14cf1e19334aebbf35e61b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041a8ad04262cc02545cc4934739e086f37b3ea04ba83195a151a736e55cf2cc68407f9dc20f19c68071ced3f6d5f20908ce9332526c955e66974aaf53c7e58ca581c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000
test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041d461cc5890a8a58d13249a485335b44671612c97b7a12004b5cfb163bdbf4c2f15dde7cb500c22f724f813b7d6fe5062076f63ee5b91c7e984f011a5d1ce6fca1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000
test_single_encoding_strategy_curve_st_eth:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 test_single_encoding_strategy_curve_st_eth:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006b56051582a970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006b56051582a970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000419861aedfb04e8f7b9fe147a7d19c41f9b1cf37a06850b09f645fb351c003967d43d365af50b08ef741348898578b7fa1454a8e9cd0390eaf6c2939d7b817d0d31c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000
test_single_encoding_strategy_curve:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 test_single_encoding_strategy_curve:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000
test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041e5679a7262a283109d2e312e4ffbec563f501f347efc00418f3a6701492635977583d4e2ff29904a33496b64f1bc2ac73796a546a495ab110cca86e6de1655b61c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000 test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004133c6e3010dfd7e69b717338de47a7129351aefa89191faa6c76250356c6adae041da1ef7cb664511e942ecd28f7b578b38dad510df3185241c8336259027ded11c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000
test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004160aa554efc922689a7f426a984f9fc278aa948b9c78f4e7cc0a5f177e5fb6859632b92d3326f710603d43d3a8ea5753c58a61316fd1bff8e0b388f74c98993b91b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041a8ad04262cc02545cc4934739e086f37b3ea04ba83195a151a736e55cf2cc68407f9dc20f19c68071ced3f6d5f20908ce9332526c955e66974aaf53c7e58ca581c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000
test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000 test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685552e700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccef000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f208f39e98ca0821f644f4fd3be7ab23ce75c0e3d6e425b00327067b482dc3963e0c481141fe530d79cc8361ea628a0f53d69cbbd09b7c2416581f04392704681b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000
test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000 test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041d461cc5890a8a58d13249a485335b44671612c97b7a12004b5cfb163bdbf4c2f15dde7cb500c22f724f813b7d6fe5062076f63ee5b91c7e984f011a5d1ce6fca1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000
test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000 test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccef000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041726b94d0c0b711c31e19e1b33bcdd87e4526b4fd804471bbafcb0fed1fdc5aea23922bb4d46f16add4f01136165643feb355d89bf31d1a25a8a1a126ee3af43e1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000
test_uniswap_v3_curve:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000 test_uniswap_v3_curve:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000
test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041a81d713c715dd501edf102a3c3a755a931e3c72e7322d66b620ece81d7c98bae3d46479041445649eeefdb6ccc09c2b933cd37eda3ae8c99034e84f5570eb2c31c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf0000000000000000000000000000000000000000000000000000000000000685552e700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccef00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410488df0c2ac790f75505dbd2754518d70fb02c32fb3bb3e63e299259037765606ca8d38d05c9c6d2d82e3b39203f378ad489f715a2aeb7df674ee4b8e5c8d1151c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000
test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0102 test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0102
test_ekubo_encode_swap_multi:01ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032 test_ekubo_encode_swap_multi:01ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032
test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c

View File

@@ -1,5 +1,6 @@
use std::io::{self, Read}; use std::io::{self, Read};
use alloy_sol_types::SolValue;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use tycho_common::{hex_bytes::Bytes, models::Chain}; use tycho_common::{hex_bytes::Bytes, models::Chain};
use tycho_execution::encoding::{ use tycho_execution::encoding::{
@@ -99,11 +100,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.build()?, .build()?,
}; };
let transactions = encoder.encode_calldata(vec![solution])?; let encoded_solutions = encoder.encode_solutions(vec![solution])?;
let encoded = serde_json::json!({ let encoded = serde_json::json!({
"to": format!("0x{}", hex::encode(&transactions[0].to)), "swaps": format!("0x{}", hex::encode(&encoded_solutions[0].swaps)),
"value": format!("0x{}", hex::encode(transactions[0].value.to_bytes_be())), "interacting_with": format!("0x{}", hex::encode(&encoded_solutions[0].interacting_with)),
"data": format!("0x{}", hex::encode(&transactions[0].data)), "selector": format!("{}",&encoded_solutions[0].selector),
"n_tokens": format!("{}", &encoded_solutions[0].n_tokens),
"permit": encoded_solutions[0].permit
.as_ref()
.map(|permit| format!("0x{}", hex::encode(permit.abi_encode())))
.unwrap_or_else(String::new),
"signature": encoded_solutions[0].signature
.as_ref()
.map(|signature| format!("0x{}", hex::encode(signature.as_bytes())))
.unwrap_or_else(String::new),
}); });
// Output the encoded result as JSON to stdout // Output the encoded result as JSON to stdout
println!( println!(

View File

@@ -20,6 +20,8 @@ pub enum EncodingError {
FatalError(String), FatalError(String),
#[error("Recoverable error: {0}")] #[error("Recoverable error: {0}")]
RecoverableError(String), RecoverableError(String),
#[error("Not implemented: {0}")]
NotImplementedError(String),
} }
impl From<io::Error> for EncodingError { impl From<io::Error> for EncodingError {

View File

@@ -19,7 +19,10 @@ use tycho_common::Bytes;
use crate::encoding::{ use crate::encoding::{
errors::EncodingError, errors::EncodingError,
evm::utils::{biguint_to_u256, bytes_to_address, encode_input, get_client, get_runtime}, evm::{
encoding_utils::encode_input,
utils::{biguint_to_u256, bytes_to_address, get_client, get_runtime},
},
models::Chain, models::Chain,
}; };

View File

@@ -14,7 +14,10 @@ use tokio::{
use crate::encoding::{ use crate::encoding::{
errors::EncodingError, errors::EncodingError,
evm::utils::{encode_input, get_client, get_runtime}, evm::{
encoding_utils::encode_input,
utils::{get_client, get_runtime},
},
}; };
/// A manager for checking if an approval is needed for interacting with a certain spender. /// A manager for checking if an approval is needed for interacting with a certain spender.

View File

@@ -145,7 +145,7 @@ impl TychoExecutorEncoderBuilder {
if let Some(chain) = self.chain { if let Some(chain) = self.chain {
let swap_encoder_registry = let swap_encoder_registry =
SwapEncoderRegistry::new(self.executors_file_path.clone(), chain.clone())?; SwapEncoderRegistry::new(self.executors_file_path.clone(), chain.clone())?;
Ok(Box::new(TychoExecutorEncoder::new(chain, swap_encoder_registry)?)) Ok(Box::new(TychoExecutorEncoder::new(swap_encoder_registry)?))
} else { } else {
Err(EncodingError::FatalError( Err(EncodingError::FatalError(
"Please set the chain and strategy before building the encoder".to_string(), "Please set the chain and strategy before building the encoder".to_string(),

View File

@@ -0,0 +1,249 @@
use alloy_primitives::{Keccak256, U256};
use alloy_sol_types::SolValue;
use num_bigint::BigUint;
use tycho_common::Bytes;
use crate::encoding::{
errors::EncodingError,
evm::utils::{biguint_to_u256, bytes_to_address},
models::{EncodedSolution, NativeAction, Solution, Transaction},
};
/// Encodes the input data for a function call to the given function selector.
pub fn encode_input(selector: &str, mut encoded_args: Vec<u8>) -> Vec<u8> {
let mut hasher = Keccak256::new();
hasher.update(selector.as_bytes());
let selector_bytes = &hasher.finalize()[..4];
let mut call_data = selector_bytes.to_vec();
// Remove extra prefix if present (32 bytes for dynamic data)
// Alloy encoding is including a prefix for dynamic data indicating the offset or length
// but at this point we don't want that
if encoded_args.len() > 32 &&
encoded_args[..32] ==
[0u8; 31]
.into_iter()
.chain([32].to_vec())
.collect::<Vec<u8>>()
{
encoded_args = encoded_args[32..].to_vec();
}
call_data.extend(encoded_args);
call_data
}
/// Encodes a transaction for the Tycho Router using one of its supported swap methods.
///
/// # Overview
///
/// This function provides an **example implementation** of how to encode a call to the Tycho
/// Router. It handles all currently supported swap selectors such as:
/// - `singleSwap`
/// - `singleSwapPermit2`
/// - `sequentialSwap`
/// - `sequentialSwapPermit2`
/// - `splitSwap`
/// - `splitSwapPermit2`
///
/// The encoding includes handling of native asset wrapping/unwrapping, permit2 support,
/// and proper input argument formatting based on the selector string.
///
/// # ⚠️ Important Responsibility Note
///
/// This function is intended as **an illustrative example only**. **Users must implement
/// their own encoding logic** to ensure:
/// - Full control of parameters passed to the router.
/// - Proper validation and setting of critical inputs such as `minAmountOut`.
///
/// While Tycho is responsible for encoding the swap paths themselves, the input arguments
/// to the router's methods act as **guardrails** for on-chain execution safety.
/// Thus, the user must **take responsibility** for ensuring correctness of all input parameters,
/// including `minAmountOut`, `receiver`, and permit2 logic.
///
/// # Min Amount Out
///
/// The `minAmountOut` calculation used here is just an example.
/// You should ideally:
/// - Query an external service (e.g., DEX aggregators, oracle, off-chain price feed).
/// - Use your own strategy to determine an accurate and safe minimum acceptable output amount.
///
/// ⚠️ If `minAmountOut` is too low, your swap may be front-run or sandwiched, resulting in loss of
/// funds.
///
/// # Parameters
/// - `encoded_solution`: The solution already encoded by Tycho, including selector and swap path.
/// - `solution`: The high-level solution including tokens, amounts, and receiver info.
/// - `token_in_already_in_router`: Whether the input token is already present in the router.
/// - `router_address`: The address of the Tycho Router contract.
/// - `native_address`: The address used to represent the native token
///
/// # Returns
/// A `Result<Transaction, EncodingError>` that either contains the full transaction data (to,
/// value, data), or an error if the inputs are invalid.
///
/// # Errors
/// - Returns `EncodingError::FatalError` if the selector is unsupported or required fields (e.g.,
/// permit or signature) are missing.
pub fn encode_tycho_router_call(
encoded_solution: EncodedSolution,
solution: &Solution,
token_in_already_in_router: bool,
native_address: Bytes,
) -> Result<Transaction, EncodingError> {
let (mut unwrap, mut wrap) = (false, false);
if let Some(action) = solution.native_action.clone() {
match action {
NativeAction::Wrap => wrap = true,
NativeAction::Unwrap => unwrap = true,
}
}
let given_amount = biguint_to_u256(&solution.given_amount);
let min_amount_out = biguint_to_u256(&solution.checked_amount);
let given_token = bytes_to_address(&solution.given_token)?;
let checked_token = bytes_to_address(&solution.checked_token)?;
let receiver = bytes_to_address(&solution.receiver)?;
let n_tokens = U256::from(encoded_solution.n_tokens);
let method_calldata = if encoded_solution
.selector
.contains("singleSwapPermit2")
{
(
given_amount,
given_token,
checked_token,
min_amount_out,
wrap,
unwrap,
receiver,
encoded_solution
.permit
.ok_or(EncodingError::FatalError(
"permit2 object must be set to use permit2".to_string(),
))?,
encoded_solution
.signature
.ok_or(EncodingError::FatalError(
"Signature must be set to use permit2".to_string(),
))?
.as_bytes()
.to_vec(),
encoded_solution.swaps,
)
.abi_encode()
} else if encoded_solution
.selector
.contains("singleSwap")
{
(
given_amount,
given_token,
checked_token,
min_amount_out,
wrap,
unwrap,
receiver,
!token_in_already_in_router,
encoded_solution.swaps,
)
.abi_encode()
} else if encoded_solution
.selector
.contains("sequentialSwapPermit2")
{
(
given_amount,
given_token,
checked_token,
min_amount_out,
wrap,
unwrap,
receiver,
encoded_solution
.permit
.ok_or(EncodingError::FatalError(
"permit2 object must be set to use permit2".to_string(),
))?,
encoded_solution
.signature
.ok_or(EncodingError::FatalError(
"Signature must be set to use permit2".to_string(),
))?
.as_bytes()
.to_vec(),
encoded_solution.swaps,
)
.abi_encode()
} else if encoded_solution
.selector
.contains("sequentialSwap")
{
(
given_amount,
given_token,
checked_token,
min_amount_out,
wrap,
unwrap,
receiver,
!token_in_already_in_router,
encoded_solution.swaps,
)
.abi_encode()
} else if encoded_solution
.selector
.contains("splitSwapPermit2")
{
(
given_amount,
given_token,
checked_token,
min_amount_out,
wrap,
unwrap,
n_tokens,
receiver,
encoded_solution
.permit
.ok_or(EncodingError::FatalError(
"permit2 object must be set to use permit2".to_string(),
))?,
encoded_solution
.signature
.ok_or(EncodingError::FatalError(
"Signature must be set to use permit2".to_string(),
))?
.as_bytes()
.to_vec(),
encoded_solution.swaps,
)
.abi_encode()
} else if encoded_solution
.selector
.contains("splitSwap")
{
(
given_amount,
given_token,
checked_token,
min_amount_out,
wrap,
unwrap,
n_tokens,
receiver,
!token_in_already_in_router,
encoded_solution.swaps,
)
.abi_encode()
} else {
Err(EncodingError::FatalError("Invalid selector for Tycho router".to_string()))?
};
let contract_interaction = encode_input(&encoded_solution.selector, method_calldata);
let value = if solution.given_token == native_address {
solution.given_amount.clone()
} else {
BigUint::ZERO
};
Ok(Transaction { to: encoded_solution.interacting_with, value, data: contract_interaction })
}

View File

@@ -1,6 +1,7 @@
pub mod approvals; pub mod approvals;
mod constants; mod constants;
pub mod encoder_builders; pub mod encoder_builders;
pub mod encoding_utils;
mod group_swaps; mod group_swaps;
pub mod strategy_encoder; pub mod strategy_encoder;
mod swap_encoder; mod swap_encoder;

File diff suppressed because it is too large Load Diff

View File

@@ -4,23 +4,10 @@ use tycho_common::Bytes;
use crate::encoding::{ use crate::encoding::{
errors::EncodingError, errors::EncodingError,
models::{NativeAction, Solution, Swap}, models::{NativeAction, Swap},
}; };
pub trait SwapValidator { pub trait SwapValidator {
/// Raises an error if the solution does not have checked amount set or slippage with checked
/// amount set.
fn validate_solution_min_amounts(&self, solution: &Solution) -> Result<(), EncodingError> {
if solution.checked_amount.is_none() &&
(solution.slippage.is_none() || solution.expected_amount.is_none())
{
return Err(EncodingError::InvalidInput(
"Checked amount or slippage with expected amount must be provided".to_string(),
))
}
Ok(())
}
/// Raises an error if swaps do not represent a valid path from the given token to the checked /// Raises an error if swaps do not represent a valid path from the given token to the checked
/// token. /// token.
/// ///
@@ -207,8 +194,6 @@ impl SwapValidator for SequentialSwapValidator {}
mod tests { mod tests {
use std::str::FromStr; use std::str::FromStr;
use num_bigint::BigUint;
use rstest::rstest;
use tycho_common::{models::protocol::ProtocolComponent, Bytes}; use tycho_common::{models::protocol::ProtocolComponent, Bytes};
use super::*; use super::*;
@@ -615,80 +600,4 @@ mod tests {
); );
assert_eq!(result, Ok(())); assert_eq!(result, Ok(()));
} }
#[rstest]
#[case::slippage_with_expected_amount_set(
Some(0.01),
Some(BigUint::from(1000u32)),
None,
Ok(())
)]
#[case::min_amount_set(
None,
None,
Some(BigUint::from(1000u32)),
Ok(())
)]
#[case::slippage_with_min_amount_set(
Some(0.01),
Some(BigUint::from(1000u32)),
Some(BigUint::from(1000u32)),
Ok(())
)]
#[case::slippage_without_expected_amount_set(
Some(0.01),
None,
None,
Err(
EncodingError::InvalidInput(
"Checked amount or slippage with expected amount must be provided".to_string()
)
)
)]
#[case::none_set(
None,
None,
None,
Err(
EncodingError::InvalidInput(
"Checked amount or slippage with expected amount must be provided".to_string()
)
)
)]
fn test_validate_min_amount_passed(
#[case] slippage: Option<f64>,
#[case] expected_amount: Option<BigUint>,
#[case] min_amount: Option<BigUint>,
#[case] expected_result: Result<(), EncodingError>,
) {
let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
let validator = SplitSwapValidator;
let swap = Swap {
component: ProtocolComponent {
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
protocol_system: "uniswap_v2".to_string(),
..Default::default()
},
token_in: weth.clone(),
token_out: usdc.clone(),
split: 0f64,
};
let solution = Solution {
exact_out: false,
given_token: weth,
checked_token: usdc,
slippage,
checked_amount: min_amount,
expected_amount,
swaps: vec![swap],
native_action: Some(NativeAction::Wrap),
..Default::default()
};
let result = validator.validate_solution_min_amounts(&solution);
assert_eq!(result, expected_result);
}
} }

View File

@@ -1,19 +1,22 @@
use std::{collections::HashSet, str::FromStr}; use std::{collections::HashSet, str::FromStr};
use num_bigint::BigUint;
use tycho_common::Bytes; use tycho_common::Bytes;
use crate::encoding::{ use crate::encoding::{
errors::EncodingError, errors::EncodingError,
evm::{ evm::{
approvals::permit2::Permit2,
constants::{GROUPABLE_PROTOCOLS, IN_TRANSFER_REQUIRED_PROTOCOLS}, constants::{GROUPABLE_PROTOCOLS, IN_TRANSFER_REQUIRED_PROTOCOLS},
encoding_utils::encode_tycho_router_call,
group_swaps::group_swaps, group_swaps::group_swaps,
strategy_encoder::strategy_encoders::{ strategy_encoder::strategy_encoders::{
SequentialSwapStrategyEncoder, SingleSwapStrategyEncoder, SplitSwapStrategyEncoder, SequentialSwapStrategyEncoder, SingleSwapStrategyEncoder, SplitSwapStrategyEncoder,
}, },
swap_encoder::swap_encoder_registry::SwapEncoderRegistry, swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
}, },
models::{Chain, EncodingContext, NativeAction, Solution, Transaction, TransferType}, models::{
Chain, EncodedSolution, EncodingContext, NativeAction, Solution, Transaction, TransferType,
},
strategy_encoder::StrategyEncoder, strategy_encoder::StrategyEncoder,
tycho_encoder::TychoEncoder, tycho_encoder::TychoEncoder,
}; };
@@ -26,6 +29,8 @@ use crate::encoding::{
/// * `split_swap_strategy`: Encoder for split swaps /// * `split_swap_strategy`: Encoder for split swaps
/// * `native_address`: Address of the chain's native token /// * `native_address`: Address of the chain's native token
/// * `wrapped_address`: Address of the chain's wrapped native token /// * `wrapped_address`: Address of the chain's wrapped native token
/// * `router_address`: Address of the Tycho router contract
/// * `token_in_already_in_router`: Indicates if the token in is already in the router at swap time
#[derive(Clone)] #[derive(Clone)]
pub struct TychoRouterEncoder { pub struct TychoRouterEncoder {
single_swap_strategy: SingleSwapStrategyEncoder, single_swap_strategy: SingleSwapStrategyEncoder,
@@ -33,6 +38,9 @@ pub struct TychoRouterEncoder {
split_swap_strategy: SplitSwapStrategyEncoder, split_swap_strategy: SplitSwapStrategyEncoder,
native_address: Bytes, native_address: Bytes,
wrapped_address: Bytes, wrapped_address: Bytes,
router_address: Bytes,
token_in_already_in_router: bool,
permit2: Option<Permit2>,
} }
impl TychoRouterEncoder { impl TychoRouterEncoder {
@@ -45,78 +53,113 @@ impl TychoRouterEncoder {
) -> Result<Self, EncodingError> { ) -> Result<Self, EncodingError> {
let native_address = chain.native_token()?; let native_address = chain.native_token()?;
let wrapped_address = chain.wrapped_token()?; let wrapped_address = chain.wrapped_token()?;
let permit2 = if let Some(swapper_pk) = swapper_pk.clone() {
Some(Permit2::new(swapper_pk, chain.clone())?)
} else {
None
};
Ok(TychoRouterEncoder { Ok(TychoRouterEncoder {
single_swap_strategy: SingleSwapStrategyEncoder::new( single_swap_strategy: SingleSwapStrategyEncoder::new(
chain.clone(), chain.clone(),
swap_encoder_registry.clone(), swap_encoder_registry.clone(),
swapper_pk.clone(), permit2.is_some(),
router_address.clone(), router_address.clone(),
token_in_already_in_router, token_in_already_in_router,
)?, )?,
sequential_swap_strategy: SequentialSwapStrategyEncoder::new( sequential_swap_strategy: SequentialSwapStrategyEncoder::new(
chain.clone(), chain.clone(),
swap_encoder_registry.clone(), swap_encoder_registry.clone(),
swapper_pk.clone(), permit2.is_some(),
router_address.clone(), router_address.clone(),
token_in_already_in_router, token_in_already_in_router,
)?, )?,
split_swap_strategy: SplitSwapStrategyEncoder::new( split_swap_strategy: SplitSwapStrategyEncoder::new(
chain, chain,
swap_encoder_registry, swap_encoder_registry,
None, permit2.is_some(),
router_address.clone(), router_address.clone(),
token_in_already_in_router, token_in_already_in_router,
)?, )?,
native_address, native_address,
wrapped_address, wrapped_address,
router_address,
token_in_already_in_router,
permit2,
}) })
} }
fn encode_solution(&self, solution: &Solution) -> Result<EncodedSolution, EncodingError> {
self.validate_solution(solution)?;
let protocols: HashSet<String> = solution
.clone()
.swaps
.into_iter()
.map(|swap| swap.component.protocol_system)
.collect();
let mut encoded_solution = if (solution.swaps.len() == 1) ||
(protocols.len() == 1 &&
protocols
.iter()
.any(|p| GROUPABLE_PROTOCOLS.contains(&p.as_str())))
{
self.single_swap_strategy
.encode_strategy(solution.clone())?
} else if solution
.swaps
.iter()
.all(|swap| swap.split == 0.0)
{
self.sequential_swap_strategy
.encode_strategy(solution.clone())?
} else {
self.split_swap_strategy
.encode_strategy(solution.clone())?
};
if let Some(permit2) = self.permit2.clone() {
let (permit, signature) = permit2.get_permit(
&self.router_address,
&solution.sender,
&solution.given_token,
&solution.given_amount,
)?;
encoded_solution.permit = Some(permit);
encoded_solution.signature = Some(signature);
}
Ok(encoded_solution)
}
} }
impl TychoEncoder for TychoRouterEncoder { impl TychoEncoder for TychoRouterEncoder {
fn encode_calldata(&self, solutions: Vec<Solution>) -> Result<Vec<Transaction>, EncodingError> { fn encode_solutions(
&self,
solutions: Vec<Solution>,
) -> Result<Vec<EncodedSolution>, EncodingError> {
let mut result: Vec<EncodedSolution> = Vec::new();
for solution in solutions.iter() {
let encoded_solution = self.encode_solution(solution)?;
result.push(encoded_solution);
}
Ok(result)
}
fn encode_full_calldata(
&self,
solutions: Vec<Solution>,
) -> Result<Vec<Transaction>, EncodingError> {
let mut transactions: Vec<Transaction> = Vec::new(); let mut transactions: Vec<Transaction> = Vec::new();
for solution in solutions.iter() { for solution in solutions.iter() {
self.validate_solution(solution)?; let encoded_solution = self.encode_solution(solution)?;
let protocols: HashSet<String> = solution let transaction = encode_tycho_router_call(
.clone() encoded_solution,
.swaps solution,
.into_iter() self.token_in_already_in_router,
.map(|swap| swap.component.protocol_system) self.native_address.clone(),
.collect(); )?;
let (contract_interaction, target_address) = if (solution.swaps.len() == 1) || transactions.push(transaction);
(protocols.len() == 1 &&
protocols
.iter()
.any(|p| GROUPABLE_PROTOCOLS.contains(&p.as_str())))
{
self.single_swap_strategy
.encode_strategy(solution.clone())?
} else if solution
.swaps
.iter()
.all(|swap| swap.split == 0.0)
{
self.sequential_swap_strategy
.encode_strategy(solution.clone())?
} else {
self.split_swap_strategy
.encode_strategy(solution.clone())?
};
let value = if solution.given_token == self.native_address {
solution.given_amount.clone()
} else {
BigUint::ZERO
};
transactions.push(Transaction {
value,
data: contract_interaction,
to: target_address,
});
} }
Ok(transactions) Ok(transactions)
} }
@@ -231,22 +274,17 @@ impl TychoEncoder for TychoRouterEncoder {
#[derive(Clone)] #[derive(Clone)]
pub struct TychoExecutorEncoder { pub struct TychoExecutorEncoder {
swap_encoder_registry: SwapEncoderRegistry, swap_encoder_registry: SwapEncoderRegistry,
native_address: Bytes,
} }
impl TychoExecutorEncoder { impl TychoExecutorEncoder {
pub fn new( pub fn new(swap_encoder_registry: SwapEncoderRegistry) -> Result<Self, EncodingError> {
chain: Chain, Ok(TychoExecutorEncoder { swap_encoder_registry })
swap_encoder_registry: SwapEncoderRegistry,
) -> Result<Self, EncodingError> {
let native_address = chain.native_token()?;
Ok(TychoExecutorEncoder { swap_encoder_registry, native_address })
} }
fn encode_executor_calldata( fn encode_executor_calldata(
&self, &self,
solution: Solution, solution: Solution,
) -> Result<(Vec<u8>, Bytes), EncodingError> { ) -> Result<EncodedSolution, EncodingError> {
let grouped_swaps = group_swaps(solution.clone().swaps); let grouped_swaps = group_swaps(solution.clone().swaps);
let number_of_groups = grouped_swaps.len(); let number_of_groups = grouped_swaps.len();
if number_of_groups > 1 { if number_of_groups > 1 {
@@ -295,29 +333,39 @@ impl TychoExecutorEncoder {
let executor_address = Bytes::from_str(swap_encoder.executor_address()) let executor_address = Bytes::from_str(swap_encoder.executor_address())
.map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?; .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?;
Ok((grouped_protocol_data, executor_address)) Ok(EncodedSolution {
swaps: grouped_protocol_data,
interacting_with: executor_address,
permit: None,
signature: None,
selector: "".to_string(),
n_tokens: 0,
})
} }
} }
impl TychoEncoder for TychoExecutorEncoder { impl TychoEncoder for TychoExecutorEncoder {
fn encode_calldata(&self, solutions: Vec<Solution>) -> Result<Vec<Transaction>, EncodingError> { fn encode_solutions(
let mut transactions: Vec<Transaction> = Vec::new(); &self,
solutions: Vec<Solution>,
) -> Result<Vec<EncodedSolution>, EncodingError> {
let solution = solutions let solution = solutions
.first() .first()
.ok_or(EncodingError::FatalError("No solutions found".to_string()))?; .ok_or(EncodingError::FatalError("No solutions found".to_string()))?;
self.validate_solution(solution)?; self.validate_solution(solution)?;
let (contract_interaction, target_address) = let encoded_solution = self.encode_executor_calldata(solution.clone())?;
self.encode_executor_calldata(solution.clone())?;
let value = if solution.given_token == self.native_address { Ok(vec![encoded_solution])
solution.given_amount.clone() }
} else {
BigUint::ZERO
};
transactions.push(Transaction { value, data: contract_interaction, to: target_address }); fn encode_full_calldata(
Ok(transactions) &self,
_solutions: Vec<Solution>,
) -> Result<Vec<Transaction>, EncodingError> {
Err(EncodingError::NotImplementedError(
"Full calldata encoding is not supported for TychoExecutorEncoder".to_string(),
))
} }
/// Raises an `EncodingError` if the solution is not considered valid. /// Raises an `EncodingError` if the solution is not considered valid.
@@ -338,7 +386,7 @@ impl TychoEncoder for TychoExecutorEncoder {
mod tests { mod tests {
use std::{collections::HashMap, str::FromStr}; use std::{collections::HashMap, str::FromStr};
use num_bigint::BigInt; use num_bigint::{BigInt, BigUint};
use tycho_common::models::{protocol::ProtocolComponent, Chain as TychoCommonChain}; use tycho_common::models::{protocol::ProtocolComponent, Chain as TychoCommonChain};
use super::*; use super::*;
@@ -458,7 +506,7 @@ mod tests {
..Default::default() ..Default::default()
}; };
let transactions = encoder.encode_calldata(vec![solution]); let transactions = encoder.encode_full_calldata(vec![solution]);
assert!(transactions.is_ok()); assert!(transactions.is_ok());
let transactions = transactions.unwrap(); let transactions = transactions.unwrap();
assert_eq!(transactions.len(), 1); assert_eq!(transactions.len(), 1);
@@ -479,16 +527,14 @@ mod tests {
given_token: usdc(), given_token: usdc(),
given_amount: BigUint::from_str("1000_000000").unwrap(), given_amount: BigUint::from_str("1000_000000").unwrap(),
checked_token: pepe(), checked_token: pepe(),
expected_amount: Some(BigUint::from_str("105_152_000000000000000000").unwrap()), checked_amount: BigUint::from_str("105_152_000000000000000000").unwrap(),
checked_amount: None,
slippage: None,
sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
swaps: vec![swap_usdc_eth_univ4(), swap_eth_pepe_univ4()], swaps: vec![swap_usdc_eth_univ4(), swap_eth_pepe_univ4()],
..Default::default() ..Default::default()
}; };
let transactions = encoder.encode_calldata(vec![solution]); let transactions = encoder.encode_full_calldata(vec![solution]);
assert!(transactions.is_ok()); assert!(transactions.is_ok());
let transactions = transactions.unwrap(); let transactions = transactions.unwrap();
assert_eq!(transactions.len(), 1); assert_eq!(transactions.len(), 1);
@@ -530,11 +576,11 @@ mod tests {
swaps: vec![swap_weth_dai, swap_dai_usdc], swaps: vec![swap_weth_dai, swap_dai_usdc],
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
native_action: Some(NativeAction::Wrap), native_action: Some(NativeAction::Wrap),
checked_amount: Some(BigUint::from(1000u32)), checked_amount: BigUint::from(1000u32),
..Default::default() ..Default::default()
}; };
let transactions = encoder.encode_calldata(vec![solution]); let transactions = encoder.encode_full_calldata(vec![solution]);
assert!(transactions.is_ok()); assert!(transactions.is_ok());
let transactions = transactions.unwrap(); let transactions = transactions.unwrap();
assert_eq!(transactions.len(), 1); assert_eq!(transactions.len(), 1);
@@ -579,7 +625,6 @@ mod tests {
exact_out: false, exact_out: false,
given_token: eth(), given_token: eth(),
checked_token: dai(), checked_token: dai(),
checked_amount: None,
swaps: vec![swap], swaps: vec![swap],
native_action: Some(NativeAction::Wrap), native_action: Some(NativeAction::Wrap),
..Default::default() ..Default::default()
@@ -693,7 +738,6 @@ mod tests {
let solution = Solution { let solution = Solution {
exact_out: false, exact_out: false,
checked_token: eth(), checked_token: eth(),
checked_amount: None,
swaps: vec![swap], swaps: vec![swap],
native_action: Some(NativeAction::Unwrap), native_action: Some(NativeAction::Unwrap),
..Default::default() ..Default::default()
@@ -1011,9 +1055,7 @@ mod tests {
#[test] #[test]
fn test_executor_encoder_encode() { fn test_executor_encoder_encode() {
let swap_encoder_registry = get_swap_encoder_registry(); let swap_encoder_registry = get_swap_encoder_registry();
let encoder = let encoder = TychoExecutorEncoder::new(swap_encoder_registry).unwrap();
TychoExecutorEncoder::new(TychoCommonChain::Ethereum.into(), swap_encoder_registry)
.unwrap();
let token_in = weth(); let token_in = weth();
let token_out = dai(); let token_out = dai();
@@ -1033,26 +1075,24 @@ mod tests {
exact_out: false, exact_out: false,
given_token: token_in, given_token: token_in,
given_amount: BigUint::from(1000000000000000000u64), given_amount: BigUint::from(1000000000000000000u64),
expected_amount: Some(BigUint::from(1000000000000000000u64)),
checked_token: token_out, checked_token: token_out,
checked_amount: None, checked_amount: BigUint::from(1000000000000000000u64),
sender: Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(), sender: Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(),
// The receiver was generated with `makeAddr("bob") using forge` // The receiver was generated with `makeAddr("bob") using forge`
receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(),
swaps: vec![swap], swaps: vec![swap],
slippage: None,
native_action: None, native_action: None,
}; };
let transactions = encoder let encoded_solutions = encoder
.encode_calldata(vec![solution]) .encode_solutions(vec![solution])
.unwrap(); .unwrap();
let transaction = transactions let encoded = encoded_solutions
.first() .first()
.expect("Expected at least one transaction"); .expect("Expected at least one encoded solution");
let hex_protocol_data = encode(&transaction.data); let hex_protocol_data = encode(&encoded.swaps);
assert_eq!( assert_eq!(
transaction.to, encoded.interacting_with,
Bytes::from_str("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f").unwrap() Bytes::from_str("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f").unwrap()
); );
assert_eq!( assert_eq!(
@@ -1075,9 +1115,7 @@ mod tests {
#[test] #[test]
fn test_executor_encoder_too_many_swaps() { fn test_executor_encoder_too_many_swaps() {
let swap_encoder_registry = get_swap_encoder_registry(); let swap_encoder_registry = get_swap_encoder_registry();
let encoder = let encoder = TychoExecutorEncoder::new(swap_encoder_registry).unwrap();
TychoExecutorEncoder::new(TychoCommonChain::Ethereum.into(), swap_encoder_registry)
.unwrap();
let token_in = weth(); let token_in = weth();
let token_out = dai(); let token_out = dai();
@@ -1096,26 +1134,22 @@ mod tests {
exact_out: false, exact_out: false,
given_token: token_in, given_token: token_in,
given_amount: BigUint::from(1000000000000000000u64), given_amount: BigUint::from(1000000000000000000u64),
expected_amount: Some(BigUint::from(1000000000000000000u64)),
checked_token: token_out, checked_token: token_out,
checked_amount: None, checked_amount: BigUint::from(1000000000000000000u64),
sender: Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(), sender: Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(),
receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(),
swaps: vec![swap.clone(), swap], swaps: vec![swap.clone(), swap],
slippage: None,
native_action: None, native_action: None,
}; };
let result = encoder.encode_calldata(vec![solution]); let result = encoder.encode_solutions(vec![solution]);
assert!(result.is_err()); assert!(result.is_err());
} }
#[test] #[test]
fn test_executor_encoder_grouped_swaps() { fn test_executor_encoder_grouped_swaps() {
let swap_encoder_registry = get_swap_encoder_registry(); let swap_encoder_registry = get_swap_encoder_registry();
let encoder = let encoder = TychoExecutorEncoder::new(swap_encoder_registry).unwrap();
TychoExecutorEncoder::new(TychoCommonChain::Ethereum.into(), swap_encoder_registry)
.unwrap();
let usdc = usdc(); let usdc = usdc();
let pepe = pepe(); let pepe = pepe();
@@ -1125,24 +1159,22 @@ mod tests {
given_token: usdc, given_token: usdc,
given_amount: BigUint::from_str("1000_000000").unwrap(), given_amount: BigUint::from_str("1000_000000").unwrap(),
checked_token: pepe, checked_token: pepe,
expected_amount: Some(BigUint::from_str("105_152_000000000000000000").unwrap()), checked_amount: BigUint::from(1000000000000000000u64),
checked_amount: None,
slippage: None,
sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
swaps: vec![swap_usdc_eth_univ4(), swap_eth_pepe_univ4()], swaps: vec![swap_usdc_eth_univ4(), swap_eth_pepe_univ4()],
..Default::default() ..Default::default()
}; };
let transactions = encoder let encoded_solutions = encoder
.encode_calldata(vec![solution]) .encode_solutions(vec![solution])
.unwrap(); .unwrap();
let transaction = transactions let encoded_solution = encoded_solutions
.first() .first()
.expect("Expected at least one transaction"); .expect("Expected at least one encoded solution");
let hex_protocol_data = encode(&transaction.data); let hex_protocol_data = encode(&encoded_solution.swaps);
assert_eq!( assert_eq!(
transaction.to, encoded_solution.interacting_with,
Bytes::from_str("0xf62849f9a0b5bf2913b396098f7c7019b51a820a").unwrap() Bytes::from_str("0xf62849f9a0b5bf2913b396098f7c7019b51a820a").unwrap()
); );
assert_eq!( assert_eq!(

View File

@@ -1,5 +1,4 @@
use std::{ use std::{
cmp::max,
env, env,
fs::OpenOptions, fs::OpenOptions,
io::{BufRead, BufReader, Write}, io::{BufRead, BufReader, Write},
@@ -10,17 +9,14 @@ use alloy::{
providers::{ProviderBuilder, RootProvider}, providers::{ProviderBuilder, RootProvider},
transports::BoxTransport, transports::BoxTransport,
}; };
use alloy_primitives::{aliases::U24, keccak256, Address, FixedBytes, Keccak256, U256, U8}; use alloy_primitives::{aliases::U24, Address, U256, U8};
use alloy_sol_types::SolValue; use alloy_sol_types::SolValue;
use num_bigint::BigUint; use num_bigint::BigUint;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use tokio::runtime::{Handle, Runtime}; use tokio::runtime::{Handle, Runtime};
use tycho_common::Bytes; use tycho_common::Bytes;
use crate::encoding::{ use crate::encoding::{errors::EncodingError, models::Swap};
errors::EncodingError,
models::{Solution, Swap},
};
/// Safely converts a `Bytes` object to an `Address` object. /// Safely converts a `Bytes` object to an `Address` object.
/// ///
@@ -40,28 +36,6 @@ pub fn biguint_to_u256(value: &BigUint) -> U256 {
U256::from_be_slice(&bytes) U256::from_be_slice(&bytes)
} }
/// Encodes the input data for a function call to the given function selector.
pub fn encode_input(selector: &str, mut encoded_args: Vec<u8>) -> Vec<u8> {
let mut hasher = Keccak256::new();
hasher.update(selector.as_bytes());
let selector_bytes = &hasher.finalize()[..4];
let mut call_data = selector_bytes.to_vec();
// Remove extra prefix if present (32 bytes for dynamic data)
// Alloy encoding is including a prefix for dynamic data indicating the offset or length
// but at this point we don't want that
if encoded_args.len() > 32 &&
encoded_args[..32] ==
[0u8; 31]
.into_iter()
.chain([32].to_vec())
.collect::<Vec<u8>>()
{
encoded_args = encoded_args[32..].to_vec();
}
call_data.extend(encoded_args);
call_data
}
/// Converts a decimal to a `U24` value. The percentage is a `f64` value between 0 and 1. /// Converts a decimal to a `U24` value. The percentage is a `f64` value between 0 and 1.
/// MAX_UINT24 corresponds to 100%. /// MAX_UINT24 corresponds to 100%.
pub fn percentage_to_uint24(decimal: f64) -> U24 { pub fn percentage_to_uint24(decimal: f64) -> U24 {
@@ -71,30 +45,6 @@ pub fn percentage_to_uint24(decimal: f64) -> U24 {
U24::from(scaled.round()) U24::from(scaled.round())
} }
/// Gets the minimum amount out for a solution to pass when executed on-chain.
///
/// The minimum amount is calculated based on the expected amount and the slippage percentage, if
/// passed. If this information is not passed, the user-passed checked amount will be used.
/// If both the slippage and minimum user-passed checked amount are passed, the maximum of the two
/// will be used.
/// If neither are passed, the minimum amount will be zero.
pub fn get_min_amount_for_solution(solution: Solution) -> BigUint {
let mut min_amount_out = solution
.checked_amount
.unwrap_or(BigUint::ZERO);
if let (Some(expected_amount), Some(slippage)) =
(solution.expected_amount.as_ref(), solution.slippage)
{
let bps = BigUint::from(10_000u32);
let slippage_percent = BigUint::from((slippage * 10000.0) as u32);
let multiplier = &bps - slippage_percent;
let expected_amount_with_slippage = (expected_amount * &multiplier) / &bps;
min_amount_out = max(min_amount_out, expected_amount_with_slippage);
}
min_amount_out
}
/// Gets the position of a token in a list of tokens. /// Gets the position of a token in a list of tokens.
pub fn get_token_position(tokens: Vec<Bytes>, token: Bytes) -> Result<U8, EncodingError> { pub fn get_token_position(tokens: Vec<Bytes>, token: Bytes) -> Result<U8, EncodingError> {
let position = U8::from( let position = U8::from(
@@ -116,12 +66,6 @@ pub fn pad_to_fixed_size<const N: usize>(input: &[u8]) -> Result<[u8; N], Encodi
Ok(padded) Ok(padded)
} }
/// Encodes a function selector to a fixed size array of 4 bytes.
pub fn encode_function_selector(selector: &str) -> FixedBytes<4> {
let hash = keccak256(selector.as_bytes());
FixedBytes::<4>::from([hash[0], hash[1], hash[2], hash[3]])
}
/// Extracts a static attribute from a swap. /// Extracts a static attribute from a swap.
pub fn get_static_attribute(swap: &Swap, attribute_name: &str) -> Result<Vec<u8>, EncodingError> { pub fn get_static_attribute(swap: &Swap, attribute_name: &str) -> Result<Vec<u8>, EncodingError> {
Ok(swap Ok(swap
@@ -215,28 +159,3 @@ pub fn write_calldata_to_file(test_identifier: &str, hex_calldata: &str) {
writeln!(file, "{line}").expect("Failed to write calldata"); writeln!(file, "{line}").expect("Failed to write calldata");
} }
} }
#[cfg(test)]
mod tests {
use num_bigint::BigUint;
use super::*;
use crate::encoding::models::Solution;
#[test]
fn test_min_amount_out_small_slippage() {
// Tests that the calculation's precision is high enough to support a slippage of 0.1%.
let solution = Solution {
exact_out: false,
given_amount: BigUint::from(1000000000000000000u64),
checked_amount: None,
slippage: Some(0.001f64),
expected_amount: Some(BigUint::from(1000000000000000000u64)),
..Default::default()
};
let min_amount_out = get_min_amount_for_solution(solution);
assert_eq!(min_amount_out, BigUint::from(999000000000000000u64));
}
}

View File

@@ -1,3 +1,4 @@
use alloy_primitives::PrimitiveSignature as Signature;
use hex; use hex;
use num_bigint::BigUint; use num_bigint::BigUint;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -7,8 +8,7 @@ use tycho_common::{
}; };
use crate::encoding::{ use crate::encoding::{
errors::EncodingError, errors::EncodingError, evm::approvals::permit2::PermitSingle, serde_primitives::biguint_string,
serde_primitives::{biguint_string, biguint_string_option},
}; };
/// Represents a solution containing details describing an order, and instructions for filling /// Represents a solution containing details describing an order, and instructions for filling
@@ -30,15 +30,9 @@ pub struct Solution {
/// supported. /// supported.
#[serde(default)] #[serde(default)]
pub exact_out: bool, pub exact_out: bool,
/// If set, it will be applied to expected_amount
pub slippage: Option<f64>,
/// Expected amount of the bought token (exact in) or sold token (exact out).
#[serde(with = "biguint_string_option")]
pub expected_amount: Option<BigUint>,
/// Minimum amount to be checked for the solution to be valid. /// Minimum amount to be checked for the solution to be valid.
/// If not set, the check will not be performed. #[serde(with = "biguint_string")]
#[serde(with = "biguint_string_option")] pub checked_amount: BigUint,
pub checked_amount: Option<BigUint>,
/// List of swaps to fulfill the solution. /// List of swaps to fulfill the solution.
pub swaps: Vec<Swap>, pub swaps: Vec<Swap>,
/// If set, the corresponding native action will be executed. /// If set, the corresponding native action will be executed.
@@ -96,6 +90,25 @@ pub struct Transaction {
pub data: Vec<u8>, pub data: Vec<u8>,
} }
/// Represents a solution that has been encoded for execution.
///
/// # Fields
/// * `swaps`: Encoded swaps to be executed.
/// * `interacting_with`: Address of the contract to be called.
/// * `selector`: The selector of the function to be called.
/// * `n_tokens`: Number of tokens in the swap.
/// * `permit`: Optional permit for the swap (if permit2 is enabled).
/// * `signature`: Optional signature for the swap (if permit2 is enabled).
#[derive(Clone, Debug)]
pub struct EncodedSolution {
pub swaps: Vec<u8>,
pub interacting_with: Bytes,
pub selector: String,
pub n_tokens: usize,
pub permit: Option<PermitSingle>,
pub signature: Option<Signature>,
}
/// Represents necessary attributes for encoding an order. /// Represents necessary attributes for encoding an order.
/// ///
/// # Fields /// # Fields

View File

@@ -1,6 +1,8 @@
use tycho_common::Bytes; use crate::encoding::{
errors::EncodingError,
use crate::encoding::{errors::EncodingError, models::Solution, swap_encoder::SwapEncoder}; models::{EncodedSolution, Solution},
swap_encoder::SwapEncoder,
};
/// A trait that defines how to encode a `Solution` for execution. /// A trait that defines how to encode a `Solution` for execution.
pub trait StrategyEncoder { pub trait StrategyEncoder {
@@ -13,11 +15,8 @@ pub trait StrategyEncoder {
/// path /// path
/// ///
/// # Returns /// # Returns
/// * `Result<(Vec<u8>, Bytes, Option<String>), EncodingError>` - A tuple containing: /// * `Result<EncodedSwaps, EncodingError>`
/// - The encoded data as bytes fn encode_strategy(&self, solution: Solution) -> Result<EncodedSolution, EncodingError>;
/// - The address of the contract to call (router or executor)
/// - Optionally, the function selector to use when calling the contract
fn encode_strategy(&self, solution: Solution) -> Result<(Vec<u8>, Bytes), EncodingError>;
/// Retrieves the swap encoder for a specific protocol system. /// Retrieves the swap encoder for a specific protocol system.
/// ///

View File

@@ -1,20 +1,78 @@
use crate::encoding::{ use crate::encoding::{
errors::EncodingError, errors::EncodingError,
models::{Solution, Transaction}, models::{EncodedSolution, Solution, Transaction},
}; };
/// A high-level encoder that converts solutions into executable transactions. Allows for modularity /// A high-level interface for encoding solutions into Tycho-compatible transactions or raw call
/// in the encoding process. /// data.
///
/// This trait is designed to abstract the encoding logic required to prepare swap transactions for
/// the Tycho Router. It enables modular and customizable construction of transactions, allowing
/// integrators to maintain full control over the execution constraints.
///
/// # User Responsibility
///
/// While this trait provides convenience methods, it is **strongly recommended** that users favor
/// [`encode_solutions`] over [`encode_calldata`]. This is because:
///
/// - `encode_solutions` returns raw [`EncodedSolution`] objects, which include Tychos swap path
/// encoding, but leave **function argument encoding entirely in the users hands**.
/// - The function arguments to the router (e.g., `minAmountOut`, `receiver`, `unwrap`, `permit2`,
/// etc.) are used as **guardrails** to ensure safe on-chain execution.
/// - Automatically constructing full transactions via [`encode_calldata`] can obscure these
/// important safeguards and may result in unexpected behavior or vulnerability to MEV.
///
/// Tycho is only responsible for generating the internal swap plan. **The user must encode the
/// outer function call arguments themselves** and verify that they enforce correct and secure
/// behavior.
pub trait TychoEncoder { pub trait TychoEncoder {
/// Encodes solutions into transactions that can be executed by the Tycho router. /// Encodes a list of [`Solution`]s into [`EncodedSolution`]s, which include the selector and
/// internal swap call data.
/// ///
/// # Arguments /// This method gives users maximum flexibility and control. It **does not** produce full
/// * `solutions` - Vector of solutions to encode, each potentially using different setups (swap /// transaction objects. Users are responsible for:
/// paths, protocols, wrapping, etc.) /// - Constructing the full calldata using their own encoding logic.
/// - Managing execution-critical parameters like `minAmountOut`.
/// ///
/// # Returns /// # Returns
/// * `Result<Vec<Transaction>, EncodingError>` - Vector of executable transactions /// A vector of encoded solutions, each containing:
fn encode_calldata(&self, solutions: Vec<Solution>) -> Result<Vec<Transaction>, EncodingError>; /// - The Tycho method selector
/// - The encoded swap path
/// - Additional metadata (e.g., permit2 information)
///
/// # Recommendation
/// Use this method if you care about execution safety and want to avoid surprises.
fn encode_solutions(
&self,
solutions: Vec<Solution>,
) -> Result<Vec<EncodedSolution>, EncodingError>;
/// Encodes a list of [`Solution`]s directly into executable transactions for the Tycho router.
///
/// This method wraps around Tychos example encoding logic (see [`encode_tycho_router_call`])
/// and should only be used for **prototyping or development**.
///
/// # Warning
/// This implementation uses default logic to construct the outer calldata (e.g., for setting
/// `minAmountOut`). This might not be optimal or safe for production use.
///
/// To ensure correctness, **users should implement their own encoding pipeline** using
/// [`encode_solutions`].
///
/// # Returns
/// A vector of fully constructed [`Transaction`]s that can be submitted to a node or bundler.
fn encode_full_calldata(
&self,
solutions: Vec<Solution>,
) -> Result<Vec<Transaction>, EncodingError>;
/// Performs solution-level validation and sanity checks.
///
/// This function can be used to verify whether a proposed solution is structurally sound and
/// ready for encoding.
///
/// # Returns
/// - `Ok(())` if the solution is valid.
/// - `Err(EncodingError)` if the solution is malformed or unsupported.
fn validate_solution(&self, solution: &Solution) -> Result<(), EncodingError>; fn validate_solution(&self, solution: &Solution) -> Result<(), EncodingError>;
} }