From 03506fabe90c00f1518e42243fd650fb561e2a39 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Tue, 18 Feb 2025 16:49:38 +0000 Subject: [PATCH 1/9] feat: Create a EVMEncoderBuilder - It has two three methods to be created: - new: where the user can pass any custom StrategyEncoder - tycho_router: where the default SplitSwapStrategyEncoder with Tycho Router will be used - direct_execution: where the user can encode only the execution data and integrate this into their workflow. - This replaces StrategyEncoderRegistry - EVMTychoEncoder holds directly the StrategyEncoder and not the registry (per one init of the EVMTychoEncoder there is only one possible StrategyEncoder) - Update quickstart - Update bin (add subcommands to bin to create a different instance of EVMEncoderBuilder) --- don't change below this line --- ENG-4246 Took 33 minutes Took 38 seconds Took 38 seconds --- README.md | 48 ++++++++----- examples/quickstart/main.rs | 17 ++--- src/bin/lib/cli.rs | 27 ++++++-- src/bin/tycho-encode.rs | 37 ++++++---- src/encoding/evm/encoder_builder.rs | 44 ++++++++++++ src/encoding/evm/mod.rs | 3 +- src/encoding/evm/strategy_encoder/mod.rs | 1 - .../strategy_encoder_registry.rs | 69 ------------------- .../strategy_encoders.rs | 7 +- .../strategy_validators.rs | 0 .../evm/swap_encoder/swap_encoder_registry.rs | 7 +- src/encoding/evm/tycho_encoder.rs | 59 +++++----------- src/encoding/mod.rs | 2 +- src/encoding/strategy_encoder.rs | 18 +---- src/encoding/tycho_encoder.rs | 3 +- 15 files changed, 156 insertions(+), 186 deletions(-) create mode 100644 src/encoding/evm/encoder_builder.rs delete mode 100644 src/encoding/evm/strategy_encoder/strategy_encoder_registry.rs rename src/encoding/evm/{strategy_encoder => }/strategy_encoders.rs (99%) rename src/encoding/evm/{strategy_encoder => }/strategy_validators.rs (100%) diff --git a/README.md b/README.md index 52e8939..9893148 100644 --- a/README.md +++ b/README.md @@ -24,30 +24,46 @@ cargo build --release cargo install --path . ``` -After installation, the `tycho-encode` command will be available to use from any directory in your terminal. The command -accepts the following options: +After installation, the `tycho-encode` command will be available to use from any directory in your terminal. + +### Commands + +The command lets you choose the encoding strategy to be used. The available strategies are: + +#### Tycho Router + +`tycho-router`: Encodes a transaction using the Tycho Router encoding strategy. Requires a private key for signing +Permit2. + +Example: + +```bash +echo '' | tycho-encode tycho-router -p 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234 +``` + +#### Direct execution + +`direct-execution`: Encodes a transaction using the direct execution encoding strategy. Does not require a private key. + +Example: + +```bash +echo '' | tycho-encode direct-execution +``` + +### Encoding Transactions + +The commands accept the following options: - `-c`: Path to the executor addresses configuration file (defaults to `src/encoding/config/executor_addresses.json`) - `-p`: Private key for signing approvals (required when direct_execution is false) -### Encoding Transactions - -To encode a transaction, you can pipe a JSON payload to the binary: - -```bash -# Using default config path -echo '' | tycho-encode - -# Using custom config path -echo '' | tycho-encode -c /path/to/your/config.json -``` - #### Example -Here's a complete example that encodes a swap from WETH to DAI using Uniswap V2: +Here's a complete example that encodes a swap from WETH to DAI using Uniswap V2 and the Tycho Router strategy: ```bash -echo '{"sender":"0x1234567890123456789012345678901234567890","receiver":"0x1234567890123456789012345678901234567890","given_token":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","given_amount":"1000000000000000000","checked_token":"0x6B175474E89094C44Da98b954EedeAC495271d0F","exact_out":false,"slippage":0.01,"expected_amount":"1000000000000000000","checked_amount":"990000000000000000","router_address":"0xaa820C29648D5EA543d712cC928377Bd7206a0E7","swaps":[{"component":{"id":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640","protocol_system":"uniswap_v2","protocol_type_name":"UniswapV2Pool","chain":"ethereum","tokens":["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"],"contract_ids":["0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"],"static_attributes":{"factory":"0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f"},"change":"Update","creation_tx":"0x0000000000000000000000000000000000000000000000000000000000000000","created_at":"2024-02-28T12:00:00"},"token_in":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","token_out":"0x6B175474E89094C44Da98b954EedeAC495271d0F","split":1.0}],"direct_execution":true}' | tycho-encode +echo '{"sender":"0x1234567890123456789012345678901234567890","receiver":"0x1234567890123456789012345678901234567890","given_token":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","given_amount":"1000000000000000000","checked_token":"0x6B175474E89094C44Da98b954EedeAC495271d0F","exact_out":false,"slippage":0.01,"expected_amount":"1000000000000000000","checked_amount":"990000000000000000","router_address":"0xaa820C29648D5EA543d712cC928377Bd7206a0E7","swaps":[{"component":{"id":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640","protocol_system":"uniswap_v2","protocol_type_name":"UniswapV2Pool","chain":"ethereum","tokens":["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"],"contract_ids":["0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"],"static_attributes":{"factory":"0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f"},"change":"Update","creation_tx":"0x0000000000000000000000000000000000000000000000000000000000000000","created_at":"2024-02-28T12:00:00"},"token_in":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","token_out":"0x6B175474E89094C44Da98b954EedeAC495271d0F","split":0.0}],"direct_execution":true}' | tycho-encode tycho-router -p 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234 ``` #### JSON Payload Structure: Solution struct diff --git a/examples/quickstart/main.rs b/examples/quickstart/main.rs index c2f534c..6e3b042 100644 --- a/examples/quickstart/main.rs +++ b/examples/quickstart/main.rs @@ -6,12 +6,8 @@ use tycho_core::{ Bytes, }; use tycho_execution::encoding::{ - evm::{ - strategy_encoder::strategy_encoder_registry::EVMStrategyEncoderRegistry, - tycho_encoder::EVMTychoEncoder, - }, + evm::encoder_builder::EVMEncoderBuilder, models::{Solution, Swap}, - strategy_encoder::StrategyEncoderRegistry, tycho_encoder::TychoEncoder, }; @@ -20,16 +16,15 @@ fn main() { let router_address = Bytes::from_str("0x1234567890abcdef1234567890abcdef12345678") .expect("Failed to create router address"); let signer_pk = - Some("0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string()); + "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); let user_address = Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") .expect("Failed to create user address"); // Initialize the encoder - let strategy_encoder_registry = - EVMStrategyEncoderRegistry::new(TychoCoreChain::Ethereum, None, signer_pk.clone()) - .expect("Failed to create strategy encoder registry"); - let encoder = EVMTychoEncoder::new(strategy_encoder_registry, TychoCoreChain::Ethereum) - .expect("Failed to create encoder"); + let encoder = EVMEncoderBuilder::tycho_router(TychoCoreChain::Ethereum, signer_pk, None) + .expect("Failed to create encoder builder") + .build() + .expect("Failed to build encoder"); // ------------------- Encode a simple swap ------------------- diff --git a/src/bin/lib/cli.rs b/src/bin/lib/cli.rs index 60e13d3..88bf9d1 100644 --- a/src/bin/lib/cli.rs +++ b/src/bin/lib/cli.rs @@ -1,4 +1,5 @@ pub use clap::Parser; +use clap::Subcommand; #[derive(Parser)] /// Encode swap transactions for the Tycho router @@ -33,12 +34,24 @@ pub use clap::Parser; /// "direct_execution": false /// } /// ``` +#[command(author, version, about, long_about = None)] pub struct Cli { - /// Private key for signing approvals (required when direct_execution is false) - #[arg(short)] - pub private_key: Option, - - /// Path to the executor addresses configuration file - #[arg(short)] - pub config_path: Option, + #[command(subcommand)] + pub command: Commands, +} + +#[derive(Subcommand)] +pub enum Commands { + /// Use the Tycho router encoding strategy + TychoRouter { + #[arg(short, long)] + config_path: Option, + #[arg(short, long)] + private_key: String, + }, + /// Use the direct execution encoding strategy + DirectExecution { + #[arg(short, long)] + config_path: Option, + }, } diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index f8bd8b3..e0a47da 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -3,21 +3,16 @@ use std::io::{self, Read}; use clap::Parser; use serde_json::Value; use tycho_core::dto::Chain; -use tycho_execution::encoding::{ - evm::{ - strategy_encoder::strategy_encoder_registry::EVMStrategyEncoderRegistry, - tycho_encoder::EVMTychoEncoder, - }, - models::Solution, - strategy_encoder::StrategyEncoderRegistry, - tycho_encoder::TychoEncoder, -}; +use tycho_execution::encoding::{models::Solution, tycho_encoder::TychoEncoder}; mod lib { pub mod cli; } use lib::cli::Cli; +use tycho_execution::encoding::{errors::EncodingError, evm::encoder_builder::EVMEncoderBuilder}; + +use crate::lib::cli::Commands; fn main() -> Result<(), Box> { let cli = Cli::parse(); @@ -33,8 +28,14 @@ fn main() -> Result<(), Box> { } // Encode the solution - let encoded = encode_swaps(&buffer, cli.config_path, cli.private_key)?; - + let encoded = match cli.command { + Commands::TychoRouter { config_path, private_key } => { + encode_swaps(&buffer, config_path, Some(private_key), true)? + } + Commands::DirectExecution { config_path } => { + encode_swaps(&buffer, config_path, None, false)? + } + }; // Output the encoded result as JSON to stdout println!( "{}", @@ -49,12 +50,20 @@ fn encode_swaps( input: &str, config_path: Option, private_key: Option, -) -> Result> { + use_tycho_router: bool, +) -> Result { let solution: Solution = serde_json::from_str(input)?; let chain = Chain::Ethereum; - let strategy_selector = EVMStrategyEncoderRegistry::new(chain, config_path, private_key)?; - let encoder = EVMTychoEncoder::new(strategy_selector, chain)?; + let encoder = if use_tycho_router { + let private_key = private_key.ok_or(EncodingError::FatalError( + "Private key is required for tycho_router".to_string(), + ))?; + EVMEncoderBuilder::tycho_router(chain, private_key, config_path)?.build()? + } else { + EVMEncoderBuilder::direct_execution(chain, config_path)?.build()? + }; + let transactions = encoder.encode_router_calldata(vec![solution])?; Ok(serde_json::json!({ diff --git a/src/encoding/evm/encoder_builder.rs b/src/encoding/evm/encoder_builder.rs new file mode 100644 index 0000000..8ec8500 --- /dev/null +++ b/src/encoding/evm/encoder_builder.rs @@ -0,0 +1,44 @@ +use tycho_core::dto::Chain; + +use crate::encoding::{ + errors::EncodingError, + evm::{ + strategy_encoders::{ExecutorStrategyEncoder, SplitSwapStrategyEncoder}, + swap_encoder::swap_encoder_registry::SwapEncoderRegistry, + tycho_encoder::EVMTychoEncoder, + }, + strategy_encoder::StrategyEncoder, +}; + +pub struct EVMEncoderBuilder { + strategy: Box, + chain: Chain, +} + +impl EVMEncoderBuilder { + pub fn new(chain: Chain, strategy: Box) -> Self { + EVMEncoderBuilder { chain, strategy } + } + pub fn tycho_router( + chain: Chain, + signer_pk: String, + executors_file_path: Option, + ) -> Result { + let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, chain)?; + let strategy = + Box::new(SplitSwapStrategyEncoder::new(signer_pk, chain, swap_encoder_registry)?); + Ok(EVMEncoderBuilder { chain, strategy }) + } + pub fn direct_execution( + chain: Chain, + executors_file_path: Option, + ) -> Result { + let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, chain)?; + let strategy = Box::new(ExecutorStrategyEncoder::new(swap_encoder_registry)); + Ok(EVMEncoderBuilder { chain, strategy }) + } + + pub fn build(self) -> Result { + EVMTychoEncoder::new(self.chain, self.strategy) + } +} diff --git a/src/encoding/evm/mod.rs b/src/encoding/evm/mod.rs index 8b2d01f..3a4dfdc 100644 --- a/src/encoding/evm/mod.rs +++ b/src/encoding/evm/mod.rs @@ -1,6 +1,7 @@ pub mod approvals; mod constants; -pub mod strategy_encoder; +pub mod encoder_builder; +pub mod strategy_encoders; mod swap_encoder; pub mod tycho_encoder; pub mod utils; diff --git a/src/encoding/evm/strategy_encoder/mod.rs b/src/encoding/evm/strategy_encoder/mod.rs index 3372a64..f02f2f1 100644 --- a/src/encoding/evm/strategy_encoder/mod.rs +++ b/src/encoding/evm/strategy_encoder/mod.rs @@ -1,4 +1,3 @@ mod group_swaps; -pub mod strategy_encoder_registry; mod strategy_encoders; mod strategy_validators; diff --git a/src/encoding/evm/strategy_encoder/strategy_encoder_registry.rs b/src/encoding/evm/strategy_encoder/strategy_encoder_registry.rs deleted file mode 100644 index 71b266d..0000000 --- a/src/encoding/evm/strategy_encoder/strategy_encoder_registry.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::collections::HashMap; - -use crate::encoding::{ - errors::EncodingError, - evm::{ - strategy_encoder::strategy_encoders::{ExecutorStrategyEncoder, SplitSwapStrategyEncoder}, - swap_encoder::swap_encoder_registry::SwapEncoderRegistry, - }, - models::{Chain, Solution}, - strategy_encoder::{StrategyEncoder, StrategyEncoderRegistry}, -}; - -/// Contains all supported strategies to encode a solution. -/// -/// # Fields -/// * `strategies` - A hashmap containing the name of the strategy as a key and the strategy encoder -/// as a value. -pub struct EVMStrategyEncoderRegistry { - strategies: HashMap>, -} - -impl StrategyEncoderRegistry for EVMStrategyEncoderRegistry { - fn new( - chain: tycho_core::dto::Chain, - executors_file_path: Option, - signer_pk: Option, - ) -> Result { - let chain = Chain::from(chain); - let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, chain.clone())?; - - let mut strategies: HashMap> = HashMap::new(); - strategies.insert( - "executor".to_string(), - Box::new(ExecutorStrategyEncoder::new(swap_encoder_registry.clone())), - ); - if let Some(signer) = signer_pk { - strategies.insert( - "split_swap".to_string(), - Box::new( - SplitSwapStrategyEncoder::new(signer, chain, swap_encoder_registry).unwrap(), - ), - ); - } - Ok(Self { strategies }) - } - fn get_encoder(&self, solution: &Solution) -> Result<&Box, EncodingError> { - if solution.direct_execution { - self.strategies - .get("executor") - .ok_or(EncodingError::FatalError("Executor strategy not found".to_string())) - } else { - self.strategies - .get("split_swap") - .ok_or(EncodingError::FatalError("Split swap strategy not found. Please pass the signer private key to the StrategySelector constructor".to_string())) - } - } -} - -impl Clone for EVMStrategyEncoderRegistry { - fn clone(&self) -> Self { - Self { - strategies: self - .strategies - .iter() - .map(|(k, v)| (k.clone(), v.clone_box())) - .collect(), - } - } -} diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoders.rs similarity index 99% rename from src/encoding/evm/strategy_encoder/strategy_encoders.rs rename to src/encoding/evm/strategy_encoders.rs index 8c5f571..0a20117 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoders.rs @@ -89,9 +89,10 @@ pub struct SplitSwapStrategyEncoder { impl SplitSwapStrategyEncoder { pub fn new( signer_pk: String, - chain: Chain, + blockchain: tycho_core::dto::Chain, swap_encoder_registry: SwapEncoderRegistry, ) -> Result { + let chain = Chain::from(blockchain); let selector = "swap(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string(); Ok(Self { permit2: Permit2::new(signer_pk, chain.clone())?, @@ -337,8 +338,8 @@ mod tests { use super::*; use crate::encoding::models::Swap; - fn eth_chain() -> Chain { - TychoCoreChain::Ethereum.into() + fn eth_chain() -> TychoCoreChain { + TychoCoreChain::Ethereum } fn eth() -> Bytes { diff --git a/src/encoding/evm/strategy_encoder/strategy_validators.rs b/src/encoding/evm/strategy_validators.rs similarity index 100% rename from src/encoding/evm/strategy_encoder/strategy_validators.rs rename to src/encoding/evm/strategy_validators.rs diff --git a/src/encoding/evm/swap_encoder/swap_encoder_registry.rs b/src/encoding/evm/swap_encoder/swap_encoder_registry.rs index 30599d3..ec73a0a 100644 --- a/src/encoding/evm/swap_encoder/swap_encoder_registry.rs +++ b/src/encoding/evm/swap_encoder/swap_encoder_registry.rs @@ -19,8 +19,9 @@ impl SwapEncoderRegistry { /// executors' addresses in the file at the given path. pub fn new( executors_file_path: Option, - blockchain: Chain, + blockchain: tycho_core::dto::Chain, ) -> Result { + let chain = Chain::from(blockchain); let config_str = if let Some(ref path) = executors_file_path { fs::read_to_string(path).map_err(|e| { EncodingError::FatalError(format!( @@ -34,8 +35,8 @@ impl SwapEncoderRegistry { let config: HashMap> = serde_json::from_str(&config_str)?; let mut encoders = HashMap::new(); let executors = config - .get(&blockchain.name) - .ok_or(EncodingError::FatalError("No executors found for blockchain".to_string()))?; + .get(&chain.name) + .ok_or(EncodingError::FatalError("No executors found for chain".to_string()))?; for (protocol, executor_address) in executors { let builder = SwapEncoderBuilder::new(protocol, executor_address); let encoder = builder.build()?; diff --git a/src/encoding/evm/tycho_encoder.rs b/src/encoding/evm/tycho_encoder.rs index d20f4ad..6ffa5ba 100644 --- a/src/encoding/evm/tycho_encoder.rs +++ b/src/encoding/evm/tycho_encoder.rs @@ -4,26 +4,27 @@ use tycho_core::Bytes; use crate::encoding::{ errors::EncodingError, models::{Chain, NativeAction, Solution, Transaction}, - strategy_encoder::StrategyEncoderRegistry, + strategy_encoder::StrategyEncoder, tycho_encoder::TychoEncoder, }; /// Represents an encoder for a swap using any strategy supported by the strategy registry. /// /// # Fields -/// * `strategy_registry`: S, the strategy registry to use to select the best strategy to encode a -/// solution, based on its supported strategies and the solution attributes. +/// * `strategy_encoder`: Strategy encoder to follow for encoding the solution /// * `native_address`: Address of the chain's native token /// * `wrapped_address`: Address of the chain's wrapped native token -#[derive(Clone)] -pub struct EVMTychoEncoder { - strategy_registry: S, +pub struct EVMTychoEncoder { + strategy_encoder: Box, native_address: Bytes, wrapped_address: Bytes, } -impl EVMTychoEncoder { - pub fn new(strategy_registry: S, chain: tycho_core::dto::Chain) -> Result { +impl EVMTychoEncoder { + pub fn new( + chain: tycho_core::dto::Chain, + strategy_encoder: Box, + ) -> Result { let chain: Chain = Chain::from(chain); if chain.name != *"ethereum" { return Err(EncodingError::InvalidInput( @@ -31,14 +32,14 @@ impl EVMTychoEncoder { )); } Ok(EVMTychoEncoder { - strategy_registry, + strategy_encoder, native_address: chain.native_token()?, wrapped_address: chain.wrapped_token()?, }) } } -impl EVMTychoEncoder { +impl EVMTychoEncoder { /// Raises an `EncodingError` if the solution is not considered valid. /// /// A solution is considered valid if all the following conditions are met: @@ -92,7 +93,7 @@ impl EVMTychoEncoder { } } -impl TychoEncoder for EVMTychoEncoder { +impl TychoEncoder for EVMTychoEncoder { fn encode_router_calldata( &self, solutions: Vec, @@ -101,11 +102,9 @@ impl TychoEncoder for EVMTychoEncoder { for solution in solutions.iter() { self.validate_solution(solution)?; - let strategy = self - .strategy_registry - .get_encoder(solution)?; - let (contract_interaction, target_address, selector) = - strategy.encode_strategy(solution.clone())?; + let (contract_interaction, target_address, selector) = self + .strategy_encoder + .encode_strategy(solution.clone())?; let value = match solution.native_action.as_ref() { Some(NativeAction::Wrap) => solution.given_amount.clone(), @@ -134,10 +133,6 @@ mod tests { models::Swap, strategy_encoder::StrategyEncoder, swap_encoder::SwapEncoder, }; - struct MockStrategyRegistry { - strategy: Box, - } - fn dai() -> Bytes { Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap() } @@ -150,23 +145,6 @@ mod tests { Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap() } - impl StrategyEncoderRegistry for MockStrategyRegistry { - fn new( - _chain: tycho_core::dto::Chain, - _executors_file_path: Option, - _signer_pk: Option, - ) -> Result { - Ok(Self { strategy: Box::new(MockStrategy) }) - } - - fn get_encoder( - &self, - _solution: &Solution, - ) -> Result<&Box, EncodingError> { - Ok(&self.strategy) - } - } - #[derive(Clone)] struct MockStrategy; @@ -192,10 +170,9 @@ mod tests { } } - fn get_mocked_tycho_encoder() -> EVMTychoEncoder { - let strategy_registry = - MockStrategyRegistry::new(TychoCoreChain::Ethereum, None, None).unwrap(); - EVMTychoEncoder::new(strategy_registry, TychoCoreChain::Ethereum).unwrap() + fn get_mocked_tycho_encoder() -> EVMTychoEncoder { + let strategy_encoder = Box::new(MockStrategy {}); + EVMTychoEncoder::new(TychoCoreChain::Ethereum, strategy_encoder).unwrap() } #[test] diff --git a/src/encoding/mod.rs b/src/encoding/mod.rs index be17c4a..6dbb7ef 100644 --- a/src/encoding/mod.rs +++ b/src/encoding/mod.rs @@ -1,4 +1,4 @@ -mod errors; +pub mod errors; #[cfg(feature = "evm")] pub mod evm; pub mod models; diff --git a/src/encoding/strategy_encoder.rs b/src/encoding/strategy_encoder.rs index 550392d..a3a5549 100644 --- a/src/encoding/strategy_encoder.rs +++ b/src/encoding/strategy_encoder.rs @@ -1,4 +1,4 @@ -use tycho_core::{dto::Chain, Bytes}; +use tycho_core::Bytes; use crate::encoding::{errors::EncodingError, models::Solution, swap_encoder::SwapEncoder}; @@ -13,19 +13,3 @@ pub trait StrategyEncoder { fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box>; fn clone_box(&self) -> Box; } - -/// Contains the supported strategies to encode a solution, and chooses the best strategy to encode -/// a solution based on the solution's attributes. -pub trait StrategyEncoderRegistry { - fn new( - chain: Chain, - executors_file_path: Option, - signer_pk: Option, - ) -> Result - where - Self: Sized; - - /// Returns the strategy encoder that should be used to encode the given solution. - #[allow(clippy::borrowed_box)] - fn get_encoder(&self, solution: &Solution) -> Result<&Box, EncodingError>; -} diff --git a/src/encoding/tycho_encoder.rs b/src/encoding/tycho_encoder.rs index 3be2277..7fcbff2 100644 --- a/src/encoding/tycho_encoder.rs +++ b/src/encoding/tycho_encoder.rs @@ -1,12 +1,11 @@ use crate::encoding::{ errors::EncodingError, models::{Solution, Transaction}, - strategy_encoder::StrategyEncoderRegistry, }; /// An encoder must implement this trait in order to encode a solution into a Transaction for /// execution using a Tycho router or related contracts. -pub trait TychoEncoder { +pub trait TychoEncoder { fn encode_router_calldata( &self, solutions: Vec, From 8537d274692aabf45a1ce79ded6c40998ba6fc50 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Tue, 18 Feb 2025 16:54:55 +0000 Subject: [PATCH 2/9] feat: Remove direct_execution from Solution This is handled when creating a new TychoEncoder --- don't change below this line --- ENG-4246 Took 4 minutes --- src/bin/lib/cli.rs | 5 ++--- src/encoding/evm/strategy_encoders.rs | 1 - src/encoding/models.rs | 5 ----- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/bin/lib/cli.rs b/src/bin/lib/cli.rs index 88bf9d1..ba83b13 100644 --- a/src/bin/lib/cli.rs +++ b/src/bin/lib/cli.rs @@ -28,10 +28,9 @@ use clap::Subcommand; /// }, /// "token_in": "0x...", /// "token_out": "0x...", -/// "split": 1.0 +/// "split": 0.0 /// }], -/// "router_address": "0x...", -/// "direct_execution": false +/// "router_address": "0x..." /// } /// ``` #[command(author, version, about, long_about = None)] diff --git a/src/encoding/evm/strategy_encoders.rs b/src/encoding/evm/strategy_encoders.rs index 0a20117..4dd4bf8 100644 --- a/src/encoding/evm/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoders.rs @@ -385,7 +385,6 @@ mod tests { // The receiver was generated with `makeAddr("bob") using forge` receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), swaps: vec![swap], - direct_execution: true, router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), slippage: None, native_action: None, diff --git a/src/encoding/models.rs b/src/encoding/models.rs index 78a4a3f..1111ffc 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -45,11 +45,6 @@ pub struct Solution { pub router_address: Bytes, /// If set, the corresponding native action will be executed. pub native_action: Option, - /// If set to true, the solution will be encoded to be sent directly to the Executor and - /// skip the router. The user is responsible for managing necessary approvals and token - /// transfers. - #[serde(default)] - pub direct_execution: bool, } /// Represents an action to be performed on the native token either before or after the swap. From 30b5ab9025ae3292e4977f1ed10ceac4850d7669 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Tue, 18 Feb 2025 16:58:24 +0000 Subject: [PATCH 3/9] fix: After rebase fixes Put the strategy_encoders.rs back inside the strategy_encoder directory --- don't change below this line --- ENG-4246 Took 3 minutes Took 12 seconds --- src/encoding/evm/encoder_builder.rs | 2 +- src/encoding/evm/mod.rs | 2 +- src/encoding/evm/{ => strategy_encoder}/strategy_encoders.rs | 0 src/encoding/evm/{ => strategy_encoder}/strategy_validators.rs | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename src/encoding/evm/{ => strategy_encoder}/strategy_encoders.rs (100%) rename src/encoding/evm/{ => strategy_encoder}/strategy_validators.rs (100%) diff --git a/src/encoding/evm/encoder_builder.rs b/src/encoding/evm/encoder_builder.rs index 8ec8500..f0efb7b 100644 --- a/src/encoding/evm/encoder_builder.rs +++ b/src/encoding/evm/encoder_builder.rs @@ -3,7 +3,7 @@ use tycho_core::dto::Chain; use crate::encoding::{ errors::EncodingError, evm::{ - strategy_encoders::{ExecutorStrategyEncoder, SplitSwapStrategyEncoder}, + strategy_encoder::strategy_encoders::{ExecutorStrategyEncoder, SplitSwapStrategyEncoder}, swap_encoder::swap_encoder_registry::SwapEncoderRegistry, tycho_encoder::EVMTychoEncoder, }, diff --git a/src/encoding/evm/mod.rs b/src/encoding/evm/mod.rs index 3a4dfdc..b86b4dd 100644 --- a/src/encoding/evm/mod.rs +++ b/src/encoding/evm/mod.rs @@ -1,7 +1,7 @@ pub mod approvals; mod constants; pub mod encoder_builder; -pub mod strategy_encoders; +pub mod strategy_encoder; mod swap_encoder; pub mod tycho_encoder; pub mod utils; diff --git a/src/encoding/evm/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs similarity index 100% rename from src/encoding/evm/strategy_encoders.rs rename to src/encoding/evm/strategy_encoder/strategy_encoders.rs diff --git a/src/encoding/evm/strategy_validators.rs b/src/encoding/evm/strategy_encoder/strategy_validators.rs similarity index 100% rename from src/encoding/evm/strategy_validators.rs rename to src/encoding/evm/strategy_encoder/strategy_validators.rs From 8794dc674af4f02a3c33e1d0ade2179a3de959a5 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Tue, 18 Feb 2025 17:14:20 +0000 Subject: [PATCH 4/9] chore: Delete unnecessary empty file --- don't change below this line --- ENG-4246 Took 3 minutes --- src/encoding/evm/models.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/encoding/evm/models.rs diff --git a/src/encoding/evm/models.rs b/src/encoding/evm/models.rs deleted file mode 100644 index e69de29..0000000 From d9d56e7a8c416aaf3334099cc0e8f880b2b3943f Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Tue, 18 Feb 2025 17:50:47 +0000 Subject: [PATCH 5/9] chore: Rename signer_pk to swapper_pk --- don't change below this line --- ENG-4246 Took 15 minutes --- README.md | 9 +++++---- examples/quickstart/main.rs | 4 ++-- src/bin/lib/cli.rs | 2 +- src/bin/tycho-encode.rs | 10 +++++----- src/encoding/evm/approvals/permit2.rs | 10 +++++----- src/encoding/evm/encoder_builder.rs | 4 ++-- src/encoding/evm/strategy_encoder/strategy_encoders.rs | 4 ++-- 7 files changed, 22 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 9893148..ada92e4 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Permit2. Example: ```bash -echo '' | tycho-encode tycho-router -p 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234 +echo '' | tycho-encode tycho-router --swapper-pk 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234 ``` #### Direct execution @@ -55,15 +55,16 @@ echo '' | tycho-encode direct-execution The commands accept the following options: -- `-c`: Path to the executor addresses configuration file (defaults to `src/encoding/config/executor_addresses.json`) -- `-p`: Private key for signing approvals (required when direct_execution is false) +- `--config_path`: Path to the executor addresses configuration file (defaults + to `src/encoding/config/executor_addresses.json`) +- `--swapper-pk`: Private key for signing approvals (required when direct_execution is false) #### Example Here's a complete example that encodes a swap from WETH to DAI using Uniswap V2 and the Tycho Router strategy: ```bash -echo '{"sender":"0x1234567890123456789012345678901234567890","receiver":"0x1234567890123456789012345678901234567890","given_token":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","given_amount":"1000000000000000000","checked_token":"0x6B175474E89094C44Da98b954EedeAC495271d0F","exact_out":false,"slippage":0.01,"expected_amount":"1000000000000000000","checked_amount":"990000000000000000","router_address":"0xaa820C29648D5EA543d712cC928377Bd7206a0E7","swaps":[{"component":{"id":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640","protocol_system":"uniswap_v2","protocol_type_name":"UniswapV2Pool","chain":"ethereum","tokens":["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"],"contract_ids":["0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"],"static_attributes":{"factory":"0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f"},"change":"Update","creation_tx":"0x0000000000000000000000000000000000000000000000000000000000000000","created_at":"2024-02-28T12:00:00"},"token_in":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","token_out":"0x6B175474E89094C44Da98b954EedeAC495271d0F","split":0.0}],"direct_execution":true}' | tycho-encode tycho-router -p 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234 +echo '{"sender":"0x1234567890123456789012345678901234567890","receiver":"0x1234567890123456789012345678901234567890","given_token":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","given_amount":"1000000000000000000","checked_token":"0x6B175474E89094C44Da98b954EedeAC495271d0F","exact_out":false,"slippage":0.01,"expected_amount":"1000000000000000000","checked_amount":"990000000000000000","router_address":"0xaa820C29648D5EA543d712cC928377Bd7206a0E7","swaps":[{"component":{"id":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640","protocol_system":"uniswap_v2","protocol_type_name":"UniswapV2Pool","chain":"ethereum","tokens":["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"],"contract_ids":["0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"],"static_attributes":{"factory":"0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f"},"change":"Update","creation_tx":"0x0000000000000000000000000000000000000000000000000000000000000000","created_at":"2024-02-28T12:00:00"},"token_in":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","token_out":"0x6B175474E89094C44Da98b954EedeAC495271d0F","split":0.0}],"direct_execution":true}' | tycho-encode tycho-router --swapper-pk 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234 ``` #### JSON Payload Structure: Solution struct diff --git a/examples/quickstart/main.rs b/examples/quickstart/main.rs index 6e3b042..b1e0c1c 100644 --- a/examples/quickstart/main.rs +++ b/examples/quickstart/main.rs @@ -15,13 +15,13 @@ fn main() { // Setup variables let router_address = Bytes::from_str("0x1234567890abcdef1234567890abcdef12345678") .expect("Failed to create router address"); - let signer_pk = + let swapper_pk = "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string(); let user_address = Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2") .expect("Failed to create user address"); // Initialize the encoder - let encoder = EVMEncoderBuilder::tycho_router(TychoCoreChain::Ethereum, signer_pk, None) + let encoder = EVMEncoderBuilder::tycho_router(TychoCoreChain::Ethereum, swapper_pk, None) .expect("Failed to create encoder builder") .build() .expect("Failed to build encoder"); diff --git a/src/bin/lib/cli.rs b/src/bin/lib/cli.rs index ba83b13..df08f07 100644 --- a/src/bin/lib/cli.rs +++ b/src/bin/lib/cli.rs @@ -46,7 +46,7 @@ pub enum Commands { #[arg(short, long)] config_path: Option, #[arg(short, long)] - private_key: String, + swapper_pk: String, }, /// Use the direct execution encoding strategy DirectExecution { diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index e0a47da..c706d4b 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -29,8 +29,8 @@ fn main() -> Result<(), Box> { // Encode the solution let encoded = match cli.command { - Commands::TychoRouter { config_path, private_key } => { - encode_swaps(&buffer, config_path, Some(private_key), true)? + Commands::TychoRouter { config_path, swapper_pk } => { + encode_swaps(&buffer, config_path, Some(swapper_pk), true)? } Commands::DirectExecution { config_path } => { encode_swaps(&buffer, config_path, None, false)? @@ -49,15 +49,15 @@ fn main() -> Result<(), Box> { fn encode_swaps( input: &str, config_path: Option, - private_key: Option, + swapper_pk: Option, use_tycho_router: bool, ) -> Result { let solution: Solution = serde_json::from_str(input)?; let chain = Chain::Ethereum; let encoder = if use_tycho_router { - let private_key = private_key.ok_or(EncodingError::FatalError( - "Private key is required for tycho_router".to_string(), + let private_key = swapper_pk.ok_or(EncodingError::FatalError( + "Swapper private key is required for tycho_router".to_string(), ))?; EVMEncoderBuilder::tycho_router(chain, private_key, config_path)?.build()? } else { diff --git a/src/encoding/evm/approvals/permit2.rs b/src/encoding/evm/approvals/permit2.rs index 2985a28..f3b8085 100644 --- a/src/encoding/evm/approvals/permit2.rs +++ b/src/encoding/evm/approvals/permit2.rs @@ -69,7 +69,7 @@ sol! { } impl Permit2 { - pub fn new(signer_pk: String, chain: Chain) -> Result { + pub fn new(swapper_pk: String, chain: Chain) -> Result { let (handle, runtime) = match Handle::try_current() { Ok(h) => (h, None), Err(_) => { @@ -80,8 +80,8 @@ impl Permit2 { } }; let client = block_in_place(|| handle.block_on(get_client()))?; - let pk = B256::from_str(&signer_pk).map_err(|_| { - EncodingError::FatalError("Failed to convert signer private key to B256".to_string()) + let pk = B256::from_str(&swapper_pk).map_err(|_| { + EncodingError::FatalError("Failed to convert swapper private key to B256".to_string()) })?; let signer = PrivateKeySigner::from_bytes(&pk).map_err(|_| { EncodingError::FatalError("Failed to create signer from private key".to_string()) @@ -224,9 +224,9 @@ mod tests { #[test] fn test_get_existing_allowance() { - let signer_pk = + let swapper_pk = "4c0883a69102937d6231471b5dbb6204fe512961708279feb1be6ae5538da033".to_string(); - let manager = Permit2::new(signer_pk, eth_chain()).unwrap(); + let manager = Permit2::new(swapper_pk, eth_chain()).unwrap(); let token = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap(); let owner = Bytes::from_str("0x2c6a3cd97c6283b95ac8c5a4459ebb0d5fd404f4").unwrap(); diff --git a/src/encoding/evm/encoder_builder.rs b/src/encoding/evm/encoder_builder.rs index f0efb7b..a5a0ff7 100644 --- a/src/encoding/evm/encoder_builder.rs +++ b/src/encoding/evm/encoder_builder.rs @@ -21,12 +21,12 @@ impl EVMEncoderBuilder { } pub fn tycho_router( chain: Chain, - signer_pk: String, + swapper_pk: String, executors_file_path: Option, ) -> Result { let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, chain)?; let strategy = - Box::new(SplitSwapStrategyEncoder::new(signer_pk, chain, swap_encoder_registry)?); + Box::new(SplitSwapStrategyEncoder::new(swapper_pk, chain, swap_encoder_registry)?); Ok(EVMEncoderBuilder { chain, strategy }) } pub fn direct_execution( diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 4dd4bf8..01de355 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -88,14 +88,14 @@ pub struct SplitSwapStrategyEncoder { impl SplitSwapStrategyEncoder { pub fn new( - signer_pk: String, + swapper_pk: String, blockchain: tycho_core::dto::Chain, swap_encoder_registry: SwapEncoderRegistry, ) -> Result { let chain = Chain::from(blockchain); let selector = "swap(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string(); Ok(Self { - permit2: Permit2::new(signer_pk, chain.clone())?, + permit2: Permit2::new(swapper_pk, chain.clone())?, selector, swap_encoder_registry, native_address: chain.native_token()?, From 4741e628b8ab00079565a3e075cc7acc2cc08b39 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Wed, 19 Feb 2025 11:00:02 +0000 Subject: [PATCH 6/9] chore: Simplify tycho-encode Don't have a cli as a lib. Move that directly inside tycho-encode.rs for simplicity --- don't change below this line --- ENG-4246 Took 29 minutes --- src/bin/lib/cli.rs | 56 ------------------------------------ src/bin/tycho-encode.rs | 64 +++++++++++++++++++++++++++++++++++------ 2 files changed, 56 insertions(+), 64 deletions(-) delete mode 100644 src/bin/lib/cli.rs diff --git a/src/bin/lib/cli.rs b/src/bin/lib/cli.rs deleted file mode 100644 index df08f07..0000000 --- a/src/bin/lib/cli.rs +++ /dev/null @@ -1,56 +0,0 @@ -pub use clap::Parser; -use clap::Subcommand; - -#[derive(Parser)] -/// Encode swap transactions for the Tycho router -/// -/// Reads a JSON object from stdin with the following structure: -/// ```json -/// { -/// "sender": "0x...", -/// "receiver": "0x...", -/// "given_token": "0x...", -/// "given_amount": "123...", -/// "checked_token": "0x...", -/// "exact_out": false, -/// "slippage": 0.01, -/// "expected_amount": "123...", -/// "checked_amount": "123...", -/// "swaps": [{ -/// "component": { -/// "id": "...", -/// "protocol_system": "...", -/// "protocol_type_name": "...", -/// "chain": "ethereum", -/// "tokens": ["0x..."], -/// "contract_ids": ["0x..."], -/// "static_attributes": {"key": "0x..."} -/// }, -/// "token_in": "0x...", -/// "token_out": "0x...", -/// "split": 0.0 -/// }], -/// "router_address": "0x..." -/// } -/// ``` -#[command(author, version, about, long_about = None)] -pub struct Cli { - #[command(subcommand)] - pub command: Commands, -} - -#[derive(Subcommand)] -pub enum Commands { - /// Use the Tycho router encoding strategy - TychoRouter { - #[arg(short, long)] - config_path: Option, - #[arg(short, long)] - swapper_pk: String, - }, - /// Use the direct execution encoding strategy - DirectExecution { - #[arg(short, long)] - config_path: Option, - }, -} diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index c706d4b..0949b8d 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -1,18 +1,66 @@ use std::io::{self, Read}; -use clap::Parser; +use clap::{Parser, Subcommand}; use serde_json::Value; use tycho_core::dto::Chain; -use tycho_execution::encoding::{models::Solution, tycho_encoder::TychoEncoder}; +use tycho_execution::encoding::{ + errors::EncodingError, evm::encoder_builder::EVMEncoderBuilder, models::Solution, + tycho_encoder::TychoEncoder, +}; -mod lib { - pub mod cli; +#[derive(Parser)] +/// Encode swap transactions for the Tycho router +/// +/// Reads a JSON object from stdin with the following structure: +/// ```json +/// { +/// "sender": "0x...", +/// "receiver": "0x...", +/// "given_token": "0x...", +/// "given_amount": "123...", +/// "checked_token": "0x...", +/// "exact_out": false, +/// "slippage": 0.01, +/// "expected_amount": "123...", +/// "checked_amount": "123...", +/// "swaps": [{ +/// "component": { +/// "id": "...", +/// "protocol_system": "...", +/// "protocol_type_name": "...", +/// "chain": "ethereum", +/// "tokens": ["0x..."], +/// "contract_ids": ["0x..."], +/// "static_attributes": {"key": "0x..."} +/// }, +/// "token_in": "0x...", +/// "token_out": "0x...", +/// "split": 0.0 +/// }], +/// "router_address": "0x..." +/// } +/// ``` +#[command(author, version, about, long_about = None)] +pub struct Cli { + #[command(subcommand)] + pub command: Commands, } -use lib::cli::Cli; -use tycho_execution::encoding::{errors::EncodingError, evm::encoder_builder::EVMEncoderBuilder}; - -use crate::lib::cli::Commands; +#[derive(Subcommand)] +pub enum Commands { + /// Use the Tycho router encoding strategy + TychoRouter { + #[arg(short, long)] + config_path: Option, + #[arg(short, long)] + swapper_pk: String, + }, + /// Use the direct execution encoding strategy + DirectExecution { + #[arg(short, long)] + config_path: Option, + }, +} fn main() -> Result<(), Box> { let cli = Cli::parse(); From 684de4fa6006400c2511f58fbd407758cbda7b52 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Wed, 19 Feb 2025 11:15:27 +0000 Subject: [PATCH 7/9] feat: Add methods to builder to set chain and strategy independently --- don't change below this line --- ENG-4246 Took 15 minutes --- examples/quickstart/main.rs | 4 +- src/bin/tycho-encode.rs | 8 ++-- src/encoding/evm/encoder_builder.rs | 60 ++++++++++++++++++++++------- 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/examples/quickstart/main.rs b/examples/quickstart/main.rs index b1e0c1c..eefe4e5 100644 --- a/examples/quickstart/main.rs +++ b/examples/quickstart/main.rs @@ -21,7 +21,9 @@ fn main() { .expect("Failed to create user address"); // Initialize the encoder - let encoder = EVMEncoderBuilder::tycho_router(TychoCoreChain::Ethereum, swapper_pk, None) + let encoder = EVMEncoderBuilder::new() + .chain(TychoCoreChain::Ethereum) + .tycho_router(swapper_pk, None) .expect("Failed to create encoder builder") .build() .expect("Failed to build encoder"); diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index 0949b8d..b297b3b 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -103,14 +103,16 @@ fn encode_swaps( let solution: Solution = serde_json::from_str(input)?; let chain = Chain::Ethereum; - let encoder = if use_tycho_router { + let mut builder = EVMEncoderBuilder::new().chain(chain); + builder = if use_tycho_router { let private_key = swapper_pk.ok_or(EncodingError::FatalError( "Swapper private key is required for tycho_router".to_string(), ))?; - EVMEncoderBuilder::tycho_router(chain, private_key, config_path)?.build()? + builder.tycho_router(private_key, config_path)? } else { - EVMEncoderBuilder::direct_execution(chain, config_path)?.build()? + builder.direct_execution(config_path)? }; + let encoder = builder.build()?; let transactions = encoder.encode_router_calldata(vec![solution])?; diff --git a/src/encoding/evm/encoder_builder.rs b/src/encoding/evm/encoder_builder.rs index a5a0ff7..332d253 100644 --- a/src/encoding/evm/encoder_builder.rs +++ b/src/encoding/evm/encoder_builder.rs @@ -11,34 +11,66 @@ use crate::encoding::{ }; pub struct EVMEncoderBuilder { - strategy: Box, - chain: Chain, + strategy: Option>, + chain: Option, +} + +impl Default for EVMEncoderBuilder { + fn default() -> Self { + Self::new() + } } impl EVMEncoderBuilder { - pub fn new(chain: Chain, strategy: Box) -> Self { - EVMEncoderBuilder { chain, strategy } + pub fn new() -> Self { + EVMEncoderBuilder { chain: None, strategy: None } + } + pub fn chain(mut self, chain: Chain) -> Self { + self.chain = Some(chain); + self + } + pub fn strategy_encoder(mut self, strategy: Box) -> Self { + self.strategy = Some(strategy); + self } pub fn tycho_router( - chain: Chain, + self, swapper_pk: String, executors_file_path: Option, ) -> Result { - let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, chain)?; - let strategy = - Box::new(SplitSwapStrategyEncoder::new(swapper_pk, chain, swap_encoder_registry)?); - Ok(EVMEncoderBuilder { chain, strategy }) + if let Some(chain) = self.chain { + let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, chain)?; + let strategy = + Box::new(SplitSwapStrategyEncoder::new(swapper_pk, chain, swap_encoder_registry)?); + Ok(EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy) }) + } else { + Err(EncodingError::FatalError( + "Please set the chain before setting the strategy".to_string(), + )) + } } pub fn direct_execution( - chain: Chain, + self, executors_file_path: Option, ) -> Result { - let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, chain)?; - let strategy = Box::new(ExecutorStrategyEncoder::new(swap_encoder_registry)); - Ok(EVMEncoderBuilder { chain, strategy }) + if let Some(chain) = self.chain { + let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, chain)?; + let strategy = Box::new(ExecutorStrategyEncoder::new(swap_encoder_registry)); + Ok(EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy) }) + } else { + Err(EncodingError::FatalError( + "Please set the chain before setting the strategy".to_string(), + )) + } } pub fn build(self) -> Result { - EVMTychoEncoder::new(self.chain, self.strategy) + if let (Some(chain), Some(strategy)) = (self.chain, self.strategy) { + EVMTychoEncoder::new(chain, strategy) + } else { + Err(EncodingError::FatalError( + "Please set the chain and strategy before building the encoder".to_string(), + )) + } } } From 359bb806f7026f3186d677e7fba8216d46709e9b Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Wed, 19 Feb 2025 17:02:32 +0000 Subject: [PATCH 8/9] docs: Add docstrings to EVMEncoderBuilder --- don't change below this line --- ENG-4246 Took 41 minutes --- src/encoding/evm/encoder_builder.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/encoding/evm/encoder_builder.rs b/src/encoding/evm/encoder_builder.rs index 332d253..efb6e73 100644 --- a/src/encoding/evm/encoder_builder.rs +++ b/src/encoding/evm/encoder_builder.rs @@ -10,6 +10,9 @@ use crate::encoding::{ strategy_encoder::StrategyEncoder, }; +/// Builder pattern for constructing an `EVMTychoEncoder` with customizable options. +/// +/// This struct allows setting a chain and strategy encoder before building the final encoder. pub struct EVMEncoderBuilder { strategy: Option>, chain: Option, @@ -29,10 +32,18 @@ impl EVMEncoderBuilder { self.chain = Some(chain); self } + + /// Sets the `strategy_encoder` manually. + /// + /// **Note**: This method should not be used in combination with `tycho_router` or + /// `direct_execution`. pub fn strategy_encoder(mut self, strategy: Box) -> Self { self.strategy = Some(strategy); self } + + /// Shortcut method to initialize a `SplitSwapStrategyEncoder` with a given `swapper_pk`. + /// **Note**: Should not be used at the same time as `strategy_encoder`. pub fn tycho_router( self, swapper_pk: String, @@ -45,10 +56,13 @@ impl EVMEncoderBuilder { Ok(EVMEncoderBuilder { chain: Some(chain), strategy: Some(strategy) }) } else { Err(EncodingError::FatalError( - "Please set the chain before setting the strategy".to_string(), + "Please set the chain before setting the tycho router".to_string(), )) } } + + /// Shortcut method to initialize an `ExecutorStrategyEncoder`. + /// **Note**: Should not be used at the same time as `strategy_encoder`. pub fn direct_execution( self, executors_file_path: Option, @@ -64,6 +78,8 @@ impl EVMEncoderBuilder { } } + /// Builds the `EVMTychoEncoder` instance using the configured chain and strategy. + /// Returns an error if either the chain or strategy has not been set. pub fn build(self) -> Result { if let (Some(chain), Some(strategy)) = (self.chain, self.strategy) { EVMTychoEncoder::new(chain, strategy) From 4f29022c42af0b598916df6af80ec9c64a09f6c9 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Wed, 19 Feb 2025 17:05:46 +0000 Subject: [PATCH 9/9] fix: After rebase fixes --- don't change below this line --- ENG-4246 Took 2 minutes --- src/encoding/evm/strategy_encoder/mod.rs | 2 +- src/encoding/evm/strategy_encoder/strategy_encoders.rs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/encoding/evm/strategy_encoder/mod.rs b/src/encoding/evm/strategy_encoder/mod.rs index f02f2f1..14137b6 100644 --- a/src/encoding/evm/strategy_encoder/mod.rs +++ b/src/encoding/evm/strategy_encoder/mod.rs @@ -1,3 +1,3 @@ mod group_swaps; -mod strategy_encoders; +pub mod strategy_encoders; mod strategy_validators; diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 01de355..73e62e9 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -442,7 +442,6 @@ mod tests { sender: Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(), receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), swaps: vec![swap.clone(), swap], - direct_execution: true, router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), slippage: None, native_action: None, @@ -493,7 +492,6 @@ mod tests { // The receiver was generated with `makeAddr("bob") using forge` receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), swaps: vec![swap_a, swap_b], - direct_execution: true, router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), slippage: None, native_action: None,