diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fcf443..ba1675d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +## [0.43.0](https://github.com/propeller-heads/tycho-execution/compare/0.42.0...0.43.0) (2025-02-19) + + +### Features + +* Add methods to builder to set chain and strategy independently ([684de4f](https://github.com/propeller-heads/tycho-execution/commit/684de4fa6006400c2511f58fbd407758cbda7b52)) +* Create a EVMEncoderBuilder ([03506fa](https://github.com/propeller-heads/tycho-execution/commit/03506fabe90c00f1518e42243fd650fb561e2a39)) +* Remove direct_execution from Solution ([8537d27](https://github.com/propeller-heads/tycho-execution/commit/8537d274692aabf45a1ce79ded6c40998ba6fc50)) + + +### Bug Fixes + +* After rebase fixes ([4f29022](https://github.com/propeller-heads/tycho-execution/commit/4f29022c42af0b598916df6af80ec9c64a09f6c9)) +* After rebase fixes ([30b5ab9](https://github.com/propeller-heads/tycho-execution/commit/30b5ab9025ae3292e4977f1ed10ceac4850d7669)) + ## [0.42.0](https://github.com/propeller-heads/tycho-execution/compare/0.41.0...0.42.0) (2025-02-19) diff --git a/Cargo.lock b/Cargo.lock index 4555578..a92ddba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4340,7 +4340,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.42.0" +version = "0.43.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 5c1b53f..870968e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.42.0" +version = "0.43.0" edition = "2021" [[bin]] diff --git a/README.md b/README.md index 52e8939..ada92e4 100644 --- a/README.md +++ b/README.md @@ -24,30 +24,47 @@ 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. -- `-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) +### 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 --swapper-pk 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 -To encode a transaction, you can pipe a JSON payload to the binary: +The commands accept the following options: -```bash -# Using default config path -echo '' | tycho-encode - -# Using custom config path -echo '' | tycho-encode -c /path/to/your/config.json -``` +- `--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: +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 --swapper-pk 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234 ``` #### JSON Payload Structure: Solution struct diff --git a/examples/quickstart/main.rs b/examples/quickstart/main.rs index c2f534c..eefe4e5 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, }; @@ -19,17 +15,18 @@ fn main() { // Setup variables let router_address = Bytes::from_str("0x1234567890abcdef1234567890abcdef12345678") .expect("Failed to create router address"); - let signer_pk = - Some("0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234".to_string()); + let swapper_pk = + "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::new() + .chain(TychoCoreChain::Ethereum) + .tycho_router(swapper_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 deleted file mode 100644 index 60e13d3..0000000 --- a/src/bin/lib/cli.rs +++ /dev/null @@ -1,44 +0,0 @@ -pub use clap::Parser; - -#[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": 1.0 -/// }], -/// "router_address": "0x...", -/// "direct_execution": false -/// } -/// ``` -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, -} diff --git a/src/bin/tycho-encode.rs b/src/bin/tycho-encode.rs index f8bd8b3..b297b3b 100644 --- a/src/bin/tycho-encode.rs +++ b/src/bin/tycho-encode.rs @@ -1,23 +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::{ - evm::{ - strategy_encoder::strategy_encoder_registry::EVMStrategyEncoderRegistry, - tycho_encoder::EVMTychoEncoder, - }, - models::Solution, - strategy_encoder::StrategyEncoderRegistry, + 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; +#[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(); @@ -33,8 +76,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, swapper_pk } => { + encode_swaps(&buffer, config_path, Some(swapper_pk), true)? + } + Commands::DirectExecution { config_path } => { + encode_swaps(&buffer, config_path, None, false)? + } + }; // Output the encoded result as JSON to stdout println!( "{}", @@ -48,13 +97,23 @@ fn main() -> Result<(), Box> { fn encode_swaps( input: &str, config_path: Option, - private_key: Option, -) -> Result> { + swapper_pk: Option, + 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 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(), + ))?; + builder.tycho_router(private_key, config_path)? + } else { + builder.direct_execution(config_path)? + }; + let encoder = builder.build()?; + let transactions = encoder.encode_router_calldata(vec![solution])?; Ok(serde_json::json!({ 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 new file mode 100644 index 0000000..efb6e73 --- /dev/null +++ b/src/encoding/evm/encoder_builder.rs @@ -0,0 +1,92 @@ +use tycho_core::dto::Chain; + +use crate::encoding::{ + errors::EncodingError, + evm::{ + strategy_encoder::strategy_encoders::{ExecutorStrategyEncoder, SplitSwapStrategyEncoder}, + swap_encoder::swap_encoder_registry::SwapEncoderRegistry, + tycho_encoder::EVMTychoEncoder, + }, + 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, +} + +impl Default for EVMEncoderBuilder { + fn default() -> Self { + Self::new() + } +} + +impl EVMEncoderBuilder { + pub fn new() -> Self { + EVMEncoderBuilder { chain: None, strategy: None } + } + pub fn chain(mut self, chain: Chain) -> Self { + 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, + executors_file_path: Option, + ) -> Result { + 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 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, + ) -> Result { + 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(), + )) + } + } + + /// 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) + } else { + Err(EncodingError::FatalError( + "Please set the chain and strategy before building the encoder".to_string(), + )) + } + } +} diff --git a/src/encoding/evm/mod.rs b/src/encoding/evm/mod.rs index 8b2d01f..b86b4dd 100644 --- a/src/encoding/evm/mod.rs +++ b/src/encoding/evm/mod.rs @@ -1,5 +1,6 @@ pub mod approvals; mod constants; +pub mod encoder_builder; pub mod strategy_encoder; mod swap_encoder; pub mod tycho_encoder; diff --git a/src/encoding/evm/models.rs b/src/encoding/evm/models.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/encoding/evm/strategy_encoder/mod.rs b/src/encoding/evm/strategy_encoder/mod.rs index 3372a64..14137b6 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; +pub 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_encoder/strategy_encoders.rs index dfa27d7..ea676d8 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -89,13 +89,14 @@ pub struct SplitSwapStrategyEncoder { impl SplitSwapStrategyEncoder { pub fn new( - signer_pk: String, - chain: Chain, + 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()?, @@ -340,8 +341,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 { @@ -387,7 +388,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, @@ -445,7 +445,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, @@ -496,7 +495,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, 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/models.rs b/src/encoding/models.rs index 12a0c22..6b24030 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. 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,