feat(tycho-router-encoder): Select strategy depending on the solution

- The tycho router address default is set at the EncoderBuilder level (not inside the strategies)
- Rename TychoCoreChain to TychoCommonChain
- Only take TychoCommonChain as an argument at the outermost level: EncoderBuilder. Everywhere else we use the execution Chain right away

--- don't change below this line ---
ENG-4332 Took 1 hour 10 minutes
This commit is contained in:
Diana Carvalho
2025-04-08 15:55:55 +01:00
parent 6fd0ab54c1
commit f5e712e0ff
6 changed files with 150 additions and 147 deletions

View File

@@ -5,7 +5,13 @@ use tycho_common::Bytes;
use crate::encoding::{
errors::EncodingError,
evm::{group_swaps::group_swaps, swap_encoder::swap_encoder_registry::SwapEncoderRegistry},
evm::{
group_swaps::group_swaps,
strategy_encoder::strategy_encoders::{
SequentialSwapStrategyEncoder, SingleSwapStrategyEncoder, SplitSwapStrategyEncoder,
},
swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
},
models::{Chain, EncodingContext, NativeAction, Solution, Transaction},
strategy_encoder::StrategyEncoder,
tycho_encoder::TychoEncoder,
@@ -14,24 +20,50 @@ use crate::encoding::{
/// Encodes solutions to be used by the TychoRouter.
///
/// # Fields
/// * `strategy_encoder`: Strategy encoder to follow for encoding the solution
/// * `single_swap_strategy`: Encoder for single swaps
/// * `sequential_swap_strategy`: Encoder for sequential swaps
/// * `split_swap_strategy`: Encoder for split swaps
/// * `native_address`: Address of the chain's native token
/// * `wrapped_address`: Address of the chain's wrapped native token
pub struct TychoRouterEncoder {
strategy_encoder: Box<dyn StrategyEncoder>,
single_swap_strategy: SingleSwapStrategyEncoder,
sequential_swap_strategy: SequentialSwapStrategyEncoder,
split_swap_strategy: SplitSwapStrategyEncoder,
native_address: Bytes,
wrapped_address: Bytes,
}
impl TychoRouterEncoder {
pub fn new(
chain: tycho_common::models::Chain,
strategy_encoder: Box<dyn StrategyEncoder>,
chain: Chain,
swap_encoder_registry: SwapEncoderRegistry,
swapper_pk: Option<String>,
router_address: Bytes,
) -> Result<Self, EncodingError> {
let chain: Chain = Chain::from(chain);
let native_address = chain.native_token()?;
let wrapped_address = chain.wrapped_token()?;
Ok(TychoRouterEncoder { strategy_encoder, native_address, wrapped_address })
Ok(TychoRouterEncoder {
single_swap_strategy: SingleSwapStrategyEncoder::new(
chain.clone(),
swap_encoder_registry.clone(),
swapper_pk.clone(),
router_address.clone(),
)?,
sequential_swap_strategy: SequentialSwapStrategyEncoder::new(
chain.clone(),
swap_encoder_registry.clone(),
swapper_pk.clone(),
router_address.clone(),
)?,
split_swap_strategy: SplitSwapStrategyEncoder::new(
chain,
swap_encoder_registry,
None,
router_address.clone(),
)?,
native_address,
wrapped_address,
})
}
}
@@ -40,10 +72,20 @@ impl TychoEncoder for TychoRouterEncoder {
let mut transactions: Vec<Transaction> = Vec::new();
for solution in solutions.iter() {
self.validate_solution(solution)?;
let (contract_interaction, target_address) = self
.strategy_encoder
.encode_strategy(solution.clone())?;
let (contract_interaction, target_address) = if solution.swaps.len() == 1 {
self.single_swap_strategy
.encode_strategy(solution.clone())?
} else if solution
.swaps
.iter()
.all(|swap| swap.split == 0.0)
{
self.sequential_swap_strategy
.encode_strategy(solution.clone())?
} else {
self.split_swap_strategy
.encode_strategy(solution.clone())?
};
let value = if solution.given_token == self.native_address {
solution.given_amount.clone()
@@ -173,10 +215,9 @@ pub struct TychoExecutorEncoder {
impl TychoExecutorEncoder {
pub fn new(
chain: tycho_common::models::Chain,
chain: Chain,
swap_encoder_registry: SwapEncoderRegistry,
) -> Result<Self, EncodingError> {
let chain: Chain = Chain::from(chain);
let native_address = chain.native_token()?;
Ok(TychoExecutorEncoder { swap_encoder_registry, native_address })
}
@@ -269,12 +310,10 @@ impl TychoEncoder for TychoExecutorEncoder {
mod tests {
use std::str::FromStr;
use tycho_common::models::{protocol::ProtocolComponent, Chain as TychoCoreChain};
use tycho_common::models::{protocol::ProtocolComponent, Chain as TychoCommonChain};
use super::*;
use crate::encoding::{
models::Swap, strategy_encoder::StrategyEncoder, swap_encoder::SwapEncoder,
};
use crate::encoding::models::Swap;
fn dai() -> Bytes {
Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap()
@@ -296,36 +335,25 @@ mod tests {
Bytes::from_str("0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599").unwrap()
}
fn get_swap_encoder_registry() -> SwapEncoderRegistry {
SwapEncoderRegistry::new(
Some("config/test_executor_addresses.json".to_string()),
TychoCommonChain::Ethereum.into(),
)
.unwrap()
}
mod router_encoder {
use super::*;
#[derive(Clone)]
struct MockStrategy;
impl StrategyEncoder for MockStrategy {
fn encode_strategy(
&self,
_solution: Solution,
) -> Result<(Vec<u8>, Bytes), EncodingError> {
Ok((
Bytes::from_str("0x1234")
.unwrap()
.to_vec(),
Bytes::from_str("0xabcd").unwrap(),
))
}
fn get_swap_encoder(&self, _protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
None
}
fn clone_box(&self) -> Box<dyn StrategyEncoder> {
Box::new(self.clone())
}
}
fn get_mocked_tycho_router_encoder() -> TychoRouterEncoder {
let strategy_encoder = Box::new(MockStrategy {});
TychoRouterEncoder::new(TychoCoreChain::Ethereum, strategy_encoder).unwrap()
TychoRouterEncoder::new(
TychoCommonChain::Ethereum.into(),
get_swap_encoder_registry(),
None,
Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(),
)
.unwrap()
}
#[test]
fn test_encode_router_calldata() {
@@ -346,7 +374,9 @@ mod tests {
exact_out: false,
given_amount: eth_amount_in.clone(),
given_token: eth(),
checked_token: dai(),
swaps: vec![swap],
receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(),
native_action: Some(NativeAction::Wrap),
..Default::default()
};
@@ -357,8 +387,10 @@ mod tests {
let transactions = transactions.unwrap();
assert_eq!(transactions.len(), 1);
assert_eq!(transactions[0].value, eth_amount_in);
assert_eq!(transactions[0].data, Bytes::from_str("0x1234").unwrap());
assert_eq!(transactions[0].to, Bytes::from_str("0xabcd").unwrap());
assert_eq!(
transactions[0].to,
Bytes::from_str("0x3ede3eca2a72b3aecc820e955b36f38437d01395").unwrap()
);
}
#[test]
@@ -824,24 +856,14 @@ mod tests {
use tycho_common::{models::protocol::ProtocolComponent, Bytes};
use super::*;
use crate::encoding::{
evm::swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
models::{Solution, Swap},
};
fn get_swap_encoder_registry() -> SwapEncoderRegistry {
SwapEncoderRegistry::new(
Some("config/test_executor_addresses.json".to_string()),
TychoCoreChain::Ethereum,
)
.unwrap()
}
use crate::encoding::models::{Solution, Swap};
#[test]
fn test_executor_encoder_encode() {
let swap_encoder_registry = get_swap_encoder_registry();
let encoder =
TychoExecutorEncoder::new(TychoCoreChain::Ethereum, swap_encoder_registry).unwrap();
TychoExecutorEncoder::new(TychoCommonChain::Ethereum.into(), swap_encoder_registry)
.unwrap();
let token_in = weth();
let token_out = dai();
@@ -902,7 +924,8 @@ mod tests {
fn test_executor_encoder_too_many_swaps() {
let swap_encoder_registry = get_swap_encoder_registry();
let encoder =
TychoExecutorEncoder::new(TychoCoreChain::Ethereum, swap_encoder_registry).unwrap();
TychoExecutorEncoder::new(TychoCommonChain::Ethereum.into(), swap_encoder_registry)
.unwrap();
let token_in = weth();
let token_out = dai();
@@ -939,7 +962,8 @@ mod tests {
fn test_executor_encoder_grouped_swaps() {
let swap_encoder_registry = get_swap_encoder_registry();
let encoder =
TychoExecutorEncoder::new(TychoCoreChain::Ethereum, swap_encoder_registry).unwrap();
TychoExecutorEncoder::new(TychoCommonChain::Ethereum.into(), swap_encoder_registry)
.unwrap();
let eth = eth();
let usdc = usdc();