From a28b54888e08fe23ed20ef8b0a385f094bca3c28 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Thu, 30 Jan 2025 19:38:22 +0000 Subject: [PATCH] fix: Post merge's fixes Because of the renaming, git couldn't identify the new files and handle the conflicts gracefully. Copied implementation for ExecutorStrategyEncoder from main Rollbacked on decision to encode the executor address and selector inside the SwapEncoders. This is only necessary for certain strategies. So it should be done at the strategy level --- don't change below this line --- ENG-4081 Took 35 minutes --- .../evm/strategy_encoder/strategy_encoders.rs | 102 +++++++++++++++--- .../evm/strategy_encoder/strategy_selector.rs | 8 +- .../evm/swap_encoder/swap_encoders.rs | 53 +++------ src/encoding/strategy_encoder.rs | 7 +- src/encoding/swap_encoder.rs | 1 + 5 files changed, 113 insertions(+), 58 deletions(-) diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index f28a768..2d776f4 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -1,9 +1,9 @@ -use std::cmp::min; +use std::{cmp::min, str::FromStr}; -use alloy_primitives::{aliases::U24, map::HashSet, U256, U8}; +use alloy_primitives::{aliases::U24, map::HashSet, FixedBytes, U256, U8}; use alloy_sol_types::SolValue; use num_bigint::BigUint; -use tycho_core::{models::Chain, Bytes}; +use tycho_core::{keccak256, models::Chain, Bytes}; use crate::encoding::{ errors::EncodingError, @@ -25,15 +25,23 @@ pub trait EVMStrategyEncoder: StrategyEncoder { token_in: U8, token_out: U8, split: U24, + executor_address: Bytes, + executor_selector: FixedBytes<4>, protocol_data: Vec, ) -> Vec { let mut encoded = Vec::new(); encoded.push(token_in.to_be_bytes_vec()[0]); encoded.push(token_out.to_be_bytes_vec()[0]); encoded.extend_from_slice(&split.to_be_bytes_vec()); + encoded.extend(executor_address.to_vec()); + encoded.extend(executor_selector); encoded.extend(protocol_data); encoded } + fn encode_executor_selector(&self, selector: &str) -> FixedBytes<4> { + let hash = keccak256(selector.as_bytes()); + FixedBytes::<4>::from([hash[0], hash[1], hash[2], hash[3]]) + } } pub struct SplitSwapStrategyEncoder { @@ -53,7 +61,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { &self, solution: Solution, router_address: Bytes, - ) -> Result, EncodingError> { + ) -> Result<(Vec, Bytes), EncodingError> { let (permit, signature) = self.permit2.get_permit( &router_address, &solution.sender, @@ -122,6 +130,10 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { })?, ), percentage_to_uint24(swap.split), + Bytes::from_str(swap_encoder.executor_address()).map_err(|_| { + EncodingError::FatalError("Invalid executor address".to_string()) + })?, + self.encode_executor_selector(swap_encoder.executor_selector()), protocol_data, ); swaps.push(swap_data); @@ -151,20 +163,20 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { .abi_encode(); let contract_interaction = encode_input(&self.selector, method_calldata); - Ok(contract_interaction) + Ok((contract_interaction, router_address)) } } /// 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 { +pub struct ExecutorStrategyEncoder {} +impl EVMStrategyEncoder for ExecutorStrategyEncoder {} +impl StrategyEncoder for ExecutorStrategyEncoder { fn encode_strategy( &self, solution: Solution, _router_address: Bytes, - ) -> Result, EncodingError> { + ) -> Result<(Vec, Bytes), EncodingError> { if solution.router_address.is_none() { return Err(EncodingError::InvalidInput( "Router address is required for straight to pool solutions".to_string(), @@ -193,8 +205,9 @@ impl StrategyEncoder for StraightToPoolStrategyEncoder { }; 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) + let executor_address = Bytes::from_str(swap_encoder.executor_address()) + .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?; + Ok((protocol_data, executor_address)) } } @@ -203,11 +216,70 @@ mod tests { use std::str::FromStr; use alloy::hex::encode; - use tycho_core::dto::ProtocolComponent; + use num_bigint::BigUint; + use tycho_core::{dto::ProtocolComponent, Bytes}; use super::*; use crate::encoding::models::Swap; + #[test] + fn test_executor_strategy_encode() { + let encoder = ExecutorStrategyEncoder {}; + + let token_in = Bytes::from("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"); + let token_out = Bytes::from("0x6b175474e89094c44da98b954eedeac495271d0f"); + + let swap = Swap { + component: ProtocolComponent { + id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(), + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: token_in.clone(), + token_out: token_out.clone(), + split: 0f64, + }; + + let solution = Solution { + exact_out: false, + given_token: token_in, + given_amount: BigUint::from(1000000000000000000u64), + expected_amount: BigUint::from(1000000000000000000u64), + checked_token: token_out, + check_amount: None, + sender: Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap(), + // The receiver was generated with `makeAddr("bob") using forge` + receiver: Bytes::from_str("0x1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e").unwrap(), + swaps: vec![swap], + direct_execution: true, + router_address: Some(Bytes::zero(20)), + slippage: None, + native_action: None, + }; + + let (protocol_data, executor_address) = encoder + .encode_strategy(solution, Bytes::zero(20)) + .unwrap(); + let hex_protocol_data = encode(&protocol_data); + assert_eq!( + executor_address, + Bytes::from_str("0x5c2f5a71f67c01775180adc06909288b4c329308").unwrap() + ); + assert_eq!( + hex_protocol_data, + String::from(concat!( + // in token + "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + // component id + "a478c2975ab1ea89e8196811f51a7b7ade33eb11", + // receiver + "1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e", + // zero for one + "00", + )) + ); + } + #[test] fn test_split_swap_strategy_encoder() { // Set up a mock private key for signing @@ -243,7 +315,7 @@ mod tests { }; let router_address = Bytes::from_str("0x2c6A3cd97c6283b95Ac8C5A4459eBB0d5Fd404F4").unwrap(); - let calldata = encoder + let (calldata, _) = encoder .encode_strategy(solution, router_address) .unwrap(); @@ -279,9 +351,9 @@ mod tests { let expected_swaps = String::from(concat!( // length of ple encoded swaps without padding - "000000000000000000000000000000000000000000000000000000000000005d", + "000000000000000000000000000000000000000000000000000000000000005c", // ple encoded swaps - "005b", + "005a", // Swap header "01", // token in index "00", // token out index diff --git a/src/encoding/evm/strategy_encoder/strategy_selector.rs b/src/encoding/evm/strategy_encoder/strategy_selector.rs index 2451711..b45cebf 100644 --- a/src/encoding/evm/strategy_encoder/strategy_selector.rs +++ b/src/encoding/evm/strategy_encoder/strategy_selector.rs @@ -2,9 +2,7 @@ use tycho_core::models::Chain; use crate::encoding::{ errors::EncodingError, - evm::strategy_encoder::strategy_encoders::{ - SplitSwapStrategyEncoder, StraightToPoolStrategyEncoder, - }, + evm::strategy_encoder::strategy_encoders::{ExecutorStrategyEncoder, SplitSwapStrategyEncoder}, models::Solution, strategy_encoder::{StrategyEncoder, StrategySelector}, }; @@ -18,8 +16,8 @@ impl StrategySelector for EVMStrategySelector { signer: Option, chain: Chain, ) -> Result, EncodingError> { - if solution.straight_to_pool { - Ok(Box::new(StraightToPoolStrategyEncoder {})) + if solution.direct_execution { + Ok(Box::new(ExecutorStrategyEncoder {})) } else { let signer_pk = signer.ok_or_else(|| { EncodingError::FatalError( diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index 661b309..6c0a894 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -1,8 +1,7 @@ use std::str::FromStr; -use alloy_primitives::{Address, Bytes as AlloyBytes, FixedBytes}; +use alloy_primitives::{Address, Bytes as AlloyBytes}; use alloy_sol_types::SolValue; -use tycho_core::keccak256; use crate::encoding::{ errors::EncodingError, @@ -13,18 +12,11 @@ use crate::encoding::{ swap_encoder::SwapEncoder, }; -pub trait EVMSwapEncoder: SwapEncoder { - fn executor_selector(&self) -> FixedBytes<4> { - let hash = keccak256("swap(uint256,bytes)".as_bytes()); - FixedBytes::<4>::from([hash[0], hash[1], hash[2], hash[3]]) - } -} - pub struct UniswapV2SwapEncoder { executor_address: String, + executor_selector: String, } -impl EVMSwapEncoder for UniswapV2SwapEncoder {} impl UniswapV2SwapEncoder { fn get_zero_to_one(sell_token_address: Address, buy_token_address: Address) -> bool { sell_token_address < buy_token_address @@ -33,7 +25,7 @@ impl UniswapV2SwapEncoder { impl SwapEncoder for UniswapV2SwapEncoder { fn new(executor_address: String) -> Self { - Self { executor_address } + Self { executor_address, executor_selector: "swap(uint256,bytes)".to_string() } } fn encode_swap( @@ -51,9 +43,6 @@ impl SwapEncoder for UniswapV2SwapEncoder { // Token in address is always needed to perform a manual transfer from the router, // since no optimizations are performed that send from one pool to the next let args = ( - Address::from_str(self.executor_address()) - .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?, - self.executor_selector(), token_in_address, component_id, bytes_to_address(&encoding_context.receiver)?, @@ -66,12 +55,16 @@ impl SwapEncoder for UniswapV2SwapEncoder { fn executor_address(&self) -> &str { &self.executor_address } + + fn executor_selector(&self) -> &str { + &self.executor_selector + } } pub struct UniswapV3SwapEncoder { executor_address: String, + executor_selector: String, } -impl EVMSwapEncoder for UniswapV3SwapEncoder {} impl UniswapV3SwapEncoder { fn get_zero_to_one(sell_token_address: Address, buy_token_address: Address) -> bool { @@ -81,7 +74,7 @@ impl UniswapV3SwapEncoder { impl SwapEncoder for UniswapV3SwapEncoder { fn new(executor_address: String) -> Self { - Self { executor_address } + Self { executor_address, executor_selector: "swap(uint256,bytes)".to_string() } } fn encode_swap( @@ -119,9 +112,6 @@ impl SwapEncoder for UniswapV3SwapEncoder { })?; let args = ( - Address::from_str(self.executor_address()) - .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?, - self.executor_selector(), token_in_address, token_out_address, pool_fee_u24, @@ -136,19 +126,22 @@ impl SwapEncoder for UniswapV3SwapEncoder { fn executor_address(&self) -> &str { &self.executor_address } + fn executor_selector(&self) -> &str { + &self.executor_selector + } } pub struct BalancerV2SwapEncoder { executor_address: String, + executor_selector: String, vault_address: String, } -impl EVMSwapEncoder for BalancerV2SwapEncoder {} - impl SwapEncoder for BalancerV2SwapEncoder { fn new(executor_address: String) -> Self { Self { executor_address, + executor_selector: "swap(uint256,bytes)".to_string(), vault_address: "0xba12222222228d8ba445958a75a0704d566bf2c8".to_string(), } } @@ -171,9 +164,6 @@ impl SwapEncoder for BalancerV2SwapEncoder { .map_err(|_| EncodingError::FatalError("Invalid component ID".to_string()))?; let args = ( - Address::from_str(self.executor_address()) - .map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?, - self.executor_selector(), bytes_to_address(&swap.token_in)?, bytes_to_address(&swap.token_out)?, component_id, @@ -187,6 +177,9 @@ impl SwapEncoder for BalancerV2SwapEncoder { fn executor_address(&self) -> &str { &self.executor_address } + fn executor_selector(&self) -> &str { + &self.executor_selector + } } #[cfg(test)] @@ -224,10 +217,6 @@ mod tests { assert_eq!( hex_swap, String::from(concat!( - // executor address - "543778987b293c7e8cf0722bb2e935ba6f4068d4", - // executor selector - "bd0625ab", // in token "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // component id @@ -270,10 +259,6 @@ mod tests { assert_eq!( hex_swap, String::from(concat!( - // executor address - "543778987b293c7e8cf0722bb2e935ba6f4068d4", - // executor selector - "bd0625ab", // in token "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // out token @@ -318,10 +303,6 @@ mod tests { assert_eq!( hex_swap, String::from(concat!( - // executor address - "543778987b293c7e8cf0722bb2e935ba6f4068d4", - // executor selector - "bd0625ab", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out diff --git a/src/encoding/strategy_encoder.rs b/src/encoding/strategy_encoder.rs index dcdcac0..99dfb36 100644 --- a/src/encoding/strategy_encoder.rs +++ b/src/encoding/strategy_encoder.rs @@ -4,8 +4,11 @@ use crate::encoding::{errors::EncodingError, models::Solution}; #[allow(dead_code)] pub trait StrategyEncoder { - fn encode_strategy(&self, to_encode: Solution,router_address: Bytes,) -> Result<(Vec, Bytes), EncodingError>; - fn selector(&self, exact_out: bool) -> &str; + fn encode_strategy( + &self, + to_encode: Solution, + router_address: Bytes, + ) -> Result<(Vec, Bytes), EncodingError>; } pub trait StrategySelector { diff --git a/src/encoding/swap_encoder.rs b/src/encoding/swap_encoder.rs index 1a1fc0c..14f49f2 100644 --- a/src/encoding/swap_encoder.rs +++ b/src/encoding/swap_encoder.rs @@ -14,4 +14,5 @@ pub trait SwapEncoder: Sync + Send { encoding_context: EncodingContext, ) -> Result, EncodingError>; fn executor_address(&self) -> &str; + fn executor_selector(&self) -> &str; }