From 75f2c3a1c52b6ee8823d449b70147cb023202e93 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Fri, 23 May 2025 11:31:05 +0100 Subject: [PATCH 01/11] feat: Create non alloy specific Permit and PermitDetails structs - Add try_from methods to convert them to the corresponding Solidity representations - Update tests Took 56 seconds --- foundry/test/assets/calldata.txt | 24 +++---- src/bin/tycho-encode.rs | 36 ++++++----- src/encoding/evm/approvals/permit2.rs | 63 +++++++++++++++---- src/encoding/evm/encoding_utils.rs | 57 +++++++++-------- .../evm/strategy_encoder/strategy_encoders.rs | 36 +++++------ src/encoding/evm/tycho_encoders.rs | 2 +- src/encoding/models.rs | 49 +++++++++++++-- 7 files changed, 177 insertions(+), 90 deletions(-) diff --git a/foundry/test/assets/calldata.txt b/foundry/test/assets/calldata.txt index 0e1927a..f5648a1 100644 --- a/foundry/test/assets/calldata.txt +++ b/foundry/test/assets/calldata.txt @@ -3,23 +3,23 @@ test_single_encoding_strategy_ekubo:5c4b639c000000000000000000000000000000000000 test_uniswap_v3_uniswap_v3:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010100000000000000000000 test_balancer_v2_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_sequential_swap_strategy_encoder_no_permit2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 -test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041972d1d0dd19889db47087a4653c517a455a19a1dc76a0a14a29fd12ae7d2d4f218ba986eada63bfdfacf78e3a7ffdce07c4064fcf11033f185819509f64b6b191b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 -test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e0000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c0bbb5f493c1d4b87c92085902e4488efba2b87740abdcc298a5adb435bfe3b23820a0677d325e34fa06e8c2a91f0d3851b00cf170885176b5f47d40983d64941b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000 -test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000419861aedfb04e8f7b9fe147a7d19c41f9b1cf37a06850b09f645fb351c003967d43d365af50b08ef741348898578b7fa1454a8e9cd0390eaf6c2939d7b817d0d31c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041cf25e6401910938ea5c874981fd6a62d202517677c6cde7546d0de7a1047bb7725946110d38bb0011e6ad191a8245b6de439a04fcde4b86284040260300603ae1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f50000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000412e33858dc4899298476636c393b474a6995d3c076b936d46b90cf6ee00adc801687753f6bad7b89f905d818d217bd94cc0936a9838c48bcaabccd6819baa5e031b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000 +test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004124f83f5b4802258cc983101d1cec5c5c3a4a6fed53e545bf8d222817cb75f61d133f1b9916cc0431ea05020b9452ecc673e8d0d27dbaf273c1aa8df9f628850e1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_single_swap_strategy_encoder_no_permit2:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_swap_strategy_encoder_no_transfer_in:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 -test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041a8ad04262cc02545cc4934739e086f37b3ea04ba83195a151a736e55cf2cc68407f9dc20f19c68071ced3f6d5f20908ce9332526c955e66974aaf53c7e58ca581c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 -test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041d461cc5890a8a58d13249a485335b44671612c97b7a12004b5cfb163bdbf4c2f15dde7cb500c22f724f813b7d6fe5062076f63ee5b91c7e984f011a5d1ce6fca1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 +test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004162924a8458b13d994bef77420a52e2c31998e7c2bb7a595650784e67313cd10767f720f6213b38446a60410060e3b0ee7359fa6b67a1a06395eaed17e1c6522d1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 +test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000415eaa6f0c1cb7ba5566023dc3eade4139ebd73f9da94bbc8281dfea578b534f4e1e72bca67d25a3286769b1c8f8ec5d437f3e6f373435c356edf0832d6496688f1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 test_single_encoding_strategy_curve_st_eth:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 -test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006b56051582a970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000419861aedfb04e8f7b9fe147a7d19c41f9b1cf37a06850b09f645fb351c003967d43d365af50b08ef741348898578b7fa1454a8e9cd0390eaf6c2939d7b817d0d31c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 +test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004124f83f5b4802258cc983101d1cec5c5c3a4a6fed53e545bf8d222817cb75f61d133f1b9916cc0431ea05020b9452ecc673e8d0d27dbaf273c1aa8df9f628850e1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_encoding_strategy_curve:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 -test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004133c6e3010dfd7e69b717338de47a7129351aefa89191faa6c76250356c6adae041da1ef7cb664511e942ecd28f7b578b38dad510df3185241c8336259027ded11c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000 -test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041a8ad04262cc02545cc4934739e086f37b3ea04ba83195a151a736e55cf2cc68407f9dc20f19c68071ced3f6d5f20908ce9332526c955e66974aaf53c7e58ca581c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 -test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685552e700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccef000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f208f39e98ca0821f644f4fd3be7ab23ce75c0e3d6e425b00327067b482dc3963e0c481141fe530d79cc8361ea628a0f53d69cbbd09b7c2416581f04392704681b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000 -test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041d461cc5890a8a58d13249a485335b44671612c97b7a12004b5cfb163bdbf4c2f15dde7cb500c22f724f813b7d6fe5062076f63ee5b91c7e984f011a5d1ce6fca1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000 -test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccef000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041726b94d0c0b711c31e19e1b33bcdd87e4526b4fd804471bbafcb0fed1fdc5aea23922bb4d46f16add4f01136165643feb355d89bf31d1a25a8a1a126ee3af43e1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000 +test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000417ce82920975101831860e9074248018ce1908c61639053bf209f22a0c708db2153d31d68c85362e7f149d9c5481145a4feed8f726a3d6f7a7d7efb2aff876faa1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000 +test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004162924a8458b13d994bef77420a52e2c31998e7c2bb7a595650784e67313cd10767f720f6213b38446a60410060e3b0ee7359fa6b67a1a06395eaed17e1c6522d1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 +test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f50000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eaa6f0c1cb7ba5566023dc3eade4139ebd73f9da94bbc8281dfea578b534f4e1e72bca67d25a3286769b1c8f8ec5d437f3e6f373435c356edf0832d6496688f1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000 +test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f50000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eaa6f0c1cb7ba5566023dc3eade4139ebd73f9da94bbc8281dfea578b534f4e1e72bca67d25a3286769b1c8f8ec5d437f3e6f373435c356edf0832d6496688f1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000 +test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004124f83f5b4802258cc983101d1cec5c5c3a4a6fed53e545bf8d222817cb75f61d133f1b9916cc0431ea05020b9452ecc673e8d0d27dbaf273c1aa8df9f628850e1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000 test_uniswap_v3_curve:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000 -test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf0000000000000000000000000000000000000000000000000000000000000685552e700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccef00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410488df0c2ac790f75505dbd2754518d70fb02c32fb3bb3e63e299259037765606ca8d38d05c9c6d2d82e3b39203f378ad489f715a2aeb7df674ee4b8e5c8d1151c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 +test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf00000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041dd1ac43cb92f11fdd55f3a6219229c3397db9bde277ebf06b865f90f1505e27322fa280a162761d23ec4269ee0e4ee23f78a56d3d9044a4b328ead78ace55e1f1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0102 test_ekubo_encode_swap_multi:01ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032 test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index fa21fbd..71ea377 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -4,7 +4,10 @@ use alloy_sol_types::SolValue; use clap::{Parser, Subcommand}; use tycho_common::{hex_bytes::Bytes, models::Chain}; use tycho_execution::encoding::{ - evm::encoder_builders::{TychoExecutorEncoderBuilder, TychoRouterEncoderBuilder}, + evm::{ + approvals::permit2::PermitSingle, + encoder_builders::{TychoExecutorEncoderBuilder, TychoRouterEncoderBuilder}, + }, models::Solution, tycho_encoder::TychoEncoder, }; @@ -102,19 +105,24 @@ fn main() -> Result<(), Box> { let encoded_solutions = encoder.encode_solutions(vec![solution])?; let encoded = serde_json::json!({ - "swaps": format!("0x{}", hex::encode(&encoded_solutions[0].swaps)), - "interacting_with": format!("0x{}", hex::encode(&encoded_solutions[0].interacting_with)), - "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), - }); + "swaps": format!("0x{}", hex::encode(&encoded_solutions[0].swaps)), + "interacting_with": format!("0x{}", hex::encode(&encoded_solutions[0].interacting_with)), + "selector": format!("{}",&encoded_solutions[0].selector), + "n_tokens": format!("{}", &encoded_solutions[0].n_tokens), + "permit": match encoded_solutions[0].permit.as_ref() { + Some(permit) => { + match PermitSingle::try_from(permit.clone()) { + Ok(sol_permit) => format!("0x{}", hex::encode(sol_permit.abi_encode())), + Err(_) => String::new(), // or log or panic or whatever fallback + } + } + None => String::new(), + }, + "signature": encoded_solutions[0].signature + .as_ref() + .map(|signature| format!("0x{}", hex::encode(signature))) + .unwrap_or_else(String::new), + }); // Output the encoded result as JSON to stdout println!( "{}", diff --git a/src/encoding/evm/approvals/permit2.rs b/src/encoding/evm/approvals/permit2.rs index 8ad8787..075143c 100644 --- a/src/encoding/evm/approvals/permit2.rs +++ b/src/encoding/evm/approvals/permit2.rs @@ -23,6 +23,7 @@ use crate::encoding::{ encoding_utils::encode_input, utils::{biguint_to_u256, bytes_to_address, get_client, get_runtime}, }, + models, models::Chain, }; @@ -68,6 +69,44 @@ sol! { } } +impl TryFrom for models::PermitSingle { + type Error = EncodingError; + + fn try_from(sol: PermitSingle) -> Result { + Ok(models::PermitSingle { + details: models::PermitDetails { + token: Bytes::from(sol.details.token.to_vec()), + amount: BigUint::from_bytes_be(&sol.details.amount.to_be_bytes::<20>()), + expiration: BigUint::from_bytes_be( + &sol.details + .expiration + .to_be_bytes::<6>(), + ), + nonce: BigUint::from_bytes_be(&sol.details.nonce.to_be_bytes::<6>()), + }, + spender: Bytes::from(sol.spender.to_vec()), + sig_deadline: BigUint::from_bytes_be(&sol.sigDeadline.to_be_bytes::<32>()), + }) + } +} + +impl TryFrom for PermitSingle { + type Error = EncodingError; + + fn try_from(p: models::PermitSingle) -> Result { + Ok(PermitSingle { + details: PermitDetails { + token: bytes_to_address(&p.details.token)?, + amount: U160::from(biguint_to_u256(&p.details.amount)), + expiration: U48::from(biguint_to_u256(&p.details.expiration)), + nonce: U48::from(biguint_to_u256(&p.details.nonce)), + }, + spender: bytes_to_address(&p.spender)?, + sigDeadline: biguint_to_u256(&p.sig_deadline), + }) + } +} + impl Permit2 { pub fn new(swapper_pk: String, chain: Chain) -> Result { let (handle, runtime) = get_runtime()?; @@ -130,7 +169,7 @@ impl Permit2 { owner: &Bytes, token: &Bytes, amount: &BigUint, - ) -> Result<(PermitSingle, Signature), EncodingError> { + ) -> Result<(models::PermitSingle, Signature), EncodingError> { let current_time = Utc::now() .naive_utc() .and_utc() @@ -163,7 +202,7 @@ impl Permit2 { "Failed to sign permit2 approval with error: {e}" )) })?; - Ok((permit_single, signature)) + Ok((models::PermitSingle::try_from(permit_single)?, signature)) } } @@ -247,16 +286,16 @@ mod tests { .get_permit(&spender, &owner, &token, &amount) .unwrap(); - let expected_details = PermitDetails { - token: bytes_to_address(&token).unwrap(), - amount: U160::from(biguint_to_u256(&amount)), - expiration: U48::from(Utc::now().timestamp() as u64 + PERMIT_EXPIRATION), - nonce: U48::from(0), + let expected_details = models::PermitDetails { + token, + amount, + expiration: BigUint::from(Utc::now().timestamp() as u64 + PERMIT_EXPIRATION), + nonce: BigUint::from(0u64), }; - let expected_permit_single = PermitSingle { + let expected_permit_single = models::PermitSingle { details: expected_details, - spender: Address::from_str("0xba12222222228d8ba445958a75a0704d566bf2c8").unwrap(), - sigDeadline: U256::from(Utc::now().timestamp() as u64 + PERMIT_SIG_EXPIRATION), + spender: Bytes::from_str("0xba12222222228d8ba445958a75a0704d566bf2c8").unwrap(), + sig_deadline: BigUint::from(Utc::now().timestamp() as u64 + PERMIT_SIG_EXPIRATION), }; assert_eq!( @@ -310,8 +349,10 @@ mod tests { let (permit, signature) = permit2 .get_permit(&spender, &anvil_account, &token, &amount) .unwrap(); + let sol_permit: PermitSingle = + PermitSingle::try_from(permit).expect("Failed to convert to PermitSingle"); let encoded = - (bytes_to_address(&anvil_account).unwrap(), permit, signature.as_bytes().to_vec()) + (bytes_to_address(&anvil_account).unwrap(), sol_permit, signature.as_bytes().to_vec()) .abi_encode(); let function_signature = diff --git a/src/encoding/evm/encoding_utils.rs b/src/encoding/evm/encoding_utils.rs index 64f973a..a3b0862 100644 --- a/src/encoding/evm/encoding_utils.rs +++ b/src/encoding/evm/encoding_utils.rs @@ -5,7 +5,10 @@ use tycho_common::Bytes; use crate::encoding::{ errors::EncodingError, - evm::utils::{biguint_to_u256, bytes_to_address}, + evm::{ + approvals::permit2::PermitSingle, + utils::{biguint_to_u256, bytes_to_address}, + }, models::{EncodedSolution, NativeAction, Solution, Transaction}, }; @@ -103,11 +106,23 @@ pub fn encode_tycho_router_call( 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 permit = if let Some(p) = encoded_solution.permit { + Some( + PermitSingle::try_from(p) + .map_err(|_| EncodingError::InvalidInput("Invalid permit".to_string()))?, + ) + } else { + None + }; let method_calldata = if encoded_solution .selector .contains("singleSwapPermit2") { + let sig = encoded_solution + .signature + .ok_or(EncodingError::FatalError("Signature must be set to use permit2".to_string()))?; + println!("sig {:}", hex::encode(&sig)); ( given_amount, given_token, @@ -116,18 +131,10 @@ pub fn encode_tycho_router_call( 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(), + permit.ok_or(EncodingError::FatalError( + "permit2 object must be set to use permit2".to_string(), + ))?, + sig, encoded_solution.swaps, ) .abi_encode() @@ -159,18 +166,14 @@ pub fn encode_tycho_router_call( wrap, unwrap, receiver, - encoded_solution - .permit - .ok_or(EncodingError::FatalError( - "permit2 object must be set to use permit2".to_string(), - ))?, + 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() @@ -203,18 +206,14 @@ pub fn encode_tycho_router_call( unwrap, n_tokens, receiver, - encoded_solution - .permit - .ok_or(EncodingError::FatalError( - "permit2 object must be set to use permit2".to_string(), - ))?, + 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() diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 7463ae9..913a6d7 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -527,11 +527,10 @@ mod tests { use super::*; use crate::encoding::{ evm::{ - approvals::permit2::{Permit2, PermitSingle}, - encoding_utils::encode_tycho_router_call, + approvals::permit2::Permit2, encoding_utils::encode_tycho_router_call, utils::write_calldata_to_file, }, - models::Swap, + models::{PermitSingle, Swap}, }; fn eth_chain() -> Chain { @@ -580,12 +579,12 @@ mod tests { use alloy_sol_types::SolValue; use super::*; + use crate::encoding::evm::utils::biguint_to_u256; #[test] fn test_single_swap_strategy_encoder() { // Performs a single swap from WETH to DAI on a USV2 pool, with no grouping // optimizations. - let checked_amount = BigUint::from_str("2659881924818443699787").unwrap(); - let expected_min_amount = U256::from_str("2659881924818443699787").unwrap(); + let checked_amount = BigUint::from_str("2018817438608734439720").unwrap(); let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); @@ -625,12 +624,13 @@ mod tests { .unwrap(); let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) .unwrap() .data; - let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount)); + let expected_min_amount_encoded = + hex::encode(U256::abi_encode(&biguint_to_u256(&checked_amount))); let expected_input = [ "30ace1b1", // Function selector "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in @@ -874,7 +874,7 @@ mod tests { let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) .unwrap() @@ -928,7 +928,7 @@ mod tests { let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) .unwrap() @@ -1004,7 +1004,7 @@ mod tests { .unwrap(); let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) .unwrap() @@ -1194,7 +1194,7 @@ mod tests { .unwrap(); let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) .unwrap() @@ -1716,7 +1716,7 @@ mod tests { .unwrap(); let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth) .unwrap() @@ -1817,7 +1817,7 @@ mod tests { .unwrap(); let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) .unwrap() @@ -1932,7 +1932,7 @@ mod tests { .unwrap(); let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) .unwrap() @@ -2096,7 +2096,7 @@ mod tests { .unwrap(); let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) .unwrap() @@ -2339,7 +2339,7 @@ mod tests { .unwrap(); let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth) .unwrap() @@ -2412,7 +2412,7 @@ mod tests { .unwrap(); let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth) .unwrap() @@ -2503,7 +2503,7 @@ mod tests { .unwrap(); let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth) .unwrap() diff --git a/src/encoding/evm/tycho_encoders.rs b/src/encoding/evm/tycho_encoders.rs index f22f59d..bbf2ec3 100644 --- a/src/encoding/evm/tycho_encoders.rs +++ b/src/encoding/evm/tycho_encoders.rs @@ -125,7 +125,7 @@ impl TychoRouterEncoder { &solution.given_amount, )?; encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature); + encoded_solution.signature = Some(signature.as_bytes().to_vec()); } Ok(encoded_solution) } diff --git a/src/encoding/models.rs b/src/encoding/models.rs index 1c95979..7ed9bcb 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -1,4 +1,3 @@ -use alloy_primitives::PrimitiveSignature as Signature; use hex; use num_bigint::BigUint; use serde::{Deserialize, Serialize}; @@ -7,9 +6,7 @@ use tycho_common::{ Bytes, }; -use crate::encoding::{ - errors::EncodingError, evm::approvals::permit2::PermitSingle, serde_primitives::biguint_string, -}; +use crate::encoding::{errors::EncodingError, serde_primitives::biguint_string}; /// Represents a solution containing details describing an order, and instructions for filling /// the order. @@ -106,7 +103,49 @@ pub struct EncodedSolution { pub selector: String, pub n_tokens: usize, pub permit: Option, - pub signature: Option, + pub signature: Option>, +} + +/// Represents a single permit for permit2. +/// +/// # Fields +/// * `details`: The details of the permit, such as token, amount, expiration, and nonce. +/// * `spender`: The address authorized to spend the tokens. +/// * `sig_deadline`: The deadline (as a timestamp) for the permit signature +#[derive(Debug, Clone)] +pub struct PermitSingle { + pub details: PermitDetails, + pub spender: Bytes, + pub sig_deadline: BigUint, +} + +/// Details of a permit. +/// +/// # Fields +/// * `token`: The token address for which the permit is granted. +/// * `amount`: The amount of tokens approved for spending. +/// * `expiration`: The expiration time (as a timestamp) for the permit. +/// * `nonce`: The unique nonce to prevent replay attacks. +#[derive(Debug, Clone)] +pub struct PermitDetails { + pub token: Bytes, + pub amount: BigUint, + pub expiration: BigUint, + pub nonce: BigUint, +} + +impl PartialEq for PermitSingle { + fn eq(&self, other: &Self) -> bool { + self.details == other.details && self.spender == other.spender + // sig_deadline is intentionally ignored + } +} + +impl PartialEq for PermitDetails { + fn eq(&self, other: &Self) -> bool { + self.token == other.token && self.amount == other.amount && self.nonce == other.nonce + // expiration is intentionally ignored + } } /// Represents necessary attributes for encoding an order. From cdb67f742f1bdbb51e0be7282a672aba90bf0ef4 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Fri, 23 May 2025 17:08:45 +0000 Subject: [PATCH 02/11] chore(release): 0.94.0 [skip ci] ## [0.94.0](https://github.com/propeller-heads/tycho-execution/compare/0.93.0...0.94.0) (2025-05-23) ### Features * Create non alloy specific Permit and PermitDetails structs ([75f2c3a](https://github.com/propeller-heads/tycho-execution/commit/75f2c3a1c52b6ee8823d449b70147cb023202e93)) --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44acb47..d211be0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.94.0](https://github.com/propeller-heads/tycho-execution/compare/0.93.0...0.94.0) (2025-05-23) + + +### Features + +* Create non alloy specific Permit and PermitDetails structs ([75f2c3a](https://github.com/propeller-heads/tycho-execution/commit/75f2c3a1c52b6ee8823d449b70147cb023202e93)) + ## [0.93.0](https://github.com/propeller-heads/tycho-execution/compare/0.92.1...0.93.0) (2025-05-22) diff --git a/Cargo.lock b/Cargo.lock index f7730e2..77b39a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4469,7 +4469,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.93.0" +version = "0.94.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index c7e9cf4..c3f6bc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.93.0" +version = "0.94.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From c62af2f232063d3a3c5baa759d484b0d2cc2c185 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Fri, 23 May 2025 18:22:19 +0100 Subject: [PATCH 03/11] feat: Don't sign permit2 objects We don't want to be responsible for holding private keys -> the user is the one that should do this outside of tycho-execution Done: - Remove signature from EncodedSolution - Introduce UserTransferType and pass that everywhere instead of is_permit2_active and token_in_already_in_router - Remove signing from permit2. Added it to the encoding_utils.rs only - Mark encode_full_calldata as deprecated - Backwards compatibility: still accept a signer for the encode_full_calldata case - Update all tests Took 2 hours 10 minutes Took 13 minutes --- examples/encoding-example/main.rs | 7 +- foundry/test/assets/calldata.txt | 24 +- src/bin/tycho-encode.rs | 26 +- src/encoding/evm/approvals/permit2.rs | 79 ++- src/encoding/evm/encoder_builders.rs | 40 +- src/encoding/evm/encoding_utils.rs | 86 +++- .../evm/strategy_encoder/strategy_encoders.rs | 460 +++++++++++------- .../transfer_optimizations.rs | 44 +- src/encoding/evm/tycho_encoders.rs | 69 +-- src/encoding/models.rs | 28 +- src/encoding/tycho_encoder.rs | 1 + 11 files changed, 512 insertions(+), 352 deletions(-) diff --git a/examples/encoding-example/main.rs b/examples/encoding-example/main.rs index b489310..7c35f06 100644 --- a/examples/encoding-example/main.rs +++ b/examples/encoding-example/main.rs @@ -7,20 +7,17 @@ use tycho_common::{ }; use tycho_execution::encoding::{ evm::encoder_builders::TychoRouterEncoderBuilder, - models::{Solution, Swap}, + models::{Solution, Swap, UserTransferType}, }; fn main() { - // Setup variables - let swapper_pk = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); let user_address = Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") .expect("Failed to create user address"); // Initialize the encoder let encoder = TychoRouterEncoderBuilder::new() .chain(Chain::Ethereum) - .swapper_pk(swapper_pk) + .user_transfer_type(UserTransferType::TransferFrom) .build() .expect("Failed to build encoder"); diff --git a/foundry/test/assets/calldata.txt b/foundry/test/assets/calldata.txt index f5648a1..d29dda6 100644 --- a/foundry/test/assets/calldata.txt +++ b/foundry/test/assets/calldata.txt @@ -3,23 +3,23 @@ test_single_encoding_strategy_ekubo:5c4b639c000000000000000000000000000000000000 test_uniswap_v3_uniswap_v3:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010100000000000000000000 test_balancer_v2_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_sequential_swap_strategy_encoder_no_permit2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 -test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041cf25e6401910938ea5c874981fd6a62d202517677c6cde7546d0de7a1047bb7725946110d38bb0011e6ad191a8245b6de439a04fcde4b86284040260300603ae1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 -test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f50000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000412e33858dc4899298476636c393b474a6995d3c076b936d46b90cf6ee00adc801687753f6bad7b89f905d818d217bd94cc0936a9838c48bcaabccd6819baa5e031b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000 -test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004124f83f5b4802258cc983101d1cec5c5c3a4a6fed53e545bf8d222817cb75f61d133f1b9916cc0431ea05020b9452ecc673e8d0d27dbaf273c1aa8df9f628850e1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000685836b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0bf00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041ca2855680741a2290342cdb9a3d4bdafffb0f1e62d3b1e8daac2320d342a97525b2d5b482e223d26d1888d7262f6f32c4f1f3a88a143b091f776245f8f4943581b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e0000000000000000000000000000000000000000000000000000000000685836b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0bf000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f568894962d9b1d0d490cad2051cb88c4fa4a1201577c417d01cc55768b4fa1a526ecd9c73611d5d9dad70c5473a625d46e1fca60e0d025dfc2e9146fa28efc41b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000 +test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004160b51ba1539713798c36fa209145fc28e33627b9f7fa4e971c5f3616d8dc6fd02d17a7f0a787466465bb98e753be08ec07cfd562396c8474dea91b2c8f97a4f11c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_single_swap_strategy_encoder_no_permit2:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_swap_strategy_encoder_no_transfer_in:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 -test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004162924a8458b13d994bef77420a52e2c31998e7c2bb7a595650784e67313cd10767f720f6213b38446a60410060e3b0ee7359fa6b67a1a06395eaed17e1c6522d1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 -test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000415eaa6f0c1cb7ba5566023dc3eade4139ebd73f9da94bbc8281dfea578b534f4e1e72bca67d25a3286769b1c8f8ec5d437f3e6f373435c356edf0832d6496688f1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 +test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685836b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0bf00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000415b7c694c0ae0b6a7c44a90ad5c409746c3dbc7c63ac7912f31cbc23f383503fe4127378bdfaffbcda64d382a2cc3a2c139f7763f11e4e21ffb5c07fc258aae581b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 +test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041e3b2388523e7a76ff5810f3a1a092627c7a890994a99a0a49ae5175c5c12657d7fb2c5be13ae8849c593ae9246e8f2de334c5977b5a0820e49a0af2ecbfd89b11c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 test_single_encoding_strategy_curve_st_eth:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 -test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004124f83f5b4802258cc983101d1cec5c5c3a4a6fed53e545bf8d222817cb75f61d133f1b9916cc0431ea05020b9452ecc673e8d0d27dbaf273c1aa8df9f628850e1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 +test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004160b51ba1539713798c36fa209145fc28e33627b9f7fa4e971c5f3616d8dc6fd02d17a7f0a787466465bb98e753be08ec07cfd562396c8474dea91b2c8f97a4f11c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_encoding_strategy_curve:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 -test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000417ce82920975101831860e9074248018ce1908c61639053bf209f22a0c708db2153d31d68c85362e7f149d9c5481145a4feed8f726a3d6f7a7d7efb2aff876faa1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000 -test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004162924a8458b13d994bef77420a52e2c31998e7c2bb7a595650784e67313cd10767f720f6213b38446a60410060e3b0ee7359fa6b67a1a06395eaed17e1c6522d1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 -test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f50000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eaa6f0c1cb7ba5566023dc3eade4139ebd73f9da94bbc8281dfea578b534f4e1e72bca67d25a3286769b1c8f8ec5d437f3e6f373435c356edf0832d6496688f1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000 -test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f50000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eaa6f0c1cb7ba5566023dc3eade4139ebd73f9da94bbc8281dfea578b534f4e1e72bca67d25a3286769b1c8f8ec5d437f3e6f373435c356edf0832d6496688f1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000 -test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004124f83f5b4802258cc983101d1cec5c5c3a4a6fed53e545bf8d222817cb75f61d133f1b9916cc0431ea05020b9452ecc673e8d0d27dbaf273c1aa8df9f628850e1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000 +test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041bacbb311e4f4bf852ac6ea61807fbfd8ef961f8f5f1b2455fd7dda16851755334c33b522343304998e3e8e48ecfd85c2b123c162ac41c5c6b26ba523a70b6ae71c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000 +test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000411b0820a1eb1d1d4e5c2da500396abb4cb864761c1b92fdaaca6cd2a6cedf82f14fe6c9a31bc617912aecdde40626dd12fb45f3a00b55050a90be6ad447a4ec811b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 +test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041e3b2388523e7a76ff5810f3a1a092627c7a890994a99a0a49ae5175c5c12657d7fb2c5be13ae8849c593ae9246e8f2de334c5977b5a0820e49a0af2ecbfd89b11c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000 +test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041e3b2388523e7a76ff5810f3a1a092627c7a890994a99a0a49ae5175c5c12657d7fb2c5be13ae8849c593ae9246e8f2de334c5977b5a0820e49a0af2ecbfd89b11c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000 +test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004160b51ba1539713798c36fa209145fc28e33627b9f7fa4e971c5f3616d8dc6fd02d17a7f0a787466465bb98e753be08ec07cfd562396c8474dea91b2c8f97a4f11c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000 test_uniswap_v3_curve:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000 -test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf00000000000000000000000000000000000000000000000000000000000006857d9ed00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683053f500000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041dd1ac43cb92f11fdd55f3a6219229c3397db9bde277ebf06b865f90f1505e27322fa280a162761d23ec4269ee0e4ee23f78a56d3d9044a4b328ead78ace55e1f1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 +test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf0000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004148ed139e954654f795a1b25b70a4ec2ed3f14bba64da8eb471af5ec3e816fbcc4f877a1ea3250c279542ba04063b9cef3bd765821765dd03957723a6c5478ab81c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0102 test_ekubo_encode_swap_multi:01ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032 test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index 71ea377..a8ad374 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -1,5 +1,10 @@ -use std::io::{self, Read}; +use std::{ + io::{self, Read}, + str::FromStr, +}; +use alloy::signers::local::PrivateKeySigner; +use alloy_primitives::B256; use alloy_sol_types::SolValue; use clap::{Parser, Subcommand}; use tycho_common::{hex_bytes::Bytes, models::Chain}; @@ -8,7 +13,7 @@ use tycho_execution::encoding::{ approvals::permit2::PermitSingle, encoder_builders::{TychoExecutorEncoderBuilder, TychoRouterEncoderBuilder}, }, - models::Solution, + models::{Solution, UserTransferType}, tycho_encoder::TychoEncoder, }; @@ -56,7 +61,7 @@ pub struct Cli { #[arg(short, long)] swapper_pk: Option, #[arg(short, long)] - token_in_already_in_router: Option, + user_transfer_type: Option, } #[derive(Subcommand)] @@ -91,10 +96,11 @@ fn main() -> Result<(), Box> { builder = builder.router_address(router_address); } if let Some(swapper_pk) = cli.swapper_pk { - builder = builder.swapper_pk(swapper_pk); + let pk = B256::from_str(&swapper_pk)?; + builder = builder.signer(PrivateKeySigner::from_bytes(&pk)?); } - if let Some(token_in_already_in_router) = cli.token_in_already_in_router { - builder = builder.token_in_already_in_router(token_in_already_in_router); + if let Some(user_transfer_type) = cli.user_transfer_type { + builder = builder.user_transfer_type(user_transfer_type); } builder.build()? } @@ -111,17 +117,13 @@ fn main() -> Result<(), Box> { "n_tokens": format!("{}", &encoded_solutions[0].n_tokens), "permit": match encoded_solutions[0].permit.as_ref() { Some(permit) => { - match PermitSingle::try_from(permit.clone()) { + match PermitSingle::try_from(permit) { Ok(sol_permit) => format!("0x{}", hex::encode(sol_permit.abi_encode())), - Err(_) => String::new(), // or log or panic or whatever fallback + Err(_) => String::new(), } } None => String::new(), }, - "signature": encoded_solutions[0].signature - .as_ref() - .map(|signature| format!("0x{}", hex::encode(signature))) - .unwrap_or_else(String::new), }); // Output the encoded result as JSON to stdout println!( diff --git a/src/encoding/evm/approvals/permit2.rs b/src/encoding/evm/approvals/permit2.rs index 075143c..efd1c90 100644 --- a/src/encoding/evm/approvals/permit2.rs +++ b/src/encoding/evm/approvals/permit2.rs @@ -4,11 +4,9 @@ use alloy::{ primitives::{aliases::U48, Address, Bytes as AlloyBytes, TxKind, U160, U256}, providers::{Provider, RootProvider}, rpc::types::{TransactionInput, TransactionRequest}, - signers::{local::PrivateKeySigner, SignerSync}, transports::BoxTransport, }; -use alloy_primitives::{PrimitiveSignature as Signature, B256}; -use alloy_sol_types::{eip712_domain, sol, SolStruct, SolValue}; +use alloy_sol_types::{sol, SolValue}; use chrono::Utc; use num_bigint::BigUint; use tokio::{ @@ -24,7 +22,6 @@ use crate::encoding::{ utils::{biguint_to_u256, bytes_to_address, get_client, get_runtime}, }, models, - models::Chain, }; /// Struct for managing Permit2 operations, including encoding approvals and fetching allowance @@ -33,8 +30,6 @@ use crate::encoding::{ pub struct Permit2 { address: Address, client: Arc>, - signer: PrivateKeySigner, - chain_id: u64, runtime_handle: Handle, // Store the runtime to prevent it from being dropped before use. // This is required since tycho-execution does not have a pre-existing runtime. @@ -69,10 +64,10 @@ sol! { } } -impl TryFrom for models::PermitSingle { +impl TryFrom<&PermitSingle> for models::PermitSingle { type Error = EncodingError; - fn try_from(sol: PermitSingle) -> Result { + fn try_from(sol: &PermitSingle) -> Result { Ok(models::PermitSingle { details: models::PermitDetails { token: Bytes::from(sol.details.token.to_vec()), @@ -90,10 +85,10 @@ impl TryFrom for models::PermitSingle { } } -impl TryFrom for PermitSingle { +impl TryFrom<&models::PermitSingle> for PermitSingle { type Error = EncodingError; - fn try_from(p: models::PermitSingle) -> Result { + fn try_from(p: &models::PermitSingle) -> Result { Ok(PermitSingle { details: PermitDetails { token: bytes_to_address(&p.details.token)?, @@ -108,22 +103,14 @@ impl TryFrom for PermitSingle { } impl Permit2 { - pub fn new(swapper_pk: String, chain: Chain) -> Result { + pub fn new() -> Result { let (handle, runtime) = get_runtime()?; let client = block_in_place(|| handle.block_on(get_client()))?; - let pk = B256::from_str(&swapper_pk).map_err(|_| { - EncodingError::FatalError("Failed to convert swapper private key to B256".to_string()) - })?; - let signer = PrivateKeySigner::from_bytes(&pk).map_err(|_| { - EncodingError::FatalError("Failed to create signer from private key".to_string()) - })?; Ok(Self { address: Address::from_str("0x000000000022D473030F116dDEE9F6B43aC78BA3") .map_err(|_| EncodingError::FatalError("Permit2 address not valid".to_string()))?, client, runtime_handle: handle, - signer, - chain_id: chain.id, runtime, }) } @@ -169,7 +156,7 @@ impl Permit2 { owner: &Bytes, token: &Bytes, amount: &BigUint, - ) -> Result<(models::PermitSingle, Signature), EncodingError> { + ) -> Result { let current_time = Utc::now() .naive_utc() .and_utc() @@ -188,21 +175,7 @@ impl Permit2 { sigDeadline: sig_deadline, }; - let domain = eip712_domain! { - name: "Permit2", - chain_id: self.chain_id, - verifying_contract: self.address, - }; - let hash = permit_single.eip712_signing_hash(&domain); - let signature = self - .signer - .sign_hash_sync(&hash) - .map_err(|e| { - EncodingError::FatalError(format!( - "Failed to sign permit2 approval with error: {e}" - )) - })?; - Ok((models::PermitSingle::try_from(permit_single)?, signature)) + Ok(models::PermitSingle::try_from(&permit_single)?) } } @@ -210,11 +183,13 @@ impl Permit2 { mod tests { use std::str::FromStr; - use alloy_primitives::Uint; + use alloy::signers::local::PrivateKeySigner; + use alloy_primitives::{Uint, B256}; use num_bigint::BigUint; use tycho_common::models::Chain as TychoCommonChain; use super::*; + use crate::encoding::{evm::encoding_utils::sign_permit, models::Chain}; // These two implementations are to avoid comparing the expiration and sig_deadline fields // because they are timestamps @@ -253,9 +228,7 @@ mod tests { #[test] fn test_get_existing_allowance() { - let swapper_pk = - "4c0883a69102937d6231471b5dbb6204fe512961708279feb1be6ae5538da033".to_string(); - let manager = Permit2::new(swapper_pk, eth_chain()).unwrap(); + let manager = Permit2::new().unwrap(); let token = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); let owner = Bytes::from_str("0x2c6a3cd97c6283b95ac8c5a4459ebb0d5fd404f4").unwrap(); @@ -272,17 +245,14 @@ mod tests { #[test] fn test_get_permit() { - // Set up a mock private key for signing - let private_key = - "4c0883a69102937d6231471b5dbb6204fe512961708279feb1be6ae5538da033".to_string(); - let permit2 = Permit2::new(private_key, eth_chain()).expect("Failed to create Permit2"); + let permit2 = Permit2::new().expect("Failed to create Permit2"); let owner = Bytes::from_str("0x2c6a3cd97c6283b95ac8c5a4459ebb0d5fd404f4").unwrap(); let spender = Bytes::from_str("0xba12222222228d8ba445958a75a0704d566bf2c8").unwrap(); let token = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); let amount = BigUint::from(1000u64); - let (permit, _) = permit2 + let permit = permit2 .get_permit(&spender, &owner, &token, &amount) .unwrap(); @@ -315,8 +285,19 @@ mod tests { let anvil_private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".to_string(); - let permit2 = - Permit2::new(anvil_private_key, eth_chain()).expect("Failed to create Permit2"); + let pk = B256::from_str(&anvil_private_key) + .map_err(|_| { + EncodingError::FatalError( + "Failed to convert swapper private key to B256".to_string(), + ) + }) + .unwrap(); + let signer = PrivateKeySigner::from_bytes(&pk) + .map_err(|_| { + EncodingError::FatalError("Failed to create signer from private key".to_string()) + }) + .unwrap(); + let permit2 = Permit2::new().expect("Failed to create Permit2"); let token = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); let amount = BigUint::from(1000u64); @@ -346,11 +327,13 @@ mod tests { let spender = Bytes::from_str("0xba12222222228d8ba445958a75a0704d566bf2c8").unwrap(); - let (permit, signature) = permit2 + let permit = permit2 .get_permit(&spender, &anvil_account, &token, &amount) .unwrap(); let sol_permit: PermitSingle = - PermitSingle::try_from(permit).expect("Failed to convert to PermitSingle"); + PermitSingle::try_from(&permit).expect("Failed to convert to PermitSingle"); + + let signature = sign_permit(eth_chain().id, &permit, signer).unwrap(); let encoded = (bytes_to_address(&anvil_account).unwrap(), sol_permit, signature.as_bytes().to_vec()) .abi_encode(); diff --git a/src/encoding/evm/encoder_builders.rs b/src/encoding/evm/encoder_builders.rs index 590605b..d8de3b0 100644 --- a/src/encoding/evm/encoder_builders.rs +++ b/src/encoding/evm/encoder_builders.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use alloy::signers::local::PrivateKeySigner; use tycho_common::{models::Chain as TychoCommonChain, Bytes}; use crate::encoding::{ @@ -9,7 +10,7 @@ use crate::encoding::{ swap_encoder::swap_encoder_registry::SwapEncoderRegistry, tycho_encoders::{TychoExecutorEncoder, TychoRouterEncoder}, }, - models::Chain, + models::{Chain, UserTransferType}, tycho_encoder::TychoEncoder, }; @@ -17,11 +18,11 @@ use crate::encoding::{ /// /// This struct allows setting a chain and strategy encoder before building the final encoder. pub struct TychoRouterEncoderBuilder { - swapper_pk: Option, chain: Option, + user_transfer_type: Option, executors_file_path: Option, router_address: Option, - token_in_already_in_router: Option, + signer: Option, } impl Default for TychoRouterEncoderBuilder { @@ -33,11 +34,11 @@ impl Default for TychoRouterEncoderBuilder { impl TychoRouterEncoderBuilder { pub fn new() -> Self { TychoRouterEncoderBuilder { - swapper_pk: None, chain: None, executors_file_path: None, router_address: None, - token_in_already_in_router: None, + signer: None, + user_transfer_type: None, } } pub fn chain(mut self, chain: TychoCommonChain) -> Self { @@ -45,6 +46,11 @@ impl TychoRouterEncoderBuilder { self } + pub fn user_transfer_type(mut self, user_transfer_type: UserTransferType) -> Self { + self.user_transfer_type = Some(user_transfer_type); + self + } + /// Sets the `executors_file_path` manually. /// If it's not set, the default path will be used (config/executor_addresses.json) pub fn executors_file_path(mut self, executors_file_path: String) -> Self { @@ -59,25 +65,15 @@ impl TychoRouterEncoderBuilder { self } - pub fn swapper_pk(mut self, swapper_pk: String) -> Self { - self.swapper_pk = Some(swapper_pk); - self - } - - // Sets the `token_in_already_in_router` flag. - // If set to true, the encoder will assume that the token in is already in the router. - // WARNING: this is an advanced feature and should be used with caution. Make sure you have - // checks to make sure that your tokens won't be lost. The Router is not considered safe to hold - // tokens, so if this is not done within the same transaction you will lose your tokens. - pub fn token_in_already_in_router(mut self, token_in_already_in_router: bool) -> Self { - self.token_in_already_in_router = Some(token_in_already_in_router); + pub fn signer(mut self, signer: PrivateKeySigner) -> Self { + self.signer = Some(signer); self } /// Builds the `TychoRouterEncoder` instance using the configured chain. /// Returns an error if either the chain has not been set. pub fn build(self) -> Result, EncodingError> { - if let Some(chain) = self.chain { + if let (Some(chain), Some(user_transfer_type)) = (self.chain, self.user_transfer_type) { let tycho_router_address; if let Some(address) = self.router_address { tycho_router_address = address; @@ -98,14 +94,14 @@ impl TychoRouterEncoderBuilder { Ok(Box::new(TychoRouterEncoder::new( chain, swap_encoder_registry, - self.swapper_pk, tycho_router_address, - self.token_in_already_in_router - .unwrap_or(false), + user_transfer_type, + self.signer, )?)) } else { Err(EncodingError::FatalError( - "Please set the chain and router address before building the encoder".to_string(), + "Please set the chain and user transfer type before building the encoder" + .to_string(), )) } } diff --git a/src/encoding/evm/encoding_utils.rs b/src/encoding/evm/encoding_utils.rs index a3b0862..fd40b70 100644 --- a/src/encoding/evm/encoding_utils.rs +++ b/src/encoding/evm/encoding_utils.rs @@ -1,5 +1,11 @@ -use alloy_primitives::{Keccak256, U256}; -use alloy_sol_types::SolValue; +use std::str::FromStr; + +use alloy::{ + primitives::U256, + signers::{local::PrivateKeySigner, Signature, SignerSync}, +}; +use alloy_primitives::{Address, Keccak256}; +use alloy_sol_types::{eip712_domain, SolStruct, SolValue}; use num_bigint::BigUint; use tycho_common::Bytes; @@ -9,7 +15,8 @@ use crate::encoding::{ approvals::permit2::PermitSingle, utils::{biguint_to_u256, bytes_to_address}, }, - models::{EncodedSolution, NativeAction, Solution, Transaction}, + models, + models::{EncodedSolution, NativeAction, Solution, Transaction, UserTransferType}, }; /// Encodes the input data for a function call to the given function selector. @@ -56,6 +63,7 @@ pub fn encode_input(selector: &str, mut encoded_args: Vec) -> Vec { /// their own encoding logic** to ensure: /// - Full control of parameters passed to the router. /// - Proper validation and setting of critical inputs such as `minAmountOut`. +/// - Signing of permit2 objects. /// /// 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. @@ -87,10 +95,12 @@ pub fn encode_input(selector: &str, mut encoded_args: Vec) -> Vec { /// - Returns `EncodingError::FatalError` if the selector is unsupported or required fields (e.g., /// permit or signature) are missing. pub fn encode_tycho_router_call( + chain_id: u64, encoded_solution: EncodedSolution, solution: &Solution, - token_in_already_in_router: bool, + user_transfer_type: UserTransferType, native_address: Bytes, + signer: Option, ) -> Result { let (mut unwrap, mut wrap) = (false, false); if let Some(action) = solution.native_action.clone() { @@ -106,23 +116,23 @@ pub fn encode_tycho_router_call( 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 permit = if let Some(p) = encoded_solution.permit { - Some( - PermitSingle::try_from(p) + let (permit, signature) = if let Some(p) = encoded_solution.permit { + let permit = Some( + PermitSingle::try_from(&p) .map_err(|_| EncodingError::InvalidInput("Invalid permit".to_string()))?, - ) + ); + let signer = signer + .ok_or(EncodingError::FatalError("Signer must be set to use permit2".to_string()))?; + let signature = sign_permit(chain_id, &p, signer)?; + (permit, signature.as_bytes().to_vec()) } else { - None + (None, vec![]) }; let method_calldata = if encoded_solution .selector .contains("singleSwapPermit2") { - let sig = encoded_solution - .signature - .ok_or(EncodingError::FatalError("Signature must be set to use permit2".to_string()))?; - println!("sig {:}", hex::encode(&sig)); ( given_amount, given_token, @@ -134,7 +144,7 @@ pub fn encode_tycho_router_call( permit.ok_or(EncodingError::FatalError( "permit2 object must be set to use permit2".to_string(), ))?, - sig, + signature, encoded_solution.swaps, ) .abi_encode() @@ -150,7 +160,7 @@ pub fn encode_tycho_router_call( wrap, unwrap, receiver, - !token_in_already_in_router, + user_transfer_type == UserTransferType::TransferFrom, encoded_solution.swaps, ) .abi_encode() @@ -169,11 +179,7 @@ pub fn encode_tycho_router_call( 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(), - ))?, + signature, encoded_solution.swaps, ) .abi_encode() @@ -189,7 +195,7 @@ pub fn encode_tycho_router_call( wrap, unwrap, receiver, - !token_in_already_in_router, + user_transfer_type == UserTransferType::TransferFrom, encoded_solution.swaps, ) .abi_encode() @@ -209,11 +215,7 @@ pub fn encode_tycho_router_call( 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(), - ))?, + signature, encoded_solution.swaps, ) .abi_encode() @@ -230,7 +232,7 @@ pub fn encode_tycho_router_call( unwrap, n_tokens, receiver, - !token_in_already_in_router, + user_transfer_type == UserTransferType::TransferFrom, encoded_solution.swaps, ) .abi_encode() @@ -246,3 +248,33 @@ pub fn encode_tycho_router_call( }; Ok(Transaction { to: encoded_solution.interacting_with, value, data: contract_interaction }) } + +/// Signs a Permit2 `PermitSingle` struct using the EIP-712 signing scheme. +/// +/// This function constructs an EIP-712 domain specific to the Permit2 contract and computes the +/// hash of the provided `PermitSingle`. It then uses the given `PrivateKeySigner` to produce +/// a cryptographic signature of the permit. +/// +/// # Warning +/// This is only an **example implementation** provided for reference purposes. +/// **Do not rely on this in production.** You should implement your own version. +pub fn sign_permit( + chain_id: u64, + permit_single: &models::PermitSingle, + signer: PrivateKeySigner, +) -> Result { + let permit2_address = Address::from_str("0x000000000022D473030F116dDEE9F6B43aC78BA3") + .map_err(|_| EncodingError::FatalError("Permit2 address not valid".to_string()))?; + let domain = eip712_domain! { + name: "Permit2", + chain_id: chain_id, + verifying_contract: permit2_address, + }; + let permit_single: PermitSingle = PermitSingle::try_from(permit_single)?; + let hash = permit_single.eip712_signing_hash(&domain); + signer + .sign_hash_sync(&hash) + .map_err(|e| { + EncodingError::FatalError(format!("Failed to sign permit2 approval with error: {e}")) + }) +} diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 913a6d7..bce32fc 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -14,7 +14,7 @@ use crate::encoding::{ swap_encoder::swap_encoder_registry::SwapEncoderRegistry, utils::{get_token_position, percentage_to_uint24, ple_encode}, }, - models::{Chain, EncodedSolution, EncodingContext, NativeAction, Solution}, + models::{Chain, EncodedSolution, EncodingContext, NativeAction, Solution, UserTransferType}, strategy_encoder::StrategyEncoder, swap_encoder::SwapEncoder, }; @@ -38,11 +38,10 @@ impl SingleSwapStrategyEncoder { pub fn new( chain: Chain, swap_encoder_registry: SwapEncoderRegistry, - permit_2_active: bool, + user_transfer_type: UserTransferType, router_address: Bytes, - token_in_already_in_router: bool, ) -> Result { - let selector = if permit_2_active { + let selector = if user_transfer_type == UserTransferType::TransferFromPermit2 { "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" } else { "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" @@ -55,7 +54,7 @@ impl SingleSwapStrategyEncoder { transfer_optimization: TransferOptimization::new( chain.native_token()?, chain.wrapped_token()?, - token_in_already_in_router, + user_transfer_type, router_address, ), }) @@ -138,7 +137,6 @@ impl StrategyEncoder for SingleSwapStrategyEncoder { interacting_with: self.router_address.clone(), swaps: swap_data, permit: None, - signature: None, n_tokens: 0, }) } @@ -179,11 +177,10 @@ impl SequentialSwapStrategyEncoder { pub fn new( chain: Chain, swap_encoder_registry: SwapEncoderRegistry, - permit_2_active: bool, + user_transfer_type: UserTransferType, router_address: Bytes, - token_in_already_in_router: bool, ) -> Result { - let selector = if permit_2_active { + let selector = if user_transfer_type == UserTransferType::TransferFromPermit2 { "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" } else { "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" @@ -199,7 +196,7 @@ impl SequentialSwapStrategyEncoder { transfer_optimization: TransferOptimization::new( chain.native_token()?, chain.wrapped_token()?, - token_in_already_in_router, + user_transfer_type, router_address, ), }) @@ -294,7 +291,6 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder { selector: self.selector.clone(), swaps: encoded_swaps, permit: None, - signature: None, n_tokens: 0, }) } @@ -335,11 +331,10 @@ impl SplitSwapStrategyEncoder { pub fn new( chain: Chain, swap_encoder_registry: SwapEncoderRegistry, - permit_2_active: bool, + user_transfer_type: UserTransferType, router_address: Bytes, - token_in_already_in_router: bool, ) -> Result { - let selector = if permit_2_active{ + let selector = if user_transfer_type == UserTransferType::TransferFromPermit2 { "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" } else { "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)" @@ -354,7 +349,7 @@ impl SplitSwapStrategyEncoder { transfer_optimization: TransferOptimization::new( chain.native_token()?, chain.wrapped_token()?, - token_in_already_in_router, + user_transfer_type, router_address, ), }) @@ -497,7 +492,6 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { selector: self.selector.clone(), swaps: encoded_swaps, permit: None, - signature: None, n_tokens: tokens_len, }) } @@ -516,8 +510,8 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { mod tests { use std::{collections::HashMap, str::FromStr}; - use alloy::hex::encode; - use alloy_primitives::{hex, Address, PrimitiveSignature as Signature, U256}; + use alloy::{hex::encode, signers::local::PrivateKeySigner}; + use alloy_primitives::{hex, Address, B256, U256}; use num_bigint::{BigInt, BigUint}; use tycho_common::{ models::{protocol::ProtocolComponent, Chain as TychoCommonChain}, @@ -555,24 +549,26 @@ mod tests { Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap() } - fn get_permit( - chain: Chain, - router_address: Bytes, - solution: &Solution, - ) -> (PermitSingle, Signature) { - // Set up a mock private key for signing (Alice's pk in our contract tests) - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - - let permit2 = Permit2::new(private_key, chain.clone()).unwrap(); - permit2 + fn get_permit(router_address: Bytes, solution: &Solution) -> PermitSingle { + let permit2 = Permit2::new().unwrap(); + let permit_single = permit2 .get_permit( &router_address, &solution.sender, &solution.given_token, &solution.given_amount, ) - .unwrap() + .unwrap(); + permit_single + } + + fn get_signer() -> PrivateKeySigner { + // Set up a mock private key for signing (Alice's pk in our contract tests) + let private_key = + "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); + + let pk = B256::from_str(&private_key).unwrap(); + PrivateKeySigner::from_bytes(&pk).unwrap() } mod single { @@ -602,9 +598,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, router_address(), - false, ) .unwrap(); let solution = Solution { @@ -622,13 +617,19 @@ mod tests { let mut encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; let expected_min_amount_encoded = hex::encode(U256::abi_encode(&biguint_to_u256(&checked_amount))); let expected_input = [ @@ -690,9 +691,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::TransferFrom, router_address(), - false, ) .unwrap(); let solution = Solution { @@ -710,9 +710,16 @@ mod tests { let encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount)); let expected_input = [ "5c4b639c", // Function selector @@ -772,9 +779,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::None, router_address(), - true, ) .unwrap(); let solution = Solution { @@ -792,9 +798,16 @@ mod tests { let encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, true, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::None, + eth(), + None, + ) + .unwrap() + .data; let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount)); let expected_input = [ "5c4b639c", // Function selector @@ -851,9 +864,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, router_address(), - false, ) .unwrap(); let solution = Solution { @@ -872,13 +884,19 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_single_swap_strategy_encoder_wrap", hex_calldata.as_str()); } @@ -905,9 +923,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - false, ) .unwrap(); let solution = Solution { @@ -926,13 +943,19 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file( @@ -982,9 +1005,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, router_address(), - false, ) .unwrap(); let solution = Solution { @@ -1002,13 +1024,19 @@ mod tests { let mut encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_sequential_swap_strategy_encoder", hex_calldata.as_str()); @@ -1048,9 +1076,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::TransferFrom, router_address(), - false, ) .unwrap(); let solution = Solution { @@ -1069,9 +1096,16 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); @@ -1170,9 +1204,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - false, ) .unwrap(); @@ -1192,13 +1225,19 @@ mod tests { let mut encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; let hex_calldata = hex::encode(&calldata); let expected_input = [ "51bcc7b6", // selector @@ -1292,9 +1331,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::TransferFrom, router_address(), - false, ) .unwrap(); let solution = Solution { @@ -1314,9 +1352,16 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_uniswap_v3_uniswap_v2", hex_calldata.as_str()); @@ -1377,9 +1422,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::TransferFrom, router_address(), - false, ) .unwrap(); let solution = Solution { @@ -1399,9 +1443,16 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_uniswap_v3_uniswap_v3", hex_calldata.as_str()); @@ -1471,9 +1522,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::TransferFrom, router_address(), - false, ) .unwrap(); let solution = Solution { @@ -1493,9 +1543,16 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_uniswap_v3_curve", hex_calldata.as_str()); @@ -1541,9 +1598,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::TransferFrom, router_address(), - false, ) .unwrap(); let solution = Solution { @@ -1563,9 +1619,16 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_balancer_v2_uniswap_v2", hex_calldata.as_str()); @@ -1687,9 +1750,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, router_address(), - false, ) .unwrap(); let solution = Solution { @@ -1714,13 +1776,19 @@ mod tests { let mut encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth, + Some(get_signer()), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_multi_protocol", hex_calldata.as_str()); @@ -1795,9 +1863,8 @@ mod tests { let encoder = SplitSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - false, ) .unwrap(); let solution = Solution { @@ -1815,13 +1882,19 @@ mod tests { let mut encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_split_swap_strategy_encoder", hex_calldata.as_str()); @@ -1907,9 +1980,8 @@ mod tests { let encoder = SplitSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - false, ) .unwrap(); @@ -1930,13 +2002,19 @@ mod tests { let mut encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; let hex_calldata = hex::encode(&calldata); let expected_input = [ @@ -2071,9 +2149,8 @@ mod tests { let encoder = SplitSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - false, ) .unwrap(); @@ -2094,13 +2171,19 @@ mod tests { let mut encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; let hex_calldata = hex::encode(&calldata); let expected_input = [ @@ -2202,9 +2285,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::TransferFrom, Bytes::from_str("0xA4AD4f68d0b91CFD19687c881e50f3A00242828c").unwrap(), - false, ) .unwrap(); @@ -2225,9 +2307,16 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_single_encoding_strategy_ekubo", hex_calldata.as_str()); } @@ -2253,9 +2342,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::TransferFrom, Bytes::from_str("0xA4AD4f68d0b91CFD19687c881e50f3A00242828c").unwrap(), - false, ) .unwrap(); @@ -2276,9 +2364,16 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_single_encoding_strategy_maverick", hex_calldata.as_str()); } @@ -2316,9 +2411,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - false, ) .unwrap(); @@ -2337,13 +2431,19 @@ mod tests { let mut encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth, + Some(get_signer()), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file( @@ -2389,9 +2489,8 @@ mod tests { let encoder = SplitSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - false, ) .unwrap(); @@ -2410,13 +2509,19 @@ mod tests { let mut encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth, + Some(get_signer()), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file( @@ -2481,9 +2586,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - true, + UserTransferType::TransferFromPermit2, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - false, ) .unwrap(); let solution = Solution { @@ -2501,13 +2605,19 @@ mod tests { let mut encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + let permit = get_permit(router_address(), &solution); encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth, + Some(get_signer()), + ) + .unwrap() + .data; let expected_input = [ "30ace1b1", // Function selector (single swap) @@ -2591,9 +2701,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::TransferFrom, router_address(), - false, ) .unwrap(); @@ -2614,9 +2723,16 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_single_encoding_strategy_curve", hex_calldata.as_str()); @@ -2657,9 +2773,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - false, + UserTransferType::TransferFrom, router_address(), - false, ) .unwrap(); @@ -2679,9 +2794,16 @@ mod tests { let encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call(encoded_solution, &solution, false, eth()) - .unwrap() - .data; + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file( diff --git a/src/encoding/evm/strategy_encoder/transfer_optimizations.rs b/src/encoding/evm/strategy_encoder/transfer_optimizations.rs index c13c028..eb3a115 100644 --- a/src/encoding/evm/strategy_encoder/transfer_optimizations.rs +++ b/src/encoding/evm/strategy_encoder/transfer_optimizations.rs @@ -8,7 +8,7 @@ use crate::encoding::{ constants::{CALLBACK_CONSTRAINED_PROTOCOLS, IN_TRANSFER_REQUIRED_PROTOCOLS}, group_swaps::SwapGroup, }, - models::TransferType, + models::{TransferType, UserTransferType}, }; /// A struct that defines how the tokens will be transferred into the given pool given the solution. @@ -16,7 +16,7 @@ use crate::encoding::{ pub struct TransferOptimization { native_token: Bytes, wrapped_token: Bytes, - token_in_already_in_router: bool, + user_transfer_type: UserTransferType, router_address: Bytes, } @@ -24,15 +24,10 @@ impl TransferOptimization { pub fn new( native_token: Bytes, wrapped_token: Bytes, - token_in_already_in_router: bool, + user_transfer_type: UserTransferType, router_address: Bytes, ) -> Self { - TransferOptimization { - native_token, - wrapped_token, - token_in_already_in_router, - router_address, - } + TransferOptimization { native_token, wrapped_token, user_transfer_type, router_address } } /// Returns the transfer type that should be used for the current transfer. @@ -55,7 +50,7 @@ impl TransferOptimization { TransferType::Transfer } else if is_first_swap { if in_transfer_required { - if self.token_in_already_in_router { + if self.user_transfer_type == UserTransferType::None { // Transfer from router to pool. TransferType::Transfer } else { @@ -64,7 +59,7 @@ impl TransferOptimization { } // in transfer is not necessary for these protocols. Only make a transfer from the // swapper to the router if the tokens are not already in the router - } else if !self.token_in_already_in_router { + } else if self.user_transfer_type != UserTransferType::None { // Transfer from swapper to router using. TransferType::TransferFrom } else { @@ -146,30 +141,30 @@ mod tests { #[rstest] // First swap tests // WETH -(univ2)-> DAI we expect a transfer from the user to the protocol - #[case(weth(), weth(), "uniswap_v2".to_string(), false, false,false, TransferType::TransferFrom)] + #[case(weth(), weth(), "uniswap_v2".to_string(), false, UserTransferType::TransferFrom,false, TransferType::TransferFrom)] // Native token swap. No transfer is needed - #[case(eth(), eth(), "uniswap_v2".to_string(),false, false,false, TransferType::None)] + #[case(eth(), eth(), "uniswap_v2".to_string(),false, UserTransferType::TransferFrom,false, TransferType::None)] // ETH -(wrap)-> WETH -(univ2)-> DAI. Only a transfer from the router into the protocol is // needed - #[case(eth(), weth(), "uniswap_v2".to_string(),true, false,false,TransferType::Transfer)] + #[case(eth(), weth(), "uniswap_v2".to_string(),true, UserTransferType::TransferFrom,false,TransferType::Transfer)] // USDC -(univ2)-> DAI and the tokens are already in the router. Only a transfer from the router // to the protocol is needed - #[case(usdc(), usdc(), "uniswap_v2".to_string(),false, true,false, TransferType::Transfer)] + #[case(usdc(), usdc(), "uniswap_v2".to_string(),false, UserTransferType::None,false, TransferType::Transfer)] // USDC -(curve)-> DAI and the tokens are already in the router. No transfer is needed - #[case(usdc(), usdc(), "vm:curve".to_string(),false, true, false,TransferType::None)] + #[case(usdc(), usdc(), "vm:curve".to_string(),false, UserTransferType::None, false,TransferType::None)] // other swaps tests // tokens need to be transferred into the pool - #[case(weth(), usdc(), "uniswap_v2".to_string(), false, false,false, TransferType::Transfer)] + #[case(weth(), usdc(), "uniswap_v2".to_string(), false, UserTransferType::TransferFrom,false, TransferType::Transfer)] // tokens are already in the pool (optimization) - #[case(weth(), usdc(), "uniswap_v2".to_string(), false, false, true, TransferType::None)] + #[case(weth(), usdc(), "uniswap_v2".to_string(), false, UserTransferType::TransferFrom, true, TransferType::None)] // tokens are already in the router and don't need a transfer - #[case(weth(), usdc(), "vm:curve".to_string(), false, false, false, TransferType::None)] + #[case(weth(), usdc(), "vm:curve".to_string(), false, UserTransferType::TransferFrom, false, TransferType::None)] fn test_get_transfers( #[case] given_token: Bytes, #[case] swap_token_in: Bytes, #[case] protocol: String, #[case] wrap: bool, - #[case] token_in_already_in_router: bool, + #[case] user_transfer_type: UserTransferType, #[case] in_between_swap_optimization: bool, #[case] expected_transfer: TransferType, ) { @@ -192,7 +187,7 @@ mod tests { swaps, }; let optimization = - TransferOptimization::new(eth(), weth(), token_in_already_in_router, router_address()); + TransferOptimization::new(eth(), weth(), user_transfer_type, router_address()); let transfer = optimization.get_transfers( swap.clone(), given_token, @@ -224,7 +219,12 @@ mod tests { #[case] expected_receiver: Bytes, #[case] expected_optimization: bool, ) { - let optimization = TransferOptimization::new(eth(), weth(), false, router_address()); + let optimization = TransferOptimization::new( + eth(), + weth(), + UserTransferType::TransferFrom, + router_address(), + ); let next_swap = if protocol.is_none() { None diff --git a/src/encoding/evm/tycho_encoders.rs b/src/encoding/evm/tycho_encoders.rs index bbf2ec3..fb89fb6 100644 --- a/src/encoding/evm/tycho_encoders.rs +++ b/src/encoding/evm/tycho_encoders.rs @@ -1,5 +1,6 @@ use std::{collections::HashSet, str::FromStr}; +use alloy::signers::local::PrivateKeySigner; use tycho_common::Bytes; use crate::encoding::{ @@ -16,6 +17,7 @@ use crate::encoding::{ }, models::{ Chain, EncodedSolution, EncodingContext, NativeAction, Solution, Transaction, TransferType, + UserTransferType, }, strategy_encoder::StrategyEncoder, tycho_encoder::TychoEncoder, @@ -24,37 +26,36 @@ use crate::encoding::{ /// Encodes solutions to be used by the TychoRouter. /// /// # Fields +/// * `chain`: Chain to be used /// * `single_swap_strategy`: Encoder for single swaps /// * `sequential_swap_strategy`: Encoder for sequential swaps /// * `split_swap_strategy`: Encoder for split swaps -/// * `native_address`: Address of the chain's 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 +/// * `user_transfer_type`: Type of user transfer +/// * `permit2`: Optional Permit2 instance for permit transfers +/// * `signer`: Optional signer (used only for permit2 and full calldata encoding) #[derive(Clone)] pub struct TychoRouterEncoder { + chain: Chain, single_swap_strategy: SingleSwapStrategyEncoder, sequential_swap_strategy: SequentialSwapStrategyEncoder, split_swap_strategy: SplitSwapStrategyEncoder, - native_address: Bytes, - wrapped_address: Bytes, router_address: Bytes, - token_in_already_in_router: bool, + user_transfer_type: UserTransferType, permit2: Option, + signer: Option, } impl TychoRouterEncoder { pub fn new( chain: Chain, swap_encoder_registry: SwapEncoderRegistry, - swapper_pk: Option, router_address: Bytes, - token_in_already_in_router: bool, + user_transfer_type: UserTransferType, + signer: Option, ) -> Result { - let native_address = chain.native_token()?; - let wrapped_address = chain.wrapped_token()?; - let permit2 = if let Some(swapper_pk) = swapper_pk.clone() { - Some(Permit2::new(swapper_pk, chain.clone())?) + let permit2 = if user_transfer_type == UserTransferType::TransferFromPermit2 { + Some(Permit2::new()?) } else { None }; @@ -62,29 +63,26 @@ impl TychoRouterEncoder { single_swap_strategy: SingleSwapStrategyEncoder::new( chain.clone(), swap_encoder_registry.clone(), - permit2.is_some(), + user_transfer_type.clone(), router_address.clone(), - token_in_already_in_router, )?, sequential_swap_strategy: SequentialSwapStrategyEncoder::new( chain.clone(), swap_encoder_registry.clone(), - permit2.is_some(), + user_transfer_type.clone(), router_address.clone(), - token_in_already_in_router, )?, split_swap_strategy: SplitSwapStrategyEncoder::new( - chain, + chain.clone(), swap_encoder_registry, - permit2.is_some(), + user_transfer_type.clone(), router_address.clone(), - token_in_already_in_router, )?, - native_address, - wrapped_address, router_address, - token_in_already_in_router, permit2, + signer, + chain, + user_transfer_type, }) } @@ -118,14 +116,13 @@ impl TychoRouterEncoder { }; if let Some(permit2) = self.permit2.clone() { - let (permit, signature) = permit2.get_permit( + let permit = permit2.get_permit( &self.router_address, &solution.sender, &solution.given_token, &solution.given_amount, )?; encoded_solution.permit = Some(permit); - encoded_solution.signature = Some(signature.as_bytes().to_vec()); } Ok(encoded_solution) } @@ -153,10 +150,12 @@ impl TychoEncoder for TychoRouterEncoder { let encoded_solution = self.encode_solution(solution)?; let transaction = encode_tycho_router_call( + self.chain.id, encoded_solution, solution, - self.token_in_already_in_router, - self.native_address.clone(), + self.user_transfer_type.clone(), + self.chain.native_token()?.clone(), + self.signer.clone(), )?; transactions.push(transaction); @@ -184,15 +183,17 @@ impl TychoEncoder for TychoRouterEncoder { if solution.swaps.is_empty() { return Err(EncodingError::FatalError("No swaps found in solution".to_string())); } + let native_address = self.chain.native_token()?; + let wrapped_address = self.chain.wrapped_token()?; if let Some(native_action) = solution.clone().native_action { if native_action == NativeAction::Wrap { - if solution.given_token != self.native_address { + if solution.given_token != native_address { return Err(EncodingError::FatalError( "Native token must be the input token in order to wrap".to_string(), )); } if let Some(first_swap) = solution.swaps.first() { - if first_swap.token_in != self.wrapped_address { + if first_swap.token_in != wrapped_address { return Err(EncodingError::FatalError( "Wrapped token must be the first swap's input in order to wrap" .to_string(), @@ -200,13 +201,13 @@ impl TychoEncoder for TychoRouterEncoder { } } } else if native_action == NativeAction::Unwrap { - if solution.checked_token != self.native_address { + if solution.checked_token != native_address { return Err(EncodingError::FatalError( "Native token must be the output token in order to unwrap".to_string(), )); } if let Some(last_swap) = solution.swaps.last() { - if last_swap.token_out != self.wrapped_address { + if last_swap.token_out != wrapped_address { return Err(EncodingError::FatalError( "Wrapped token must be the last swap's output in order to unwrap" .to_string(), @@ -337,7 +338,6 @@ impl TychoExecutorEncoder { swaps: grouped_protocol_data, interacting_with: executor_address, permit: None, - signature: None, selector: "".to_string(), n_tokens: 0, }) @@ -474,13 +474,14 @@ mod tests { TychoRouterEncoder::new( TychoCommonChain::Ethereum.into(), get_swap_encoder_registry(), - None, Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), - false, + UserTransferType::TransferFrom, + None, ) .unwrap() } #[test] + #[allow(deprecated)] fn test_encode_router_calldata_single_swap() { let encoder = get_tycho_router_encoder(); let eth_amount_in = BigUint::from(1000u32); @@ -520,6 +521,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_encode_router_calldata_single_swap_group() { let encoder = get_tycho_router_encoder(); let solution = Solution { @@ -543,6 +545,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_encode_router_calldata_sequential_swap() { let encoder = get_tycho_router_encoder(); let eth_amount_in = BigUint::from(1000u32); diff --git a/src/encoding/models.rs b/src/encoding/models.rs index 7ed9bcb..527c853 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -1,3 +1,4 @@ +use clap::ValueEnum; use hex; use num_bigint::BigUint; use serde::{Deserialize, Serialize}; @@ -8,6 +9,31 @@ use tycho_common::{ use crate::encoding::{errors::EncodingError, serde_primitives::biguint_string}; +/// Specifies the method for transferring user funds into Tycho execution. +/// +/// Options: +/// +/// - `TransferFromPermit2`: Use Permit2 for token transfer. +/// - You must manually approve the Permit2 contract and sign the permit object externally +/// (outside `tycho-execution`). +/// +/// - `TransferFrom`: Use standard ERC-20 approval and `transferFrom`. +/// - You must approve the Tycho Router contract to spend your tokens via standard `approve()` +/// calls. +/// +/// - `None`: No transfer will be performed. +/// - Assumes the tokens are already present in the Tycho Router. +/// - **Warning**: This is an advanced mode. Ensure your logic guarantees that the tokens are +/// already in the router at the time of execution. +/// - The Tycho router is **not** designed to safely hold tokens. If tokens are not transferred +/// and used in the **same transaction**, they will be permanently lost. +#[derive(Clone, Debug, PartialEq, ValueEnum)] +pub enum UserTransferType { + TransferFromPermit2, + TransferFrom, + None, +} + /// Represents a solution containing details describing an order, and instructions for filling /// the order. #[derive(Clone, Default, Debug, Deserialize, Serialize)] @@ -95,7 +121,6 @@ pub struct Transaction { /// * `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, @@ -103,7 +128,6 @@ pub struct EncodedSolution { pub selector: String, pub n_tokens: usize, pub permit: Option, - pub signature: Option>, } /// Represents a single permit for permit2. diff --git a/src/encoding/tycho_encoder.rs b/src/encoding/tycho_encoder.rs index f66b1d4..99b0216 100644 --- a/src/encoding/tycho_encoder.rs +++ b/src/encoding/tycho_encoder.rs @@ -61,6 +61,7 @@ pub trait TychoEncoder { /// /// # Returns /// A vector of fully constructed [`Transaction`]s that can be submitted to a node or bundler. + #[deprecated(note = "Please use `encode_solutions` instead")] fn encode_full_calldata( &self, solutions: Vec, From f5d9ab03da0392f4df56d593dea95cb888272287 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Mon, 26 May 2025 10:27:09 +0100 Subject: [PATCH 04/11] chore: Improve docstrings Took 23 minutes --- src/bin/tycho-encode.rs | 8 -------- src/encoding/evm/approvals/permit2.rs | 2 +- src/encoding/evm/encoder_builders.rs | 3 +++ src/encoding/evm/tycho_encoders.rs | 1 - 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index a8ad374..449f223 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -29,8 +29,6 @@ use tycho_execution::encoding::{ /// "given_amount": "123...", /// "checked_token": "0x...", /// "exact_out": false, -/// "slippage": 0.01, -/// "expected_amount": "123...", /// "checked_amount": "123...", /// "swaps": [{ /// "component": { @@ -59,8 +57,6 @@ pub struct Cli { #[arg(short, long)] router_address: Option, #[arg(short, long)] - swapper_pk: Option, - #[arg(short, long)] user_transfer_type: Option, } @@ -95,10 +91,6 @@ fn main() -> Result<(), Box> { if let Some(router_address) = cli.router_address { builder = builder.router_address(router_address); } - if let Some(swapper_pk) = cli.swapper_pk { - let pk = B256::from_str(&swapper_pk)?; - builder = builder.signer(PrivateKeySigner::from_bytes(&pk)?); - } if let Some(user_transfer_type) = cli.user_transfer_type { builder = builder.user_transfer_type(user_transfer_type); } diff --git a/src/encoding/evm/approvals/permit2.rs b/src/encoding/evm/approvals/permit2.rs index efd1c90..f9c7cac 100644 --- a/src/encoding/evm/approvals/permit2.rs +++ b/src/encoding/evm/approvals/permit2.rs @@ -149,7 +149,7 @@ impl Permit2 { ))), } } - /// Creates permit single and signature + /// Creates permit single pub fn get_permit( &self, spender: &Bytes, diff --git a/src/encoding/evm/encoder_builders.rs b/src/encoding/evm/encoder_builders.rs index d8de3b0..84caae7 100644 --- a/src/encoding/evm/encoder_builders.rs +++ b/src/encoding/evm/encoder_builders.rs @@ -65,6 +65,9 @@ impl TychoRouterEncoderBuilder { self } + /// Sets the `signer` for the encoder. This is used to sign permit2 objects. This is only needed + /// if you intend to get the full calldata for the transfer. We do not recommend using this + /// option, you should sign and create the function calldata entirely on your own. pub fn signer(mut self, signer: PrivateKeySigner) -> Self { self.signer = Some(signer); self diff --git a/src/encoding/evm/tycho_encoders.rs b/src/encoding/evm/tycho_encoders.rs index fb89fb6..496ef76 100644 --- a/src/encoding/evm/tycho_encoders.rs +++ b/src/encoding/evm/tycho_encoders.rs @@ -271,7 +271,6 @@ impl TychoEncoder for TychoRouterEncoder { /// /// # Fields /// * `swap_encoder_registry`: Registry of swap encoders -/// * `native_address`: Address of the chain's native token #[derive(Clone)] pub struct TychoExecutorEncoder { swap_encoder_registry: SwapEncoderRegistry, From d12e3d3b5fc302b1c266424189f984884b2d9ed7 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Mon, 26 May 2025 11:36:32 +0100 Subject: [PATCH 05/11] fix: Bring back swapper_pk for backwards compatibility Took 16 minutes --- src/bin/tycho-encode.rs | 13 +++---- src/encoding/evm/approvals/permit2.rs | 2 +- src/encoding/evm/encoder_builders.rs | 34 ++++++++++++++----- .../evm/strategy_encoder/strategy_encoders.rs | 5 ++- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index 449f223..3af9033 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -1,10 +1,5 @@ -use std::{ - io::{self, Read}, - str::FromStr, -}; +use std::io::{self, Read}; -use alloy::signers::local::PrivateKeySigner; -use alloy_primitives::B256; use alloy_sol_types::SolValue; use clap::{Parser, Subcommand}; use tycho_common::{hex_bytes::Bytes, models::Chain}; @@ -58,6 +53,8 @@ pub struct Cli { router_address: Option, #[arg(short, long)] user_transfer_type: Option, + #[arg(short, long)] + swapper_pk: Option, } #[derive(Subcommand)] @@ -94,6 +91,10 @@ fn main() -> Result<(), Box> { if let Some(user_transfer_type) = cli.user_transfer_type { builder = builder.user_transfer_type(user_transfer_type); } + #[allow(deprecated)] + if let Some(swapper_pk) = cli.swapper_pk { + builder = builder.swapper_pk(swapper_pk); + } builder.build()? } Commands::TychoExecutor => TychoExecutorEncoderBuilder::new() diff --git a/src/encoding/evm/approvals/permit2.rs b/src/encoding/evm/approvals/permit2.rs index f9c7cac..6c58707 100644 --- a/src/encoding/evm/approvals/permit2.rs +++ b/src/encoding/evm/approvals/permit2.rs @@ -175,7 +175,7 @@ impl Permit2 { sigDeadline: sig_deadline, }; - Ok(models::PermitSingle::try_from(&permit_single)?) + models::PermitSingle::try_from(&permit_single) } } diff --git a/src/encoding/evm/encoder_builders.rs b/src/encoding/evm/encoder_builders.rs index 84caae7..dfa7d65 100644 --- a/src/encoding/evm/encoder_builders.rs +++ b/src/encoding/evm/encoder_builders.rs @@ -1,6 +1,7 @@ -use std::collections::HashMap; +use std::{collections::HashMap, str::FromStr}; use alloy::signers::local::PrivateKeySigner; +use alloy_primitives::B256; use tycho_common::{models::Chain as TychoCommonChain, Bytes}; use crate::encoding::{ @@ -22,7 +23,7 @@ pub struct TychoRouterEncoderBuilder { user_transfer_type: Option, executors_file_path: Option, router_address: Option, - signer: Option, + swapper_pk: Option, } impl Default for TychoRouterEncoderBuilder { @@ -37,7 +38,7 @@ impl TychoRouterEncoderBuilder { chain: None, executors_file_path: None, router_address: None, - signer: None, + swapper_pk: None, user_transfer_type: None, } } @@ -65,11 +66,15 @@ impl TychoRouterEncoderBuilder { self } - /// Sets the `signer` for the encoder. This is used to sign permit2 objects. This is only needed - /// if you intend to get the full calldata for the transfer. We do not recommend using this - /// option, you should sign and create the function calldata entirely on your own. - pub fn signer(mut self, signer: PrivateKeySigner) -> Self { - self.signer = Some(signer); + /// Sets the `swapper_pk` for the encoder. This is used to sign permit2 objects. This is only + /// needed if you intend to get the full calldata for the transfer. We do not recommend + /// using this option, you should sign and create the function calldata entirely on your + /// own. + #[deprecated( + note = "This is deprecated and will be removed in the future. You should sign and create the function calldata on your own." + )] + pub fn swapper_pk(mut self, swapper_pk: String) -> Self { + self.swapper_pk = Some(swapper_pk); self } @@ -94,12 +99,23 @@ impl TychoRouterEncoderBuilder { let swap_encoder_registry = SwapEncoderRegistry::new(self.executors_file_path.clone(), chain.clone())?; + let signer = if let Some(pk) = self.swapper_pk { + let pk = B256::from_str(&pk).map_err(|_| { + EncodingError::FatalError("Invalid swapper private key provided".to_string()) + })?; + Some(PrivateKeySigner::from_bytes(&pk).map_err(|_| { + EncodingError::FatalError("Failed to create signer".to_string()) + })?) + } else { + None + }; + Ok(Box::new(TychoRouterEncoder::new( chain, swap_encoder_registry, tycho_router_address, user_transfer_type, - self.signer, + signer, )?)) } else { Err(EncodingError::FatalError( diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index bce32fc..b92a713 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -551,15 +551,14 @@ mod tests { fn get_permit(router_address: Bytes, solution: &Solution) -> PermitSingle { let permit2 = Permit2::new().unwrap(); - let permit_single = permit2 + permit2 .get_permit( &router_address, &solution.sender, &solution.given_token, &solution.given_amount, ) - .unwrap(); - permit_single + .unwrap() } fn get_signer() -> PrivateKeySigner { From 4f85e3e88ee83c99c952fb60abaf4b7e09a0858a Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Mon, 26 May 2025 17:08:44 +0100 Subject: [PATCH 06/11] chore: Move tests from strategy_encoders to tycho_encoders Most of these tests were actually integration tests, so they should be in the outer level of the package. Took 1 hour 5 minutes --- foundry/test/assets/calldata.txt | 24 +- .../evm/strategy_encoder/strategy_encoders.rs | 1934 +------------- src/encoding/evm/tycho_encoders.rs | 2238 ++++++++++++++++- 3 files changed, 2314 insertions(+), 1882 deletions(-) diff --git a/foundry/test/assets/calldata.txt b/foundry/test/assets/calldata.txt index d29dda6..539328a 100644 --- a/foundry/test/assets/calldata.txt +++ b/foundry/test/assets/calldata.txt @@ -3,23 +3,23 @@ test_single_encoding_strategy_ekubo:5c4b639c000000000000000000000000000000000000 test_uniswap_v3_uniswap_v3:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000692e234dae75c793f67a35089c9d99245e1c58470b2260fac5e5542a773aa44fbcfedf7c193bc2c599a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc299ac8ca7087fa4a2a1fb6357269965a2014abc35010100000000000000000000 test_balancer_v2_uniswap_v2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000c80072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e004375dff511095cc5a197a54140a24efef3a416010000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_sequential_swap_strategy_encoder_no_permit2:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 -test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000685836b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0bf00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041ca2855680741a2290342cdb9a3d4bdafffb0f1e62d3b1e8daac2320d342a97525b2d5b482e223d26d1888d7262f6f32c4f1f3a88a143b091f776245f8f4943581b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 -test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e0000000000000000000000000000000000000000000000000000000000685836b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0bf000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f568894962d9b1d0d490cad2051cb88c4fa4a1201577c417d01cc55768b4fa1a526ecd9c73611d5d9dad70c5473a625d46e1fca60e0d025dfc2e9146fa28efc41b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000 -test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004160b51ba1539713798c36fa209145fc28e33627b9f7fa4e971c5f3616d8dc6fd02d17a7f0a787466465bb98e753be08ec07cfd562396c8474dea91b2c8f97a4f11c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f300000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000416a1ae07bda4ca6c8503f2233052eea5a5dfb8c7755dfb108797fef2bacdddaa266ffba24db3614fb8a933c0bcceda3aecffa6db0781ecb436e6bbda5eea459621b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_usv4_eth_out:30ace1b100000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e0000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f300000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000416f3729ae9d9802c1e9c996adac1ecd83bba3bbd9526e9d6b78b700624a5143e23a23bdfb10ba1dda78f51a819a9cd9fa247484c288c5102a384a0c86ad389d431b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c0000000000000000000000000000000000000000 +test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f300000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041d86a1edea6862ef9c8df971417410c87f6cf21d40542760db70fdd800b82f7c9722702f735433a898b8cc208b131cd865589d11f5028da2b9866667e0d38bfd51b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_single_swap_strategy_encoder_no_permit2:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_swap_strategy_encoder_no_transfer_in:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 -test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685836b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0bf00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000415b7c694c0ae0b6a7c44a90ad5c409746c3dbc7c63ac7912f31cbc23f383503fe4127378bdfaffbcda64d382a2cc3a2c139f7763f11e4e21ffb5c07fc258aae581b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 -test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041e3b2388523e7a76ff5810f3a1a092627c7a890994a99a0a49ae5175c5c12657d7fb2c5be13ae8849c593ae9246e8f2de334c5977b5a0820e49a0af2ecbfd89b11c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 +test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f300000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000412b2ccc98803d3057de86d25f9dda53a5e41c5200ff6355b7627e6ce570adfe555a5fed532f95ca6c0a189594a7eef964398c5dbbddf2931b111c3888ec57301e1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 +test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f300000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004133e8a83bf0ab458c292bf06e076844c9a2b2fbfbd2fbf1e9b13dd7c79a594597378fd61fe3b24def396d55636a50eb5c847367b1c1108e99c4f31172af84d6481c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 test_single_encoding_strategy_curve_st_eth:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 -test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004160b51ba1539713798c36fa209145fc28e33627b9f7fa4e971c5f3616d8dc6fd02d17a7f0a787466465bb98e753be08ec07cfd562396c8474dea91b2c8f97a4f11c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 +test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006d70b85442ed96492800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f300000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041d86a1edea6862ef9c8df971417410c87f6cf21d40542760db70fdd800b82f7c9722702f735433a898b8cc208b131cd865589d11f5028da2b9866667e0d38bfd51b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_encoding_strategy_curve:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 -test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041bacbb311e4f4bf852ac6ea61807fbfd8ef961f8f5f1b2455fd7dda16851755334c33b522343304998e3e8e48ecfd85c2b123c162ac41c5c6b26ba523a70b6ae71c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000 -test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000411b0820a1eb1d1d4e5c2da500396abb4cb864761c1b92fdaaca6cd2a6cedf82f14fe6c9a31bc617912aecdde40626dd12fb45f3a00b55050a90be6ad447a4ec811b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 -test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041e3b2388523e7a76ff5810f3a1a092627c7a890994a99a0a49ae5175c5c12657d7fb2c5be13ae8849c593ae9246e8f2de334c5977b5a0820e49a0af2ecbfd89b11c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000 -test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041e3b2388523e7a76ff5810f3a1a092627c7a890994a99a0a49ae5175c5c12657d7fb2c5be13ae8849c593ae9246e8f2de334c5977b5a0820e49a0af2ecbfd89b11c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000 -test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004160b51ba1539713798c36fa209145fc28e33627b9f7fa4e971c5f3616d8dc6fd02d17a7f0a787466465bb98e753be08ec07cfd562396c8474dea91b2c8f97a4f11c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000 +test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f300000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410bc65c37597bc37547d90657196ff352b0d9d80df5435b74efb5fda78c203a047fdc53c8f77c800db59ddda08e3479e64cb706cc7296d4f68cb0ab46f03e8ff91b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000 +test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f300000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000412b2ccc98803d3057de86d25f9dda53a5e41c5200ff6355b7627e6ce570adfe555a5fed532f95ca6c0a189594a7eef964398c5dbbddf2931b111c3888ec57301e1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 +test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f300000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004133e8a83bf0ab458c292bf06e076844c9a2b2fbfbd2fbf1e9b13dd7c79a594597378fd61fe3b24def396d55636a50eb5c847367b1c1108e99c4f31172af84d6481c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000 +test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f300000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004133e8a83bf0ab458c292bf06e076844c9a2b2fbfbd2fbf1e9b13dd7c79a594597378fd61fe3b24def396d55636a50eb5c847367b1c1108e99c4f31172af84d6481c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000 +test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685c1eeb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f3000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041d86a1edea6862ef9c8df971417410c87f6cf21d40542760db70fdd800b82f7c9722702f735433a898b8cc208b131cd865589d11f5028da2b9866667e0d38bfd51b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000 test_uniswap_v3_curve:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000 -test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf0000000000000000000000000000000000000000000000000000000000000685836b800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000000000000000000000000000000000000000006830b0c000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004148ed139e954654f795a1b25b70a4ec2ed3f14bba64da8eb471af5ec3e816fbcc4f877a1ea3250c279542ba04063b9cef3bd765821765dd03957723a6c5478ab81c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 +test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf0000000000000000000000000000000000000000000000000000000000000685c1eec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000683498f400000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041aa54dffe78286acee8d7bc6c0adf055c63efb57333eaf9684f8d5dac70a81c342970954434cfaf8c5beeeccb4ab9ed1c2dd8ae2e3031fb689b42e938d5c7841a1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0102 test_ekubo_encode_swap_multi:01ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032 test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index b92a713..aa17095 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -510,8 +510,8 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { mod tests { use std::{collections::HashMap, str::FromStr}; - use alloy::{hex::encode, signers::local::PrivateKeySigner}; - use alloy_primitives::{hex, Address, B256, U256}; + use alloy::hex::encode; + use alloy_primitives::hex; use num_bigint::{BigInt, BigUint}; use tycho_common::{ models::{protocol::ProtocolComponent, Chain as TychoCommonChain}, @@ -519,22 +519,12 @@ mod tests { }; use super::*; - use crate::encoding::{ - evm::{ - approvals::permit2::Permit2, encoding_utils::encode_tycho_router_call, - utils::write_calldata_to_file, - }, - models::{PermitSingle, Swap}, - }; + use crate::encoding::models::Swap; fn eth_chain() -> Chain { TychoCommonChain::Ethereum.into() } - fn eth() -> Bytes { - Bytes::from(hex!("0000000000000000000000000000000000000000").to_vec()) - } - fn weth() -> Bytes { Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec()) } @@ -549,32 +539,9 @@ mod tests { Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap() } - fn get_permit(router_address: Bytes, solution: &Solution) -> PermitSingle { - let permit2 = Permit2::new().unwrap(); - permit2 - .get_permit( - &router_address, - &solution.sender, - &solution.given_token, - &solution.given_amount, - ) - .unwrap() - } - - fn get_signer() -> PrivateKeySigner { - // Set up a mock private key for signing (Alice's pk in our contract tests) - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - - let pk = B256::from_str(&private_key).unwrap(); - PrivateKeySigner::from_bytes(&pk).unwrap() - } - mod single { - use alloy_sol_types::SolValue; use super::*; - use crate::encoding::evm::utils::biguint_to_u256; #[test] fn test_single_swap_strategy_encoder() { // Performs a single swap from WETH to DAI on a USV2 pool, with no grouping @@ -613,126 +580,11 @@ mod tests { ..Default::default() }; - let mut encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - let expected_min_amount_encoded = - hex::encode(U256::abi_encode(&biguint_to_u256(&checked_amount))); - let expected_input = [ - "30ace1b1", // Function selector - "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in - "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out - &expected_min_amount_encoded, // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - ] - .join(""); - - // after this there is the permit and because of the deadlines (that depend on block - // time) it's hard to assert - - let expected_swap = String::from(concat!( - // length of encoded swap without padding - "0000000000000000000000000000000000000000000000000000000000000052", - // Swap data - "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "00", // zero2one - "00", // transfer type TransferFrom - "0000000000000000000000000000", // padding - )); - let hex_calldata = encode(&calldata); - - assert_eq!(hex_calldata[..456], expected_input); - assert_eq!(hex_calldata[1224..], expected_swap); - write_calldata_to_file("test_single_swap_strategy_encoder", &hex_calldata.to_string()); - } - - #[test] - fn test_single_swap_strategy_encoder_no_permit2() { - // Performs a single swap from WETH to DAI on a USV2 pool, without permit2 and no - // grouping optimizations. - - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); - - let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap(); - let expected_min_amount = U256::from_str("1_640_000000000000000000").unwrap(); - - let swap = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: dai.clone(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SingleSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFrom, - router_address(), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: dai, - checked_amount, - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap], - ..Default::default() - }; - let encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount)); - let expected_input = [ - "5c4b639c", // Function selector - "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in - "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out - &expected_min_amount_encoded, // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "0000000000000000000000000000000000000000000000000000000000000001", // transfer from needed - "0000000000000000000000000000000000000000000000000000000000000120", // offset of swap bytes - "0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding + let expected_swap = String::from(concat!( // Swap data "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in @@ -740,17 +592,12 @@ mod tests { "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver "00", // zero2one "00", // transfer type TransferFrom - "0000000000000000000000000000", // padding - ] - .join(""); + )); + let hex_calldata = encode(&encoded_solution.swaps); - let hex_calldata = encode(&calldata); - - assert_eq!(hex_calldata, expected_input); - write_calldata_to_file( - "test_single_swap_strategy_encoder_no_permit2", - hex_calldata.as_str(), - ); + assert_eq!(hex_calldata, expected_swap); + assert_eq!(encoded_solution.selector, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string()); + assert_eq!(encoded_solution.interacting_with, router_address()); } #[test] @@ -762,7 +609,6 @@ mod tests { let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap(); - let expected_min_amount = U256::from_str("1_640_000000000000000000").unwrap(); let swap = Swap { component: ProtocolComponent { @@ -797,30 +643,8 @@ mod tests { let encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::None, - eth(), - None, - ) - .unwrap() - .data; - let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount)); - let expected_input = [ - "5c4b639c", // Function selector - "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in - "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out - &expected_min_amount_encoded, // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "0000000000000000000000000000000000000000000000000000000000000000", // transfer from not needed - "0000000000000000000000000000000000000000000000000000000000000120", // offset of swap bytes - "0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding + let expected_input = [ // Swap data "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in @@ -828,219 +652,24 @@ mod tests { "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver "00", // zero2one "01", // transfer type Transfer - "0000000000000000000000000000", // padding ] - .join(""); + .join(""); - let hex_calldata = encode(&calldata); + let hex_calldata = encode(&encoded_solution.swaps); assert_eq!(hex_calldata, expected_input); - write_calldata_to_file( - "test_single_swap_strategy_encoder_no_transfer_in", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_swap_strategy_encoder_wrap() { - // Performs a single swap from WETH to DAI on a USV2 pool, wrapping ETH - // Note: This test does not assert anything. It is only used to obtain integration test - // data for our router solidity test. - - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); - - let swap = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth(), - token_out: dai.clone(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SingleSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFromPermit2, - router_address(), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: eth(), - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: dai, - checked_amount: BigUint::from_str("1659881924818443699787").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap], - native_action: Some(NativeAction::Wrap), - }; - - let mut encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_single_swap_strategy_encoder_wrap", hex_calldata.as_str()); - } - - #[test] - fn test_single_swap_strategy_encoder_unwrap() { - // Performs a single swap from DAI to WETH on a USV2 pool, unwrapping ETH at the end - // Note: This test does not assert anything. It is only used to obtain integration test - // data for our router solidity test. - - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); - - let swap = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: dai.clone(), - token_out: weth(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SingleSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFromPermit2, - Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: dai, - given_amount: BigUint::from_str("3_000_000000000000000000").unwrap(), - checked_token: eth(), - checked_amount: BigUint::from_str("1_000000000000000000").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap], - native_action: Some(NativeAction::Unwrap), - }; - - let mut encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_swap_strategy_encoder_unwrap", - hex_calldata.as_str(), + assert_eq!( + encoded_solution.selector, + "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" + .to_string() ); + assert_eq!(encoded_solution.interacting_with, router_address()); } } mod sequential { use super::*; - #[test] - fn test_sequential_swap_strategy_encoder() { - // Note: This test does not assert anything. It is only used to obtain integration test - // data for our router solidity test. - // - // Performs a sequential swap from WETH to USDC though WBTC using USV2 pools - // - // WETH ───(USV2)──> WBTC ───(USV2)──> USDC - - let weth = weth(); - let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - }; - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SequentialSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFromPermit2, - router_address(), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], - ..Default::default() - }; - - let mut encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_sequential_swap_strategy_encoder", hex_calldata.as_str()); - } - #[test] fn test_sequential_swap_strategy_encoder_no_permit2() { // Performs a sequential swap from WETH to USDC though WBTC using USV2 pools @@ -1095,32 +724,9 @@ mod tests { .encode_strategy(solution.clone()) .unwrap(); - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); + let hex_calldata = encode(&encoded_solution.swaps); let expected = String::from(concat!( - "e21dd0d3", /* function selector */ - "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in - "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token ou - "00000000000000000000000000000000000000000000000000000000018f61ec", /* min amount out */ - "0000000000000000000000000000000000000000000000000000000000000000", // wrap - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "0000000000000000000000000000000000000000000000000000000000000001", /* transfer from needed */ - "0000000000000000000000000000000000000000000000000000000000000120", /* length ple - * encode */ - "00000000000000000000000000000000000000000000000000000000000000a8", // swap 1 "0052", // swap length "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address @@ -1130,775 +736,28 @@ mod tests { "00", // zero to one "00", // transfer type TransferFrom // swap 2 - "0052", // swap length - "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address - "2260fac5e5542a773aa44fbcfedf7c193bc2c599", // token in - "004375dff511095cc5a197a54140a24efef3a416", // component id - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver (final user) - "01", // zero to one - "02", // transfer type None - "000000000000000000000000000000000000000000000000", // padding + "0052", // swap length + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address + "2260fac5e5542a773aa44fbcfedf7c193bc2c599", // token in + "004375dff511095cc5a197a54140a24efef3a416", // component id + "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver (final user) + "01", // zero to one + "02", // transfer type None )); assert_eq!(hex_calldata, expected); - write_calldata_to_file( - "test_sequential_swap_strategy_encoder_no_permit2", - hex_calldata.as_str(), + assert_eq!( + encoded_solution.selector, + "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" + .to_string() ); - } - - #[test] - fn test_sequential_strategy_cyclic_swap() { - // This test has start and end tokens that are the same - // The flow is: - // USDC -> WETH -> USDC using two pools - - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - // Create two Uniswap V3 pools for the cyclic swap - // USDC -> WETH (Pool 1) - let swap_usdc_weth = Swap { - component: ProtocolComponent { - id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* USDC-WETH USV3 - * Pool 1 */ - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(500).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: weth.clone(), - split: 0f64, - }; - - // WETH -> USDC (Pool 2) - let swap_weth_usdc = Swap { - component: ProtocolComponent { - id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), /* USDC-WETH USV3 - * Pool 2 */ - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: usdc.clone(), - split: 0f64, - }; - - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SequentialSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFromPermit2, - Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - ) - .unwrap(); - - let solution = Solution { - exact_out: false, - given_token: usdc.clone(), - given_amount: BigUint::from_str("100000000").unwrap(), // 100 USDC (6 decimals) - checked_token: usdc.clone(), - checked_amount: BigUint::from_str("99389294").unwrap(), /* Expected output - * from test */ - swaps: vec![swap_usdc_weth, swap_weth_usdc], - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - ..Default::default() - }; - - let mut encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - let hex_calldata = hex::encode(&calldata); - let expected_input = [ - "51bcc7b6", // selector - "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token - "0000000000000000000000000000000000000000000000000000000005ec8f6e", // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap action - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - ] - .join(""); - - let expected_swaps = [ - "00000000000000000000000000000000000000000000000000000000000000d6", // length of ple encoded swaps without padding - "0069", // ple encoded swaps - "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out - "0001f4", // pool fee - "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver - "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id - "01", // zero2one - "00", // transfer type TransferFrom - "0069", // ple encoded swaps - "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address - "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out - "000bb8", // pool fee - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id - "00", // zero2one - "01", // transfer type Transfer - "00000000000000000000", // padding - ] - .join(""); - - assert_eq!(hex_calldata[..456], expected_input); - assert_eq!(hex_calldata[1224..], expected_swaps); - write_calldata_to_file("test_sequential_strategy_cyclic_swap", hex_calldata.as_str()); - } - - mod optimized_transfers { - // In this module we test the ability to chain swaps or not. Different protocols are - // tested. The encoded data is used for solidity tests as well - use super::*; - - #[test] - fn test_uniswap_v3_uniswap_v2() { - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - // - // Performs a sequential swap from WETH to USDC though WBTC using USV3 and USV2 - // pools - // - // WETH ───(USV3)──> WBTC ───(USV2)──> USDC - - let weth = weth(); - let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD".to_string(), - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - }; - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SequentialSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFrom, - router_address(), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_uniswap_v3_uniswap_v2", hex_calldata.as_str()); - } - - #[test] - fn test_uniswap_v3_uniswap_v3() { - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - // - // Performs a sequential swap from WETH to USDC though WBTC using USV3 pools - // There is no optimization between the two USV3 pools, this is currently not - // supported. - // - // WETH ───(USV3)──> WBTC ───(USV3)──> USDC - - let weth = weth(); - let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD".to_string(), - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - }; - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x99ac8cA7087fA4A2A1FB6357269965A2014ABc35".to_string(), - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SequentialSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFrom, - router_address(), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_uniswap_v3_uniswap_v3", hex_calldata.as_str()); - } - - #[test] - fn test_uniswap_v3_curve() { - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - // - // Performs a sequential swap from WETH to USDT though WBTC using USV3 and curve - // pools - // - // WETH ───(USV3)──> WBTC ───(curve)──> USDT - - let weth = weth(); - let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdt = Bytes::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD".to_string(), - protocol_system: "uniswap_v3".to_string(), - static_attributes: { - let mut attrs = HashMap::new(); - attrs.insert( - "fee".to_string(), - Bytes::from(BigInt::from(3000).to_signed_bytes_be()), - ); - attrs - }, - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - }; - - let swap_wbtc_usdt = Swap { - component: ProtocolComponent { - id: String::from("0xD51a44d3FaE010294C616388b506AcdA1bfAAE46"), - protocol_system: String::from("vm:curve"), - static_attributes: { - let mut attrs: HashMap = HashMap::new(); - attrs.insert( - "factory".into(), - Bytes::from( - "0x0000000000000000000000000000000000000000" - .as_bytes() - .to_vec(), - ), - ); - attrs.insert( - "coins".into(), - Bytes::from_str("0x5b22307864616331376639353864326565353233613232303632303639393435393763313364383331656337222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307863303261616133396232323366653864306130653563346632376561643930383363373536636332225d") - .unwrap(), - ); - attrs - }, - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdt.clone(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SequentialSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFrom, - router_address(), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdt, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdt], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_uniswap_v3_curve", hex_calldata.as_str()); - } - - #[test] - fn test_balancer_v2_uniswap_v2() { - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - // - // Performs a sequential swap from WETH to USDC though WBTC using balancer and USV2 - // pools - // - // WETH ───(balancer)──> WBTC ───(USV2)──> USDC - - let weth = weth(); - let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e" - .to_string(), - protocol_system: "vm:balancer_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - }; - - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SequentialSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFrom, - router_address(), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_balancer_v2_uniswap_v2", hex_calldata.as_str()); - } - - #[test] - fn test_multi_protocol() { - // Note: This test does not assert anything. It is only used to obtain integration - // test data for our router solidity test. - // - // Performs the following swap: - // - // DAI ─(USV2)-> WETH ─(bal)─> WBTC ─(curve)─> USDT ─(ekubo)─> USDC ─(USV4)─> ETH - - let weth = weth(); - let eth = eth(); - let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - let usdt = Bytes::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap(); - let dai = Bytes::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F").unwrap(); - - let usv2_swap_dai_weth = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: dai.clone(), - token_out: weth.clone(), - split: 0f64, - }; - - let balancer_swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e" - .to_string(), - protocol_system: "vm:balancer_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - split: 0f64, - }; - - let curve_swap_wbtc_usdt = Swap { - component: ProtocolComponent { - id: String::from("0xD51a44d3FaE010294C616388b506AcdA1bfAAE46"), - protocol_system: String::from("vm:curve"), - static_attributes: { - let mut attrs: HashMap = HashMap::new(); - attrs.insert( - "factory".into(), - Bytes::from( - "0x0000000000000000000000000000000000000000" - .as_bytes() - .to_vec(), - ), - ); - attrs.insert( - "coins".into(), - Bytes::from_str("0x5b22307864616331376639353864326565353233613232303632303639393435393763313364383331656337222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307863303261616133396232323366653864306130653563346632376561643930383363373536636332225d") - .unwrap(), - ); - attrs - }, - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdt.clone(), - split: 0f64, - }; - - // Ekubo - - let component = ProtocolComponent { - // All Ekubo swaps go through the core contract - not necessary to specify pool - // id for test - protocol_system: "ekubo_v2".to_string(), - // 0.0025% fee & 0.005% base pool - static_attributes: HashMap::from([ - ("fee".to_string(), Bytes::from(461168601842738_u64)), - ("tick_spacing".to_string(), Bytes::from(50_u32)), - ("extension".to_string(), Bytes::zero(20)), - ]), - ..Default::default() - }; - let ekubo_swap_usdt_usdc = Swap { - component, - token_in: usdt.clone(), - token_out: usdc.clone(), - split: 0f64, - }; - - // USV4 - // Fee and tick spacing information for this test is obtained by querying the - // USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e - // Using the poolKeys function with the first 25 bytes of the pool id - let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be()); - let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be()); - let mut static_attributes_usdc_eth: HashMap = HashMap::new(); - static_attributes_usdc_eth.insert("key_lp_fee".into(), pool_fee_usdc_eth); - static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth); - - let usv4_swap_usdc_eth = Swap { - component: ProtocolComponent { - id: "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d" - .to_string(), - protocol_system: "uniswap_v4".to_string(), - static_attributes: static_attributes_usdc_eth, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: eth.clone(), - split: 0f64, - }; - - // Put all components together - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SequentialSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFromPermit2, - router_address(), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: dai, - given_amount: BigUint::from_str("1500_000000000000000000").unwrap(), - checked_token: eth.clone(), - checked_amount: BigUint::from_str("732214216964381330").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") - .unwrap(), - swaps: vec![ - usv2_swap_dai_weth, - balancer_swap_weth_wbtc, - curve_swap_wbtc_usdt, - ekubo_swap_usdt_usdc, - usv4_swap_usdc_eth, - ], - ..Default::default() - }; - - let mut encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth, - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_multi_protocol", hex_calldata.as_str()); - } + assert_eq!(encoded_solution.interacting_with, router_address()); } } mod split { use super::*; - #[test] - fn test_split_swap_strategy_encoder() { - // Note: This test does not assert anything. It is only used to obtain integration test - // data for our router solidity test. - // - // Performs a split swap from WETH to USDC though WBTC and DAI using USV2 pools - // - // ┌──(USV2)──> WBTC ───(USV2)──> USDC - // WETH ─┤ - // └──(USV2)──> DAI ───(USV2)──> USDC - // - - let weth = weth(); - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); - let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - let swap_weth_dai = Swap { - component: ProtocolComponent { - id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: dai.clone(), - split: 0.5f64, - }; - let swap_weth_wbtc = Swap { - component: ProtocolComponent { - id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: weth.clone(), - token_out: wbtc.clone(), - // This represents the remaining 50%, but to avoid any rounding errors we set this - // to 0 to signify "the remainder of the WETH value". It should - // still be very close to 50% - split: 0f64, - }; - let swap_dai_usdc = Swap { - component: ProtocolComponent { - id: "0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: dai.clone(), - token_out: usdc.clone(), - split: 0f64, - }; - let swap_wbtc_usdc = Swap { - component: ProtocolComponent { - id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), - protocol_system: "uniswap_v2".to_string(), - ..Default::default() - }, - token_in: wbtc.clone(), - token_out: usdc.clone(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SplitSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFromPermit2, - Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: weth, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: usdc, - checked_amount: BigUint::from_str("26173932").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc], - ..Default::default() - }; - - let mut encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_split_swap_strategy_encoder", hex_calldata.as_str()); - } - #[test] fn test_split_input_cyclic_swap() { // This test has start and end tokens that are the same @@ -1998,78 +857,56 @@ mod tests { ..Default::default() }; - let mut encoded_solution = encoder + let encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; + let hex_calldata = hex::encode(&encoded_solution.swaps); - let hex_calldata = hex::encode(&calldata); - let expected_input = [ - "7c553846", // selector - "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token - "0000000000000000000000000000000000000000000000000000000005ef619b", // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap action - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action - "0000000000000000000000000000000000000000000000000000000000000002", // tokens length - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - ] - .join(""); let expected_swaps = [ - "0000000000000000000000000000000000000000000000000000000000000139", // length of ple encoded swaps without padding - "006e", // ple encoded swaps - "00", // token in index - "01", // token out index - "999999", // split + "006e", // ple encoded swaps + "00", // token in index + "01", // token out index + "999999", // split "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out - "0001f4", // pool fee + "0001f4", // pool fee "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id - "01", // zero2one - "00", // transfer type TransferFrom - "006e", // ple encoded swaps - "00", // token in index - "01", // token out index - "000000", // split + "01", // zero2one + "00", // transfer type TransferFrom + "006e", // ple encoded swaps + "00", // token in index + "01", // token out index + "000000", // split "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out - "000bb8", // pool fee + "000bb8", // pool fee "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id - "01", // zero2one - "00", // transfer type TransferFrom - "0057", // ple encoded swaps - "01", // token in index - "00", // token out index - "000000", // split + "01", // zero2one + "00", // transfer type TransferFrom + "0057", // ple encoded swaps + "01", // token in index + "00", // token out index + "000000", // split "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address, "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id, "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - "00", // zero2one - "01", // transfer type Transfer - "00000000000000" // padding + "00", // zero2one + "01", // transfer type Transfer ] - .join(""); - assert_eq!(hex_calldata[..520], expected_input); - assert_eq!(hex_calldata[1288..], expected_swaps); - write_calldata_to_file("test_split_input_cyclic_swap", hex_calldata.as_str()); + .join(""); + assert_eq!(hex_calldata, expected_swaps); + assert_eq!( + encoded_solution.selector, + "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" + .to_string() + ); + assert_eq!(encoded_solution.interacting_with, router_address()); } #[test] @@ -2148,7 +985,7 @@ mod tests { let encoder = SplitSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - UserTransferType::TransferFromPermit2, + UserTransferType::TransferFrom, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), ) .unwrap(); @@ -2167,648 +1004,57 @@ mod tests { ..Default::default() }; - let mut encoded_solution = encoder + let encoded_solution = encoder .encode_strategy(solution.clone()) .unwrap(); - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth(), - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = hex::encode(&calldata); - let expected_input = [ - "7c553846", // selector - "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token - "0000000000000000000000000000000000000000000000000000000005e703f4", // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap action - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action - "0000000000000000000000000000000000000000000000000000000000000002", // tokens length - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - ] - .join(""); + let hex_calldata = hex::encode(&encoded_solution.swaps); let expected_swaps = [ - "0000000000000000000000000000000000000000000000000000000000000139", // length of ple encoded swaps without padding - "0057", // ple encoded swaps - "00", // token in index - "01", // token out index - "000000", // split + "0057", // ple encoded swaps + "00", // token in index + "01", // token out index + "000000", // split "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver - "01", // zero2one - "00", // transfer type TransferFrom - "006e", // ple encoded swaps - "01", // token in index - "00", // token out index - "999999", // split + "01", // zero2one + "00", // transfer type TransferFrom + "006e", // ple encoded swaps + "01", // token in index + "00", // token out index + "999999", // split "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out - "0001f4", // pool fee + "0001f4", // pool fee "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id - "00", // zero2one - "01", // transfer type Transfer - "006e", // ple encoded swaps - "01", // token in index - "00", // token out index - "000000", // split + "00", // zero2one + "01", // transfer type Transfer + "006e", // ple encoded swaps + "01", // token in index + "00", // token out index + "000000", // split "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out - "000bb8", // pool fee + "000bb8", // pool fee "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id - "00", // zero2one - "01", // transfer type Transfer - "00000000000000" // padding - ] - .join(""); - - assert_eq!(hex_calldata[..520], expected_input); - assert_eq!(hex_calldata[1288..], expected_swaps); - write_calldata_to_file("test_split_output_cyclic_swap", hex_calldata.as_str()); - } - } - - mod protocol_integration { - // in this module we test protocol specific logic by creating the calldata that then is - // used in the solidity tests - use super::*; - - #[test] - fn test_single_encoding_strategy_ekubo() { - // ETH ──(EKUBO)──> USDC - - let token_in = Bytes::from(Address::ZERO.as_slice()); - let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC - - let static_attributes = HashMap::from([ - ("fee".to_string(), Bytes::from(0_u64)), - ("tick_spacing".to_string(), Bytes::from(0_u32)), - ( - "extension".to_string(), - Bytes::from("0x51d02a5948496a67827242eabc5725531342527c"), - ), /* Oracle */ - ]); - - let component = ProtocolComponent { - // All Ekubo swaps go through the core contract - not necessary to specify pool id - // for test - protocol_system: "ekubo_v2".to_string(), - static_attributes, - ..Default::default() - }; - - let swap = Swap { - component, - token_in: token_in.clone(), - token_out: token_out.clone(), - split: 0f64, - }; - - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SingleSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFrom, - Bytes::from_str("0xA4AD4f68d0b91CFD19687c881e50f3A00242828c").unwrap(), - ) - .unwrap(); - - let solution = Solution { - exact_out: false, - given_token: token_in, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: token_out, - checked_amount: BigUint::from_str("1000").unwrap(), - // Alice - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_single_encoding_strategy_ekubo", hex_calldata.as_str()); - } - - #[test] - fn test_single_encoding_strategy_maverick() { - // GHO -> (maverick) -> USDC - let maverick_pool = ProtocolComponent { - id: String::from("0x14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67"), - protocol_system: String::from("vm:maverick_v2"), - ..Default::default() - }; - let token_in = Bytes::from("0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f"); - let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); - let swap = Swap { - component: maverick_pool, - token_in: token_in.clone(), - token_out: token_out.clone(), - split: 0f64, - }; - - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SingleSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFrom, - Bytes::from_str("0xA4AD4f68d0b91CFD19687c881e50f3A00242828c").unwrap(), - ) - .unwrap(); - - let solution = Solution { - exact_out: false, - given_token: token_in, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: token_out, - checked_amount: BigUint::from_str("1000").unwrap(), - // Alice - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_single_encoding_strategy_maverick", hex_calldata.as_str()); - } - - #[test] - fn test_single_encoding_strategy_usv4_eth_in() { - // Performs a single swap from ETH to PEPE using a USV4 pool - // Note: This test does not assert anything. It is only used to obtain integration test - // data for our router solidity test. - // - // ETH ───(USV4)──> PEPE - // - let eth = eth(); - let pepe = Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap(); - - let pool_fee_eth_pepe = Bytes::from(BigInt::from(25000).to_signed_bytes_be()); - let tick_spacing_eth_pepe = Bytes::from(BigInt::from(500).to_signed_bytes_be()); - let mut static_attributes_eth_pepe: HashMap = HashMap::new(); - static_attributes_eth_pepe.insert("key_lp_fee".into(), pool_fee_eth_pepe); - static_attributes_eth_pepe.insert("tick_spacing".into(), tick_spacing_eth_pepe); - - let swap_eth_pepe = Swap { - component: ProtocolComponent { - id: "0xecd73ecbf77219f21f129c8836d5d686bbc27d264742ddad620500e3e548e2c9" - .to_string(), - protocol_system: "uniswap_v4".to_string(), - static_attributes: static_attributes_eth_pepe, - ..Default::default() - }, - token_in: eth.clone(), - token_out: pepe.clone(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SingleSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFromPermit2, - Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - ) - .unwrap(); - - let solution = Solution { - exact_out: false, - given_token: eth.clone(), - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: pepe, - checked_amount: BigUint::from_str("152373460199848577067005852").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap_eth_pepe], - ..Default::default() - }; - - let mut encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth, - Some(get_signer()), - ) - .unwrap() - .data; - let hex_calldata = encode(&calldata); - - write_calldata_to_file( - "test_single_encoding_strategy_usv4_eth_in", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_encoding_strategy_usv4_eth_out() { - // Performs a single swap from USDC to ETH using a USV4 pool - // Note: This test does not assert anything. It is only used to obtain integration test - // data for our router solidity test. - // - // USDC ───(USV4)──> ETH - // - let eth = eth(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - - // Fee and tick spacing information for this test is obtained by querying the - // USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e - // Using the poolKeys function with the first 25 bytes of the pool id - let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be()); - let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be()); - let mut static_attributes_usdc_eth: HashMap = HashMap::new(); - static_attributes_usdc_eth.insert("key_lp_fee".into(), pool_fee_usdc_eth); - static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth); - - let swap_usdc_eth = Swap { - component: ProtocolComponent { - id: "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d" - .to_string(), - protocol_system: "uniswap_v4".to_string(), - static_attributes: static_attributes_usdc_eth, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: eth.clone(), - split: 0f64, - }; - - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SplitSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFromPermit2, - Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - ) - .unwrap(); - - let solution = Solution { - exact_out: false, - given_token: usdc, - given_amount: BigUint::from_str("3000_000000").unwrap(), - checked_token: eth.clone(), - checked_amount: BigUint::from_str("1117254495486192350").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap_usdc_eth], - ..Default::default() - }; - - let mut encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth, - Some(get_signer()), - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_encoding_strategy_usv4_eth_out", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_encoding_strategy_usv4_grouped_swap() { - // Performs a sequential swap from USDC to PEPE though ETH using two consecutive USV4 - // pools - // - // USDC ──(USV4)──> ETH ───(USV4)──> PEPE - // - - let eth = eth(); - let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); - let pepe = Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap(); - - // Fee and tick spacing information for this test is obtained by querying the - // USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e - // Using the poolKeys function with the first 25 bytes of the pool id - let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be()); - let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be()); - let mut static_attributes_usdc_eth: HashMap = HashMap::new(); - static_attributes_usdc_eth.insert("key_lp_fee".into(), pool_fee_usdc_eth); - static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth); - - let pool_fee_eth_pepe = Bytes::from(BigInt::from(25000).to_signed_bytes_be()); - let tick_spacing_eth_pepe = Bytes::from(BigInt::from(500).to_signed_bytes_be()); - let mut static_attributes_eth_pepe: HashMap = HashMap::new(); - static_attributes_eth_pepe.insert("key_lp_fee".into(), pool_fee_eth_pepe); - static_attributes_eth_pepe.insert("tick_spacing".into(), tick_spacing_eth_pepe); - - let swap_usdc_eth = Swap { - component: ProtocolComponent { - id: "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d" - .to_string(), - protocol_system: "uniswap_v4".to_string(), - static_attributes: static_attributes_usdc_eth, - ..Default::default() - }, - token_in: usdc.clone(), - token_out: eth.clone(), - split: 0f64, - }; - - let swap_eth_pepe = Swap { - component: ProtocolComponent { - id: "0xecd73ecbf77219f21f129c8836d5d686bbc27d264742ddad620500e3e548e2c9" - .to_string(), - protocol_system: "uniswap_v4".to_string(), - static_attributes: static_attributes_eth_pepe, - ..Default::default() - }, - token_in: eth.clone(), - token_out: pepe.clone(), - split: 0f64, - }; - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SingleSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFromPermit2, - Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), - ) - .unwrap(); - let solution = Solution { - exact_out: false, - given_token: usdc, - given_amount: BigUint::from_str("1000_000000").unwrap(), - checked_token: pepe, - checked_amount: BigUint::from_str("97191013220606467325121599").unwrap(), - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap_usdc_eth, swap_eth_pepe], - ..Default::default() - }; - - let mut encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - let permit = get_permit(router_address(), &solution); - encoded_solution.permit = Some(permit); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFromPermit2, - eth, - Some(get_signer()), - ) - .unwrap() - .data; - - let expected_input = [ - "30ace1b1", // Function selector (single swap) - "000000000000000000000000000000000000000000000000000000003b9aca00", // amount in - "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in - "0000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933", // token out - "0000000000000000000000000000000000000000005064ff624d54346285543f", // min amount out - "0000000000000000000000000000000000000000000000000000000000000000", // wrap - "0000000000000000000000000000000000000000000000000000000000000000", // unwrap - "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - ] - .join(""); - - // after this there is the permit and because of the deadlines (that depend on block - // time) it's hard to assert - - let expected_swaps = String::from(concat!( - // length of ple encoded swaps without padding - "0000000000000000000000000000000000000000000000000000000000000086", - // Swap data header - "f62849f9a0b5bf2913b396098f7c7019b51a820a", // executor address - // Protocol data - "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // group token in - "6982508145454ce325ddbe47a25d4ec3d2311933", // group token in "00", // zero2one - "00", // transfer type TransferFrom - "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver - // First pool params - "0000000000000000000000000000000000000000", // intermediary token (ETH) - "000bb8", // fee - "00003c", // tick spacing - // Second pool params - "6982508145454ce325ddbe47a25d4ec3d2311933", // intermediary token (PEPE) - "0061a8", // fee - "0001f4", // tick spacing - "0000000000000000000000000000000000000000000000000000" // padding - )); + "01", // transfer type Transfer + ] + .join(""); - let hex_calldata = encode(&calldata); - - assert_eq!(hex_calldata[..456], expected_input); - assert_eq!(hex_calldata[1224..], expected_swaps); - write_calldata_to_file( - "test_single_encoding_strategy_usv4_grouped_swap", - hex_calldata.as_str(), - ); - } - - #[test] - fn test_single_encoding_strategy_curve() { - // UWU ──(curve 2 crypto pool)──> WETH - - let token_in = Bytes::from("0x55C08ca52497e2f1534B59E2917BF524D4765257"); // UWU - let token_out = Bytes::from("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); // WETH - - let static_attributes = HashMap::from([( - "factory".to_string(), - Bytes::from( - "0x98ee851a00abee0d95d08cf4ca2bdce32aeaaf7f" - .as_bytes() - .to_vec(), - )), - ("coins".to_string(), Bytes::from_str("0x5b22307863303261616133396232323366653864306130653563346632376561643930383363373536636332222c22307835356330386361353234393765326631353334623539653239313762663532346434373635323537225d").unwrap()), - ]); - - let component = ProtocolComponent { - id: String::from("0x77146B0a1d08B6844376dF6d9da99bA7F1b19e71"), - protocol_system: String::from("vm:curve"), - static_attributes, - ..Default::default() - }; - - let swap = Swap { - component, - token_in: token_in.clone(), - token_out: token_out.clone(), - split: 0f64, - }; - - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SingleSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFrom, - router_address(), - ) - .unwrap(); - - let solution = Solution { - exact_out: false, - given_token: token_in, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: token_out, - checked_amount: BigUint::from_str("1").unwrap(), - // Alice - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file("test_single_encoding_strategy_curve", hex_calldata.as_str()); - } - - #[test] - fn test_single_encoding_strategy_curve_st_eth() { - // ETH ──(curve stETH pool)──> STETH - - let token_in = Bytes::from("0x0000000000000000000000000000000000000000"); // ETH - let token_out = Bytes::from("0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"); // STETH - - let static_attributes = HashMap::from([( - "factory".to_string(), - Bytes::from( - "0x0000000000000000000000000000000000000000" - .as_bytes() - .to_vec(), - ), - ), - ("coins".to_string(), Bytes::from_str("0x5b22307865656565656565656565656565656565656565656565656565656565656565656565656565656565222c22307861653761623936353230646533613138653565313131623565616162303935333132643766653834225d").unwrap()),]); - - let component = ProtocolComponent { - id: String::from("0xDC24316b9AE028F1497c275EB9192a3Ea0f67022"), - protocol_system: String::from("vm:curve"), - static_attributes, - ..Default::default() - }; - - let swap = Swap { - component, - token_in: token_in.clone(), - token_out: token_out.clone(), - split: 0f64, - }; - - let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = SingleSwapStrategyEncoder::new( - eth_chain(), - swap_encoder_registry, - UserTransferType::TransferFrom, - router_address(), - ) - .unwrap(); - - let solution = Solution { - exact_out: false, - given_token: token_in, - given_amount: BigUint::from_str("1_000000000000000000").unwrap(), - checked_token: token_out, - checked_amount: BigUint::from_str("1").unwrap(), - // Alice - sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - swaps: vec![swap], - ..Default::default() - }; - - let encoded_solution = encoder - .encode_strategy(solution.clone()) - .unwrap(); - let calldata = encode_tycho_router_call( - eth_chain().id, - encoded_solution, - &solution, - UserTransferType::TransferFrom, - eth(), - None, - ) - .unwrap() - .data; - - let hex_calldata = encode(&calldata); - write_calldata_to_file( - "test_single_encoding_strategy_curve_st_eth", - hex_calldata.as_str(), + assert_eq!(hex_calldata, expected_swaps); + assert_eq!( + encoded_solution.selector, + "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)" + .to_string() ); + assert_eq!(encoded_solution.interacting_with, router_address()); } } } diff --git a/src/encoding/evm/tycho_encoders.rs b/src/encoding/evm/tycho_encoders.rs index 496ef76..5b96f9b 100644 --- a/src/encoding/evm/tycho_encoders.rs +++ b/src/encoding/evm/tycho_encoders.rs @@ -458,10 +458,29 @@ mod tests { } } + fn router_address() -> Bytes { + Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap() + } + + fn eth_chain() -> Chain { + TychoCommonChain::Ethereum.into() + } + fn get_swap_encoder_registry() -> SwapEncoderRegistry { SwapEncoderRegistry::new( Some("config/test_executor_addresses.json".to_string()), - TychoCommonChain::Ethereum.into(), + eth_chain(), + ) + .unwrap() + } + + fn get_tycho_router_encoder(user_transfer_type: UserTransferType) -> TychoRouterEncoder { + TychoRouterEncoder::new( + eth_chain(), + get_swap_encoder_registry(), + router_address(), + user_transfer_type, + None, ) .unwrap() } @@ -469,20 +488,10 @@ mod tests { mod router_encoder { use super::*; - fn get_tycho_router_encoder() -> TychoRouterEncoder { - TychoRouterEncoder::new( - TychoCommonChain::Ethereum.into(), - get_swap_encoder_registry(), - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), - UserTransferType::TransferFrom, - None, - ) - .unwrap() - } #[test] #[allow(deprecated)] fn test_encode_router_calldata_single_swap() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let eth_amount_in = BigUint::from(1000u32); let swap = Swap { component: ProtocolComponent { @@ -522,7 +531,7 @@ mod tests { #[test] #[allow(deprecated)] fn test_encode_router_calldata_single_swap_group() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let solution = Solution { exact_out: false, given_token: usdc(), @@ -546,7 +555,7 @@ mod tests { #[test] #[allow(deprecated)] fn test_encode_router_calldata_sequential_swap() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let eth_amount_in = BigUint::from(1000u32); let swap_weth_dai = Swap { component: ProtocolComponent { @@ -593,7 +602,7 @@ mod tests { #[test] fn test_validate_fails_for_exact_out() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let solution = Solution { exact_out: true, // This should cause an error ..Default::default() @@ -611,7 +620,7 @@ mod tests { #[test] fn test_validate_passes_for_wrap() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let swap = Swap { component: ProtocolComponent { id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), @@ -639,7 +648,7 @@ mod tests { #[test] fn test_validate_fails_for_wrap_wrong_input() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let swap = Swap { component: ProtocolComponent { id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), @@ -672,7 +681,7 @@ mod tests { #[test] fn test_validate_fails_for_wrap_wrong_first_swap() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let swap = Swap { component: ProtocolComponent { id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), @@ -705,7 +714,7 @@ mod tests { #[test] fn test_validate_fails_no_swaps() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let solution = Solution { exact_out: false, given_token: eth(), @@ -725,7 +734,7 @@ mod tests { #[test] fn test_validate_passes_for_unwrap() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let swap = Swap { component: ProtocolComponent { id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), @@ -752,7 +761,7 @@ mod tests { #[test] fn test_validate_fails_for_unwrap_wrong_output() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let swap = Swap { component: ProtocolComponent { id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), @@ -786,7 +795,7 @@ mod tests { #[test] fn test_validate_fails_for_unwrap_wrong_last_swap() { - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let swap = Swap { component: ProtocolComponent { id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), @@ -824,7 +833,7 @@ mod tests { // DAI - -> DAI // 50% -> WETH // (some of the pool addresses in this test are fake) - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let swaps = vec![ Swap { component: ProtocolComponent { @@ -876,7 +885,7 @@ mod tests { // This test should fail because the cyclical swap is not the first and last token // DAI -> WETH -> USDC -> DAI -> WBTC // (some of the pool addresses in this test are fake) - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let swaps = vec![ Swap { component: ProtocolComponent { @@ -945,7 +954,7 @@ mod tests { // WETH -> DAI // -> WETH // (some of the pool addresses in this test are fake) - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let swaps = vec![ Swap { component: ProtocolComponent { @@ -997,7 +1006,7 @@ mod tests { // This validation fails because there is a native action with a valid cyclical swap // ETH -> WETH -> DAI -> WETH // (some of the pool addresses in this test are fake) - let encoder = get_tycho_router_encoder(); + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); let swaps = vec![ Swap { component: ProtocolComponent { @@ -1208,4 +1217,2181 @@ mod tests { ); } } + + mod integration { + use std::{collections::HashMap, str::FromStr}; + + use alloy::{hex::encode, signers::local::PrivateKeySigner}; + use alloy_primitives::{hex, Address, B256, U256}; + use alloy_sol_types::SolValue; + use num_bigint::{BigInt, BigUint}; + use tycho_common::{models::protocol::ProtocolComponent, Bytes}; + + use super::*; + use crate::encoding::evm::utils::{biguint_to_u256, write_calldata_to_file}; + + fn get_signer() -> PrivateKeySigner { + // Set up a mock private key for signing (Alice's pk in our contract tests) + let private_key = + "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); + + let pk = B256::from_str(&private_key).unwrap(); + PrivateKeySigner::from_bytes(&pk).unwrap() + } + + mod single { + use super::*; + + #[test] + fn test_single_swap_strategy_encoder() { + // Performs a single swap from WETH to DAI on a USV2 pool, with no grouping + // optimizations. + let checked_amount = BigUint::from_str("2018817438608734439720").unwrap(); + let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); + let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); + + let swap = Swap { + component: ProtocolComponent { + id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth.clone(), + token_out: dai.clone(), + split: 0f64, + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: weth, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: dai, + checked_amount: checked_amount.clone(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap], + ..Default::default() + }; + + let encoded_solutions = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solutions[0].clone(), + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; + let expected_min_amount_encoded = + encode(U256::abi_encode(&biguint_to_u256(&checked_amount))); + let expected_input = [ + "30ace1b1", // Function selector + "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in + "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out + &expected_min_amount_encoded, // min amount out + "0000000000000000000000000000000000000000000000000000000000000000", // wrap + "0000000000000000000000000000000000000000000000000000000000000000", // unwrap + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + ] + .join(""); + + // after this there is the permit and because of the deadlines (that depend on block + // time) it's hard to assert + + let expected_swap = String::from(concat!( + // length of encoded swap without padding + "0000000000000000000000000000000000000000000000000000000000000052", + // Swap data + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id + "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + "00", // zero2one + "00", // transfer type TransferFrom + "0000000000000000000000000000", // padding + )); + let hex_calldata = encode(&calldata); + + assert_eq!(hex_calldata[..456], expected_input); + assert_eq!(hex_calldata[1224..], expected_swap); + write_calldata_to_file( + "test_single_swap_strategy_encoder", + &hex_calldata.to_string(), + ); + } + + #[test] + fn test_single_swap_strategy_encoder_no_permit2() { + // Performs a single swap from WETH to DAI on a USV2 pool, without permit2 and no + // grouping optimizations. + + let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); + let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); + + let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap(); + let expected_min_amount = U256::from_str("1_640_000000000000000000").unwrap(); + + let swap = Swap { + component: ProtocolComponent { + id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth.clone(), + token_out: dai.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: weth, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: dai, + checked_amount, + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; + let expected_min_amount_encoded = encode(U256::abi_encode(&expected_min_amount)); + let expected_input = [ + "5c4b639c", // Function selector + "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in + "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out + &expected_min_amount_encoded, // min amount out + "0000000000000000000000000000000000000000000000000000000000000000", // wrap + "0000000000000000000000000000000000000000000000000000000000000000", // unwrap + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + "0000000000000000000000000000000000000000000000000000000000000001", // transfer from needed + "0000000000000000000000000000000000000000000000000000000000000120", // offset of swap bytes + "0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding + + // Swap data + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id + "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + "00", // zero2one + "00", // transfer type TransferFrom + "0000000000000000000000000000", // padding + ] + .join(""); + + let hex_calldata = encode(&calldata); + + assert_eq!(hex_calldata, expected_input); + write_calldata_to_file( + "test_single_swap_strategy_encoder_no_permit2", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_single_swap_strategy_encoder_no_transfer_in() { + // Performs a single swap from WETH to DAI on a USV2 pool assuming that the tokens + // are already in the router + + let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); + let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); + + let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap(); + let expected_min_amount = U256::from_str("1_640_000000000000000000").unwrap(); + + let swap = Swap { + component: ProtocolComponent { + id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth.clone(), + token_out: dai.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::None); + + let solution = Solution { + exact_out: false, + given_token: weth, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: dai, + checked_amount, + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::None, + eth(), + None, + ) + .unwrap() + .data; + let expected_min_amount_encoded = encode(U256::abi_encode(&expected_min_amount)); + let expected_input = [ + "5c4b639c", // Function selector + "0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in + "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out + &expected_min_amount_encoded, // min amount out + "0000000000000000000000000000000000000000000000000000000000000000", // wrap + "0000000000000000000000000000000000000000000000000000000000000000", // unwrap + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + "0000000000000000000000000000000000000000000000000000000000000000", // transfer from not needed + "0000000000000000000000000000000000000000000000000000000000000120", // offset of swap bytes + "0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding + + // Swap data + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id + "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + "00", // zero2one + "01", // transfer type Transfer + "0000000000000000000000000000", // padding + ] + .join(""); + + let hex_calldata = encode(&calldata); + + assert_eq!(hex_calldata, expected_input); + write_calldata_to_file( + "test_single_swap_strategy_encoder_no_transfer_in", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_single_swap_strategy_encoder_wrap() { + // Performs a single swap from WETH to DAI on a USV2 pool, wrapping ETH + // Note: This test does not assert anything. It is only used to obtain integration + // test data for our router solidity test. + + let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); + + let swap = Swap { + component: ProtocolComponent { + id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth(), + token_out: dai.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: eth(), + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: dai, + checked_amount: BigUint::from_str("1659881924818443699787").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap], + native_action: Some(NativeAction::Wrap), + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); + write_calldata_to_file( + "test_single_swap_strategy_encoder_wrap", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_single_swap_strategy_encoder_unwrap() { + // Performs a single swap from DAI to WETH on a USV2 pool, unwrapping ETH at the end + // Note: This test does not assert anything. It is only used to obtain integration + // test data for our router solidity test. + + let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); + + let swap = Swap { + component: ProtocolComponent { + id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: dai.clone(), + token_out: weth(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: dai, + given_amount: BigUint::from_str("3_000_000000000000000000").unwrap(), + checked_token: eth(), + checked_amount: BigUint::from_str("1_000000000000000000").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap], + native_action: Some(NativeAction::Unwrap), + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file( + "test_single_swap_strategy_encoder_unwrap", + hex_calldata.as_str(), + ); + } + } + + mod sequential { + use super::*; + + #[test] + fn test_sequential_swap_strategy_encoder() { + // Note: This test does not assert anything. It is only used to obtain integration + // test data for our router solidity test. + // + // Performs a sequential swap from WETH to USDC though WBTC using USV2 pools + // + // WETH ───(USV2)──> WBTC ───(USV2)──> USDC + + let weth = weth(); + let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); + let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + let swap_weth_wbtc = Swap { + component: ProtocolComponent { + id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth.clone(), + token_out: wbtc.clone(), + split: 0f64, + }; + let swap_wbtc_usdc = Swap { + component: ProtocolComponent { + id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: wbtc.clone(), + token_out: usdc.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: weth, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: usdc, + checked_amount: BigUint::from_str("26173932").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file( + "test_sequential_swap_strategy_encoder", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_sequential_swap_strategy_encoder_no_permit2() { + // Performs a sequential swap from WETH to USDC though WBTC using USV2 pools + // + // WETH ───(USV2)──> WBTC ───(USV2)──> USDC + + let weth = weth(); + let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); + let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + let swap_weth_wbtc = Swap { + component: ProtocolComponent { + id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth.clone(), + token_out: wbtc.clone(), + split: 0f64, + }; + let swap_wbtc_usdc = Swap { + component: ProtocolComponent { + id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: wbtc.clone(), + token_out: usdc.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: weth, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: usdc, + checked_amount: BigUint::from_str("26173932").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + + let expected = String::from(concat!( + "e21dd0d3", /* function selector */ + "0000000000000000000000000000000000000000000000000de0b6b3a7640000", /* amount in */ + "000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token ou + "00000000000000000000000000000000000000000000000000000000018f61ec", /* min amount out */ + "0000000000000000000000000000000000000000000000000000000000000000", // wrap + "0000000000000000000000000000000000000000000000000000000000000000", // unwrap + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + "0000000000000000000000000000000000000000000000000000000000000001", /* transfer from needed */ + "0000000000000000000000000000000000000000000000000000000000000120", /* length ple + * encode */ + "00000000000000000000000000000000000000000000000000000000000000a8", + // swap 1 + "0052", // swap length + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "bb2b8038a1640196fbe3e38816f3e67cba72d940", // component id + "004375dff511095cc5a197a54140a24efef3a416", // receiver (next pool) + "00", // zero to one + "00", // transfer type TransferFrom + // swap 2 + "0052", // swap length + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address + "2260fac5e5542a773aa44fbcfedf7c193bc2c599", // token in + "004375dff511095cc5a197a54140a24efef3a416", // component id + "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver (final user) + "01", // zero to one + "02", // transfer type None + "000000000000000000000000000000000000000000000000", // padding + )); + + assert_eq!(hex_calldata, expected); + write_calldata_to_file( + "test_sequential_swap_strategy_encoder_no_permit2", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_sequential_strategy_cyclic_swap() { + // This test has start and end tokens that are the same + // The flow is: + // USDC -> WETH -> USDC using two pools + + let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); + let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + // Create two Uniswap V3 pools for the cyclic swap + // USDC -> WETH (Pool 1) + let swap_usdc_weth = Swap { + component: ProtocolComponent { + id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* USDC-WETH USV3 + * Pool 1 */ + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(500).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: usdc.clone(), + token_out: weth.clone(), + split: 0f64, + }; + + // WETH -> USDC (Pool 2) + let swap_weth_usdc = Swap { + component: ProtocolComponent { + id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), /* USDC-WETH USV3 + * Pool 2 */ + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(3000).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: weth.clone(), + token_out: usdc.clone(), + split: 0f64, + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: usdc.clone(), + given_amount: BigUint::from_str("100000000").unwrap(), // 100 USDC (6 decimals) + checked_token: usdc.clone(), + checked_amount: BigUint::from_str("99389294").unwrap(), /* Expected output + * from test */ + swaps: vec![swap_usdc_weth, swap_weth_usdc], + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; + let hex_calldata = hex::encode(&calldata); + let expected_input = [ + "51bcc7b6", // selector + "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token + "0000000000000000000000000000000000000000000000000000000005ec8f6e", // min amount out + "0000000000000000000000000000000000000000000000000000000000000000", // wrap action + "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + ] + .join(""); + + let expected_swaps = [ + "00000000000000000000000000000000000000000000000000000000000000d6", // length of ple encoded swaps without padding + "0069", // ple encoded swaps + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out + "0001f4", // pool fee + "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver + "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id + "01", // zero2one + "00", // transfer type TransferFrom + "0069", // ple encoded swaps + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out + "000bb8", // pool fee + "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id + "00", // zero2one + "01", // transfer type Transfer + "00000000000000000000", // padding + ] + .join(""); + + assert_eq!(hex_calldata[..456], expected_input); + assert_eq!(hex_calldata[1224..], expected_swaps); + write_calldata_to_file( + "test_sequential_strategy_cyclic_swap", + hex_calldata.as_str(), + ); + } + + mod optimized_transfers { + // In this module we test the ability to chain swaps or not. Different protocols are + // tested. The encoded data is used for solidity tests as well + use super::*; + + #[test] + fn test_uniswap_v3_uniswap_v2() { + // Note: This test does not assert anything. It is only used to obtain + // integration test data for our router solidity test. + // + // Performs a sequential swap from WETH to USDC though WBTC using USV3 and USV2 + // pools + // + // WETH ───(USV3)──> WBTC ───(USV2)──> USDC + + let weth = weth(); + let wbtc = + Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); + let usdc = + Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + let swap_weth_wbtc = Swap { + component: ProtocolComponent { + id: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD".to_string(), + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(3000).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: weth.clone(), + token_out: wbtc.clone(), + split: 0f64, + }; + let swap_wbtc_usdc = Swap { + component: ProtocolComponent { + id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: wbtc.clone(), + token_out: usdc.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: weth, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: usdc, + checked_amount: BigUint::from_str("26173932").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file("test_uniswap_v3_uniswap_v2", hex_calldata.as_str()); + } + + #[test] + fn test_uniswap_v3_uniswap_v3() { + // Note: This test does not assert anything. It is only used to obtain + // integration test data for our router solidity test. + // + // Performs a sequential swap from WETH to USDC though WBTC using USV3 pools + // There is no optimization between the two USV3 pools, this is currently not + // supported. + // + // WETH ───(USV3)──> WBTC ───(USV3)──> USDC + + let weth = weth(); + let wbtc = + Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); + let usdc = + Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + let swap_weth_wbtc = Swap { + component: ProtocolComponent { + id: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD".to_string(), + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(3000).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: weth.clone(), + token_out: wbtc.clone(), + split: 0f64, + }; + let swap_wbtc_usdc = Swap { + component: ProtocolComponent { + id: "0x99ac8cA7087fA4A2A1FB6357269965A2014ABc35".to_string(), + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(3000).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: wbtc.clone(), + token_out: usdc.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: weth, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: usdc, + checked_amount: BigUint::from_str("26173932").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file("test_uniswap_v3_uniswap_v3", hex_calldata.as_str()); + } + + #[test] + fn test_uniswap_v3_curve() { + // Note: This test does not assert anything. It is only used to obtain + // integration test data for our router solidity test. + // + // Performs a sequential swap from WETH to USDT though WBTC using USV3 and curve + // pools + // + // WETH ───(USV3)──> WBTC ───(curve)──> USDT + + let weth = weth(); + let wbtc = + Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); + let usdt = + Bytes::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap(); + + let swap_weth_wbtc = Swap { + component: ProtocolComponent { + id: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD".to_string(), + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(3000).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: weth.clone(), + token_out: wbtc.clone(), + split: 0f64, + }; + + let swap_wbtc_usdt = Swap { + component: ProtocolComponent { + id: String::from("0xD51a44d3FaE010294C616388b506AcdA1bfAAE46"), + protocol_system: String::from("vm:curve"), + static_attributes: { + let mut attrs: HashMap = HashMap::new(); + attrs.insert( + "factory".into(), + Bytes::from( + "0x0000000000000000000000000000000000000000" + .as_bytes() + .to_vec(), + ), + ); + attrs.insert( + "coins".into(), + Bytes::from_str("0x5b22307864616331376639353864326565353233613232303632303639393435393763313364383331656337222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307863303261616133396232323366653864306130653563346632376561643930383363373536636332225d") + .unwrap(), + ); + attrs + }, + ..Default::default() + }, + token_in: wbtc.clone(), + token_out: usdt.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: weth, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: usdt, + checked_amount: BigUint::from_str("26173932").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_weth_wbtc, swap_wbtc_usdt], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file("test_uniswap_v3_curve", hex_calldata.as_str()); + } + + #[test] + fn test_balancer_v2_uniswap_v2() { + // Note: This test does not assert anything. It is only used to obtain + // integration test data for our router solidity test. + // + // Performs a sequential swap from WETH to USDC though WBTC using balancer and + // USV2 pools + // + // WETH ───(balancer)──> WBTC ───(USV2)──> USDC + + let weth = weth(); + let wbtc = + Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); + let usdc = + Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + let swap_weth_wbtc = Swap { + component: ProtocolComponent { + id: + "0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e" + .to_string(), + protocol_system: "vm:balancer_v2".to_string(), + ..Default::default() + }, + token_in: weth.clone(), + token_out: wbtc.clone(), + split: 0f64, + }; + + let swap_wbtc_usdc = Swap { + component: ProtocolComponent { + id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: wbtc.clone(), + token_out: usdc.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: weth, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: usdc, + checked_amount: BigUint::from_str("26173932").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_weth_wbtc, swap_wbtc_usdc], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file("test_balancer_v2_uniswap_v2", hex_calldata.as_str()); + } + + #[test] + fn test_multi_protocol() { + // Note: This test does not assert anything. It is only used to obtain + // integration test data for our router solidity test. + // + // Performs the following swap: + // + // DAI ─(USV2)-> WETH ─(bal)─> WBTC ─(curve)─> USDT ─(ekubo)─> USDC ─(USV4)─> + // ETH + + let weth = weth(); + let eth = eth(); + let wbtc = + Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); + let usdc = + Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + let usdt = + Bytes::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap(); + let dai = + Bytes::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F").unwrap(); + + let usv2_swap_dai_weth = Swap { + component: ProtocolComponent { + id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: dai.clone(), + token_out: weth.clone(), + split: 0f64, + }; + + let balancer_swap_weth_wbtc = Swap { + component: ProtocolComponent { + id: + "0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e" + .to_string(), + protocol_system: "vm:balancer_v2".to_string(), + ..Default::default() + }, + token_in: weth.clone(), + token_out: wbtc.clone(), + split: 0f64, + }; + + let curve_swap_wbtc_usdt = Swap { + component: ProtocolComponent { + id: String::from("0xD51a44d3FaE010294C616388b506AcdA1bfAAE46"), + protocol_system: String::from("vm:curve"), + static_attributes: { + let mut attrs: HashMap = HashMap::new(); + attrs.insert( + "factory".into(), + Bytes::from( + "0x0000000000000000000000000000000000000000" + .as_bytes() + .to_vec(), + ), + ); + attrs.insert( + "coins".into(), + Bytes::from_str("0x5b22307864616331376639353864326565353233613232303632303639393435393763313364383331656337222c22307832323630666163356535353432613737336161343466626366656466376331393362633263353939222c22307863303261616133396232323366653864306130653563346632376561643930383363373536636332225d") + .unwrap(), + ); + attrs + }, + ..Default::default() + }, + token_in: wbtc.clone(), + token_out: usdt.clone(), + split: 0f64, + }; + + // Ekubo + + let component = ProtocolComponent { + // All Ekubo swaps go through the core contract - not necessary to specify + // pool id for test + protocol_system: "ekubo_v2".to_string(), + // 0.0025% fee & 0.005% base pool + static_attributes: HashMap::from([ + ("fee".to_string(), Bytes::from(461168601842738_u64)), + ("tick_spacing".to_string(), Bytes::from(50_u32)), + ("extension".to_string(), Bytes::zero(20)), + ]), + ..Default::default() + }; + let ekubo_swap_usdt_usdc = Swap { + component, + token_in: usdt.clone(), + token_out: usdc.clone(), + split: 0f64, + }; + + // USV4 + // Fee and tick spacing information for this test is obtained by querying the + // USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e + // Using the poolKeys function with the first 25 bytes of the pool id + let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be()); + let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be()); + let mut static_attributes_usdc_eth: HashMap = HashMap::new(); + static_attributes_usdc_eth.insert("key_lp_fee".into(), pool_fee_usdc_eth); + static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth); + + let usv4_swap_usdc_eth = Swap { + component: ProtocolComponent { + id: + "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d" + .to_string(), + protocol_system: "uniswap_v4".to_string(), + static_attributes: static_attributes_usdc_eth, + ..Default::default() + }, + token_in: usdc.clone(), + token_out: eth.clone(), + split: 0f64, + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + // Put all components together + let solution = Solution { + exact_out: false, + given_token: dai, + given_amount: BigUint::from_str("1500_000000000000000000").unwrap(), + checked_token: eth.clone(), + checked_amount: BigUint::from_str("732214216964381330").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![ + usv2_swap_dai_weth, + balancer_swap_weth_wbtc, + curve_swap_wbtc_usdt, + ekubo_swap_usdt_usdc, + usv4_swap_usdc_eth, + ], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth, + Some(get_signer()), + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file("test_multi_protocol", hex_calldata.as_str()); + } + } + } + + mod split { + use super::*; + + #[test] + fn test_split_swap_strategy_encoder() { + // Note: This test does not assert anything. It is only used to obtain integration + // test data for our router solidity test. + // + // Performs a split swap from WETH to USDC though WBTC and DAI using USV2 pools + // + // ┌──(USV2)──> WBTC ───(USV2)──> USDC + // WETH ─┤ + // └──(USV2)──> DAI ───(USV2)──> USDC + // + + let weth = weth(); + let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); + let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); + let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + let swap_weth_dai = Swap { + component: ProtocolComponent { + id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth.clone(), + token_out: dai.clone(), + split: 0.5f64, + }; + let swap_weth_wbtc = Swap { + component: ProtocolComponent { + id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth.clone(), + token_out: wbtc.clone(), + // This represents the remaining 50%, but to avoid any rounding errors we set + // this to 0 to signify "the remainder of the WETH value". + // It should still be very close to 50% + split: 0f64, + }; + let swap_dai_usdc = Swap { + component: ProtocolComponent { + id: "0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: dai.clone(), + token_out: usdc.clone(), + split: 0f64, + }; + let swap_wbtc_usdc = Swap { + component: ProtocolComponent { + id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: wbtc.clone(), + token_out: usdc.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: weth, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: usdc, + checked_amount: BigUint::from_str("26173932").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file("test_split_swap_strategy_encoder", hex_calldata.as_str()); + } + + #[test] + fn test_split_input_cyclic_swap() { + // This test has start and end tokens that are the same + // The flow is: + // ┌─ (USV3, 60% split) ──> WETH ─┐ + // │ │ + // USDC ──────┤ ├──(USV2)──> USDC + // │ │ + // └─ (USV3, 40% split) ──> WETH ─┘ + + let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); + let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + // USDC -> WETH (Pool 1) - 60% of input + let swap_usdc_weth_pool1 = Swap { + component: ProtocolComponent { + id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* USDC-WETH USV3 + * Pool 1 */ + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(500).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: usdc.clone(), + token_out: weth.clone(), + split: 0.6f64, // 60% of input + }; + + // USDC -> WETH (Pool 2) - 40% of input (remaining) + let swap_usdc_weth_pool2 = Swap { + component: ProtocolComponent { + id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), /* USDC-WETH USV3 + * Pool 2 */ + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(3000).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: usdc.clone(), + token_out: weth.clone(), + split: 0f64, // Remaining 40% + }; + + // WETH -> USDC (Pool 2) + let swap_weth_usdc_pool2 = Swap { + component: ProtocolComponent { + id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), /* USDC-WETH USV2 + * Pool 2 */ + protocol_system: "uniswap_v2".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(3000).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: weth.clone(), + token_out: usdc.clone(), + split: 0.0f64, + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: usdc.clone(), + given_amount: BigUint::from_str("100000000").unwrap(), // 100 USDC (6 decimals) + checked_token: usdc.clone(), + checked_amount: BigUint::from_str("99574171").unwrap(), /* Expected output + * from + * test */ + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_usdc_weth_pool1, swap_usdc_weth_pool2, swap_weth_usdc_pool2], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; + + let hex_calldata = hex::encode(&calldata); + let expected_input = [ + "7c553846", // selector + "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token + "0000000000000000000000000000000000000000000000000000000005ef619b", // min amount out + "0000000000000000000000000000000000000000000000000000000000000000", // wrap action + "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action + "0000000000000000000000000000000000000000000000000000000000000002", // tokens length + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + ] + .join(""); + let expected_swaps = [ + "0000000000000000000000000000000000000000000000000000000000000139", // length of ple encoded swaps without padding + "006e", // ple encoded swaps + "00", // token in index + "01", // token out index + "999999", // split + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out + "0001f4", // pool fee + "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver + "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id + "01", // zero2one + "00", // transfer type TransferFrom + "006e", // ple encoded swaps + "00", // token in index + "01", // token out index + "000000", // split + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out + "000bb8", // pool fee + "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver + "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id + "01", // zero2one + "00", // transfer type TransferFrom + "0057", // ple encoded swaps + "01", // token in index + "00", // token out index + "000000", // split + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address, + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id, + "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + "00", // zero2one + "01", // transfer type Transfer + "00000000000000" // padding + ] + .join(""); + assert_eq!(hex_calldata[..520], expected_input); + assert_eq!(hex_calldata[1288..], expected_swaps); + write_calldata_to_file("test_split_input_cyclic_swap", hex_calldata.as_str()); + } + + #[test] + fn test_split_output_cyclic_swap() { + // This test has start and end tokens that are the same + // The flow is: + // ┌─── (USV3, 60% split) ───┐ + // │ │ + // USDC ──(USV2) ── WETH──| ├─> USDC + // │ │ + // └─── (USV3, 40% split) ───┘ + + let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); + let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + let swap_usdc_weth_v2 = Swap { + component: ProtocolComponent { + id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), /* USDC-WETH USV2 */ + protocol_system: "uniswap_v2".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(500).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: usdc.clone(), + token_out: weth.clone(), + split: 0.0f64, + }; + + let swap_weth_usdc_v3_pool1 = Swap { + component: ProtocolComponent { + id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* USDC-WETH USV3 + * Pool 1 */ + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(500).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: weth.clone(), + token_out: usdc.clone(), + split: 0.6f64, + }; + + let swap_weth_usdc_v3_pool2 = Swap { + component: ProtocolComponent { + id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), /* USDC-WETH USV3 + * Pool 2 */ + protocol_system: "uniswap_v3".to_string(), + static_attributes: { + let mut attrs = HashMap::new(); + attrs.insert( + "fee".to_string(), + Bytes::from(BigInt::from(3000).to_signed_bytes_be()), + ); + attrs + }, + ..Default::default() + }, + token_in: weth.clone(), + token_out: usdc.clone(), + split: 0.0f64, + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: usdc.clone(), + given_amount: BigUint::from_str("100000000").unwrap(), // 100 USDC (6 decimals) + checked_token: usdc.clone(), + checked_amount: BigUint::from_str("99025908").unwrap(), /* Expected output + * from + * test */ + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![ + swap_usdc_weth_v2, + swap_weth_usdc_v3_pool1, + swap_weth_usdc_v3_pool2, + ], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth(), + Some(get_signer()), + ) + .unwrap() + .data; + + let hex_calldata = hex::encode(&calldata); + let expected_input = [ + "7c553846", // selector + "0000000000000000000000000000000000000000000000000000000005f5e100", // given amount + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token + "0000000000000000000000000000000000000000000000000000000005e703f4", // min amount out + "0000000000000000000000000000000000000000000000000000000000000000", // wrap action + "0000000000000000000000000000000000000000000000000000000000000000", // unwrap action + "0000000000000000000000000000000000000000000000000000000000000002", // tokens length + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + ] + .join(""); + + let expected_swaps = [ + "0000000000000000000000000000000000000000000000000000000000000139", // length of ple encoded swaps without padding + "0057", // ple encoded swaps + "00", // token in index + "01", // token out index + "000000", // split + "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in + "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id + "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver + "01", // zero2one + "00", // transfer type TransferFrom + "006e", // ple encoded swaps + "01", // token in index + "00", // token out index + "999999", // split + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out + "0001f4", // pool fee + "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id + "00", // zero2one + "01", // transfer type Transfer + "006e", // ple encoded swaps + "01", // token in index + "00", // token out index + "000000", // split + "2e234dae75c793f67a35089c9d99245e1c58470b", // executor address + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out + "000bb8", // pool fee + "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id + "00", // zero2one + "01", // transfer type Transfer + "00000000000000" // padding + ] + .join(""); + + assert_eq!(hex_calldata[..520], expected_input); + assert_eq!(hex_calldata[1288..], expected_swaps); + write_calldata_to_file("test_split_output_cyclic_swap", hex_calldata.as_str()); + } + } + + mod protocol_integration { + // in this module we test protocol specific logic by creating the calldata that then is + // used in the solidity tests + use super::*; + + #[test] + fn test_single_encoding_strategy_ekubo() { + // ETH ──(EKUBO)──> USDC + + let token_in = Bytes::from(Address::ZERO.as_slice()); + let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC + + let static_attributes = HashMap::from([ + ("fee".to_string(), Bytes::from(0_u64)), + ("tick_spacing".to_string(), Bytes::from(0_u32)), + ( + "extension".to_string(), + Bytes::from("0x51d02a5948496a67827242eabc5725531342527c"), + ), /* Oracle */ + ]); + + let component = ProtocolComponent { + // All Ekubo swaps go through the core contract - not necessary to specify pool + // id for test + protocol_system: "ekubo_v2".to_string(), + static_attributes, + ..Default::default() + }; + + let swap = Swap { + component, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: token_in, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: token_out, + checked_amount: BigUint::from_str("1000").unwrap(), + // Alice + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); + write_calldata_to_file( + "test_single_encoding_strategy_ekubo", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_single_encoding_strategy_maverick() { + // GHO -> (maverick) -> USDC + let maverick_pool = ProtocolComponent { + id: String::from("0x14Cf6D2Fe3E1B326114b07d22A6F6bb59e346c67"), + protocol_system: String::from("vm:maverick_v2"), + ..Default::default() + }; + let token_in = Bytes::from("0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f"); + let token_out = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); + let swap = Swap { + component: maverick_pool, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: token_in, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: token_out, + checked_amount: BigUint::from_str("1000").unwrap(), + // Alice + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); + write_calldata_to_file( + "test_single_encoding_strategy_maverick", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_single_encoding_strategy_usv4_eth_in() { + // Performs a single swap from ETH to PEPE using a USV4 pool + // Note: This test does not assert anything. It is only used to obtain integration + // test data for our router solidity test. + // + // ETH ───(USV4)──> PEPE + // + let eth = eth(); + let pepe = Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap(); + + let pool_fee_eth_pepe = Bytes::from(BigInt::from(25000).to_signed_bytes_be()); + let tick_spacing_eth_pepe = Bytes::from(BigInt::from(500).to_signed_bytes_be()); + let mut static_attributes_eth_pepe: HashMap = HashMap::new(); + static_attributes_eth_pepe.insert("key_lp_fee".into(), pool_fee_eth_pepe); + static_attributes_eth_pepe.insert("tick_spacing".into(), tick_spacing_eth_pepe); + + let swap_eth_pepe = Swap { + component: ProtocolComponent { + id: "0xecd73ecbf77219f21f129c8836d5d686bbc27d264742ddad620500e3e548e2c9" + .to_string(), + protocol_system: "uniswap_v4".to_string(), + static_attributes: static_attributes_eth_pepe, + ..Default::default() + }, + token_in: eth.clone(), + token_out: pepe.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: eth.clone(), + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: pepe, + checked_amount: BigUint::from_str("152373460199848577067005852").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_eth_pepe], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth, + Some(get_signer()), + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); + + write_calldata_to_file( + "test_single_encoding_strategy_usv4_eth_in", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_single_encoding_strategy_usv4_eth_out() { + // Performs a single swap from USDC to ETH using a USV4 pool + // Note: This test does not assert anything. It is only used to obtain integration + // test data for our router solidity test. + // + // USDC ───(USV4)──> ETH + // + let eth = eth(); + let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + + // Fee and tick spacing information for this test is obtained by querying the + // USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e + // Using the poolKeys function with the first 25 bytes of the pool id + let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be()); + let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be()); + let mut static_attributes_usdc_eth: HashMap = HashMap::new(); + static_attributes_usdc_eth.insert("key_lp_fee".into(), pool_fee_usdc_eth); + static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth); + + let swap_usdc_eth = Swap { + component: ProtocolComponent { + id: "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d" + .to_string(), + protocol_system: "uniswap_v4".to_string(), + static_attributes: static_attributes_usdc_eth, + ..Default::default() + }, + token_in: usdc.clone(), + token_out: eth.clone(), + split: 0f64, + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: usdc, + given_amount: BigUint::from_str("3000_000000").unwrap(), + checked_token: eth.clone(), + checked_amount: BigUint::from_str("1117254495486192350").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_usdc_eth], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth, + Some(get_signer()), + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file( + "test_single_encoding_strategy_usv4_eth_out", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_single_encoding_strategy_usv4_grouped_swap() { + // Performs a sequential swap from USDC to PEPE though ETH using two consecutive + // USV4 pools + // + // USDC ──(USV4)──> ETH ───(USV4)──> PEPE + // + + let eth = eth(); + let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); + let pepe = Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap(); + + // Fee and tick spacing information for this test is obtained by querying the + // USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e + // Using the poolKeys function with the first 25 bytes of the pool id + let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be()); + let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be()); + let mut static_attributes_usdc_eth: HashMap = HashMap::new(); + static_attributes_usdc_eth.insert("key_lp_fee".into(), pool_fee_usdc_eth); + static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth); + + let pool_fee_eth_pepe = Bytes::from(BigInt::from(25000).to_signed_bytes_be()); + let tick_spacing_eth_pepe = Bytes::from(BigInt::from(500).to_signed_bytes_be()); + let mut static_attributes_eth_pepe: HashMap = HashMap::new(); + static_attributes_eth_pepe.insert("key_lp_fee".into(), pool_fee_eth_pepe); + static_attributes_eth_pepe.insert("tick_spacing".into(), tick_spacing_eth_pepe); + + let swap_usdc_eth = Swap { + component: ProtocolComponent { + id: "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d" + .to_string(), + protocol_system: "uniswap_v4".to_string(), + static_attributes: static_attributes_usdc_eth, + ..Default::default() + }, + token_in: usdc.clone(), + token_out: eth.clone(), + split: 0f64, + }; + + let swap_eth_pepe = Swap { + component: ProtocolComponent { + id: "0xecd73ecbf77219f21f129c8836d5d686bbc27d264742ddad620500e3e548e2c9" + .to_string(), + protocol_system: "uniswap_v4".to_string(), + static_attributes: static_attributes_eth_pepe, + ..Default::default() + }, + token_in: eth.clone(), + token_out: pepe.clone(), + split: 0f64, + }; + let encoder = get_tycho_router_encoder(UserTransferType::TransferFromPermit2); + + let solution = Solution { + exact_out: false, + given_token: usdc, + given_amount: BigUint::from_str("1000_000000").unwrap(), + checked_token: pepe, + checked_amount: BigUint::from_str("97191013220606467325121599").unwrap(), + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap_usdc_eth, swap_eth_pepe], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFromPermit2, + eth, + Some(get_signer()), + ) + .unwrap() + .data; + + let expected_input = [ + "30ace1b1", // Function selector (single swap) + "000000000000000000000000000000000000000000000000000000003b9aca00", // amount in + "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in + "0000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933", // token out + "0000000000000000000000000000000000000000005064ff624d54346285543f", // min amount out + "0000000000000000000000000000000000000000000000000000000000000000", // wrap + "0000000000000000000000000000000000000000000000000000000000000000", // unwrap + "000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + ] + .join(""); + + // after this there is the permit and because of the deadlines (that depend on block + // time) it's hard to assert + + let expected_swaps = String::from(concat!( + // length of ple encoded swaps without padding + "0000000000000000000000000000000000000000000000000000000000000086", + // Swap data header + "f62849f9a0b5bf2913b396098f7c7019b51a820a", // executor address + // Protocol data + "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // group token in + "6982508145454ce325ddbe47a25d4ec3d2311933", // group token in + "00", // zero2one + "00", // transfer type TransferFrom + "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver + // First pool params + "0000000000000000000000000000000000000000", // intermediary token (ETH) + "000bb8", // fee + "00003c", // tick spacing + // Second pool params + "6982508145454ce325ddbe47a25d4ec3d2311933", // intermediary token (PEPE) + "0061a8", // fee + "0001f4", // tick spacing + "0000000000000000000000000000000000000000000000000000" // padding + )); + + let hex_calldata = encode(&calldata); + + assert_eq!(hex_calldata[..456], expected_input); + assert_eq!(hex_calldata[1224..], expected_swaps); + write_calldata_to_file( + "test_single_encoding_strategy_usv4_grouped_swap", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_single_encoding_strategy_curve() { + // UWU ──(curve 2 crypto pool)──> WETH + + let token_in = Bytes::from("0x55C08ca52497e2f1534B59E2917BF524D4765257"); // UWU + let token_out = Bytes::from("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); // WETH + + let static_attributes = HashMap::from([( + "factory".to_string(), + Bytes::from( + "0x98ee851a00abee0d95d08cf4ca2bdce32aeaaf7f" + .as_bytes() + .to_vec(), + )), + ("coins".to_string(), Bytes::from_str("0x5b22307863303261616133396232323366653864306130653563346632376561643930383363373536636332222c22307835356330386361353234393765326631353334623539653239313762663532346434373635323537225d").unwrap()), + ]); + + let component = ProtocolComponent { + id: String::from("0x77146B0a1d08B6844376dF6d9da99bA7F1b19e71"), + protocol_system: String::from("vm:curve"), + static_attributes, + ..Default::default() + }; + + let swap = Swap { + component, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: token_in, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: token_out, + checked_amount: BigUint::from_str("1").unwrap(), + // Alice + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file( + "test_single_encoding_strategy_curve", + hex_calldata.as_str(), + ); + } + + #[test] + fn test_single_encoding_strategy_curve_st_eth() { + // ETH ──(curve stETH pool)──> STETH + + let token_in = Bytes::from("0x0000000000000000000000000000000000000000"); // ETH + let token_out = Bytes::from("0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"); // STETH + + let static_attributes = HashMap::from([( + "factory".to_string(), + Bytes::from( + "0x0000000000000000000000000000000000000000" + .as_bytes() + .to_vec(), + ), + ), + ("coins".to_string(), Bytes::from_str("0x5b22307865656565656565656565656565656565656565656565656565656565656565656565656565656565222c22307861653761623936353230646533613138653565313131623565616162303935333132643766653834225d").unwrap()),]); + + let component = ProtocolComponent { + id: String::from("0xDC24316b9AE028F1497c275EB9192a3Ea0f67022"), + protocol_system: String::from("vm:curve"), + static_attributes, + ..Default::default() + }; + + let swap = Swap { + component, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + }; + + let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom); + + let solution = Solution { + exact_out: false, + given_token: token_in, + given_amount: BigUint::from_str("1_000000000000000000").unwrap(), + checked_token: token_out, + checked_amount: BigUint::from_str("1").unwrap(), + // Alice + sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), + receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") + .unwrap(), + swaps: vec![swap], + ..Default::default() + }; + + let encoded_solution = encoder + .encode_solutions(vec![solution.clone()]) + .unwrap()[0] + .clone(); + + let calldata = encode_tycho_router_call( + eth_chain().id, + encoded_solution, + &solution, + UserTransferType::TransferFrom, + eth(), + None, + ) + .unwrap() + .data; + + let hex_calldata = encode(&calldata); + write_calldata_to_file( + "test_single_encoding_strategy_curve_st_eth", + hex_calldata.as_str(), + ); + } + } + } } From 5ddd2a9cd7d41d8899ff3667ca2587dc89fdce6a Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Mon, 26 May 2025 17:16:35 +0100 Subject: [PATCH 07/11] fix: Make functions in encoding_utils.rs private They shouldn't be used outside the crate by the users. They should reimplement this functions themselves. Move encode_input to utils.rs Took 8 minutes --- src/encoding/evm/approvals/permit2.rs | 5 +--- .../approvals/protocol_approvals_manager.rs | 5 +--- src/encoding/evm/encoding_utils.rs | 27 +++---------------- src/encoding/evm/mod.rs | 2 +- src/encoding/evm/utils.rs | 24 ++++++++++++++++- 5 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/encoding/evm/approvals/permit2.rs b/src/encoding/evm/approvals/permit2.rs index 6c58707..ee4d572 100644 --- a/src/encoding/evm/approvals/permit2.rs +++ b/src/encoding/evm/approvals/permit2.rs @@ -17,10 +17,7 @@ use tycho_common::Bytes; use crate::encoding::{ errors::EncodingError, - evm::{ - encoding_utils::encode_input, - utils::{biguint_to_u256, bytes_to_address, get_client, get_runtime}, - }, + evm::utils::{biguint_to_u256, bytes_to_address, encode_input, get_client, get_runtime}, models, }; diff --git a/src/encoding/evm/approvals/protocol_approvals_manager.rs b/src/encoding/evm/approvals/protocol_approvals_manager.rs index 8b5769b..364a4ce 100644 --- a/src/encoding/evm/approvals/protocol_approvals_manager.rs +++ b/src/encoding/evm/approvals/protocol_approvals_manager.rs @@ -14,10 +14,7 @@ use tokio::{ use crate::encoding::{ errors::EncodingError, - evm::{ - encoding_utils::encode_input, - utils::{get_client, get_runtime}, - }, + evm::utils::{encode_input, get_client, get_runtime}, }; /// A manager for checking if an approval is needed for interacting with a certain spender. diff --git a/src/encoding/evm/encoding_utils.rs b/src/encoding/evm/encoding_utils.rs index fd40b70..438ebe0 100644 --- a/src/encoding/evm/encoding_utils.rs +++ b/src/encoding/evm/encoding_utils.rs @@ -4,7 +4,7 @@ use alloy::{ primitives::U256, signers::{local::PrivateKeySigner, Signature, SignerSync}, }; -use alloy_primitives::{Address, Keccak256}; +use alloy_primitives::Address; use alloy_sol_types::{eip712_domain, SolStruct, SolValue}; use num_bigint::BigUint; use tycho_common::Bytes; @@ -13,34 +13,13 @@ use crate::encoding::{ errors::EncodingError, evm::{ approvals::permit2::PermitSingle, + utils, utils::{biguint_to_u256, bytes_to_address}, }, models, models::{EncodedSolution, NativeAction, Solution, Transaction, UserTransferType}, }; -/// Encodes the input data for a function call to the given function selector. -pub fn encode_input(selector: &str, mut encoded_args: Vec) -> Vec { - 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::>() - { - 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 @@ -240,7 +219,7 @@ pub fn encode_tycho_router_call( Err(EncodingError::FatalError("Invalid selector for Tycho router".to_string()))? }; - let contract_interaction = encode_input(&encoded_solution.selector, method_calldata); + let contract_interaction = utils::encode_input(&encoded_solution.selector, method_calldata); let value = if solution.given_token == native_address { solution.given_amount.clone() } else { diff --git a/src/encoding/evm/mod.rs b/src/encoding/evm/mod.rs index eea7873..b0672f2 100644 --- a/src/encoding/evm/mod.rs +++ b/src/encoding/evm/mod.rs @@ -1,7 +1,7 @@ pub mod approvals; mod constants; pub mod encoder_builders; -pub mod encoding_utils; +mod encoding_utils; mod group_swaps; pub mod strategy_encoder; mod swap_encoder; diff --git a/src/encoding/evm/utils.rs b/src/encoding/evm/utils.rs index df61ef4..bad8f5f 100644 --- a/src/encoding/evm/utils.rs +++ b/src/encoding/evm/utils.rs @@ -9,7 +9,7 @@ use alloy::{ providers::{ProviderBuilder, RootProvider}, transports::BoxTransport, }; -use alloy_primitives::{aliases::U24, Address, U256, U8}; +use alloy_primitives::{aliases::U24, Address, Keccak256, U256, U8}; use alloy_sol_types::SolValue; use num_bigint::BigUint; use once_cell::sync::Lazy; @@ -159,3 +159,25 @@ pub fn write_calldata_to_file(test_identifier: &str, hex_calldata: &str) { writeln!(file, "{line}").expect("Failed to write calldata"); } } + +/// Encodes the input data for a function call to the given function selector. +pub fn encode_input(selector: &str, mut encoded_args: Vec) -> Vec { + 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::>() + { + encoded_args = encoded_args[32..].to_vec(); + } + call_data.extend(encoded_args); + call_data +} From 72f2734f2ce5d7e201e48ca5f03d4eb2979be149 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 26 May 2025 16:42:06 +0000 Subject: [PATCH 08/11] chore(release): 0.95.0 [skip ci] ## [0.95.0](https://github.com/propeller-heads/tycho-execution/compare/0.94.0...0.95.0) (2025-05-26) ### Features * Don't sign permit2 objects ([c62af2f](https://github.com/propeller-heads/tycho-execution/commit/c62af2f232063d3a3c5baa759d484b0d2cc2c185)) ### Bug Fixes * Bring back swapper_pk for backwards compatibility ([d12e3d3](https://github.com/propeller-heads/tycho-execution/commit/d12e3d3b5fc302b1c266424189f984884b2d9ed7)) * Make functions in encoding_utils.rs private ([5ddd2a9](https://github.com/propeller-heads/tycho-execution/commit/5ddd2a9cd7d41d8899ff3667ca2587dc89fdce6a)) --- CHANGELOG.md | 13 +++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d211be0..ef9ed76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## [0.95.0](https://github.com/propeller-heads/tycho-execution/compare/0.94.0...0.95.0) (2025-05-26) + + +### Features + +* Don't sign permit2 objects ([c62af2f](https://github.com/propeller-heads/tycho-execution/commit/c62af2f232063d3a3c5baa759d484b0d2cc2c185)) + + +### Bug Fixes + +* Bring back swapper_pk for backwards compatibility ([d12e3d3](https://github.com/propeller-heads/tycho-execution/commit/d12e3d3b5fc302b1c266424189f984884b2d9ed7)) +* Make functions in encoding_utils.rs private ([5ddd2a9](https://github.com/propeller-heads/tycho-execution/commit/5ddd2a9cd7d41d8899ff3667ca2587dc89fdce6a)) + ## [0.94.0](https://github.com/propeller-heads/tycho-execution/compare/0.93.0...0.94.0) (2025-05-23) diff --git a/Cargo.lock b/Cargo.lock index 77b39a7..ff387b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4469,7 +4469,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.94.0" +version = "0.95.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index c3f6bc5..6cf5619 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.94.0" +version = "0.95.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution" From 73c1a2db6ac66d1cdad5726cfa998e272fa4cb14 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Tue, 27 May 2025 09:37:24 +0100 Subject: [PATCH 09/11] chore: Rename selector to function signature Took 8 minutes --- examples/encoding-example/main.rs | 16 ++++---- src/bin/tycho-encode.rs | 2 +- src/encoding/evm/encoding_utils.rs | 25 ++++++------ .../evm/strategy_encoder/strategy_encoders.rs | 40 +++++++++---------- .../evm/swap_encoder/swap_encoders.rs | 4 -- src/encoding/evm/tycho_encoders.rs | 2 +- src/encoding/models.rs | 5 +-- src/encoding/tycho_encoder.rs | 6 +-- 8 files changed, 48 insertions(+), 52 deletions(-) diff --git a/examples/encoding-example/main.rs b/examples/encoding-example/main.rs index 7c35f06..ff10123 100644 --- a/examples/encoding-example/main.rs +++ b/examples/encoding-example/main.rs @@ -64,10 +64,10 @@ fn main() { .clone(); println!(" ====== Simple swap WETH -> USDC ======"); println!( - "The simple swap encoded solution should be sent to address {:?} and selector {:?} and the \ - following encoded data: {:?}", + "The simple swap encoded solution should be sent to address {:?} with function signature {:?} and the \ + following encoded swaps: {:?}", encoded_solution.interacting_with, - encoded_solution.selector, + encoded_solution.function_signature, hex::encode(encoded_solution.swaps) ); @@ -138,10 +138,10 @@ fn main() { println!(" ====== Complex split swap WETH -> USDC ======"); println!( - "The complex swaps encoded solution should be sent to address {:?} and selector {:?} and the \ - following encoded data: {:?}", - complex_encoded_solution.interacting_with, - complex_encoded_solution.selector, - hex::encode(complex_encoded_solution.swaps) + "The complex swaps encoded solution should be sent to address {:?} with function signature {:?} and the \ + following encoded swaps: {:?}", + complex_encoded_solution.interacting_with, + complex_encoded_solution.function_signature, + hex::encode(complex_encoded_solution.swaps) ); } diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index 3af9033..b55c424 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -106,7 +106,7 @@ fn main() -> Result<(), Box> { let encoded = serde_json::json!({ "swaps": format!("0x{}", hex::encode(&encoded_solutions[0].swaps)), "interacting_with": format!("0x{}", hex::encode(&encoded_solutions[0].interacting_with)), - "selector": format!("{}",&encoded_solutions[0].selector), + "function_signature": format!("{}",&encoded_solutions[0].function_signature), "n_tokens": format!("{}", &encoded_solutions[0].n_tokens), "permit": match encoded_solutions[0].permit.as_ref() { Some(permit) => { diff --git a/src/encoding/evm/encoding_utils.rs b/src/encoding/evm/encoding_utils.rs index 438ebe0..f39a60b 100644 --- a/src/encoding/evm/encoding_utils.rs +++ b/src/encoding/evm/encoding_utils.rs @@ -34,7 +34,7 @@ use crate::encoding::{ /// - `splitSwapPermit2` /// /// The encoding includes handling of native asset wrapping/unwrapping, permit2 support, -/// and proper input argument formatting based on the selector string. +/// and proper input argument formatting based on the function signature string. /// /// # ⚠️ Important Responsibility Note /// @@ -60,7 +60,7 @@ use crate::encoding::{ /// funds. /// /// # Parameters -/// - `encoded_solution`: The solution already encoded by Tycho, including selector and swap path. +/// - `encoded_solution`: The solution already encoded by Tycho. /// - `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. @@ -71,8 +71,8 @@ use crate::encoding::{ /// 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. +/// - Returns `EncodingError::FatalError` if the function signature is unsupported or required +/// fields (e.g., permit or signature) are missing. pub fn encode_tycho_router_call( chain_id: u64, encoded_solution: EncodedSolution, @@ -109,7 +109,7 @@ pub fn encode_tycho_router_call( }; let method_calldata = if encoded_solution - .selector + .function_signature .contains("singleSwapPermit2") { ( @@ -128,7 +128,7 @@ pub fn encode_tycho_router_call( ) .abi_encode() } else if encoded_solution - .selector + .function_signature .contains("singleSwap") { ( @@ -144,7 +144,7 @@ pub fn encode_tycho_router_call( ) .abi_encode() } else if encoded_solution - .selector + .function_signature .contains("sequentialSwapPermit2") { ( @@ -163,7 +163,7 @@ pub fn encode_tycho_router_call( ) .abi_encode() } else if encoded_solution - .selector + .function_signature .contains("sequentialSwap") { ( @@ -179,7 +179,7 @@ pub fn encode_tycho_router_call( ) .abi_encode() } else if encoded_solution - .selector + .function_signature .contains("splitSwapPermit2") { ( @@ -199,7 +199,7 @@ pub fn encode_tycho_router_call( ) .abi_encode() } else if encoded_solution - .selector + .function_signature .contains("splitSwap") { ( @@ -216,10 +216,11 @@ pub fn encode_tycho_router_call( ) .abi_encode() } else { - Err(EncodingError::FatalError("Invalid selector for Tycho router".to_string()))? + Err(EncodingError::FatalError("Invalid function signature for Tycho router".to_string()))? }; - let contract_interaction = utils::encode_input(&encoded_solution.selector, method_calldata); + let contract_interaction = + utils::encode_input(&encoded_solution.function_signature, method_calldata); let value = if solution.given_token == native_address { solution.given_amount.clone() } else { diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index aa17095..9038fca 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -23,13 +23,13 @@ use crate::encoding::{ /// /// # Fields /// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders -/// * `selector`: String, the selector for the swap function in the router contract +/// * `function_signature`: String, the signature for the swap function in the router contract /// * `router_address`: Address of the router to be used to execute swaps /// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers #[derive(Clone)] pub struct SingleSwapStrategyEncoder { swap_encoder_registry: SwapEncoderRegistry, - selector: String, + function_signature: String, router_address: Bytes, transfer_optimization: TransferOptimization, } @@ -41,14 +41,14 @@ impl SingleSwapStrategyEncoder { user_transfer_type: UserTransferType, router_address: Bytes, ) -> Result { - let selector = if user_transfer_type == UserTransferType::TransferFromPermit2 { + let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 { "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" } else { "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" }.to_string(); Ok(Self { - selector, + function_signature, swap_encoder_registry, router_address: router_address.clone(), transfer_optimization: TransferOptimization::new( @@ -133,7 +133,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder { grouped_protocol_data, ); Ok(EncodedSolution { - selector: self.selector.clone(), + function_signature: self.function_signature.clone(), interacting_with: self.router_address.clone(), swaps: swap_data, permit: None, @@ -155,7 +155,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder { /// /// # Fields /// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders -/// * `selector`: String, the selector for the swap function in the router contract +/// * `function_signature`: String, the signature for the swap function in the router contract /// * `native_address`: Address of the chain's native token /// * `wrapped_address`: Address of the chain's wrapped token /// * `router_address`: Address of the router to be used to execute swaps @@ -165,7 +165,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder { #[derive(Clone)] pub struct SequentialSwapStrategyEncoder { swap_encoder_registry: SwapEncoderRegistry, - selector: String, + function_signature: String, router_address: Bytes, native_address: Bytes, wrapped_address: Bytes, @@ -180,14 +180,14 @@ impl SequentialSwapStrategyEncoder { user_transfer_type: UserTransferType, router_address: Bytes, ) -> Result { - let selector = if user_transfer_type == UserTransferType::TransferFromPermit2 { + let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 { "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" } else { "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" }.to_string(); Ok(Self { - selector, + function_signature, swap_encoder_registry, router_address: router_address.clone(), native_address: chain.native_token()?, @@ -288,7 +288,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder { let encoded_swaps = ple_encode(swaps); Ok(EncodedSolution { interacting_with: self.router_address.clone(), - selector: self.selector.clone(), + function_signature: self.function_signature.clone(), swaps: encoded_swaps, permit: None, n_tokens: 0, @@ -309,7 +309,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder { /// /// # Fields /// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders -/// * `selector`: String, the selector for the swap function in the router contract +/// * `function_signature`: String, the signature for the swap function in the router contract /// * `native_address`: Address of the chain's native token /// * `wrapped_address`: Address of the chain's wrapped token /// * `split_swap_validator`: SplitSwapValidator, responsible for checking validity of split swap @@ -319,7 +319,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder { #[derive(Clone)] pub struct SplitSwapStrategyEncoder { swap_encoder_registry: SwapEncoderRegistry, - selector: String, + function_signature: String, native_address: Bytes, wrapped_address: Bytes, split_swap_validator: SplitSwapValidator, @@ -334,13 +334,13 @@ impl SplitSwapStrategyEncoder { user_transfer_type: UserTransferType, router_address: Bytes, ) -> Result { - let selector = if user_transfer_type == UserTransferType::TransferFromPermit2 { + let function_signature = if user_transfer_type == UserTransferType::TransferFromPermit2 { "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" } else { "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)" }.to_string(); Ok(Self { - selector, + function_signature, swap_encoder_registry, native_address: chain.native_token()?, wrapped_address: chain.wrapped_token()?, @@ -489,7 +489,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { }; Ok(EncodedSolution { interacting_with: self.router_address.clone(), - selector: self.selector.clone(), + function_signature: self.function_signature.clone(), swaps: encoded_swaps, permit: None, n_tokens: tokens_len, @@ -596,7 +596,7 @@ mod tests { let hex_calldata = encode(&encoded_solution.swaps); assert_eq!(hex_calldata, expected_swap); - assert_eq!(encoded_solution.selector, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string()); + assert_eq!(encoded_solution.function_signature, "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string()); assert_eq!(encoded_solution.interacting_with, router_address()); } @@ -659,7 +659,7 @@ mod tests { assert_eq!(hex_calldata, expected_input); assert_eq!( - encoded_solution.selector, + encoded_solution.function_signature, "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" .to_string() ); @@ -747,7 +747,7 @@ mod tests { assert_eq!(hex_calldata, expected); assert_eq!( - encoded_solution.selector, + encoded_solution.function_signature, "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" .to_string() ); @@ -902,7 +902,7 @@ mod tests { .join(""); assert_eq!(hex_calldata, expected_swaps); assert_eq!( - encoded_solution.selector, + encoded_solution.function_signature, "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" .to_string() ); @@ -1050,7 +1050,7 @@ mod tests { assert_eq!(hex_calldata, expected_swaps); assert_eq!( - encoded_solution.selector, + encoded_solution.function_signature, "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)" .to_string() ); diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 2c123ef..11cd019 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -19,7 +19,6 @@ use crate::encoding::{ /// /// # Fields /// * `executor_address` - The address of the executor contract that will perform the swap. -/// * `swap_selector` - The selector of the swap function in the executor contract. #[derive(Clone)] pub struct UniswapV2SwapEncoder { executor_address: String, @@ -78,7 +77,6 @@ impl SwapEncoder for UniswapV2SwapEncoder { /// /// # Fields /// * `executor_address` - The address of the executor contract that will perform the swap. -/// * `swap_selector` - The selector of the swap function in the executor contract. #[derive(Clone)] pub struct UniswapV3SwapEncoder { executor_address: String, @@ -140,8 +138,6 @@ impl SwapEncoder for UniswapV3SwapEncoder { /// /// # Fields /// * `executor_address` - The address of the executor contract that will perform the swap. -/// * `swap_selector` - The selector of the swap function in the executor contract. -/// * `callback_selector` - The selector of the callback function in the executor contract. #[derive(Clone)] pub struct UniswapV4SwapEncoder { executor_address: String, diff --git a/src/encoding/evm/tycho_encoders.rs b/src/encoding/evm/tycho_encoders.rs index 5b96f9b..14f76bd 100644 --- a/src/encoding/evm/tycho_encoders.rs +++ b/src/encoding/evm/tycho_encoders.rs @@ -337,7 +337,7 @@ impl TychoExecutorEncoder { swaps: grouped_protocol_data, interacting_with: executor_address, permit: None, - selector: "".to_string(), + function_signature: "".to_string(), n_tokens: 0, }) } diff --git a/src/encoding/models.rs b/src/encoding/models.rs index 527c853..b8e03f4 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -105,7 +105,6 @@ impl Swap { /// * `to`: Address of the contract to call with the calldata /// * `value`: Native token value to be sent with the transaction. /// * `data`: Encoded calldata for the transaction. -/// * `selector`: Only relevant for direct executions. The selector of the function to be called. #[derive(Clone, Debug)] pub struct Transaction { pub to: Bytes, @@ -118,14 +117,14 @@ pub struct Transaction { /// # 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. +/// * `function_signature`: The signature of the function to be called. /// * `n_tokens`: Number of tokens in the swap. /// * `permit`: Optional permit for the swap (if permit2 is enabled). #[derive(Clone, Debug)] pub struct EncodedSolution { pub swaps: Vec, pub interacting_with: Bytes, - pub selector: String, + pub function_signature: String, pub n_tokens: usize, pub permit: Option, } diff --git a/src/encoding/tycho_encoder.rs b/src/encoding/tycho_encoder.rs index 99b0216..4c5c230 100644 --- a/src/encoding/tycho_encoder.rs +++ b/src/encoding/tycho_encoder.rs @@ -26,8 +26,8 @@ use crate::encoding::{ /// outer function call arguments themselves** and verify that they enforce correct and secure /// behavior. pub trait TychoEncoder { - /// Encodes a list of [`Solution`]s into [`EncodedSolution`]s, which include the selector and - /// internal swap call data. + /// Encodes a list of [`Solution`]s into [`EncodedSolution`]s, which include the function + /// signature and internal swap call data. /// /// This method gives users maximum flexibility and control. It **does not** produce full /// transaction objects. Users are responsible for: @@ -36,7 +36,7 @@ pub trait TychoEncoder { /// /// # Returns /// A vector of encoded solutions, each containing: - /// - The Tycho method selector + /// - The Tycho method function signature /// - The encoded swap path /// - Additional metadata (e.g., permit2 information) /// From 92d36b9f48d30aad2d1227c21f490ac9f47daa7b Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Tue, 27 May 2025 12:12:47 +0100 Subject: [PATCH 10/11] fix: Move encode_input back into encoding_utils.rs We don't want the user to be able to use it outside the crate Took 16 minutes --- src/encoding/evm/approvals/permit2.rs | 5 +++- .../approvals/protocol_approvals_manager.rs | 5 +++- src/encoding/evm/encoding_utils.rs | 28 ++++++++++++++++--- src/encoding/evm/utils.rs | 24 +--------------- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/encoding/evm/approvals/permit2.rs b/src/encoding/evm/approvals/permit2.rs index ee4d572..6c58707 100644 --- a/src/encoding/evm/approvals/permit2.rs +++ b/src/encoding/evm/approvals/permit2.rs @@ -17,7 +17,10 @@ use tycho_common::Bytes; use crate::encoding::{ 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, }; diff --git a/src/encoding/evm/approvals/protocol_approvals_manager.rs b/src/encoding/evm/approvals/protocol_approvals_manager.rs index 364a4ce..8b5769b 100644 --- a/src/encoding/evm/approvals/protocol_approvals_manager.rs +++ b/src/encoding/evm/approvals/protocol_approvals_manager.rs @@ -14,7 +14,10 @@ use tokio::{ use crate::encoding::{ 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. diff --git a/src/encoding/evm/encoding_utils.rs b/src/encoding/evm/encoding_utils.rs index f39a60b..3295280 100644 --- a/src/encoding/evm/encoding_utils.rs +++ b/src/encoding/evm/encoding_utils.rs @@ -4,7 +4,7 @@ use alloy::{ primitives::U256, signers::{local::PrivateKeySigner, Signature, SignerSync}, }; -use alloy_primitives::Address; +use alloy_primitives::{Address, Keccak256}; use alloy_sol_types::{eip712_domain, SolStruct, SolValue}; use num_bigint::BigUint; use tycho_common::Bytes; @@ -13,7 +13,6 @@ use crate::encoding::{ errors::EncodingError, evm::{ approvals::permit2::PermitSingle, - utils, utils::{biguint_to_u256, bytes_to_address}, }, models, @@ -219,8 +218,7 @@ pub fn encode_tycho_router_call( Err(EncodingError::FatalError("Invalid function signature for Tycho router".to_string()))? }; - let contract_interaction = - utils::encode_input(&encoded_solution.function_signature, method_calldata); + let contract_interaction = encode_input(&encoded_solution.function_signature, method_calldata); let value = if solution.given_token == native_address { solution.given_amount.clone() } else { @@ -258,3 +256,25 @@ pub fn sign_permit( EncodingError::FatalError(format!("Failed to sign permit2 approval with error: {e}")) }) } + +/// Encodes the input data for a function call to the given function selector. +pub fn encode_input(selector: &str, mut encoded_args: Vec) -> Vec { + 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::>() + { + encoded_args = encoded_args[32..].to_vec(); + } + call_data.extend(encoded_args); + call_data +} diff --git a/src/encoding/evm/utils.rs b/src/encoding/evm/utils.rs index bad8f5f..df61ef4 100644 --- a/src/encoding/evm/utils.rs +++ b/src/encoding/evm/utils.rs @@ -9,7 +9,7 @@ use alloy::{ providers::{ProviderBuilder, RootProvider}, transports::BoxTransport, }; -use alloy_primitives::{aliases::U24, Address, Keccak256, U256, U8}; +use alloy_primitives::{aliases::U24, Address, U256, U8}; use alloy_sol_types::SolValue; use num_bigint::BigUint; use once_cell::sync::Lazy; @@ -159,25 +159,3 @@ pub fn write_calldata_to_file(test_identifier: &str, hex_calldata: &str) { writeln!(file, "{line}").expect("Failed to write calldata"); } } - -/// Encodes the input data for a function call to the given function selector. -pub fn encode_input(selector: &str, mut encoded_args: Vec) -> Vec { - 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::>() - { - encoded_args = encoded_args[32..].to_vec(); - } - call_data.extend(encoded_args); - call_data -} From d6ee85e8da4434451c41b1de3c176d9ad686564c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 27 May 2025 14:28:03 +0000 Subject: [PATCH 11/11] chore(release): 0.95.1 [skip ci] ## [0.95.1](https://github.com/propeller-heads/tycho-execution/compare/0.95.0...0.95.1) (2025-05-27) ### Bug Fixes * Move encode_input back into encoding_utils.rs ([92d36b9](https://github.com/propeller-heads/tycho-execution/commit/92d36b9f48d30aad2d1227c21f490ac9f47daa7b)) --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef9ed76..3b09b71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.95.1](https://github.com/propeller-heads/tycho-execution/compare/0.95.0...0.95.1) (2025-05-27) + + +### Bug Fixes + +* Move encode_input back into encoding_utils.rs ([92d36b9](https://github.com/propeller-heads/tycho-execution/commit/92d36b9f48d30aad2d1227c21f490ac9f47daa7b)) + ## [0.95.0](https://github.com/propeller-heads/tycho-execution/compare/0.94.0...0.95.0) (2025-05-26) diff --git a/Cargo.lock b/Cargo.lock index ff387b2..7d5b788 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4469,7 +4469,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.95.0" +version = "0.95.1" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 6cf5619..ec1f275 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.95.0" +version = "0.95.1" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution"