feat: Refactor Registries
Interface changes: - Rename StrategySelector to StrategyEncoderRegistry - Implement clone for SwapEncoder The StrategyEncoderRegistry needs to be initialised at the highest level and the passed to the TychoEncoder. The TychoEncoder doesn't hold the chain nor the signer_pk as attributes anymore The StrategyEncoderRegistry does: - Initialises the SwapEncoderRegistry - Initialises all the strategies and saves them in a HashMap - Later, the TychoEncoder only reads from this hashmap The StrategyEncoder now each holds a SwapEncoderRegistry as an attribute and they use this to get the correct SwapEncoder instead of reading from the global SWAP_ENCODER_REGISTRY Simplified the SwapEncoderRegistry to not need a Config (everything is done inside itself) All SwapEncoders implement clone Took 2 hours 29 minutes Took 11 seconds Took 2 minutes
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
pub mod strategy_encoder_registry;
|
||||
mod strategy_encoders;
|
||||
pub mod strategy_selector;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tycho_core::models::Chain;
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::{
|
||||
strategy_encoder::strategy_encoders::{ExecutorStrategyEncoder, SplitSwapStrategyEncoder},
|
||||
swap_encoder::registry::SwapEncoderRegistry,
|
||||
},
|
||||
models::Solution,
|
||||
strategy_encoder::{StrategyEncoder, StrategyEncoderRegistry},
|
||||
};
|
||||
|
||||
pub struct EVMStrategyEncoderRegistry {
|
||||
strategies: HashMap<String, Box<dyn StrategyEncoder>>,
|
||||
}
|
||||
|
||||
impl StrategyEncoderRegistry for EVMStrategyEncoderRegistry {
|
||||
fn new(
|
||||
chain: Chain,
|
||||
executors_file_path: &str,
|
||||
signer_pk: Option<String>,
|
||||
) -> Result<Self, EncodingError> {
|
||||
let swap_encoder_registry = SwapEncoderRegistry::new(executors_file_path, Chain::Ethereum)?;
|
||||
|
||||
let mut strategies: HashMap<String, Box<dyn StrategyEncoder>> = 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<dyn StrategyEncoder>, 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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,12 @@ use crate::encoding::{
|
||||
evm::{
|
||||
approvals::permit2::Permit2,
|
||||
constants::WETH_ADDRESS,
|
||||
swap_encoder::SWAP_ENCODER_REGISTRY,
|
||||
swap_encoder::registry::SwapEncoderRegistry,
|
||||
utils::{biguint_to_u256, bytes_to_address, encode_input, percentage_to_uint24},
|
||||
},
|
||||
models::{EncodingContext, NativeAction, Solution},
|
||||
strategy_encoder::StrategyEncoder,
|
||||
swap_encoder::SwapEncoder,
|
||||
};
|
||||
|
||||
pub trait EVMStrategyEncoder: StrategyEncoder {
|
||||
@@ -54,14 +55,19 @@ pub trait EVMStrategyEncoder: StrategyEncoder {
|
||||
}
|
||||
|
||||
pub struct SplitSwapStrategyEncoder {
|
||||
swap_encoder_registry: SwapEncoderRegistry,
|
||||
permit2: Permit2,
|
||||
selector: String,
|
||||
}
|
||||
|
||||
impl SplitSwapStrategyEncoder {
|
||||
pub fn new(signer_pk: String, chain: Chain) -> Result<Self, EncodingError> {
|
||||
pub fn new(
|
||||
signer_pk: String,
|
||||
chain: Chain,
|
||||
swap_encoder_registry: SwapEncoderRegistry,
|
||||
) -> Result<Self, EncodingError> {
|
||||
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)?, selector })
|
||||
Ok(Self { permit2: Permit2::new(signer_pk, chain)?, selector, swap_encoder_registry })
|
||||
}
|
||||
}
|
||||
impl EVMStrategyEncoder for SplitSwapStrategyEncoder {}
|
||||
@@ -134,16 +140,14 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||
|
||||
let mut swaps = vec![];
|
||||
for swap in solution.swaps.iter() {
|
||||
let registry = SWAP_ENCODER_REGISTRY
|
||||
.read()
|
||||
.map_err(|_| {
|
||||
EncodingError::FatalError(
|
||||
"Failed to read the swap encoder registry".to_string(),
|
||||
)
|
||||
let swap_encoder = self
|
||||
.get_swap_encoder(&swap.component.protocol_system)
|
||||
.ok_or_else(|| {
|
||||
EncodingError::InvalidInput(format!(
|
||||
"Swap encoder not found for protocol: {}",
|
||||
swap.component.protocol_system
|
||||
))
|
||||
})?;
|
||||
let swap_encoder = registry
|
||||
.get_encoder(&swap.component.protocol_system)
|
||||
.expect("Swap encoder not found");
|
||||
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: router_address.clone(),
|
||||
@@ -201,11 +205,24 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||
let contract_interaction = encode_input(&self.selector, method_calldata);
|
||||
Ok((contract_interaction, router_address))
|
||||
}
|
||||
|
||||
fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
|
||||
self.swap_encoder_registry
|
||||
.get_encoder(protocol_system)
|
||||
}
|
||||
}
|
||||
|
||||
/// This strategy encoder is used for solutions that are sent directly to the pool.
|
||||
/// Only 1 solution with 1 swap is supported.
|
||||
pub struct ExecutorStrategyEncoder {}
|
||||
pub struct ExecutorStrategyEncoder {
|
||||
swap_encoder_registry: SwapEncoderRegistry,
|
||||
}
|
||||
|
||||
impl ExecutorStrategyEncoder {
|
||||
pub fn new(swap_encoder_registry: SwapEncoderRegistry) -> Self {
|
||||
Self { swap_encoder_registry }
|
||||
}
|
||||
}
|
||||
impl EVMStrategyEncoder for ExecutorStrategyEncoder {}
|
||||
impl StrategyEncoder for ExecutorStrategyEncoder {
|
||||
fn encode_strategy(
|
||||
@@ -223,13 +240,9 @@ impl StrategyEncoder for ExecutorStrategyEncoder {
|
||||
.swaps
|
||||
.first()
|
||||
.ok_or_else(|| EncodingError::InvalidInput("No swaps found in solution".to_string()))?;
|
||||
let registry = SWAP_ENCODER_REGISTRY
|
||||
.read()
|
||||
.map_err(|_| {
|
||||
EncodingError::FatalError("Failed to read the swap encoder registry".to_string())
|
||||
})?;
|
||||
let swap_encoder = registry
|
||||
.get_encoder(&swap.component.protocol_system)
|
||||
|
||||
let swap_encoder = self
|
||||
.get_swap_encoder(&swap.component.protocol_system)
|
||||
.ok_or_else(|| {
|
||||
EncodingError::InvalidInput(format!(
|
||||
"Swap encoder not found for protocol: {}",
|
||||
@@ -248,6 +261,10 @@ impl StrategyEncoder for ExecutorStrategyEncoder {
|
||||
.map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?;
|
||||
Ok((protocol_data, executor_address))
|
||||
}
|
||||
fn get_swap_encoder(&self, protocol_system: &str) -> Option<&Box<dyn SwapEncoder>> {
|
||||
self.swap_encoder_registry
|
||||
.get_encoder(protocol_system)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -265,9 +282,15 @@ mod tests {
|
||||
models::Swap,
|
||||
};
|
||||
|
||||
fn get_swap_encoder_registry() -> SwapEncoderRegistry {
|
||||
SwapEncoderRegistry::new("src/encoding/config/executor_addresses.json", Chain::Ethereum)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_executor_strategy_encode() {
|
||||
let encoder = ExecutorStrategyEncoder {};
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = ExecutorStrategyEncoder::new(swap_encoder_registry);
|
||||
|
||||
let token_in = Bytes::from("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2");
|
||||
let token_out = Bytes::from("0x6b175474e89094c44da98b954eedeac495271d0f");
|
||||
@@ -373,8 +396,10 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
};
|
||||
|
||||
let encoder = SplitSwapStrategyEncoder::new(private_key, Chain::Ethereum).unwrap();
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder =
|
||||
SplitSwapStrategyEncoder::new(private_key, Chain::Ethereum, swap_encoder_registry)
|
||||
.unwrap();
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
given_token: weth,
|
||||
@@ -608,8 +633,10 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
};
|
||||
|
||||
let encoder = SplitSwapStrategyEncoder::new(private_key, Chain::Ethereum).unwrap();
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder =
|
||||
SplitSwapStrategyEncoder::new(private_key, Chain::Ethereum, swap_encoder_registry)
|
||||
.unwrap();
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
given_token: weth,
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
use tycho_core::models::Chain;
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::strategy_encoder::strategy_encoders::{ExecutorStrategyEncoder, SplitSwapStrategyEncoder},
|
||||
models::Solution,
|
||||
strategy_encoder::{StrategyEncoder, StrategySelector},
|
||||
};
|
||||
|
||||
pub struct EVMStrategySelector;
|
||||
|
||||
impl StrategySelector for EVMStrategySelector {
|
||||
fn select_strategy(
|
||||
&self,
|
||||
solution: &Solution,
|
||||
signer: Option<String>,
|
||||
chain: Chain,
|
||||
) -> Result<Box<dyn StrategyEncoder>, EncodingError> {
|
||||
if solution.direct_execution {
|
||||
Ok(Box::new(ExecutorStrategyEncoder {}))
|
||||
} else {
|
||||
let signer_pk = signer.ok_or_else(|| {
|
||||
EncodingError::FatalError(
|
||||
"Signer is required for SplitSwapStrategyEncoder".to_string(),
|
||||
)
|
||||
})?;
|
||||
Ok(Box::new(SplitSwapStrategyEncoder::new(signer_pk, chain)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user