diff --git a/Cargo.toml b/Cargo.toml index fcb5be9..b0fc154 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,9 @@ num-bigint = "0.4.6" tokio = { version = "1.38.0", features = ["full"] } -alloy = { version = "0.5.4", features = ["providers"] } -alloy-sol-types = { version = "0.8.14" } -alloy-primitives = { version = "0.8.9" } +alloy = { version = "0.5.4", features = ["providers"], optional = true } +alloy-sol-types = { version = "0.8.14", optional = true } +alloy-primitives = { version = "0.8.9", optional = true } tycho-core = { git = "https://github.com/propeller-heads/tycho-indexer.git", package = "tycho-core", tag = "0.46.0" } hex = "0.4.3" anyhow = "1.0.95" @@ -20,5 +20,9 @@ num-traits = "0.2.19" serde = { version = "1.0.217", features = ["derive"] } serde_json = "1.0.135" +[features] +default = ["evm"] +evm = ["alloy", "alloy-sol-types", "alloy-primitives"] + [profile.bench] debug = true diff --git a/src/encoding/approvals/mod.rs b/src/encoding/approvals/mod.rs deleted file mode 100644 index 8fbe120..0000000 --- a/src/encoding/approvals/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) mod approvals_manager; -pub(crate) mod interface; -mod permit2; diff --git a/src/encoding/swap_encoder/config.json b/src/encoding/config/executor_addresses.json similarity index 100% rename from src/encoding/swap_encoder/config.json rename to src/encoding/config/executor_addresses.json diff --git a/src/encoding/evm/approvals/mod.rs b/src/encoding/evm/approvals/mod.rs new file mode 100644 index 0000000..49f6861 --- /dev/null +++ b/src/encoding/evm/approvals/mod.rs @@ -0,0 +1,2 @@ +pub mod permit2; +pub mod protocol_approvals_manager; diff --git a/src/encoding/approvals/permit2.rs b/src/encoding/evm/approvals/permit2.rs similarity index 93% rename from src/encoding/approvals/permit2.rs rename to src/encoding/evm/approvals/permit2.rs index e89adbd..eba7179 100644 --- a/src/encoding/approvals/permit2.rs +++ b/src/encoding/evm/approvals/permit2.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use alloy_primitives::U256; use tycho_core::Bytes; -use crate::encoding::approvals::interface::{Approval, UserApprovalsManager}; +use crate::encoding::user_approvals_manager::{Approval, UserApprovalsManager}; #[allow(dead_code)] pub struct Permit2 { diff --git a/src/encoding/approvals/approvals_manager.rs b/src/encoding/evm/approvals/protocol_approvals_manager.rs similarity index 100% rename from src/encoding/approvals/approvals_manager.rs rename to src/encoding/evm/approvals/protocol_approvals_manager.rs diff --git a/src/encoding/evm/mod.rs b/src/encoding/evm/mod.rs new file mode 100644 index 0000000..d2abfd8 --- /dev/null +++ b/src/encoding/evm/mod.rs @@ -0,0 +1,5 @@ +pub mod approvals; +mod router_encoder; +mod strategy_encoder; +mod swap_encoder; +mod utils; diff --git a/src/encoding/evm/router_encoder.rs b/src/encoding/evm/router_encoder.rs new file mode 100644 index 0000000..ff81be1 --- /dev/null +++ b/src/encoding/evm/router_encoder.rs @@ -0,0 +1,81 @@ +use alloy_sol_types::SolValue; +use anyhow::Error; +use num_bigint::BigUint; + +use crate::encoding::{ + evm::utils::{encode_input, ple_encode}, + models::{NativeAction, Solution, Transaction, PROPELLER_ROUTER_ADDRESS}, + router_encoder::RouterEncoder, + strategy_encoder::StrategySelector, + user_approvals_manager::{Approval, UserApprovalsManager}, +}; + +#[allow(dead_code)] +pub struct EVMRouterEncoder { + strategy_selector: S, + approvals_manager: A, +} + +#[allow(dead_code)] +impl EVMRouterEncoder { + pub fn new(strategy_selector: S, approvals_manager: A) -> Self { + EVMRouterEncoder { strategy_selector, approvals_manager } + } +} +impl RouterEncoder for EVMRouterEncoder { + fn encode_router_calldata(&self, solutions: Vec) -> Result { + let _approvals_calldata = self.handle_approvals(&solutions)?; // TODO: where should we append this? + let mut calldata_list: Vec> = Vec::new(); + let encode_for_batch_execute = solutions.len() > 1; + let mut value = BigUint::ZERO; + for solution in solutions.iter() { + let exact_out = solution.exact_out; + let straight_to_pool = solution.straight_to_pool; + + let strategy = self + .strategy_selector + .select_strategy(solution); + let method_calldata = strategy.encode_strategy((*solution).clone())?; + + let contract_interaction = if encode_for_batch_execute { + let args = (strategy.action_type(exact_out) as u16, method_calldata); + args.abi_encode() + } else if straight_to_pool { + method_calldata + } else { + encode_input(strategy.selector(exact_out), method_calldata) + }; + calldata_list.push(contract_interaction); + + if solution.native_action.clone().unwrap() == NativeAction::Wrap { + value += solution.given_amount.clone(); + } + } + let data = if encode_for_batch_execute { + let args = (false, ple_encode(calldata_list)); + encode_input("batchExecute(bytes)", args.abi_encode()) + } else { + calldata_list[0].clone() + }; + + Ok(Transaction { data, value }) + } + + fn handle_approvals(&self, solutions: &[Solution]) -> Result, Error> { + let mut approvals = Vec::new(); + for solution in solutions.iter() { + approvals.push(Approval { + token: solution.given_token.clone(), + spender: solution + .router_address + .clone() + .unwrap_or(PROPELLER_ROUTER_ADDRESS.clone()), + amount: solution.given_amount.clone(), + owner: solution.sender.clone(), + }); + } + Ok(self + .approvals_manager + .encode_approvals(approvals)) + } +} diff --git a/src/encoding/evm/strategy_encoder/encoder.rs b/src/encoding/evm/strategy_encoder/encoder.rs new file mode 100644 index 0000000..57750c7 --- /dev/null +++ b/src/encoding/evm/strategy_encoder/encoder.rs @@ -0,0 +1,198 @@ +use std::{cmp::min, str::FromStr}; + +use alloy_primitives::Address; +use alloy_sol_types::SolValue; +use anyhow::Error; +use num_bigint::BigUint; +use num_traits::Zero; + +use crate::encoding::{ + evm::{ + swap_encoder::SWAP_ENCODER_REGISTRY, + utils::{biguint_to_u256, ple_encode}, + }, + models::{ActionType, EncodingContext, NativeAction, Solution, PROPELLER_ROUTER_ADDRESS}, + strategy_encoder::StrategyEncoder, +}; + +#[allow(dead_code)] +pub trait EVMStrategyEncoder: StrategyEncoder { + fn encode_protocol_header( + &self, + protocol_data: Vec, + executor_address: Address, + // Token indices, split, and token inclusion are only used for split swaps + token_in: u16, + token_out: u16, + split: u16, // not sure what should be the type of this :/ + ) -> Vec { + let args = (executor_address, token_in, token_out, split, protocol_data); + args.abi_encode() + } +} + +pub struct SingleSwapStrategyEncoder {} +impl EVMStrategyEncoder for SingleSwapStrategyEncoder {} + +impl StrategyEncoder for SingleSwapStrategyEncoder { + fn encode_strategy(&self, _solution: Solution) -> Result, Error> { + todo!() + } + + fn action_type(&self, exact_out: bool) -> ActionType { + if exact_out { + ActionType::SingleExactOut + } else { + ActionType::SingleExactIn + } + } + + fn selector(&self, exact_out: bool) -> &str { + if exact_out { + "singleExactOut(uint256, bytes)" + } else { + "singleExactIn(uint256, bytes)" + } + } +} + +pub struct SequentialStrategyEncoder {} +impl EVMStrategyEncoder for SequentialStrategyEncoder {} + +impl StrategyEncoder for SequentialStrategyEncoder { + fn encode_strategy(&self, solution: Solution) -> Result, Error> { + let check_amount = if solution.check_amount.is_some() { + let mut check_amount = solution.check_amount.clone().unwrap(); + if solution.slippage.is_some() { + let one_hundred = BigUint::from(100u32); + let slippage_percent = BigUint::from((solution.slippage.unwrap() * 100.0) as u32); + let multiplier = &one_hundred - slippage_percent; + let expected_amount_with_slippage = + (&solution.expected_amount * multiplier) / one_hundred; + check_amount = min(check_amount, expected_amount_with_slippage); + } + check_amount + } else { + BigUint::ZERO + }; + + let mut swaps = vec![]; + for (index, swap) in solution.swaps.iter().enumerate() { + let is_last = index == solution.swaps.len() - 1; + let registry = SWAP_ENCODER_REGISTRY.read().unwrap(); + let swap_encoder = registry + .get_encoder(&swap.component.protocol_system) + .expect("Swap encoder not found"); + let router_address = if solution.router_address.is_some() { + solution.router_address.clone().unwrap() + } else { + PROPELLER_ROUTER_ADDRESS.clone() + }; + let receiver = if is_last { solution.receiver.clone() } else { router_address.clone() }; + + let encoding_context = EncodingContext { + receiver, + exact_out: solution.exact_out, + address_for_approvals: router_address, + }; + let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context)?; + let executor_address = swap_encoder.executor_address(); + let swap_data = self.encode_protocol_header( + protocol_data, + Address::from_str(executor_address).expect("Couldn't convert executor address"), + 0, + 0, + 0, + ); + swaps.push(swap_data); + } + + let encoded_swaps = ple_encode(swaps); + + let (mut unwrap, mut wrap) = (false, false); + if solution.native_action.is_some() { + match solution.native_action.unwrap() { + NativeAction::Wrap => wrap = true, + NativeAction::Unwrap => unwrap = true, + } + } + let method_calldata = ( + wrap, + unwrap, + biguint_to_u256(&solution.given_amount), + !check_amount.is_zero(), /* if check_amount is zero, then we don't need to check */ + biguint_to_u256(&check_amount), + encoded_swaps, + ) + .abi_encode(); + Ok(method_calldata) + } + + fn action_type(&self, exact_out: bool) -> ActionType { + if exact_out { + ActionType::SequentialExactOut + } else { + ActionType::SequentialExactIn + } + } + + fn selector(&self, exact_out: bool) -> &str { + if exact_out { + "sequentialExactOut(uint256, uint256, bytes[])" + } else { + "sequentialExactIn(uint256, uint256, bytes[])" + } + } +} + +pub struct SplitSwapStrategyEncoder {} +impl EVMStrategyEncoder for SplitSwapStrategyEncoder {} +impl StrategyEncoder for SplitSwapStrategyEncoder { + fn encode_strategy(&self, _solution: Solution) -> Result, Error> { + todo!() + } + fn action_type(&self, _exact_out: bool) -> ActionType { + ActionType::SplitIn + } + + fn selector(&self, _exact_out: bool) -> &str { + "splitExactIn(uint256, address, uint256, bytes[])" + } +} + +/// This strategy encoder is used for solutions that are sent directly to the pool. +/// Only 1 solution with 1 swap is supported. +pub struct StraightToPoolStrategyEncoder {} +impl EVMStrategyEncoder for StraightToPoolStrategyEncoder {} +impl StrategyEncoder for StraightToPoolStrategyEncoder { + fn encode_strategy(&self, solution: Solution) -> Result, Error> { + if solution.router_address.is_none() { + return Err(anyhow::anyhow!( + "Router address is required for straight to pool solutions" + )); + } + let swap = solution.swaps.first().unwrap(); + let registry = SWAP_ENCODER_REGISTRY.read().unwrap(); + let swap_encoder = registry + .get_encoder(&swap.component.protocol_system) + .expect("Swap encoder not found"); + let router_address = solution.router_address.unwrap(); + + let encoding_context = EncodingContext { + receiver: solution.receiver, + exact_out: solution.exact_out, + address_for_approvals: router_address, + }; + let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context)?; + // TODO: here we need to pass also the address of the executor to be used + Ok(protocol_data) + } + + fn action_type(&self, _exact_out: bool) -> ActionType { + unimplemented!(); + } + + fn selector(&self, _exact_out: bool) -> &str { + unimplemented!(); + } +} diff --git a/src/encoding/evm/strategy_encoder/mod.rs b/src/encoding/evm/strategy_encoder/mod.rs new file mode 100644 index 0000000..9d36e12 --- /dev/null +++ b/src/encoding/evm/strategy_encoder/mod.rs @@ -0,0 +1,2 @@ +mod encoder; +mod selector; diff --git a/src/encoding/strategy_selector.rs b/src/encoding/evm/strategy_encoder/selector.rs similarity index 68% rename from src/encoding/strategy_selector.rs rename to src/encoding/evm/strategy_encoder/selector.rs index 4a50139..a98432e 100644 --- a/src/encoding/strategy_selector.rs +++ b/src/encoding/evm/strategy_encoder/selector.rs @@ -1,19 +1,15 @@ use crate::encoding::{ - models::Solution, - strategy_encoder::{ + evm::strategy_encoder::encoder::{ SequentialStrategyEncoder, SingleSwapStrategyEncoder, SplitSwapStrategyEncoder, - StraightToPoolStrategyEncoder, StrategyEncoder, + StraightToPoolStrategyEncoder, }, + models::Solution, + strategy_encoder::{StrategyEncoder, StrategySelector}, }; -pub trait StrategySelector { - #[allow(dead_code)] - fn select_strategy(&self, solution: &Solution) -> Box; -} +pub struct EVMStrategySelector; -pub struct DefaultStrategySelector; - -impl StrategySelector for DefaultStrategySelector { +impl StrategySelector for EVMStrategySelector { fn select_strategy(&self, solution: &Solution) -> Box { if solution.straight_to_pool { Box::new(StraightToPoolStrategyEncoder {}) diff --git a/src/encoding/swap_encoder/builder.rs b/src/encoding/evm/swap_encoder/builder.rs similarity index 85% rename from src/encoding/swap_encoder/builder.rs rename to src/encoding/evm/swap_encoder/builder.rs index 5f6ffd1..141370e 100644 --- a/src/encoding/swap_encoder/builder.rs +++ b/src/encoding/evm/swap_encoder/builder.rs @@ -1,5 +1,6 @@ -use crate::encoding::swap_encoder::swap_struct_encoder::{ - BalancerV2SwapEncoder, SwapEncoder, UniswapV2SwapEncoder, +use crate::encoding::{ + evm::swap_encoder::encoders::{BalancerV2SwapEncoder, UniswapV2SwapEncoder}, + swap_encoder::SwapEncoder, }; pub struct SwapEncoderBuilder { diff --git a/src/encoding/swap_encoder/swap_struct_encoder.rs b/src/encoding/evm/swap_encoder/encoders.rs similarity index 87% rename from src/encoding/swap_encoder/swap_struct_encoder.rs rename to src/encoding/evm/swap_encoder/encoders.rs index 51168c0..dc90acc 100644 --- a/src/encoding/swap_encoder/swap_struct_encoder.rs +++ b/src/encoding/evm/swap_encoder/encoders.rs @@ -5,19 +5,13 @@ use alloy_sol_types::SolValue; use anyhow::Error; use crate::encoding::{ - approvals::approvals_manager::ProtocolApprovalsManager, + evm::{ + approvals::protocol_approvals_manager::ProtocolApprovalsManager, utils::bytes_to_address, + }, models::{EncodingContext, Swap}, - utils::bytes_to_address, + swap_encoder::SwapEncoder, }; -pub trait SwapEncoder: Sync + Send { - fn new(executor_address: String) -> Self - where - Self: Sized; - fn encode_swap(&self, swap: Swap, encoding_context: EncodingContext) -> Result, Error>; - fn executor_address(&self) -> &str; -} - pub struct UniswapV2SwapEncoder { executor_address: String, } diff --git a/src/encoding/swap_encoder/mod.rs b/src/encoding/evm/swap_encoder/mod.rs similarity index 54% rename from src/encoding/swap_encoder/mod.rs rename to src/encoding/evm/swap_encoder/mod.rs index 82521f2..2c5cd10 100644 --- a/src/encoding/swap_encoder/mod.rs +++ b/src/encoding/evm/swap_encoder/mod.rs @@ -1,17 +1,18 @@ +mod builder; +mod encoders; +mod registry; + use std::sync::RwLock; use lazy_static::lazy_static; use tycho_core::dto::Chain; -use crate::encoding::swap_encoder::registry::{Config, SwapEncoderRegistry}; - -mod builder; -mod registry; -mod swap_struct_encoder; +use crate::encoding::evm::swap_encoder::registry::{Config, SwapEncoderRegistry}; lazy_static! { pub static ref SWAP_ENCODER_REGISTRY: RwLock = { - let config = Config::from_file("config.json").expect("Failed to load configuration file"); + let config = Config::from_file("src/encoding/config/executor_addresses.json") + .expect("Failed to load configuration file"); RwLock::new(SwapEncoderRegistry::new(config, Chain::Ethereum)) }; } diff --git a/src/encoding/swap_encoder/registry.rs b/src/encoding/evm/swap_encoder/registry.rs similarity index 88% rename from src/encoding/swap_encoder/registry.rs rename to src/encoding/evm/swap_encoder/registry.rs index b2cd081..d78d01c 100644 --- a/src/encoding/swap_encoder/registry.rs +++ b/src/encoding/evm/swap_encoder/registry.rs @@ -3,9 +3,7 @@ use std::{collections::HashMap, fs}; use serde::Deserialize; use tycho_core::dto::Chain; -use crate::encoding::swap_encoder::{ - builder::SwapEncoderBuilder, swap_struct_encoder::SwapEncoder, -}; +use crate::encoding::{evm::swap_encoder::builder::SwapEncoderBuilder, swap_encoder::SwapEncoder}; pub struct SwapEncoderRegistry { encoders: HashMap>, @@ -19,7 +17,7 @@ impl SwapEncoderRegistry { .get(&blockchain) .unwrap_or_else(|| panic!("No executors found for blockchain: {}", blockchain)); for (protocol, executor_address) in executors { - let builder = SwapEncoderBuilder::new(&protocol, &executor_address); + let builder = SwapEncoderBuilder::new(protocol, executor_address); let encoder = builder.build().unwrap_or_else(|_| { panic!("Failed to build swap encoder for protocol: {}", protocol) }); diff --git a/src/encoding/utils.rs b/src/encoding/evm/utils.rs similarity index 100% rename from src/encoding/utils.rs rename to src/encoding/evm/utils.rs diff --git a/src/encoding/mod.rs b/src/encoding/mod.rs index d9e8233..12cdf8a 100644 --- a/src/encoding/mod.rs +++ b/src/encoding/mod.rs @@ -1,7 +1,7 @@ -mod approvals; +#[cfg(feature = "evm")] +mod evm; mod models; mod router_encoder; mod strategy_encoder; -mod strategy_selector; mod swap_encoder; -mod utils; +mod user_approvals_manager; diff --git a/src/encoding/models.rs b/src/encoding/models.rs index 89ad2e1..9009d57 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -53,6 +53,7 @@ pub enum NativeAction { } #[derive(Clone)] +#[allow(dead_code)] pub struct Swap { /// Protocol component from tycho indexer pub component: ProtocolComponent, @@ -71,12 +72,14 @@ pub struct Transaction { pub value: BigUint, } +#[allow(dead_code)] pub struct EncodingContext { pub receiver: Bytes, pub exact_out: bool, pub address_for_approvals: Bytes, } +#[allow(dead_code)] pub enum ActionType { SingleExactIn = 1, SingleExactOut = 2, diff --git a/src/encoding/router_encoder.rs b/src/encoding/router_encoder.rs index 1f44bf9..834531f 100644 --- a/src/encoding/router_encoder.rs +++ b/src/encoding/router_encoder.rs @@ -1,78 +1,13 @@ -use alloy_sol_types::SolValue; use anyhow::Error; -use num_bigint::BigUint; use crate::encoding::{ - approvals::interface::{Approval, UserApprovalsManager}, - models::{NativeAction, Solution, Transaction, PROPELLER_ROUTER_ADDRESS}, - strategy_selector::StrategySelector, - utils::{encode_input, ple_encode}, + models::{Solution, Transaction}, + strategy_encoder::StrategySelector, + user_approvals_manager::UserApprovalsManager, }; #[allow(dead_code)] -struct RouterEncoder { - strategy_selector: S, - approvals_manager: A, -} - -#[allow(dead_code)] -impl RouterEncoder { - pub fn new(strategy_selector: S, approvals_manager: A) -> Self { - RouterEncoder { strategy_selector, approvals_manager } - } - pub fn encode_router_calldata(&self, solutions: Vec) -> Result { - let _approvals_calldata = self.handle_approvals(&solutions)?; // TODO: where should we append this? - let mut calldata_list: Vec> = Vec::new(); - let encode_for_batch_execute = solutions.len() > 1; - let mut value = BigUint::ZERO; - for solution in solutions.iter() { - let exact_out = solution.exact_out; - let straight_to_pool = solution.straight_to_pool; - - let strategy = self - .strategy_selector - .select_strategy(solution); - let method_calldata = strategy.encode_strategy((*solution).clone())?; - - let contract_interaction = if encode_for_batch_execute { - let args = (strategy.action_type(exact_out) as u16, method_calldata); - args.abi_encode() - } else if straight_to_pool { - method_calldata - } else { - encode_input(strategy.selector(exact_out), method_calldata) - }; - calldata_list.push(contract_interaction); - - if solution.native_action.clone().unwrap() == NativeAction::Wrap { - value += solution.given_amount.clone(); - } - } - let data = if encode_for_batch_execute { - let args = (false, ple_encode(calldata_list)); - encode_input("batchExecute(bytes)", args.abi_encode()) - } else { - calldata_list[0].clone() - }; - - Ok(Transaction { data, value }) - } - - fn handle_approvals(&self, solutions: &[Solution]) -> Result, Error> { - let mut approvals = Vec::new(); - for solution in solutions.iter() { - approvals.push(Approval { - token: solution.given_token.clone(), - spender: solution - .router_address - .clone() - .unwrap_or(PROPELLER_ROUTER_ADDRESS.clone()), - amount: solution.given_amount.clone(), - owner: solution.sender.clone(), - }); - } - Ok(self - .approvals_manager - .encode_approvals(approvals)) - } +pub trait RouterEncoder { + fn encode_router_calldata(&self, solutions: Vec) -> Result; + fn handle_approvals(&self, solutions: &[Solution]) -> Result, Error>; } diff --git a/src/encoding/strategy_encoder.rs b/src/encoding/strategy_encoder.rs index aa2ffd8..a18cb41 100644 --- a/src/encoding/strategy_encoder.rs +++ b/src/encoding/strategy_encoder.rs @@ -1,16 +1,6 @@ -use std::{cmp::min, str::FromStr}; - -use alloy_primitives::Address; -use alloy_sol_types::SolValue; use anyhow::Error; -use num_bigint::BigUint; -use num_traits::Zero; -use crate::encoding::{ - models::{ActionType, EncodingContext, NativeAction, Solution, PROPELLER_ROUTER_ADDRESS}, - swap_encoder::SWAP_ENCODER_REGISTRY, - utils::{biguint_to_u256, ple_encode}, -}; +use crate::encoding::models::{ActionType, Solution}; #[allow(dead_code)] pub trait StrategyEncoder { @@ -18,181 +8,9 @@ pub trait StrategyEncoder { fn action_type(&self, exact_out: bool) -> ActionType; fn selector(&self, exact_out: bool) -> &str; - - fn encode_protocol_header( - &self, - protocol_data: Vec, - executor_address: Address, - // Token indices, split, and token inclusion are only used for split swaps - token_in: u16, - token_out: u16, - split: u16, // not sure what should be the type of this :/ - ) -> Vec { - let args = (executor_address, token_in, token_out, split, protocol_data); - args.abi_encode() - } } -pub struct SingleSwapStrategyEncoder {} - -impl StrategyEncoder for SingleSwapStrategyEncoder { - fn encode_strategy(&self, _solution: Solution) -> Result, Error> { - todo!() - } - - fn action_type(&self, exact_out: bool) -> ActionType { - if exact_out { - ActionType::SingleExactOut - } else { - ActionType::SingleExactIn - } - } - - fn selector(&self, exact_out: bool) -> &str { - if exact_out { - "singleExactOut(uint256, bytes)" - } else { - "singleExactIn(uint256, bytes)" - } - } -} - -pub struct SequentialStrategyEncoder {} - -impl StrategyEncoder for SequentialStrategyEncoder { - fn encode_strategy(&self, solution: Solution) -> Result, Error> { - let check_amount = if solution.check_amount.is_some() { - let mut check_amount = solution.check_amount.clone().unwrap(); - if solution.slippage.is_some() { - let one_hundred = BigUint::from(100u32); - let slippage_percent = BigUint::from((solution.slippage.unwrap() * 100.0) as u32); - let multiplier = &one_hundred - slippage_percent; - let expected_amount_with_slippage = - (&solution.expected_amount * multiplier) / one_hundred; - check_amount = min(check_amount, expected_amount_with_slippage); - } - check_amount - } else { - BigUint::ZERO - }; - - let mut swaps = vec![]; - for (index, swap) in solution.swaps.iter().enumerate() { - let is_last = index == solution.swaps.len() - 1; - let registry = SWAP_ENCODER_REGISTRY.read().unwrap(); - let swap_encoder = registry - .get_encoder(&swap.component.protocol_system) - .expect("Swap encoder not found"); - let router_address = if solution.router_address.is_some() { - solution.router_address.clone().unwrap() - } else { - PROPELLER_ROUTER_ADDRESS.clone() - }; - let receiver = if is_last { solution.receiver.clone() } else { router_address.clone() }; - - let encoding_context = EncodingContext { - receiver, - exact_out: solution.exact_out, - address_for_approvals: router_address, - }; - let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context)?; - let executor_address = swap_encoder.executor_address(); - let swap_data = self.encode_protocol_header( - protocol_data, - Address::from_str(executor_address).expect("Couldn't convert executor address"), - 0, - 0, - 0, - ); - swaps.push(swap_data); - } - - let encoded_swaps = ple_encode(swaps); - - let (mut unwrap, mut wrap) = (false, false); - if solution.native_action.is_some() { - match solution.native_action.unwrap() { - NativeAction::Wrap => wrap = true, - NativeAction::Unwrap => unwrap = true, - } - } - let method_calldata = ( - wrap, - unwrap, - biguint_to_u256(&solution.given_amount), - !check_amount.is_zero(), /* if check_amount is zero, then we don't need to check */ - biguint_to_u256(&check_amount), - encoded_swaps, - ) - .abi_encode(); - Ok(method_calldata) - } - - fn action_type(&self, exact_out: bool) -> ActionType { - if exact_out { - ActionType::SequentialExactOut - } else { - ActionType::SequentialExactIn - } - } - - fn selector(&self, exact_out: bool) -> &str { - if exact_out { - "sequentialExactOut(uint256, uint256, bytes[])" - } else { - "sequentialExactIn(uint256, uint256, bytes[])" - } - } -} - -pub struct SplitSwapStrategyEncoder {} - -impl StrategyEncoder for SplitSwapStrategyEncoder { - fn encode_strategy(&self, _solution: Solution) -> Result, Error> { - todo!() - } - fn action_type(&self, _exact_out: bool) -> ActionType { - ActionType::SplitIn - } - - fn selector(&self, _exact_out: bool) -> &str { - "splitExactIn(uint256, address, uint256, bytes[])" - } -} - -/// This strategy encoder is used for solutions that are sent directly to the pool. -/// Only 1 solution with 1 swap is supported. -pub struct StraightToPoolStrategyEncoder {} - -impl StrategyEncoder for StraightToPoolStrategyEncoder { - fn encode_strategy(&self, solution: Solution) -> Result, Error> { - if solution.router_address.is_none() { - return Err(anyhow::anyhow!( - "Router address is required for straight to pool solutions" - )); - } - let swap = solution.swaps.first().unwrap(); - let registry = SWAP_ENCODER_REGISTRY.read().unwrap(); - let swap_encoder = registry - .get_encoder(&swap.component.protocol_system) - .expect("Swap encoder not found"); - let router_address = solution.router_address.unwrap(); - - let encoding_context = EncodingContext { - receiver: solution.receiver, - exact_out: solution.exact_out, - address_for_approvals: router_address, - }; - let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context)?; - // TODO: here we need to pass also the address of the executor to be used - Ok(protocol_data) - } - - fn action_type(&self, _exact_out: bool) -> ActionType { - unimplemented!(); - } - - fn selector(&self, _exact_out: bool) -> &str { - unimplemented!(); - } +pub trait StrategySelector { + #[allow(dead_code)] + fn select_strategy(&self, solution: &Solution) -> Box; } diff --git a/src/encoding/swap_encoder.rs b/src/encoding/swap_encoder.rs new file mode 100644 index 0000000..c361485 --- /dev/null +++ b/src/encoding/swap_encoder.rs @@ -0,0 +1,12 @@ +use anyhow::Error; + +use crate::encoding::models::{EncodingContext, Swap}; + +#[allow(dead_code)] +pub trait SwapEncoder: Sync + Send { + fn new(executor_address: String) -> Self + where + Self: Sized; + fn encode_swap(&self, swap: Swap, encoding_context: EncodingContext) -> Result, Error>; + fn executor_address(&self) -> &str; +} diff --git a/src/encoding/approvals/interface.rs b/src/encoding/user_approvals_manager.rs similarity index 100% rename from src/encoding/approvals/interface.rs rename to src/encoding/user_approvals_manager.rs