diff --git a/foundry/test/assets/calldata.txt b/foundry/test/assets/calldata.txt index 28bd7c1..0e1927a 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:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c800000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041b70914df20e4b2675adc5a61d26b959e0fd21e7cce6503b884d5ba9e1db2c82164112e4dc362b9a3ef794a03fcfebe0710700028b79abe8b7de525288ac904991c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 -test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c8000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c33e9da2d2a5bea1087e27f42b9255fcaea06efa76558843f6192d571b15c09c5818e1a4b3a111bb6accc4cb8b583c318d2386e0e94cc87b9fcc4065db0d65611c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000 -test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_usv4_grouped_swap:30ace1b1000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041972d1d0dd19889db47087a4653c517a455a19a1dc76a0a14a29fd12ae7d2d4f218ba986eada63bfdfacf78e3a7ffdce07c4064fcf11033f185819509f64b6b191b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d23119330000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000000000000000 +test_single_encoding_strategy_usv4_eth_out:7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e0000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c0bbb5f493c1d4b87c92085902e4488efba2b87740abdcc298a5adb435bfe3b23820a0677d325e34fa06e8c2a91f0d3851b00cf170885176b5f47d40983d64941b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c00000000000000000000000000 +test_sequential_swap_strategy_encoder:51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000419861aedfb04e8f7b9fe147a7d19c41f9b1cf37a06850b09f645fb351c003967d43d365af50b08ef741348898578b7fa1454a8e9cd0390eaf6c2939d7b817d0d31c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d940004375dff511095cc5a197a54140a24efef3a416000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20102000000000000000000000000000000000000000000000000 test_single_swap_strategy_encoder_no_permit2:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_swap_strategy_encoder_no_transfer_in:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000058e7926ee858a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 -test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c800000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041f59744bfe3e154be7c72429eb93db7ef2ec00344feeb1376195a2e3f133437f7533c3df955de146e229e4a37fda22acbe11d9393215d14cf1e19334aebbf35e61b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 -test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 +test_single_encoding_strategy_usv4_eth_in:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000007e0a55d4322a6e93c2379c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041a8ad04262cc02545cc4934739e086f37b3ea04ba83195a151a736e55cf2cc68407f9dc20f19c68071ced3f6d5f20908ce9332526c955e66974aaf53c7e58ca581c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006cf62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc26982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000 +test_sequential_strategy_cyclic_swap:51bcc7b60000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ec8f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041d461cc5890a8a58d13249a485335b44671612c97b7a12004b5cfb163bdbf4c2f15dde7cb500c22f724f813b7d6fe5062076f63ee5b91c7e984f011a5d1ce6fca1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f5640010000692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000000000 test_single_encoding_strategy_curve_st_eth:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe84000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f670220100010002cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 -test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006b56051582a970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 +test_single_swap_strategy_encoder:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000006b56051582a970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000685552e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccee00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000419861aedfb04e8f7b9fe147a7d19c41f9b1cf37a06850b09f645fb351c003967d43d365af50b08ef741348898578b7fa1454a8e9cd0390eaf6c2939d7b817d0d31c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000 test_single_encoding_strategy_curve:5c4b639c0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000691d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e710201000100cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000 -test_single_swap_strategy_encoder_unwrap:30ace1b10000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041e5679a7262a283109d2e312e4ffbec563f501f347efc00418f3a6701492635977583d4e2ff29904a33496b64f1bc2ac73796a546a495ab110cca86e6de1655b61c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000000000000000000000000000000 -test_single_swap_strategy_encoder_wrap:30ace1b10000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f000000000000000000000000000000000000000000000059fb7d3830e6fc064b00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004160aa554efc922689a7f426a984f9fc278aa948b9c78f4e7cc0a5f177e5fb6859632b92d3326f710603d43d3a8ea5753c58a61316fd1bff8e0b388f74c98993b91b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb11cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200010000000000000000000000000000 -test_split_output_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005e703f4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc288e6a0c2ddd26feeb64f039a2c41296fcb3f56400001006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb8cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc28ad599c3a0ff1de082011efddc58f1908eb6e6d8000100000000000000 -test_split_input_cyclic_swap:7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e1000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415eb0e4c29f56696a0a8c0445740b653ae67c0f52fe364f52adc19c84fd6fdc837bc0df41516f33500ebe7f05f653bc0e53ec3ec27c223484d29e4567bf5a0f561c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dccd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000100000000000000 -test_split_swap_strategy_encoder:7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c90000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000410c38cb809e33a4b6239b5aab3ac879ec39ba3a6979404f6cfc882cc15b81e92f02fef7985c8edd3985b39224f2738ca6bbcfe7acf0cd44d8bab89636dcce3bfb1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d5cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20101005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a416cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2010100000000000000000000000000000000000000000000000000000000 +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_uniswap_v3_curve:e21dd0d30000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000d600692e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599000bb83ede3eca2a72b3aecc820e955b36f38437d01395cbcdf9626bc03e24f779434178a73a0b4bad62ed000000691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae460301000102cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000 -test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000000000000000000000000000000000000068529cc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682b16c900000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041a81d713c715dd501edf102a3c3a755a931e3c72e7322d66b620ece81d7c98bae3d46479041445649eeefdb6ccc09c2b933cd37eda3ae8c99034e84f5570eb2c31c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 +test_multi_protocol:51bcc7b600000000000000000000000000000000000000000000005150ae84a8cdf000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2958f36da71a9200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000005150ae84a8cdf0000000000000000000000000000000000000000000000000000000000000685552e700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000682dccef00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000410488df0c2ac790f75505dbd2754518d70fb02c32fb3bb3e63e299259037765606ca8d38d05c9c6d2d82e3b39203f378ad489f715a2aeb7df674ee4b8e5c8d1151c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021400525615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139501000072c7183455a4c133ae270771860664b6b7ec320bb1c02aaa39b223fe8d0a0e5c4f27ead9083c756cc22260fac5e5542a773aa44fbcfedf7c193bc2c599a6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e3ede3eca2a72b3aecc820e955b36f38437d01395010200691d1499e622d69689cdf9004d05ec547d650ff2112260fac5e5542a773aa44fbcfedf7c193bc2c599dac17f958d2ee523a2206206994597c13d831ec7d51a44d3fae010294c616388b506acda1bfaae4603010001023ede3eca2a72b3aecc820e955b36f38437d013950071a0cb889707d426a7a386870a03bc70d1b0697598013ede3eca2a72b3aecc820e955b36f38437d01395dac17f958d2ee523a2206206994597c13d831ec7a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001a36e2eb1c43200000032006cf62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000001cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000bb800003c000000000000000000000000 test_encode_balancer_v2:c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0102 test_ekubo_encode_swap_multi:01ca4f73fe97d0b987a0d12b39bbd562c779bab6f60000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4851d02a5948496a67827242eabc5725531342527c000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000001a36e2eb1c43200000032 test_encode_uniswap_v4_sequential_swap:4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990101cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c diff --git a/src/encoding/errors.rs b/src/encoding/errors.rs index 85242d2..2949261 100644 --- a/src/encoding/errors.rs +++ b/src/encoding/errors.rs @@ -20,6 +20,8 @@ pub enum EncodingError { FatalError(String), #[error("Recoverable error: {0}")] RecoverableError(String), + #[error("Not implemented: {0}")] + NotImplementedError(String), } impl From for EncodingError { diff --git a/src/encoding/evm/approvals/permit2.rs b/src/encoding/evm/approvals/permit2.rs index 51b1f6a..8ad8787 100644 --- a/src/encoding/evm/approvals/permit2.rs +++ b/src/encoding/evm/approvals/permit2.rs @@ -19,7 +19,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::Chain, }; 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 new file mode 100644 index 0000000..c70a129 --- /dev/null +++ b/src/encoding/evm/encoding_utils.rs @@ -0,0 +1,251 @@ +use alloy_primitives::{Keccak256, U256}; +use alloy_sol_types::SolValue; +use num_bigint::BigUint; +use tycho_common::Bytes; + +use crate::encoding::{ + errors::EncodingError, + evm::utils::{biguint_to_u256, bytes_to_address, get_min_amount_for_solution}, + models::{EncodedSolution, NativeAction, Solution, Transaction}, +}; + +/// Encodes the input data for a function call to the given function selector. +pub fn encode_input(selector: &str, mut encoded_args: Vec) -> 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 +/// +/// This function provides an **example implementation** of how to encode a call to the Tycho +/// Router. It handles all currently supported swap selectors such as: +/// - `singleSwap` +/// - `singleSwapPermit2` +/// - `sequentialSwap` +/// - `sequentialSwapPermit2` +/// - `splitSwap` +/// - `splitSwapPermit2` +/// +/// The encoding includes handling of native asset wrapping/unwrapping, permit2 support, +/// and proper input argument formatting based on the selector string. +/// +/// # ⚠️ Important Responsibility Note +/// +/// This function is intended as **an illustrative example only**. **Users must implement +/// their own encoding logic** to ensure: +/// - Full control of parameters passed to the router. +/// - Proper validation and setting of critical inputs such as `minAmountOut`. +/// +/// While Tycho is responsible for encoding the swap paths themselves, the input arguments +/// to the router's methods act as **guardrails** for on-chain execution safety. +/// Thus, the user must **take responsibility** for ensuring correctness of all input parameters, +/// including `minAmountOut`, `receiver`, and permit2 logic. +/// +/// # Min Amount Out +/// +/// The `minAmountOut` calculation used here is just an example. +/// You should ideally: +/// - Query an external service (e.g., DEX aggregators, oracle, off-chain price feed). +/// - Use your own strategy to determine an accurate and safe minimum acceptable output amount. +/// +/// ⚠️ If `minAmountOut` is too low, your swap may be front-run or sandwiched, resulting in loss of +/// funds. +/// +/// # Parameters +/// - `encoded_solution`: The solution already encoded by Tycho, including selector and swap path. +/// - `solution`: The high-level solution including tokens, amounts, and receiver info. +/// - `token_in_already_in_router`: Whether the input token is already present in the router. +/// - `router_address`: The address of the Tycho Router contract. +/// - `native_address`: The address used to represent the native token +/// +/// # Returns +/// A `Result` that either contains the full transaction data (to, +/// value, data), or an error if the inputs are invalid. +/// +/// # Errors +/// - Returns `EncodingError::FatalError` if the selector is unsupported or required fields (e.g., +/// permit or signature) are missing. +pub fn encode_tycho_router_call( + encoded_solution: EncodedSolution, + solution: &Solution, + token_in_already_in_router: bool, + router_address: Bytes, + native_address: Bytes, +) -> Result { + let min_amount_out = get_min_amount_for_solution(solution.clone()); + let (mut unwrap, mut wrap) = (false, false); + if let Some(action) = solution.native_action.clone() { + match action { + NativeAction::Wrap => wrap = true, + NativeAction::Unwrap => unwrap = true, + } + } + + let given_amount = biguint_to_u256(&solution.given_amount); + let min_amount_out = biguint_to_u256(&min_amount_out); + let given_token = bytes_to_address(&solution.given_token)?; + let checked_token = bytes_to_address(&solution.checked_token)?; + let receiver = bytes_to_address(&solution.receiver)?; + let n_tokens = U256::from(encoded_solution.n_tokens); + + let method_calldata = if encoded_solution + .selector + .contains("singleSwapPermit2") + { + ( + given_amount, + given_token, + checked_token, + min_amount_out, + wrap, + unwrap, + receiver, + encoded_solution + .permit + .ok_or(EncodingError::FatalError( + "permit2 object must be set to use permit2".to_string(), + ))?, + encoded_solution + .signature + .ok_or(EncodingError::FatalError( + "Signature must be set to use permit2".to_string(), + ))? + .as_bytes() + .to_vec(), + encoded_solution.swaps, + ) + .abi_encode() + } else if encoded_solution + .selector + .contains("singleSwap") + { + ( + given_amount, + given_token, + checked_token, + min_amount_out, + wrap, + unwrap, + receiver, + !token_in_already_in_router, + encoded_solution.swaps, + ) + .abi_encode() + } else if encoded_solution + .selector + .contains("sequentialSwapPermit2") + { + ( + given_amount, + given_token, + checked_token, + min_amount_out, + wrap, + unwrap, + receiver, + encoded_solution + .permit + .ok_or(EncodingError::FatalError( + "permit2 object must be set to use permit2".to_string(), + ))?, + encoded_solution + .signature + .ok_or(EncodingError::FatalError( + "Signature must be set to use permit2".to_string(), + ))? + .as_bytes() + .to_vec(), + encoded_solution.swaps, + ) + .abi_encode() + } else if encoded_solution + .selector + .contains("sequentialSwap") + { + ( + given_amount, + given_token, + checked_token, + min_amount_out, + wrap, + unwrap, + receiver, + !token_in_already_in_router, + encoded_solution.swaps, + ) + .abi_encode() + } else if encoded_solution + .selector + .contains("splitSwapPermit2") + { + ( + given_amount, + given_token, + checked_token, + min_amount_out, + wrap, + unwrap, + n_tokens, + receiver, + encoded_solution + .permit + .ok_or(EncodingError::FatalError( + "permit2 object must be set to use permit2".to_string(), + ))?, + encoded_solution + .signature + .ok_or(EncodingError::FatalError( + "Signature must be set to use permit2".to_string(), + ))? + .as_bytes() + .to_vec(), + encoded_solution.swaps, + ) + .abi_encode() + } else if encoded_solution + .selector + .contains("splitSwap") + { + ( + given_amount, + given_token, + checked_token, + min_amount_out, + wrap, + unwrap, + n_tokens, + receiver, + !token_in_already_in_router, + encoded_solution.swaps, + ) + .abi_encode() + } else { + Err(EncodingError::FatalError("Invalid selector for Tycho router".to_string()))? + }; + + let contract_interaction = encode_input(&encoded_solution.selector, method_calldata); + let value = if solution.given_token == native_address { + solution.given_amount.clone() + } else { + BigUint::ZERO + }; + Ok(Transaction { to: router_address, value, data: contract_interaction }) +} diff --git a/src/encoding/evm/mod.rs b/src/encoding/evm/mod.rs index 344aaa1..eea7873 100644 --- a/src/encoding/evm/mod.rs +++ b/src/encoding/evm/mod.rs @@ -1,6 +1,7 @@ pub mod approvals; mod constants; pub mod encoder_builders; +pub mod encoding_utils; mod group_swaps; pub mod strategy_encoder; mod swap_encoder; diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index df9719e..bd487ab 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1,25 +1,20 @@ use std::{collections::HashSet, str::FromStr}; -use alloy_primitives::{aliases::U24, U256, U8}; -use alloy_sol_types::SolValue; +use alloy_primitives::{aliases::U24, U8}; use tycho_common::Bytes; use crate::encoding::{ errors::EncodingError, evm::{ - approvals::permit2::Permit2, group_swaps::group_swaps, strategy_encoder::{ strategy_validators::{SequentialSwapValidator, SplitSwapValidator, SwapValidator}, transfer_optimizations::TransferOptimization, }, swap_encoder::swap_encoder_registry::SwapEncoderRegistry, - utils::{ - biguint_to_u256, bytes_to_address, encode_input, get_min_amount_for_solution, - get_token_position, percentage_to_uint24, ple_encode, - }, + utils::{get_token_position, percentage_to_uint24, ple_encode}, }, - models::{Chain, EncodingContext, NativeAction, Solution}, + models::{Chain, EncodedSolution, EncodingContext, NativeAction, Solution}, strategy_encoder::StrategyEncoder, swap_encoder::SwapEncoder, }; @@ -28,41 +23,32 @@ use crate::encoding::{ /// /// # Fields /// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders -/// * `permit2`: Permit2, responsible for managing permit2 operations and providing necessary -/// signatures and permit2 objects for calling the router /// * `selector`: String, the selector 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 -/// * `token_in_already_in_router`: bool, whether the token in is already in the router #[derive(Clone)] pub struct SingleSwapStrategyEncoder { swap_encoder_registry: SwapEncoderRegistry, - permit2: Option, selector: String, router_address: Bytes, transfer_optimization: TransferOptimization, - token_in_already_in_router: bool, } impl SingleSwapStrategyEncoder { pub fn new( chain: Chain, swap_encoder_registry: SwapEncoderRegistry, - swapper_pk: Option, + permit_2_active: bool, router_address: Bytes, token_in_already_in_router: bool, ) -> Result { - let (permit2, selector) = if let Some(swapper_pk) = swapper_pk { - (Some(Permit2::new(swapper_pk, chain.clone())?), "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string()) + let selector = if permit_2_active { + "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" } else { - ( - None, - "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" - .to_string(), - ) - }; + "singleSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" + }.to_string(); + Ok(Self { - permit2, selector, swap_encoder_registry, router_address: router_address.clone(), @@ -72,7 +58,6 @@ impl SingleSwapStrategyEncoder { token_in_already_in_router, router_address, ), - token_in_already_in_router, }) } @@ -87,7 +72,7 @@ impl SingleSwapStrategyEncoder { } impl StrategyEncoder for SingleSwapStrategyEncoder { - fn encode_strategy(&self, solution: Solution) -> Result<(Vec, Bytes), EncodingError> { + fn encode_strategy(&self, solution: Solution) -> Result { let grouped_swaps = group_swaps(solution.clone().swaps); let number_of_groups = grouped_swaps.len(); if number_of_groups != 1 { @@ -106,8 +91,6 @@ impl StrategyEncoder for SingleSwapStrategyEncoder { )) } - let min_amount_out = get_min_amount_for_solution(solution.clone()); - let (mut unwrap, mut wrap) = (false, false); if let Some(action) = solution.native_action.clone() { match action { @@ -150,44 +133,13 @@ impl StrategyEncoder for SingleSwapStrategyEncoder { .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?, grouped_protocol_data, ); - - let method_calldata = if let Some(permit2) = self.permit2.clone() { - let (permit, signature) = permit2.get_permit( - &self.router_address, - &solution.sender, - &solution.given_token, - &solution.given_amount, - )?; - ( - biguint_to_u256(&solution.given_amount), - bytes_to_address(&solution.given_token)?, - bytes_to_address(&solution.checked_token)?, - biguint_to_u256(&min_amount_out), - wrap, - unwrap, - bytes_to_address(&solution.receiver)?, - permit, - signature.as_bytes().to_vec(), - swap_data, - ) - .abi_encode() - } else { - ( - biguint_to_u256(&solution.given_amount), - bytes_to_address(&solution.given_token)?, - bytes_to_address(&solution.checked_token)?, - biguint_to_u256(&min_amount_out), - wrap, - unwrap, - bytes_to_address(&solution.receiver)?, - !self.token_in_already_in_router, - swap_data, - ) - .abi_encode() - }; - - let contract_interaction = encode_input(&self.selector, method_calldata); - Ok((contract_interaction, self.router_address.clone())) + Ok(EncodedSolution { + selector: self.selector.clone(), + swaps: swap_data, + permit: None, + signature: None, + n_tokens: 0, + }) } fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box> { @@ -204,8 +156,6 @@ impl StrategyEncoder for SingleSwapStrategyEncoder { /// /// # Fields /// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders -/// * `permit2`: Permit2, responsible for managing permit2 operations and providing necessary -/// signatures and permit2 objects for calling the router /// * `selector`: String, the selector 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 @@ -213,39 +163,32 @@ impl StrategyEncoder for SingleSwapStrategyEncoder { /// * `sequential_swap_validator`: SequentialSwapValidator, responsible for checking validity of /// sequential swap solutions /// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers -/// * `token_in_already_in_router`: bool, whether the token in is already in the router #[derive(Clone)] pub struct SequentialSwapStrategyEncoder { swap_encoder_registry: SwapEncoderRegistry, - permit2: Option, selector: String, router_address: Bytes, native_address: Bytes, wrapped_address: Bytes, sequential_swap_validator: SequentialSwapValidator, transfer_optimization: TransferOptimization, - token_in_already_in_router: bool, } impl SequentialSwapStrategyEncoder { pub fn new( chain: Chain, swap_encoder_registry: SwapEncoderRegistry, - swapper_pk: Option, + permit_2_active: bool, router_address: Bytes, token_in_already_in_router: bool, ) -> Result { - let (permit2, selector) = if let Some(swapper_pk) = swapper_pk { - (Some(Permit2::new(swapper_pk, chain.clone())?), "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string()) + let selector = if permit_2_active { + "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" } else { - ( - None, - "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" - .to_string(), - ) - }; + "sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)" + + }.to_string(); Ok(Self { - permit2, selector, swap_encoder_registry, router_address: router_address.clone(), @@ -258,7 +201,6 @@ impl SequentialSwapStrategyEncoder { token_in_already_in_router, router_address, ), - token_in_already_in_router, }) } @@ -273,7 +215,7 @@ impl SequentialSwapStrategyEncoder { } impl StrategyEncoder for SequentialSwapStrategyEncoder { - fn encode_strategy(&self, solution: Solution) -> Result<(Vec, Bytes), EncodingError> { + fn encode_strategy(&self, solution: Solution) -> Result { self.sequential_swap_validator .validate_solution_min_amounts(&solution)?; self.sequential_swap_validator @@ -286,14 +228,12 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder { &self.wrapped_address, )?; - let min_amount_out = get_min_amount_for_solution(solution.clone()); let grouped_swaps = group_swaps(solution.swaps); - let (mut unwrap, mut wrap) = (false, false); + let mut wrap = false; if let Some(action) = solution.native_action.clone() { - match action { - NativeAction::Wrap => wrap = true, - NativeAction::Unwrap => unwrap = true, + if action == NativeAction::Wrap { + wrap = true } } @@ -350,43 +290,13 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder { } let encoded_swaps = ple_encode(swaps); - let method_calldata = if let Some(permit2) = self.permit2.clone() { - let (permit, signature) = permit2.get_permit( - &self.router_address, - &solution.sender, - &solution.given_token, - &solution.given_amount, - )?; - ( - biguint_to_u256(&solution.given_amount), - bytes_to_address(&solution.given_token)?, - bytes_to_address(&solution.checked_token)?, - biguint_to_u256(&min_amount_out), - wrap, - unwrap, - bytes_to_address(&solution.receiver)?, - permit, - signature.as_bytes().to_vec(), - encoded_swaps, - ) - .abi_encode() - } else { - ( - biguint_to_u256(&solution.given_amount), - bytes_to_address(&solution.given_token)?, - bytes_to_address(&solution.checked_token)?, - biguint_to_u256(&min_amount_out), - wrap, - unwrap, - bytes_to_address(&solution.receiver)?, - !self.token_in_already_in_router, - encoded_swaps, - ) - .abi_encode() - }; - - let contract_interaction = encode_input(&self.selector, method_calldata); - Ok((contract_interaction, self.router_address.clone())) + Ok(EncodedSolution { + selector: self.selector.clone(), + swaps: encoded_swaps, + permit: None, + signature: None, + n_tokens: 0, + }) } fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box> { @@ -403,8 +313,6 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder { /// /// # Fields /// * `swap_encoder_registry`: SwapEncoderRegistry, containing all possible swap encoders -/// * `permit2`: Permit2, responsible for managing permit2 operations and providing necessary -/// signatures and permit2 objects for calling the router /// * `selector`: String, the selector 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 @@ -412,39 +320,31 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder { /// solutions /// * `router_address`: Address of the router to be used to execute swaps /// * `transfer_optimization`: TransferOptimization, responsible for optimizing the token transfers -/// * `token_in_already_in_router`: bool, whether the token in is already in the router #[derive(Clone)] pub struct SplitSwapStrategyEncoder { swap_encoder_registry: SwapEncoderRegistry, - permit2: Option, selector: String, native_address: Bytes, wrapped_address: Bytes, split_swap_validator: SplitSwapValidator, router_address: Bytes, transfer_optimization: TransferOptimization, - token_in_already_in_router: bool, } impl SplitSwapStrategyEncoder { pub fn new( chain: Chain, swap_encoder_registry: SwapEncoderRegistry, - swapper_pk: Option, + permit_2_active: bool, router_address: Bytes, token_in_already_in_router: bool, ) -> Result { - let (permit2, selector) = if let Some(swapper_pk) = swapper_pk { - (Some(Permit2::new(swapper_pk, chain.clone())?), "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string()) + let selector = if permit_2_active{ + "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)" } else { - ( - None, "splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)" - .to_string(), - ) - }; + }.to_string(); Ok(Self { - permit2, selector, swap_encoder_registry, native_address: chain.native_token()?, @@ -457,7 +357,6 @@ impl SplitSwapStrategyEncoder { token_in_already_in_router, router_address, ), - token_in_already_in_router, }) } @@ -482,7 +381,7 @@ impl SplitSwapStrategyEncoder { } impl StrategyEncoder for SplitSwapStrategyEncoder { - fn encode_strategy(&self, solution: Solution) -> Result<(Vec, Bytes), EncodingError> { + fn encode_strategy(&self, solution: Solution) -> Result { self.split_swap_validator .validate_solution_min_amounts(&solution)?; self.split_swap_validator @@ -497,8 +396,6 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { &self.wrapped_address, )?; - let min_amount_out = get_min_amount_for_solution(solution.clone()); - // The tokens array is composed of the given token, the checked token and all the // intermediary tokens in between. The contract expects the tokens to be in this order. let solution_tokens: HashSet = @@ -597,45 +494,13 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { } else { tokens.len() }; - let method_calldata = if let Some(permit2) = self.permit2.clone() { - let (permit, signature) = permit2.get_permit( - &self.router_address, - &solution.sender, - &solution.given_token, - &solution.given_amount, - )?; - ( - biguint_to_u256(&solution.given_amount), - bytes_to_address(&solution.given_token)?, - bytes_to_address(&solution.checked_token)?, - biguint_to_u256(&min_amount_out), - wrap, - unwrap, - U256::from(tokens_len), - bytes_to_address(&solution.receiver)?, - permit, - signature.as_bytes().to_vec(), - encoded_swaps, - ) - .abi_encode() - } else { - ( - biguint_to_u256(&solution.given_amount), - bytes_to_address(&solution.given_token)?, - bytes_to_address(&solution.checked_token)?, - biguint_to_u256(&min_amount_out), - wrap, - unwrap, - U256::from(tokens_len), - bytes_to_address(&solution.receiver)?, - !self.token_in_already_in_router, - encoded_swaps, - ) - .abi_encode() - }; - - let contract_interaction = encode_input(&self.selector, method_calldata); - Ok((contract_interaction, self.router_address.clone())) + Ok(EncodedSolution { + selector: self.selector.clone(), + swaps: encoded_swaps, + permit: None, + signature: None, + n_tokens: tokens_len, + }) } fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box> { @@ -653,7 +518,7 @@ mod tests { use std::{collections::HashMap, str::FromStr}; use alloy::hex::encode; - use alloy_primitives::{hex, Address}; + use alloy_primitives::{hex, Address, PrimitiveSignature as Signature, U256}; use num_bigint::{BigInt, BigUint}; use rstest::rstest; use tycho_common::{ @@ -662,7 +527,14 @@ mod tests { }; use super::*; - use crate::encoding::{evm::utils::write_calldata_to_file, models::Swap}; + use crate::encoding::{ + evm::{ + approvals::permit2::{Permit2, PermitSingle}, + encoding_utils::encode_tycho_router_call, + utils::write_calldata_to_file, + }, + models::Swap, + }; fn eth_chain() -> Chain { TychoCommonChain::Ethereum.into() @@ -682,7 +554,33 @@ mod tests { .unwrap() } + fn router_address() -> Bytes { + 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 + .get_permit( + &router_address, + &solution.sender, + &solution.given_token, + &solution.given_amount, + ) + .unwrap() + } + mod single { + use alloy_sol_types::SolValue; + use super::*; #[rstest] #[case::with_check_no_slippage( @@ -712,10 +610,6 @@ mod tests { // Performs a single swap from WETH to DAI on a USV2 pool, with no grouping // optimizations. - // Set up a mock private key for signing - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); @@ -733,8 +627,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key), - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + true, + router_address(), false, ) .unwrap(); @@ -752,9 +646,22 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount)); let expected_input = [ "30ace1b1", // Function selector @@ -823,8 +730,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + false, + router_address(), false, ) .unwrap(); @@ -842,9 +749,18 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount)); let expected_input = [ "5c4b639c", // Function selector @@ -906,8 +822,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + false, + router_address(), true, ) .unwrap(); @@ -925,9 +841,18 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + true, + router_address(), + eth(), + ) + .unwrap() + .data; let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount)); let expected_input = [ "5c4b639c", // Function selector @@ -968,10 +893,6 @@ mod tests { // Note: This test does not assert anything. It is only used to obtain integration test // data for our router solidity test. - // Set up a mock private key for signing - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); let swap = Swap { @@ -988,8 +909,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key), - Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), + true, + router_address(), false, ) .unwrap(); @@ -1007,10 +928,23 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_single_swap_strategy_encoder_wrap", hex_calldata.as_str()); } @@ -1021,10 +955,6 @@ mod tests { // Note: This test does not assert anything. It is only used to obtain integration test // data for our router solidity test. - // Set up a mock private key for signing - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); let swap = Swap { @@ -1041,7 +971,7 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key), + true, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), false, ) @@ -1060,10 +990,24 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); write_calldata_to_file( "test_single_swap_strategy_encoder_unwrap", @@ -1084,10 +1028,6 @@ mod tests { // // WETH ───(USV2)──> WBTC ───(USV2)──> USDC - // Set up a mock private key for signing - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let weth = weth(); let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); @@ -1116,8 +1056,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key), - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + true, + router_address(), false, ) .unwrap(); @@ -1134,9 +1074,22 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_sequential_swap_strategy_encoder", hex_calldata.as_str()); @@ -1176,8 +1129,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + false, + router_address(), false, ) .unwrap(); @@ -1194,10 +1147,20 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); let expected = String::from(concat!( @@ -1245,10 +1208,6 @@ mod tests { // The flow is: // USDC -> WETH -> USDC using two pools - // Set up a mock private key for signing (Alice's pk in our router tests) - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); @@ -1299,7 +1258,7 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key), + true, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), false, ) @@ -1312,8 +1271,7 @@ mod tests { checked_token: usdc.clone(), expected_amount: None, checked_amount: Some(BigUint::from_str("99389294").unwrap()), /* Expected output - * from - * test */ + * from test */ slippage: None, swaps: vec![swap_usdc_weth, swap_weth_usdc], sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), @@ -1321,9 +1279,22 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let hex_calldata = hex::encode(&calldata); let expected_input = [ "51bcc7b6", // selector @@ -1417,8 +1388,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + false, + router_address(), false, ) .unwrap(); @@ -1436,10 +1407,20 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); write_calldata_to_file("test_uniswap_v3_uniswap_v2", hex_calldata.as_str()); } @@ -1499,8 +1480,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + false, + router_address(), false, ) .unwrap(); @@ -1518,10 +1499,20 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); write_calldata_to_file("test_uniswap_v3_uniswap_v3", hex_calldata.as_str()); } @@ -1590,8 +1581,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + false, + router_address(), false, ) .unwrap(); @@ -1609,10 +1600,20 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); write_calldata_to_file("test_uniswap_v3_curve", hex_calldata.as_str()); } @@ -1657,8 +1658,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + false, + router_address(), false, ) .unwrap(); @@ -1676,10 +1677,20 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); write_calldata_to_file("test_balancer_v2_uniswap_v2", hex_calldata.as_str()); } @@ -1700,11 +1711,6 @@ mod tests { let usdt = Bytes::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap(); let dai = Bytes::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F").unwrap(); - // Set up a mock private key for signing (Alice's pk in our router tests) - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234" - .to_string(); - let usv2_swap_dai_weth = Swap { component: ProtocolComponent { id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), @@ -1805,8 +1811,8 @@ mod tests { let encoder = SequentialSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key), - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + true, + router_address(), false, ) .unwrap(); @@ -1814,7 +1820,7 @@ mod tests { exact_out: false, given_token: dai, given_amount: BigUint::from_str("1500_000000000000000000").unwrap(), - checked_token: eth, + checked_token: eth.clone(), expected_amount: None, checked_amount: Some(BigUint::from_str("732214216964381330").unwrap()), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), @@ -1830,9 +1836,22 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth, + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_multi_protocol", hex_calldata.as_str()); @@ -1855,10 +1874,6 @@ mod tests { // └──(USV2)──> DAI ───(USV2)──> USDC // - // Set up a mock private key for signing - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let weth = weth(); let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap(); let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap(); @@ -1911,7 +1926,7 @@ mod tests { let encoder = SplitSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key), + true, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), false, ) @@ -1929,9 +1944,22 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_split_swap_strategy_encoder", hex_calldata.as_str()); @@ -1947,10 +1975,6 @@ mod tests { // │ │ // └─ (USV3, 40% split) ──> WETH ─┘ - // Set up a mock private key for signing (Alice's pk in our router tests) - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); @@ -2021,7 +2045,7 @@ mod tests { let encoder = SplitSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key.clone()), + true, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), false, ) @@ -2043,9 +2067,22 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let hex_calldata = hex::encode(&calldata); let expected_input = [ @@ -2114,10 +2151,6 @@ mod tests { // │ │ // └─── (USV3, 40% split) ───┘ - // Set up a mock private key for signing (Alice's pk in our router tests) - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap(); let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); @@ -2184,7 +2217,7 @@ mod tests { let encoder = SplitSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key.clone()), + true, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), false, ) @@ -2206,9 +2239,22 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let hex_calldata = hex::encode(&calldata); let expected_input = [ @@ -2310,7 +2356,7 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, + false, Bytes::from_str("0xA4AD4f68d0b91CFD19687c881e50f3A00242828c").unwrap(), false, ) @@ -2331,10 +2377,19 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_single_encoding_strategy_ekubo", hex_calldata.as_str()); } @@ -2360,7 +2415,7 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, + false, Bytes::from_str("0xA4AD4f68d0b91CFD19687c881e50f3A00242828c").unwrap(), false, ) @@ -2381,10 +2436,19 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file("test_single_encoding_strategy_maverick", hex_calldata.as_str()); } @@ -2397,10 +2461,6 @@ mod tests { // // ETH ───(USV4)──> PEPE // - // Set up a mock private key for signing (Alice's pk in our router tests) - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let eth = eth(); let pepe = Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap(); @@ -2426,7 +2486,7 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key), + true, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), false, ) @@ -2434,7 +2494,7 @@ mod tests { let solution = Solution { exact_out: false, - given_token: eth, + given_token: eth.clone(), given_amount: BigUint::from_str("1_000000000000000000").unwrap(), checked_token: pepe, expected_amount: None, @@ -2446,9 +2506,17 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = + encode_tycho_router_call(encoded_solution, &solution, false, router_address(), eth) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file( @@ -2465,10 +2533,6 @@ mod tests { // // USDC ───(USV4)──> ETH // - // Set up a mock private key for signing (Alice's pk in our router tests) - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let eth = eth(); let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); @@ -2498,7 +2562,7 @@ mod tests { let encoder = SplitSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key), + true, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), false, ) @@ -2508,7 +2572,7 @@ mod tests { exact_out: false, given_token: usdc, given_amount: BigUint::from_str("3000_000000").unwrap(), - checked_token: eth, + checked_token: eth.clone(), expected_amount: None, checked_amount: Some(BigUint::from_str("1117254495486192350").unwrap()), slippage: None, @@ -2518,9 +2582,17 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = + encode_tycho_router_call(encoded_solution, &solution, false, router_address(), eth) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file( @@ -2537,10 +2609,6 @@ mod tests { // USDC ──(USV4)──> ETH ───(USV4)──> PEPE // - // Set up a mock private key for signing (Alice's pk in our router tests) - let private_key = - "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); - let eth = eth(); let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); let pepe = Bytes::from_str("0x6982508145454Ce325dDbE47a25d4ec3d2311933").unwrap(); @@ -2589,7 +2657,7 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - Some(private_key), + true, Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395"), false, ) @@ -2608,9 +2676,17 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let mut encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let (permit, signature) = get_permit(eth_chain(), router_address(), &solution); + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + + let calldata = + encode_tycho_router_call(encoded_solution, &solution, false, router_address(), eth) + .unwrap() + .data; let expected_input = [ "30ace1b1", // Function selector (single swap) @@ -2694,8 +2770,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + false, + router_address(), false, ) .unwrap(); @@ -2715,10 +2791,20 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; + let hex_calldata = encode(&calldata); write_calldata_to_file("test_single_encoding_strategy_curve", hex_calldata.as_str()); } @@ -2758,8 +2844,8 @@ mod tests { let encoder = SingleSwapStrategyEncoder::new( eth_chain(), swap_encoder_registry, - None, - Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), + false, + router_address(), false, ) .unwrap(); @@ -2779,9 +2865,18 @@ mod tests { ..Default::default() }; - let (calldata, _) = encoder - .encode_strategy(solution) + let encoded_solution = encoder + .encode_strategy(solution.clone()) .unwrap(); + let calldata = encode_tycho_router_call( + encoded_solution, + &solution, + false, + router_address(), + eth(), + ) + .unwrap() + .data; let hex_calldata = encode(&calldata); write_calldata_to_file( diff --git a/src/encoding/evm/tycho_encoders.rs b/src/encoding/evm/tycho_encoders.rs index 51d27f5..45dd497 100644 --- a/src/encoding/evm/tycho_encoders.rs +++ b/src/encoding/evm/tycho_encoders.rs @@ -6,14 +6,18 @@ use tycho_common::Bytes; use crate::encoding::{ errors::EncodingError, evm::{ + approvals::permit2::Permit2, constants::{GROUPABLE_PROTOCOLS, IN_TRANSFER_REQUIRED_PROTOCOLS}, + encoding_utils::encode_tycho_router_call, group_swaps::group_swaps, strategy_encoder::strategy_encoders::{ SequentialSwapStrategyEncoder, SingleSwapStrategyEncoder, SplitSwapStrategyEncoder, }, swap_encoder::swap_encoder_registry::SwapEncoderRegistry, }, - models::{Chain, EncodingContext, NativeAction, Solution, Transaction, TransferType}, + models::{ + Chain, EncodedSolution, EncodingContext, NativeAction, Solution, Transaction, TransferType, + }, strategy_encoder::StrategyEncoder, tycho_encoder::TychoEncoder, }; @@ -26,6 +30,8 @@ use crate::encoding::{ /// * `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 #[derive(Clone)] pub struct TychoRouterEncoder { single_swap_strategy: SingleSwapStrategyEncoder, @@ -33,6 +39,9 @@ pub struct TychoRouterEncoder { split_swap_strategy: SplitSwapStrategyEncoder, native_address: Bytes, wrapped_address: Bytes, + router_address: Bytes, + token_in_already_in_router: bool, + permit2: Option, } impl TychoRouterEncoder { @@ -45,78 +54,111 @@ impl TychoRouterEncoder { ) -> 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())?) + } else { + None + }; Ok(TychoRouterEncoder { single_swap_strategy: SingleSwapStrategyEncoder::new( chain.clone(), swap_encoder_registry.clone(), - swapper_pk.clone(), + permit2.is_some(), router_address.clone(), token_in_already_in_router, )?, sequential_swap_strategy: SequentialSwapStrategyEncoder::new( chain.clone(), swap_encoder_registry.clone(), - swapper_pk.clone(), + permit2.is_some(), router_address.clone(), token_in_already_in_router, )?, split_swap_strategy: SplitSwapStrategyEncoder::new( chain, swap_encoder_registry, - None, + permit2.is_some(), router_address.clone(), token_in_already_in_router, )?, native_address, wrapped_address, + router_address, + token_in_already_in_router, + permit2, }) } + + fn encode_solution(&self, solution: &Solution) -> Result { + self.validate_solution(solution)?; + let protocols: HashSet = solution + .clone() + .swaps + .into_iter() + .map(|swap| swap.component.protocol_system) + .collect(); + + let mut encoded_solution = if (solution.swaps.len() == 1) || + (protocols.len() == 1 && + protocols + .iter() + .any(|p| GROUPABLE_PROTOCOLS.contains(&p.as_str()))) + { + self.single_swap_strategy + .encode_strategy(solution.clone())? + } else if solution + .swaps + .iter() + .all(|swap| swap.split == 0.0) + { + self.sequential_swap_strategy + .encode_strategy(solution.clone())? + } else { + self.split_swap_strategy + .encode_strategy(solution.clone())? + }; + + if let Some(permit2) = self.permit2.clone() { + let (permit, signature) = permit2.get_permit( + &self.router_address, + &solution.sender, + &solution.given_token, + &solution.given_amount, + )?; + encoded_solution.permit = Some(permit); + encoded_solution.signature = Some(signature); + } + Ok(encoded_solution) + } } impl TychoEncoder for TychoRouterEncoder { + fn encode_solutions( + &self, + solutions: Vec, + ) -> Result, EncodingError> { + let mut result: Vec = Vec::new(); + for solution in solutions.iter() { + let encoded_solution = self.encode_solution(solution)?; + result.push(encoded_solution); + } + Ok(result) + } + fn encode_calldata(&self, solutions: Vec) -> Result, EncodingError> { let mut transactions: Vec = Vec::new(); for solution in solutions.iter() { - self.validate_solution(solution)?; + let encoded_solution = self.encode_solution(solution)?; - let protocols: HashSet = solution - .clone() - .swaps - .into_iter() - .map(|swap| swap.component.protocol_system) - .collect(); + let transaction = encode_tycho_router_call( + encoded_solution, + solution, + self.token_in_already_in_router, + self.router_address.clone(), + self.native_address.clone(), + )?; - let (contract_interaction, target_address) = if (solution.swaps.len() == 1) || - (protocols.len() == 1 && - protocols - .iter() - .any(|p| GROUPABLE_PROTOCOLS.contains(&p.as_str()))) - { - self.single_swap_strategy - .encode_strategy(solution.clone())? - } else if solution - .swaps - .iter() - .all(|swap| swap.split == 0.0) - { - self.sequential_swap_strategy - .encode_strategy(solution.clone())? - } else { - self.split_swap_strategy - .encode_strategy(solution.clone())? - }; - - let value = if solution.given_token == self.native_address { - solution.given_amount.clone() - } else { - BigUint::ZERO - }; - - transactions.push(Transaction { - value, - data: contract_interaction, - to: target_address, - }); + transactions.push(transaction); } Ok(transactions) } @@ -300,6 +342,14 @@ impl TychoExecutorEncoder { } impl TychoEncoder for TychoExecutorEncoder { + fn encode_solutions( + &self, + _solutions: Vec, + ) -> Result, EncodingError> { + Err(EncodingError::NotImplementedError( + "Encoding solutions for TychoExecutorEncoder is not implemented".to_string(), + )) + } fn encode_calldata(&self, solutions: Vec) -> Result, EncodingError> { let mut transactions: Vec = Vec::new(); let solution = solutions diff --git a/src/encoding/evm/utils.rs b/src/encoding/evm/utils.rs index 20e821e..ab53b4d 100644 --- a/src/encoding/evm/utils.rs +++ b/src/encoding/evm/utils.rs @@ -10,7 +10,7 @@ use alloy::{ providers::{ProviderBuilder, RootProvider}, transports::BoxTransport, }; -use alloy_primitives::{aliases::U24, keccak256, Address, FixedBytes, Keccak256, U256, U8}; +use alloy_primitives::{aliases::U24, Address, U256, U8}; use alloy_sol_types::SolValue; use num_bigint::BigUint; use once_cell::sync::Lazy; @@ -40,28 +40,6 @@ pub fn biguint_to_u256(value: &BigUint) -> U256 { U256::from_be_slice(&bytes) } -/// Encodes the input data for a function call to the given function selector. -pub fn encode_input(selector: &str, mut encoded_args: Vec) -> 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 -} - /// Converts a decimal to a `U24` value. The percentage is a `f64` value between 0 and 1. /// MAX_UINT24 corresponds to 100%. pub fn percentage_to_uint24(decimal: f64) -> U24 { @@ -116,12 +94,6 @@ pub fn pad_to_fixed_size(input: &[u8]) -> Result<[u8; N], Encodi Ok(padded) } -/// Encodes a function selector to a fixed size array of 4 bytes. -pub fn encode_function_selector(selector: &str) -> FixedBytes<4> { - let hash = keccak256(selector.as_bytes()); - FixedBytes::<4>::from([hash[0], hash[1], hash[2], hash[3]]) -} - /// Extracts a static attribute from a swap. pub fn get_static_attribute(swap: &Swap, attribute_name: &str) -> Result, EncodingError> { Ok(swap diff --git a/src/encoding/models.rs b/src/encoding/models.rs index ef8ae1b..53406e2 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -1,3 +1,4 @@ +use alloy_primitives::PrimitiveSignature as Signature; use hex; use num_bigint::BigUint; use serde::{Deserialize, Serialize}; @@ -8,6 +9,7 @@ use tycho_common::{ use crate::encoding::{ errors::EncodingError, + evm::approvals::permit2::PermitSingle, serde_primitives::{biguint_string, biguint_string_option}, }; @@ -96,6 +98,22 @@ pub struct Transaction { pub data: Vec, } +/// Represents a solution that has been encoded for execution. +/// +/// # Fields +/// * `swaps`: Encoded swaps to be executed. +/// * `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). +pub struct EncodedSolution { + pub swaps: Vec, + pub selector: String, + pub n_tokens: usize, + pub permit: Option, + pub signature: Option, +} + /// Represents necessary attributes for encoding an order. /// /// # Fields diff --git a/src/encoding/strategy_encoder.rs b/src/encoding/strategy_encoder.rs index 57630a0..f983507 100644 --- a/src/encoding/strategy_encoder.rs +++ b/src/encoding/strategy_encoder.rs @@ -1,6 +1,8 @@ -use tycho_common::Bytes; - -use crate::encoding::{errors::EncodingError, models::Solution, swap_encoder::SwapEncoder}; +use crate::encoding::{ + errors::EncodingError, + models::{EncodedSolution, Solution}, + swap_encoder::SwapEncoder, +}; /// A trait that defines how to encode a `Solution` for execution. pub trait StrategyEncoder { @@ -13,11 +15,8 @@ pub trait StrategyEncoder { /// path /// /// # Returns - /// * `Result<(Vec, Bytes, Option), EncodingError>` - A tuple containing: - /// - The encoded data as bytes - /// - The address of the contract to call (router or executor) - /// - Optionally, the function selector to use when calling the contract - fn encode_strategy(&self, solution: Solution) -> Result<(Vec, Bytes), EncodingError>; + /// * `Result` + fn encode_strategy(&self, solution: Solution) -> Result; /// Retrieves the swap encoder for a specific protocol system. /// diff --git a/src/encoding/tycho_encoder.rs b/src/encoding/tycho_encoder.rs index b724c39..7e0365a 100644 --- a/src/encoding/tycho_encoder.rs +++ b/src/encoding/tycho_encoder.rs @@ -1,20 +1,75 @@ use crate::encoding::{ errors::EncodingError, - models::{Solution, Transaction}, + models::{EncodedSolution, Solution, Transaction}, }; -/// A high-level encoder that converts solutions into executable transactions. Allows for modularity -/// in the encoding process. +/// A high-level interface for encoding solutions into Tycho-compatible transactions or raw call +/// data. +/// +/// This trait is designed to abstract the encoding logic required to prepare swap transactions for +/// the Tycho Router. It enables modular and customizable construction of transactions, allowing +/// integrators to maintain full control over the execution constraints. +/// +/// # User Responsibility +/// +/// While this trait provides convenience methods, it is **strongly recommended** that users favor +/// [`encode_solutions`] over [`encode_calldata`]. This is because: +/// +/// - `encode_solutions` returns raw [`EncodedSolution`] objects, which include Tycho’s swap path +/// encoding, but leave **function argument encoding entirely in the user’s hands**. +/// - The function arguments to the router (e.g., `minAmountOut`, `receiver`, `unwrap`, `permit2`, +/// etc.) are used as **guardrails** to ensure safe on-chain execution. +/// - Automatically constructing full transactions via [`encode_calldata`] can obscure these +/// important safeguards and may result in unexpected behavior or vulnerability to MEV. +/// +/// Tycho is only responsible for generating the internal swap plan. **The user must encode the +/// outer function call arguments themselves** and verify that they enforce correct and secure +/// behavior. pub trait TychoEncoder { - /// Encodes solutions into transactions that can be executed by the Tycho router. + /// Encodes a list of [`Solution`]s into [`EncodedSolution`]s, which include the selector and + /// internal swap call data. /// - /// # Arguments - /// * `solutions` - Vector of solutions to encode, each potentially using different setups (swap - /// paths, protocols, wrapping, etc.) + /// This method gives users maximum flexibility and control. It **does not** produce full + /// transaction objects. Users are responsible for: + /// - Constructing the full calldata using their own encoding logic. + /// - Managing execution-critical parameters like `minAmountOut`. /// /// # Returns - /// * `Result, EncodingError>` - Vector of executable transactions + /// A vector of encoded solutions, each containing: + /// - The Tycho method selector + /// - The encoded swap path + /// - Additional metadata (e.g., permit2 information) + /// + /// # Recommendation + /// Use this method if you care about execution safety and want to avoid surprises. + fn encode_solutions( + &self, + solutions: Vec, + ) -> Result, EncodingError>; + + /// Encodes a list of [`Solution`]s directly into executable transactions for the Tycho router. + /// + /// This method wraps around Tycho’s example encoding logic (see [`encode_tycho_router_call`]) + /// and should only be used for **prototyping or development**. + /// + /// # Warning + /// This implementation uses default logic to construct the outer calldata (e.g., for setting + /// `minAmountOut`). This might not be optimal or safe for production use. + /// + /// To ensure correctness, **users should implement their own encoding pipeline** using + /// [`encode_solutions`]. + /// + /// # Returns + /// A vector of fully constructed [`Transaction`]s that can be submitted to a node or bundler. fn encode_calldata(&self, solutions: Vec) -> Result, EncodingError>; + /// Performs solution-level validation and sanity checks. + /// + /// This function can be used to verify whether a proposed solution is structurally sound and + /// ready for encoding. + /// + /// # Returns + /// - `Ok(())` if the solution is valid. + /// - `Err(EncodingError)` if the solution is malformed or unsupported. fn validate_solution(&self, solution: &Solution) -> Result<(), EncodingError>; }