From d5c589d2c09da8e7f22b40be6e5b236e0eb16645 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 26 Mar 2025 12:42:28 +0100 Subject: [PATCH 1/5] feat: Remove router_address from Solution, set default - The router address can be set when creating the SplitSwapStrategy, which now takes an Option as input during initialization (another breaking interface change) - This change allows us to change our router address in the future with minimal effect on the users. Users should only pass the router address in the split swap initialization if they intend to use their own router for execution. - This change also means that the router address does not need to be passed with the solution, even when using the executor strategy (which was pointless). - I thought of having a router_address() method to set this in the encoder builder - but it seemed too messy, since we don't need the router address for execution for example. We would then potentially unnecessarily load and set the default router address when not needed. It is also not even used at the highest level EVMTychoEncoder, so it makes more sense for it to be directly associated with the swap strategy instead. - Users will now not be able to encode for different routers without re-initializing the strategy. We assumed this use case to be very rare and not worth supporting. --- config/router_addresses.json | 4 + examples/encoding-example/main.rs | 3 - src/encoding/evm/constants.rs | 1 + src/encoding/evm/encoder_builder.rs | 3 +- .../evm/strategy_encoder/strategy_encoders.rs | 141 ++++++++++++------ src/encoding/evm/tycho_encoder.rs | 1 - src/encoding/models.rs | 5 +- 7 files changed, 103 insertions(+), 55 deletions(-) create mode 100644 config/router_addresses.json diff --git a/config/router_addresses.json b/config/router_addresses.json new file mode 100644 index 0000000..ecc244a --- /dev/null +++ b/config/router_addresses.json @@ -0,0 +1,4 @@ +{ + "ethereum": "0x6512E8f80Ab24e6dD6eB042897898516c3175375", + "base": "0xC2C23b0199525DE070D126860133dc3badaD2EEb" +} diff --git a/examples/encoding-example/main.rs b/examples/encoding-example/main.rs index 70b46d9..7ccc0bc 100644 --- a/examples/encoding-example/main.rs +++ b/examples/encoding-example/main.rs @@ -13,8 +13,6 @@ use tycho_execution::encoding::{ fn main() { // Setup variables - let router_address = Bytes::from_str("0x1234567890abcdef1234567890abcdef12345678") - .expect("Failed to create router address"); let swapper_pk = "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); let user_address = Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") @@ -61,7 +59,6 @@ fn main() { exact_out: false, // it's an exact in solution checked_amount: None, // the amount out will not be checked in execution swaps: vec![simple_swap], - router_address, ..Default::default() }; diff --git a/src/encoding/evm/constants.rs b/src/encoding/evm/constants.rs index d742100..0b6eb48 100644 --- a/src/encoding/evm/constants.rs +++ b/src/encoding/evm/constants.rs @@ -1,6 +1,7 @@ use std::{collections::HashSet, sync::LazyLock}; pub const DEFAULT_EXECUTORS_JSON: &str = include_str!("../../../config/executor_addresses.json"); +pub const DEFAULT_ROUTERS_JSON: &str = include_str!("../../../config/router_addresses.json"); /// These protocols support the optimization of grouping swaps. /// diff --git a/src/encoding/evm/encoder_builder.rs b/src/encoding/evm/encoder_builder.rs index 7a13804..f39d6e6 100644 --- a/src/encoding/evm/encoder_builder.rs +++ b/src/encoding/evm/encoder_builder.rs @@ -57,7 +57,7 @@ impl EVMEncoderBuilder { let swap_encoder_registry = SwapEncoderRegistry::new(self.executors_file_path.clone(), chain)?; let strategy = - Box::new(SplitSwapStrategyEncoder::new(chain, swap_encoder_registry, None)?); + Box::new(SplitSwapStrategyEncoder::new(chain, swap_encoder_registry, None, None)?); Ok(EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy), @@ -83,6 +83,7 @@ impl EVMEncoderBuilder { chain, swap_encoder_registry, Some(swapper_pk), + None, )?); Ok(EVMEncoderBuilder { chain: Some(chain), diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 7ac8fd7..1dcc3bd 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1,4 +1,7 @@ -use std::{collections::HashSet, str::FromStr}; +use std::{ + collections::{HashMap, HashSet}, + str::FromStr, +}; use alloy_primitives::{aliases::U24, U256, U8}; use alloy_sol_types::SolValue; @@ -8,6 +11,7 @@ use crate::encoding::{ errors::EncodingError, evm::{ approvals::permit2::Permit2, + constants::DEFAULT_ROUTERS_JSON, strategy_encoder::{group_swaps::group_swaps, strategy_validators::SplitSwapValidator}, swap_encoder::swap_encoder_registry::SwapEncoderRegistry, utils::{ @@ -68,6 +72,7 @@ pub trait EVMStrategyEncoder: StrategyEncoder { /// * `wrapped_address`: Address of the chain's wrapped token /// * `split_swap_validator`: SplitSwapValidator, responsible for checking validity of split swap /// solutions +/// * `router_address`: Address of the router to be used to execute swaps #[derive(Clone)] pub struct SplitSwapStrategyEncoder { swap_encoder_registry: SwapEncoderRegistry, @@ -76,6 +81,7 @@ pub struct SplitSwapStrategyEncoder { native_address: Bytes, wrapped_address: Bytes, split_swap_validator: SplitSwapValidator, + router_address: Bytes, } impl SplitSwapStrategyEncoder { @@ -83,6 +89,7 @@ impl SplitSwapStrategyEncoder { blockchain: tycho_common::models::Chain, swap_encoder_registry: SwapEncoderRegistry, swapper_pk: Option, + router_address: Option, ) -> Result { let chain = Chain::from(blockchain); let (permit2, selector) = if let Some(swapper_pk) = swapper_pk { @@ -93,6 +100,21 @@ impl SplitSwapStrategyEncoder { "swap(uint256,address,address,uint256,bool,bool,uint256,address,bytes)".to_string(), ) }; + + let tycho_router_address; + if let Some(address) = router_address { + tycho_router_address = address; + } else { + let default_routers: HashMap = + serde_json::from_str(DEFAULT_ROUTERS_JSON)?; + tycho_router_address = default_routers + .get(&chain.name) + .ok_or(EncodingError::FatalError( + "No default router address found for chain".to_string(), + ))? + .to_owned(); + } + Ok(Self { permit2, selector, @@ -100,6 +122,7 @@ impl SplitSwapStrategyEncoder { native_address: chain.native_token()?, wrapped_address: chain.wrapped_token()?, split_swap_validator: SplitSwapValidator, + router_address: tycho_router_address, }) } } @@ -182,9 +205,9 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { let mut grouped_protocol_data: Vec = vec![]; for swap in grouped_swap.swaps.iter() { let encoding_context = EncodingContext { - receiver: solution.router_address.clone(), + receiver: self.router_address.clone(), exact_out: solution.exact_out, - router_address: solution.router_address.clone(), + router_address: self.router_address.clone(), group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), }; @@ -213,7 +236,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { }; let method_calldata = if let Some(permit2) = self.permit2.clone() { let (permit, signature) = permit2.get_permit( - &solution.router_address, + &self.router_address, &solution.sender, &solution.given_token, &solution.given_amount, @@ -248,7 +271,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { }; let contract_interaction = encode_input(&self.selector, method_calldata); - Ok((contract_interaction, solution.router_address)) + Ok((contract_interaction, self.router_address.clone())) } fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box> { @@ -293,7 +316,6 @@ impl StrategyEncoder for ExecutorStrategyEncoder { .ok_or_else(|| EncodingError::FatalError("Swap grouping failed".to_string()))?; let receiver = solution.receiver; - let router_address = solution.router_address; let swap_encoder = self .get_swap_encoder(&grouped_swap.protocol_system) @@ -309,7 +331,7 @@ impl StrategyEncoder for ExecutorStrategyEncoder { let encoding_context = EncodingContext { receiver: receiver.clone(), exact_out: solution.exact_out, - router_address: router_address.clone(), + router_address: Bytes::from("0x0000000000000000000000000000000000000000"), group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), }; @@ -396,7 +418,6 @@ mod tests { // The receiver was generated with `makeAddr("bob") using forge` receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), swaps: vec![swap], - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), slippage: None, native_action: None, }; @@ -452,7 +473,6 @@ mod tests { sender: Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(), receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), swaps: vec![swap.clone(), swap], - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), slippage: None, native_action: None, }; @@ -521,7 +541,6 @@ mod tests { slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap_usdc_eth, swap_eth_pepe], ..Default::default() }; @@ -606,9 +625,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: weth, @@ -619,7 +642,6 @@ mod tests { checked_amount, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap], ..Default::default() }; @@ -707,9 +729,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: eth(), @@ -719,7 +745,6 @@ mod tests { checked_amount: Some(BigUint::from_str("2659881924818443699787").unwrap()), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap], native_action: Some(NativeAction::Wrap), ..Default::default() @@ -756,9 +781,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: dai, @@ -768,7 +797,6 @@ mod tests { checked_amount: Some(BigUint::from_str("1_000000000000000000").unwrap()), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap], native_action: Some(NativeAction::Unwrap), ..Default::default() @@ -846,9 +874,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: weth, @@ -858,7 +890,6 @@ mod tests { checked_amount: Some(BigUint::from_str("26173932").unwrap()), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap_weth_dai, swap_weth_wbtc, swap_dai_usdc, swap_wbtc_usdc], ..Default::default() }; @@ -927,9 +958,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: usdc, @@ -940,7 +975,6 @@ mod tests { slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap_usdc_eth, swap_eth_pepe], ..Default::default() }; @@ -1094,8 +1128,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, None).unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + None, + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, given_token: weth, @@ -1106,7 +1145,6 @@ mod tests { checked_amount, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap], ..Default::default() }; @@ -1183,9 +1221,13 @@ mod tests { split: 0f64, }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, @@ -1197,7 +1239,6 @@ mod tests { slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap_eth_pepe], ..Default::default() }; @@ -1247,9 +1288,13 @@ mod tests { }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, @@ -1261,7 +1306,6 @@ mod tests { slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap_usdc_eth], ..Default::default() }; @@ -1331,9 +1375,13 @@ mod tests { }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, Some(private_key)) - .unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + Some(private_key), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), + ) + .unwrap(); let solution = Solution { exact_out: false, @@ -1345,7 +1393,6 @@ mod tests { * test */ slippage: None, swaps: vec![swap_usdc_weth, swap_weth_usdc], - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), ..Default::default() @@ -1483,6 +1530,7 @@ mod tests { eth_chain(), swap_encoder_registry, Some(private_key.clone()), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), ) .unwrap(); @@ -1494,7 +1542,6 @@ mod tests { expected_amount: None, checked_amount: Some(BigUint::from_str("99574171").unwrap()), /* Expected output from * test */ - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), slippage: None, @@ -1640,6 +1687,7 @@ mod tests { eth_chain(), swap_encoder_registry, Some(private_key.clone()), + Some(Bytes::from("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395")), ) .unwrap(); @@ -1651,7 +1699,6 @@ mod tests { expected_amount: None, checked_amount: Some(BigUint::from_str("99525908").unwrap()), /* Expected output from * test */ - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), slippage: None, diff --git a/src/encoding/evm/tycho_encoder.rs b/src/encoding/evm/tycho_encoder.rs index 5f20b2e..85f541f 100644 --- a/src/encoding/evm/tycho_encoder.rs +++ b/src/encoding/evm/tycho_encoder.rs @@ -249,7 +249,6 @@ mod tests { exact_out: false, given_amount: eth_amount_in.clone(), given_token: eth(), - router_address: Bytes::from_str("0x1234567890abcdef1234567890abcdef12345678").unwrap(), swaps: vec![swap], native_action: Some(NativeAction::Wrap), ..Default::default() diff --git a/src/encoding/models.rs b/src/encoding/models.rs index af8a780..04c2c44 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -41,8 +41,6 @@ pub struct Solution { pub checked_amount: Option, /// List of swaps to fulfill the solution. pub swaps: Vec, - /// Address of the router contract to be used for the swaps. - pub router_address: Bytes, /// If set, the corresponding native action will be executed. pub native_action: Option, } @@ -104,7 +102,8 @@ pub struct Transaction { /// /// * `receiver`: Address of the receiver of the out token after the swaps are completed. /// * `exact_out`: true if the solution is a buy order, false if it is a sell order. -/// * `router_address`: Address of the router contract to be used for the swaps. +/// * `router_address`: Address of the router contract to be used for the swaps. Zero address if +/// solution does not require router address. /// * `group_token_in`: Token to be used as the input for the group swap. /// * `group_token_out`: Token to be used as the output for the group swap. #[derive(Clone, Debug)] From 8865e22116dcb8c291caa745de78b4e6241315c8 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 1 Apr 2025 19:55:38 +0200 Subject: [PATCH 2/5] feat: Make EncodingContext.router_address optional --- .../evm/strategy_encoder/strategy_encoders.rs | 4 +-- .../evm/swap_encoder/swap_encoders.rs | 34 ++++++++++++------- src/encoding/models.rs | 2 +- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 1dcc3bd..aed32da 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -207,7 +207,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { let encoding_context = EncodingContext { receiver: self.router_address.clone(), exact_out: solution.exact_out, - router_address: self.router_address.clone(), + router_address: Some(self.router_address.clone()), group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), }; @@ -331,7 +331,7 @@ impl StrategyEncoder for ExecutorStrategyEncoder { let encoding_context = EncodingContext { receiver: receiver.clone(), exact_out: solution.exact_out, - router_address: Bytes::from("0x0000000000000000000000000000000000000000"), + router_address: None, group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), }; diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index f119681..6a8ef9d 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -229,13 +229,21 @@ impl SwapEncoder for BalancerV2SwapEncoder { ) -> Result, EncodingError> { let token_approvals_manager = ProtocolApprovalsManager::new()?; let token = bytes_to_address(&swap.token_in)?; - let router_address = bytes_to_address(&encoding_context.router_address)?; - let approval_needed = token_approvals_manager.approval_needed( - token, - router_address, - Address::from_str(&self.vault_address) - .map_err(|_| EncodingError::FatalError("Invalid vault address".to_string()))?, - )?; + let approval_needed: bool; + + if let Some(router_address) = encoding_context.router_address { + let tycho_router_address = bytes_to_address( + &router_address, + )?; + approval_needed = token_approvals_manager.approval_needed( + token, + tycho_router_address, + Address::from_str(&self.vault_address) + .map_err(|_| EncodingError::FatalError("Invalid vault address".to_string()))?, + )?; + } else { + approval_needed = true; + } let component_id = AlloyBytes::from_str(&swap.component.id) .map_err(|_| EncodingError::FatalError("Invalid component ID".to_string()))?; @@ -350,7 +358,7 @@ mod tests { let encoding_context = EncodingContext { receiver: Bytes::from("0x0000000000000000000000000000000000000001"), exact_out: false, - router_address: Bytes::zero(20), + router_address: Some(Bytes::zero(20)), group_token_in: token_in.clone(), group_token_out: token_out.clone(), }; @@ -397,7 +405,7 @@ mod tests { let encoding_context = EncodingContext { receiver: Bytes::from("0x0000000000000000000000000000000000000001"), exact_out: false, - router_address: Bytes::zero(20), + router_address: Some(Bytes::zero(20)), group_token_in: token_in.clone(), group_token_out: token_out.clone(), }; @@ -445,7 +453,7 @@ mod tests { // The receiver was generated with `makeAddr("bob") using forge` receiver: Bytes::from("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"), exact_out: false, - router_address: Bytes::zero(20), + router_address: Some(Bytes::zero(20)), group_token_in: token_in.clone(), group_token_out: token_out.clone(), }; @@ -503,7 +511,7 @@ mod tests { receiver: Bytes::from("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f"), exact_out: false, // Same as the executor address - router_address: Bytes::from("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f"), + router_address: Some(Bytes::from("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f")), group_token_in: token_in.clone(), group_token_out: token_out.clone(), @@ -567,7 +575,7 @@ mod tests { let encoding_context = EncodingContext { receiver: Bytes::from("0x0000000000000000000000000000000000000001"), exact_out: false, - router_address: Bytes::zero(20), + router_address: Some(Bytes::zero(20)), group_token_in: group_token_in.clone(), // Token out is the same as the group token out group_token_out: token_out.clone(), @@ -606,7 +614,7 @@ mod tests { let context = EncodingContext { receiver: receiver_address.clone(), exact_out: false, - router_address: router_address.clone(), + router_address: Some(router_address.clone()), group_token_in: usde_address.clone(), group_token_out: wbtc_address.clone(), }; diff --git a/src/encoding/models.rs b/src/encoding/models.rs index 04c2c44..a8fce6a 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -110,7 +110,7 @@ pub struct Transaction { pub struct EncodingContext { pub receiver: Bytes, pub exact_out: bool, - pub router_address: Bytes, + pub router_address: Option, pub group_token_in: Bytes, pub group_token_out: Bytes, } From b397ddd2beb007d0bed378949d35a9ce5c5b76c9 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 1 Apr 2025 19:59:58 +0200 Subject: [PATCH 3/5] fix: fix Solution.router_address for Ekubo --- src/encoding/evm/strategy_encoder/strategy_encoders.rs | 10 +++++++--- src/encoding/evm/swap_encoder/swap_encoders.rs | 8 +++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index aed32da..e6ff1fd 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1077,8 +1077,13 @@ mod tests { }; let swap_encoder_registry = get_swap_encoder_registry(); - let encoder = - SplitSwapStrategyEncoder::new(eth_chain(), swap_encoder_registry, None).unwrap(); + let encoder = SplitSwapStrategyEncoder::new( + eth_chain(), + swap_encoder_registry, + None, + Some(Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap()), + ) + .unwrap(); let solution = Solution { exact_out: false, @@ -1091,7 +1096,6 @@ mod tests { // Alice sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), - router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), swaps: vec![swap], ..Default::default() }; diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 6a8ef9d..ad6ffc9 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -232,9 +232,7 @@ impl SwapEncoder for BalancerV2SwapEncoder { let approval_needed: bool; if let Some(router_address) = encoding_context.router_address { - let tycho_router_address = bytes_to_address( - &router_address, - )?; + let tycho_router_address = bytes_to_address(&router_address)?; approval_needed = token_approvals_manager.approval_needed( token, tycho_router_address, @@ -742,7 +740,7 @@ mod tests { group_token_in: token_in.clone(), group_token_out: token_out.clone(), exact_out: false, - router_address: Bytes::default(), + router_address: Some(Bytes::default()), }; let encoder = EkuboSwapEncoder::new(String::default()); @@ -780,7 +778,7 @@ mod tests { group_token_in: group_token_in.clone(), group_token_out: group_token_out.clone(), exact_out: false, - router_address: Bytes::default(), + router_address: Some(Bytes::default()), }; let first_swap = Swap { From 99adaa97966e1863d0effe430ddc38e6e23ed7ac Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 2 Apr 2025 00:54:56 +0200 Subject: [PATCH 4/5] chore: Update latest router address in router_addresses.json --- config/router_addresses.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/router_addresses.json b/config/router_addresses.json index ecc244a..bac76ba 100644 --- a/config/router_addresses.json +++ b/config/router_addresses.json @@ -1,4 +1,4 @@ { - "ethereum": "0x6512E8f80Ab24e6dD6eB042897898516c3175375", + "ethereum": "0x0178f471f219737c51d6005556d2f44de011a08a", "base": "0xC2C23b0199525DE070D126860133dc3badaD2EEb" } From c336a28905a1829da78997ea2126849fdabbcfc6 Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 2 Apr 2025 01:12:02 +0200 Subject: [PATCH 5/5] feat: Support manual router address setting in builder --- src/encoding/evm/encoder_builder.rs | 30 ++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/encoding/evm/encoder_builder.rs b/src/encoding/evm/encoder_builder.rs index f39d6e6..e20c148 100644 --- a/src/encoding/evm/encoder_builder.rs +++ b/src/encoding/evm/encoder_builder.rs @@ -1,4 +1,4 @@ -use tycho_common::models::Chain; +use tycho_common::{models::Chain, Bytes}; use crate::encoding::{ errors::EncodingError, @@ -17,6 +17,7 @@ pub struct EVMEncoderBuilder { strategy: Option>, chain: Option, executors_file_path: Option, + router_address: Option, } impl Default for EVMEncoderBuilder { @@ -27,7 +28,12 @@ impl Default for EVMEncoderBuilder { impl EVMEncoderBuilder { pub fn new() -> Self { - EVMEncoderBuilder { chain: None, strategy: None, executors_file_path: None } + EVMEncoderBuilder { + chain: None, + strategy: None, + executors_file_path: None, + router_address: None, + } } pub fn chain(mut self, chain: Chain) -> Self { self.chain = Some(chain); @@ -41,6 +47,13 @@ impl EVMEncoderBuilder { self } + /// Sets the `router_address` manually. + /// If it's not set, the default router address will be used (config/router_addresses.json) + pub fn router_address(mut self, router_address: Bytes) -> Self { + self.router_address = Some(router_address); + self + } + /// Sets the `strategy_encoder` manually. /// /// **Note**: This method should not be used in combination with `tycho_router` or @@ -56,12 +69,17 @@ impl EVMEncoderBuilder { if let Some(chain) = self.chain { let swap_encoder_registry = SwapEncoderRegistry::new(self.executors_file_path.clone(), chain)?; - let strategy = - Box::new(SplitSwapStrategyEncoder::new(chain, swap_encoder_registry, None, None)?); + let strategy = Box::new(SplitSwapStrategyEncoder::new( + chain, + swap_encoder_registry, + None, + self.router_address.clone(), + )?); Ok(EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy), executors_file_path: self.executors_file_path, + router_address: self.router_address, }) } else { Err(EncodingError::FatalError( @@ -83,12 +101,13 @@ impl EVMEncoderBuilder { chain, swap_encoder_registry, Some(swapper_pk), - None, + self.router_address.clone(), )?); Ok(EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy), executors_file_path: self.executors_file_path, + router_address: self.router_address, }) } else { Err(EncodingError::FatalError( @@ -108,6 +127,7 @@ impl EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy), executors_file_path: self.executors_file_path, + router_address: self.router_address, }) } else { Err(EncodingError::FatalError(