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)]