chore: merge main
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use std::{io, str::Utf8Error};
|
||||
|
||||
use thiserror::Error;
|
||||
use tycho_common::simulation::errors::SimulationError;
|
||||
|
||||
/// Represents the outer-level, user-facing errors of the tycho-execution encoding package.
|
||||
///
|
||||
@@ -41,3 +42,15 @@ impl From<Utf8Error> for EncodingError {
|
||||
EncodingError::FatalError(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SimulationError> for EncodingError {
|
||||
fn from(err: SimulationError) -> Self {
|
||||
match err {
|
||||
SimulationError::FatalError(err_msg) => EncodingError::FatalError(err_msg),
|
||||
SimulationError::InvalidInput(err_msg, ..) => EncodingError::InvalidInput(err_msg),
|
||||
SimulationError::RecoverableError(error_msg) => {
|
||||
EncodingError::RecoverableError(error_msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,10 +31,6 @@ pub struct Permit2 {
|
||||
address: Address,
|
||||
client: EVMProvider,
|
||||
runtime_handle: Handle,
|
||||
// Store the runtime to prevent it from being dropped before use.
|
||||
// This is required since tycho-execution does not have a pre-existing runtime.
|
||||
// However, if the library is used in a context where a runtime already exists, it is not
|
||||
// necessary to store it.
|
||||
#[allow(dead_code)]
|
||||
runtime: Option<Arc<Runtime>>,
|
||||
}
|
||||
|
||||
@@ -23,10 +23,6 @@ use crate::encoding::{
|
||||
pub struct ProtocolApprovalsManager {
|
||||
client: EVMProvider,
|
||||
runtime_handle: Handle,
|
||||
// Store the runtime to prevent it from being dropped before use.
|
||||
// This is required since tycho-execution does not have a pre-existing runtime.
|
||||
// However, if the library is used in a context where a runtime already exists, it is not
|
||||
// necessary to store it.
|
||||
#[allow(dead_code)]
|
||||
runtime: Option<Arc<Runtime>>,
|
||||
}
|
||||
|
||||
@@ -12,15 +12,15 @@ use crate::encoding::{evm::constants::GROUPABLE_PROTOCOLS, models::Swap};
|
||||
/// * `swaps`: Vec<Swap>, the sequence of swaps to be executed as a group
|
||||
/// * `split`: f64, the split percentage of the first swap in the group
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SwapGroup<'a> {
|
||||
pub struct SwapGroup {
|
||||
pub token_in: Bytes,
|
||||
pub token_out: Bytes,
|
||||
pub protocol_system: String,
|
||||
pub swaps: Vec<Swap<'a>>,
|
||||
pub swaps: Vec<Swap>,
|
||||
pub split: f64,
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for SwapGroup<'a> {
|
||||
impl PartialEq for SwapGroup {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.token_in == other.token_in &&
|
||||
self.token_out == other.token_out &&
|
||||
@@ -34,7 +34,7 @@ impl<'a> PartialEq for SwapGroup<'a> {
|
||||
///
|
||||
/// An example where this applies is the case of USV4, which uses a PoolManager contract
|
||||
/// to save token transfers on consecutive swaps.
|
||||
pub fn group_swaps<'a>(swaps: &'a Vec<Swap<'a>>) -> Vec<SwapGroup<'a>> {
|
||||
pub fn group_swaps(swaps: &Vec<Swap>) -> Vec<SwapGroup> {
|
||||
let mut grouped_swaps: Vec<SwapGroup> = Vec::new();
|
||||
let mut current_group: Option<SwapGroup> = None;
|
||||
let mut last_swap_protocol = "".to_string();
|
||||
@@ -87,7 +87,7 @@ mod tests {
|
||||
use tycho_common::{models::protocol::ProtocolComponent, Bytes};
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::models::Swap;
|
||||
use crate::encoding::models::SwapBuilder;
|
||||
|
||||
fn weth() -> Bytes {
|
||||
Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec())
|
||||
@@ -105,41 +105,26 @@ mod tests {
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
|
||||
let swap_weth_wbtc = Swap {
|
||||
component: ProtocolComponent {
|
||||
protocol_system: "uniswap_v4_hooks".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: wbtc.clone(),
|
||||
// This represents the remaining 50%, but to avoid any rounding errors we set this to
|
||||
// 0 to signify "the remainder of the WETH value". It should still be very close to 50%
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
protocol_system: "uniswap_v4_hooks".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: wbtc.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_usdc_dai = Swap {
|
||||
component: ProtocolComponent {
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_wbtc = SwapBuilder::new(
|
||||
ProtocolComponent { protocol_system: "uniswap_v4_hooks".to_string(), ..Default::default() },
|
||||
weth.clone(),
|
||||
wbtc.clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let swap_wbtc_usdc = SwapBuilder::new(
|
||||
ProtocolComponent { protocol_system: "uniswap_v4_hooks".to_string(), ..Default::default() },
|
||||
wbtc.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let swap_usdc_dai = SwapBuilder::new(
|
||||
ProtocolComponent { protocol_system: "uniswap_v2".to_string(), ..Default::default() },
|
||||
usdc.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.build();
|
||||
let swaps = vec![swap_weth_wbtc.clone(), swap_wbtc_usdc.clone(), swap_usdc_dai.clone()];
|
||||
let grouped_swaps = group_swaps(&swaps);
|
||||
|
||||
@@ -179,52 +164,34 @@ mod tests {
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
|
||||
let swap_wbtc_weth = Swap {
|
||||
component: ProtocolComponent {
|
||||
protocol_system: "uniswap_v4_hooks".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: wbtc.clone(),
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
protocol_system: "uniswap_v4_hooks".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_dai = Swap {
|
||||
component: ProtocolComponent {
|
||||
protocol_system: "uniswap_v4_hooks".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
// This represents the remaining 50%, but to avoid any rounding errors we set this to
|
||||
// 0 to signify "the remainder of the WETH value". It should still be very close to 50%
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_dai_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
protocol_system: "uniswap_v4_hooks".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_weth = SwapBuilder::new(
|
||||
ProtocolComponent { protocol_system: "uniswap_v4_hooks".to_string(), ..Default::default() },
|
||||
wbtc.clone(),
|
||||
weth.clone(),
|
||||
)
|
||||
.build();
|
||||
let swap_weth_usdc = SwapBuilder::new(
|
||||
ProtocolComponent { protocol_system: "uniswap_v4_hooks".to_string(), ..Default::default() },
|
||||
weth.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.split(0.5f64)
|
||||
.build();
|
||||
let swap_weth_dai = SwapBuilder::new(
|
||||
ProtocolComponent { protocol_system: "uniswap_v4_hooks".to_string(), ..Default::default() },
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.build();
|
||||
// Split 0 represents the remaining 50%, but to avoid any rounding errors we set this to
|
||||
// 0 to signify "the remainder of the WETH value". It should still be very close to 50%
|
||||
|
||||
let swap_dai_usdc = SwapBuilder::new(
|
||||
ProtocolComponent { protocol_system: "uniswap_v4_hooks".to_string(), ..Default::default() },
|
||||
dai.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build();
|
||||
let swaps = vec![
|
||||
swap_wbtc_weth.clone(),
|
||||
swap_weth_usdc.clone(),
|
||||
@@ -275,52 +242,38 @@ mod tests {
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
|
||||
let swap_weth_wbtc = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap_weth_wbtc = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
protocol_system: "vm:balancer_v3".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: wbtc.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
weth.clone(),
|
||||
wbtc.clone(),
|
||||
)
|
||||
.split(0.5f64)
|
||||
.build();
|
||||
|
||||
let swap_wbtc_usdc = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
protocol_system: "vm:balancer_v3".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: wbtc.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_dai = Swap {
|
||||
component: ProtocolComponent {
|
||||
protocol_system: "uniswap_v4_hooks".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
// This represents the remaining 50%, but to avoid any rounding errors we set this to
|
||||
// 0 to signify "the remainder of the WETH value". It should still be very close to 50%
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_dai_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
protocol_system: "uniswap_v4_hooks".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
wbtc.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build();
|
||||
let swap_weth_dai = SwapBuilder::new(
|
||||
ProtocolComponent { protocol_system: "uniswap_v4_hooks".to_string(), ..Default::default() },
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.build();
|
||||
let swap_dai_usdc = SwapBuilder::new(
|
||||
ProtocolComponent { protocol_system: "uniswap_v4_hooks".to_string(), ..Default::default() },
|
||||
dai.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let swaps = vec![
|
||||
swap_weth_wbtc.clone(),
|
||||
|
||||
@@ -5,5 +5,7 @@ mod encoding_utils;
|
||||
mod group_swaps;
|
||||
pub mod strategy_encoder;
|
||||
mod swap_encoder;
|
||||
#[cfg(feature = "test-utils")]
|
||||
pub mod testing_utils;
|
||||
pub mod tycho_encoders;
|
||||
pub mod utils;
|
||||
|
||||
@@ -237,10 +237,11 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
|
||||
|
||||
let grouped_swaps = group_swaps(&solution.swaps);
|
||||
|
||||
let mut wrap = false;
|
||||
let (mut wrap, mut unwrap) = (false, false);
|
||||
if let Some(action) = &solution.native_action {
|
||||
if action == &NativeAction::Wrap {
|
||||
wrap = true
|
||||
match *action {
|
||||
NativeAction::Wrap => wrap = true,
|
||||
NativeAction::Unwrap => unwrap = true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +261,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
|
||||
let next_swap = grouped_swaps.get(i + 1);
|
||||
let (swap_receiver, next_swap_optimization) = self
|
||||
.transfer_optimization
|
||||
.get_receiver(&solution.receiver, next_swap)?;
|
||||
.get_receiver(&solution.receiver, next_swap, unwrap)?;
|
||||
next_in_between_swap_optimization_allowed = next_swap_optimization;
|
||||
|
||||
let transfer = self
|
||||
@@ -544,7 +545,6 @@ mod tests {
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::models::Swap;
|
||||
|
||||
fn eth_chain() -> Chain {
|
||||
Chain::Ethereum
|
||||
@@ -565,8 +565,8 @@ mod tests {
|
||||
}
|
||||
|
||||
mod single {
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::models::SwapBuilder;
|
||||
#[test]
|
||||
fn test_single_swap_strategy_encoder() {
|
||||
// Performs a single swap from WETH to DAI on a USV2 pool, with no grouping
|
||||
@@ -575,18 +575,16 @@ mod tests {
|
||||
let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.build();
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SingleSwapStrategyEncoder::new(
|
||||
eth_chain(),
|
||||
@@ -637,18 +635,16 @@ mod tests {
|
||||
|
||||
let checked_amount = BigUint::from_str("1_640_000000000000000000").unwrap();
|
||||
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.build();
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SingleSwapStrategyEncoder::new(
|
||||
eth_chain(),
|
||||
@@ -698,6 +694,7 @@ mod tests {
|
||||
|
||||
mod sequential {
|
||||
use super::*;
|
||||
use crate::encoding::models::SwapBuilder;
|
||||
|
||||
#[test]
|
||||
fn test_sequential_swap_strategy_encoder_no_permit2() {
|
||||
@@ -709,30 +706,26 @@ mod tests {
|
||||
let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap();
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
|
||||
let swap_weth_wbtc = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap_weth_wbtc = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xBb2b8038a1640196FbE3e38816F3e67Cba72D940".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
weth.clone(),
|
||||
wbtc.clone(),
|
||||
)
|
||||
.build();
|
||||
let swap_wbtc_usdc = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x004375Dff511095CC5A197A54140a24eFEF3A416".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: wbtc.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
wbtc.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build();
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SequentialSwapStrategyEncoder::new(
|
||||
eth_chain(),
|
||||
@@ -790,6 +783,7 @@ mod tests {
|
||||
|
||||
mod split {
|
||||
use super::*;
|
||||
use crate::encoding::models::SwapBuilder;
|
||||
|
||||
#[test]
|
||||
fn test_split_input_cyclic_swap() {
|
||||
@@ -805,8 +799,8 @@ mod tests {
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
|
||||
// USDC -> WETH (Pool 1) - 60% of input
|
||||
let swap_usdc_weth_pool1 = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap_usdc_weth_pool1 = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* USDC-WETH USV3
|
||||
* Pool 1 */
|
||||
protocol_system: "uniswap_v3".to_string(),
|
||||
@@ -820,16 +814,15 @@ mod tests {
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc.clone(),
|
||||
token_out: weth.clone(),
|
||||
split: 0.6f64, // 60% of input
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
usdc.clone(),
|
||||
weth.clone(),
|
||||
)
|
||||
.split(0.6f64)
|
||||
.build();
|
||||
|
||||
// USDC -> WETH (Pool 2) - 40% of input (remaining)
|
||||
let swap_usdc_weth_pool2 = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap_usdc_weth_pool2 = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), /* USDC-WETH USV3
|
||||
* Pool 2 */
|
||||
protocol_system: "uniswap_v3".to_string(),
|
||||
@@ -843,16 +836,14 @@ mod tests {
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc.clone(),
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None, // Remaining 40%
|
||||
protocol_state: None,
|
||||
};
|
||||
usdc.clone(),
|
||||
weth.clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
// WETH -> USDC (Pool 2)
|
||||
let swap_weth_usdc_pool2 = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap_weth_usdc_pool2 = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), /* USDC-WETH USV2
|
||||
* Pool 2 */
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
@@ -866,13 +857,10 @@ mod tests {
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
weth.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build();
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SplitSwapStrategyEncoder::new(
|
||||
eth_chain(),
|
||||
@@ -961,8 +949,8 @@ mod tests {
|
||||
let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
|
||||
let swap_usdc_weth_v2 = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap_usdc_weth_v2 = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(), // USDC-WETH USV2
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
static_attributes: {
|
||||
@@ -975,15 +963,13 @@ mod tests {
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc.clone(),
|
||||
token_out: weth.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
usdc.clone(),
|
||||
weth.clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let swap_weth_usdc_v3_pool1 = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap_weth_usdc_v3_pool1 = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640".to_string(), /* USDC-WETH USV3
|
||||
* Pool 1 */
|
||||
protocol_system: "uniswap_v3".to_string(),
|
||||
@@ -997,17 +983,16 @@ mod tests {
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0.6f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
weth.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.split(0.6f64)
|
||||
.build();
|
||||
|
||||
let swap_weth_usdc_v3_pool2 = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap_weth_usdc_v3_pool2 = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8".to_string(), /* USDC-WETH USV3
|
||||
* Pool 2 */
|
||||
* Pool 1 */
|
||||
protocol_system: "uniswap_v3".to_string(),
|
||||
static_attributes: {
|
||||
let mut attrs = HashMap::new();
|
||||
@@ -1019,12 +1004,10 @@ mod tests {
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
weth.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SplitSwapStrategyEncoder::new(
|
||||
|
||||
@@ -197,7 +197,7 @@ mod tests {
|
||||
use tycho_common::{models::protocol::ProtocolComponent, Bytes};
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::models::Swap;
|
||||
use crate::encoding::models::{Swap, SwapBuilder};
|
||||
|
||||
#[test]
|
||||
fn test_validate_path_single_swap() {
|
||||
@@ -205,18 +205,16 @@ mod tests {
|
||||
let eth = Bytes::from_str("0x0000000000000000000000000000000000000000").unwrap();
|
||||
let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
let swaps = vec![Swap {
|
||||
component: ProtocolComponent {
|
||||
let swaps = vec![SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.build()];
|
||||
let result = validator.validate_swap_path(&swaps, &weth, &dai, &None, ð, &weth);
|
||||
assert_eq!(result, Ok(()));
|
||||
}
|
||||
@@ -229,30 +227,27 @@ mod tests {
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
let swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.split(0.5f64)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
dai.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build(),
|
||||
];
|
||||
let result = validator.validate_swap_path(&swaps, &weth, &usdc, &None, ð, &weth);
|
||||
assert_eq!(result, Ok(()));
|
||||
@@ -268,31 +263,28 @@ mod tests {
|
||||
let wbtc = Bytes::from_str("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599").unwrap();
|
||||
|
||||
let disconnected_swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool1".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.split(0.5f64)
|
||||
.build(),
|
||||
// This swap is disconnected from the WETH->DAI path
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool2".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: wbtc.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
wbtc.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build(),
|
||||
];
|
||||
let result =
|
||||
validator.validate_swap_path(&disconnected_swaps, &weth, &usdc, &None, ð, &weth);
|
||||
@@ -310,30 +302,26 @@ mod tests {
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
|
||||
let cyclic_swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool1".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc.clone(),
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
usdc.clone(),
|
||||
weth.clone(),
|
||||
)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool2".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
weth.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build(),
|
||||
];
|
||||
|
||||
// Test with USDC as both given token and checked token
|
||||
@@ -349,18 +337,17 @@ mod tests {
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
|
||||
let unreachable_swaps = vec![Swap {
|
||||
component: ProtocolComponent {
|
||||
let unreachable_swaps = vec![SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool1".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 1.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.split(1.0)
|
||||
.build()];
|
||||
let result =
|
||||
validator.validate_swap_path(&unreachable_swaps, &weth, &usdc, &None, ð, &weth);
|
||||
assert!(matches!(
|
||||
@@ -389,18 +376,16 @@ mod tests {
|
||||
let validator = SplitSwapValidator;
|
||||
let weth = Bytes::from_str("0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").unwrap();
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
let swaps = vec![Swap {
|
||||
component: ProtocolComponent {
|
||||
let swaps = vec![SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.build()];
|
||||
let result = validator.validate_split_percentages(&swaps);
|
||||
assert_eq!(result, Ok(()));
|
||||
}
|
||||
@@ -413,42 +398,38 @@ mod tests {
|
||||
|
||||
// Valid case: Multiple swaps with proper splits (50%, 30%, remainder)
|
||||
let valid_swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool1".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.split(0.5)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool2".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.3,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.split(0.3)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool3".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.0, // Remainder (20%)
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.build(),
|
||||
];
|
||||
assert!(validator
|
||||
.validate_split_percentages(&valid_swaps)
|
||||
@@ -462,30 +443,28 @@ mod tests {
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
|
||||
let invalid_total_swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool1".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.7,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.split(0.7)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool2".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.3,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.split(0.3)
|
||||
.build(),
|
||||
];
|
||||
assert!(matches!(
|
||||
validator.validate_split_percentages(&invalid_total_swaps),
|
||||
@@ -500,30 +479,27 @@ mod tests {
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
|
||||
let invalid_zero_position_swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool1".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool2".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.split(0.5)
|
||||
.build(),
|
||||
];
|
||||
assert!(matches!(
|
||||
validator.validate_split_percentages(&invalid_zero_position_swaps),
|
||||
@@ -538,42 +514,38 @@ mod tests {
|
||||
let dai = Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap();
|
||||
|
||||
let invalid_overflow_swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool1".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.6,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.split(0.6)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool2".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.split(0.5)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool3".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: dai.clone(),
|
||||
split: 0.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
weth.clone(),
|
||||
dai.clone(),
|
||||
)
|
||||
.build(),
|
||||
];
|
||||
assert!(matches!(
|
||||
validator.validate_split_percentages(&invalid_overflow_swaps),
|
||||
@@ -588,18 +560,16 @@ mod tests {
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
let weth = Bytes::from_str("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2").unwrap();
|
||||
|
||||
let swaps = vec![Swap {
|
||||
component: ProtocolComponent {
|
||||
let swaps = vec![SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool1".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth.clone(),
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
weth.clone(),
|
||||
usdc.clone(),
|
||||
)
|
||||
.build()];
|
||||
|
||||
let result = validator.validate_swap_path(
|
||||
&swaps,
|
||||
@@ -619,18 +589,16 @@ mod tests {
|
||||
let usdc = Bytes::from_str("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").unwrap();
|
||||
let weth = Bytes::from_str("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2").unwrap();
|
||||
|
||||
let swaps = vec![Swap {
|
||||
component: ProtocolComponent {
|
||||
let swaps = vec![SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "pool1".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc.clone(),
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
usdc.clone(),
|
||||
weth.clone(),
|
||||
)
|
||||
.build()];
|
||||
|
||||
let result = validator.validate_swap_path(
|
||||
&swaps,
|
||||
|
||||
@@ -82,6 +82,7 @@ impl TransferOptimization {
|
||||
&self,
|
||||
solution_receiver: &Bytes,
|
||||
next_swap: Option<&SwapGroup>,
|
||||
unwrap: bool,
|
||||
) -> Result<(Bytes, bool), EncodingError> {
|
||||
if let Some(next) = next_swap {
|
||||
// if the protocol of the next swap supports transfer in optimization
|
||||
@@ -104,7 +105,11 @@ impl TransferOptimization {
|
||||
}
|
||||
} else {
|
||||
// last swap - there is no next swap
|
||||
Ok((solution_receiver.clone(), false))
|
||||
if unwrap {
|
||||
Ok((self.router_address.clone(), false))
|
||||
} else {
|
||||
Ok((solution_receiver.clone(), false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,7 +121,7 @@ mod tests {
|
||||
use tycho_common::models::protocol::ProtocolComponent;
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::models::Swap;
|
||||
use crate::encoding::models::SwapBuilder;
|
||||
|
||||
fn weth() -> Bytes {
|
||||
Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec())
|
||||
@@ -169,18 +174,16 @@ mod tests {
|
||||
#[case] expected_transfer: TransferType,
|
||||
) {
|
||||
// The swap token is the same as the given token, which is not the native token
|
||||
let swaps = vec![Swap {
|
||||
component: ProtocolComponent {
|
||||
let swaps = vec![SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: swap_token_in.clone(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
swap_token_in.clone(),
|
||||
dai(),
|
||||
)
|
||||
.build()];
|
||||
let swap = SwapGroup {
|
||||
protocol_system: protocol,
|
||||
token_in: swap_token_in,
|
||||
@@ -204,16 +207,19 @@ mod tests {
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
// there is no next swap -> receiver is the solution receiver
|
||||
#[case(None, receiver(), false)]
|
||||
// there is no next swap but there is an unwrap -> receiver is the router
|
||||
#[case(None, true, router_address(), false)]
|
||||
// there is no next swap and no unwrap -> receiver is the solution receiver
|
||||
#[case(None, false, receiver(), false)]
|
||||
// protocol of next swap supports transfer in optimization
|
||||
#[case(Some("uniswap_v2"), component_id(), true)]
|
||||
#[case(Some("uniswap_v2"), false, component_id(), true)]
|
||||
// protocol of next swap supports transfer in optimization but is callback constrained
|
||||
#[case(Some("uniswap_v3"), router_address(), false)]
|
||||
#[case(Some("uniswap_v3"), false, router_address(), false)]
|
||||
// protocol of next swap does not support transfer in optimization
|
||||
#[case(Some("vm:curve"), router_address(), false)]
|
||||
#[case(Some("vm:curve"), false, router_address(), false)]
|
||||
fn test_get_receiver(
|
||||
#[case] protocol: Option<&str>,
|
||||
#[case] unwrap: bool,
|
||||
#[case] expected_receiver: Bytes,
|
||||
#[case] expected_optimization: bool,
|
||||
) {
|
||||
@@ -232,22 +238,20 @@ mod tests {
|
||||
token_in: usdc(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
swaps: vec![Swap {
|
||||
component: ProtocolComponent {
|
||||
swaps: vec![SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
protocol_system: protocol.unwrap().to_string(),
|
||||
id: component_id().to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}],
|
||||
usdc(),
|
||||
dai(),
|
||||
)
|
||||
.build()],
|
||||
})
|
||||
};
|
||||
|
||||
let result = optimization.get_receiver(&receiver(), next_swap.as_ref());
|
||||
let result = optimization.get_receiver(&receiver(), next_swap.as_ref(), unwrap);
|
||||
|
||||
assert!(result.is_ok());
|
||||
let (actual_receiver, optimization_flag) = result.unwrap();
|
||||
|
||||
@@ -5,8 +5,9 @@ use tycho_common::models::Chain;
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::swap_encoder::swap_encoders::{
|
||||
BalancerV2SwapEncoder, BalancerV3SwapEncoder, CurveSwapEncoder, EkuboSwapEncoder,
|
||||
MaverickV2SwapEncoder, UniswapV2SwapEncoder, UniswapV3SwapEncoder, UniswapV4SwapEncoder,
|
||||
BalancerV2SwapEncoder, BalancerV3SwapEncoder, BebopSwapEncoder, CurveSwapEncoder,
|
||||
EkuboSwapEncoder, HashflowSwapEncoder, MaverickV2SwapEncoder, UniswapV2SwapEncoder,
|
||||
UniswapV3SwapEncoder, UniswapV4SwapEncoder,
|
||||
},
|
||||
swap_encoder::SwapEncoder,
|
||||
};
|
||||
@@ -87,6 +88,14 @@ impl SwapEncoderBuilder {
|
||||
self.chain,
|
||||
self.config,
|
||||
)?)),
|
||||
"rfq:bebop" => {
|
||||
Ok(Box::new(BebopSwapEncoder::new(self.executor_address, self.chain, self.config)?))
|
||||
}
|
||||
"rfq:hashflow" => Ok(Box::new(HashflowSwapEncoder::new(
|
||||
self.executor_address,
|
||||
self.chain,
|
||||
self.config,
|
||||
)?)),
|
||||
_ => Err(EncodingError::FatalError(format!(
|
||||
"Unknown protocol system: {}",
|
||||
self.protocol_system
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
92
src/encoding/evm/testing_utils.rs
Normal file
92
src/encoding/evm/testing_utils.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
// This module is used in integration tests as well
|
||||
use std::{any::Any, collections::HashMap};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use num_bigint::BigUint;
|
||||
use tycho_common::{
|
||||
dto::ProtocolStateDelta,
|
||||
models::{protocol::GetAmountOutParams, token::Token},
|
||||
simulation::{
|
||||
errors::{SimulationError, TransitionError},
|
||||
indicatively_priced::{IndicativelyPriced, SignedQuote},
|
||||
protocol_sim::{Balances, GetAmountOutResult, ProtocolSim},
|
||||
},
|
||||
Bytes,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MockRFQState {
|
||||
pub quote_amount_out: BigUint,
|
||||
pub quote_data: HashMap<String, Bytes>,
|
||||
}
|
||||
impl ProtocolSim for MockRFQState {
|
||||
fn fee(&self) -> f64 {
|
||||
panic!("MockRFQState does not implement fee")
|
||||
}
|
||||
|
||||
fn spot_price(&self, _base: &Token, _quote: &Token) -> Result<f64, SimulationError> {
|
||||
panic!("MockRFQState does not implement fee")
|
||||
}
|
||||
|
||||
fn get_amount_out(
|
||||
&self,
|
||||
_amount_in: BigUint,
|
||||
_token_in: &Token,
|
||||
_token_out: &Token,
|
||||
) -> Result<GetAmountOutResult, SimulationError> {
|
||||
panic!("MockRFQState does not implement fee")
|
||||
}
|
||||
|
||||
fn get_limits(
|
||||
&self,
|
||||
_sell_token: Bytes,
|
||||
_buy_token: Bytes,
|
||||
) -> Result<(BigUint, BigUint), SimulationError> {
|
||||
panic!("MockRFQState does not implement fee")
|
||||
}
|
||||
|
||||
fn delta_transition(
|
||||
&mut self,
|
||||
_delta: ProtocolStateDelta,
|
||||
_tokens: &HashMap<Bytes, Token>,
|
||||
_balances: &Balances,
|
||||
) -> Result<(), TransitionError<String>> {
|
||||
panic!("MockRFQState does not implement fee")
|
||||
}
|
||||
|
||||
fn clone_box(&self) -> Box<dyn ProtocolSim> {
|
||||
panic!("MockRFQState does not implement fee")
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
panic!("MockRFQState does not implement fee")
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
panic!("MockRFQState does not implement fee")
|
||||
}
|
||||
|
||||
fn eq(&self, _other: &dyn ProtocolSim) -> bool {
|
||||
panic!("MockRFQState does not implement fee")
|
||||
}
|
||||
|
||||
fn as_indicatively_priced(&self) -> Result<&dyn IndicativelyPriced, SimulationError> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl IndicativelyPriced for MockRFQState {
|
||||
async fn request_signed_quote(
|
||||
&self,
|
||||
params: GetAmountOutParams,
|
||||
) -> Result<SignedQuote, SimulationError> {
|
||||
Ok(SignedQuote {
|
||||
base_token: params.token_in,
|
||||
quote_token: params.token_out,
|
||||
amount_in: params.amount_in,
|
||||
amount_out: self.quote_amount_out.clone(),
|
||||
quote_attributes: self.quote_data.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -406,7 +406,7 @@ mod tests {
|
||||
use tycho_common::models::{protocol::ProtocolComponent, Chain};
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::models::Swap;
|
||||
use crate::encoding::models::{Swap, SwapBuilder};
|
||||
|
||||
fn dai() -> Bytes {
|
||||
Bytes::from_str("0x6b175474e89094c44da98b954eedeac495271d0f").unwrap()
|
||||
@@ -435,48 +435,44 @@ mod tests {
|
||||
// Fee and tick spacing information for this test is obtained by querying the
|
||||
// USV4 Position Manager contract: 0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e
|
||||
// Using the poolKeys function with the first 25 bytes of the pool id
|
||||
fn swap_usdc_eth_univ4() -> Swap<'static> {
|
||||
fn swap_usdc_eth_univ4() -> Swap {
|
||||
let pool_fee_usdc_eth = Bytes::from(BigInt::from(3000).to_signed_bytes_be());
|
||||
let tick_spacing_usdc_eth = Bytes::from(BigInt::from(60).to_signed_bytes_be());
|
||||
let mut static_attributes_usdc_eth: HashMap<String, Bytes> = HashMap::new();
|
||||
static_attributes_usdc_eth.insert("key_lp_fee".into(), pool_fee_usdc_eth);
|
||||
static_attributes_usdc_eth.insert("tick_spacing".into(), tick_spacing_usdc_eth);
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xdce6394339af00981949f5f3baf27e3610c76326a700af57e4b3e3ae4977f78d"
|
||||
.to_string(),
|
||||
protocol_system: "uniswap_v4_hooks".to_string(),
|
||||
static_attributes: static_attributes_usdc_eth,
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc().clone(),
|
||||
token_out: eth().clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}
|
||||
usdc().clone(),
|
||||
eth().clone(),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
fn swap_eth_pepe_univ4() -> Swap<'static> {
|
||||
fn swap_eth_pepe_univ4() -> Swap {
|
||||
let pool_fee_eth_pepe = Bytes::from(BigInt::from(25000).to_signed_bytes_be());
|
||||
let tick_spacing_eth_pepe = Bytes::from(BigInt::from(500).to_signed_bytes_be());
|
||||
let mut static_attributes_eth_pepe: HashMap<String, Bytes> = HashMap::new();
|
||||
static_attributes_eth_pepe.insert("key_lp_fee".into(), pool_fee_eth_pepe);
|
||||
static_attributes_eth_pepe.insert("tick_spacing".into(), tick_spacing_eth_pepe);
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xecd73ecbf77219f21f129c8836d5d686bbc27d264742ddad620500e3e548e2c9"
|
||||
.to_string(),
|
||||
protocol_system: "uniswap_v4_hooks".to_string(),
|
||||
static_attributes: static_attributes_eth_pepe,
|
||||
..Default::default()
|
||||
},
|
||||
token_in: eth().clone(),
|
||||
token_out: pepe().clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}
|
||||
eth().clone(),
|
||||
pepe().clone(),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
fn router_address() -> Bytes {
|
||||
@@ -514,18 +510,16 @@ mod tests {
|
||||
fn test_encode_router_calldata_single_swap() {
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let eth_amount_in = BigUint::from(1000u32);
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
weth().clone(),
|
||||
dai().clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
@@ -580,31 +574,27 @@ mod tests {
|
||||
fn test_encode_router_calldata_sequential_swap() {
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let eth_amount_in = BigUint::from(1000u32);
|
||||
let swap_weth_dai = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap_weth_dai = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
weth().clone(),
|
||||
dai().clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let swap_dai_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap_dai_usdc = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: usdc(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
dai().clone(),
|
||||
usdc().clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
@@ -674,18 +664,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_validate_passes_for_wrap() {
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
weth().clone(),
|
||||
dai().clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
@@ -704,18 +692,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_validate_fails_for_wrap_wrong_input() {
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
weth().clone(),
|
||||
dai().clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
@@ -739,18 +725,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_validate_fails_for_wrap_wrong_first_swap() {
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: eth(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
eth().clone(),
|
||||
dai().clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
@@ -794,18 +778,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_validate_passes_for_unwrap() {
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
dai().clone(),
|
||||
weth().clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
@@ -823,18 +805,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_validate_fails_for_unwrap_wrong_output() {
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
dai().clone(),
|
||||
weth().clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
@@ -859,18 +839,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_validate_fails_for_unwrap_wrong_last_swap() {
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: eth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
dai().clone(),
|
||||
eth().clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
@@ -900,42 +878,36 @@ mod tests {
|
||||
// (some of the pool addresses in this test are fake)
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: weth(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
dai().clone(),
|
||||
weth().clone(),
|
||||
)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x0000000000000000000000000000000000000000".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
dai().clone(),
|
||||
weth().clone(),
|
||||
)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x0000000000000000000000000000000000000000".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
weth().clone(),
|
||||
dai().clone(),
|
||||
)
|
||||
.build(),
|
||||
];
|
||||
|
||||
let solution = Solution {
|
||||
@@ -958,54 +930,46 @@ mod tests {
|
||||
// (some of the pool addresses in this test are fake)
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
dai().clone(),
|
||||
weth().clone(),
|
||||
)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth(),
|
||||
token_out: usdc(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
weth().clone(),
|
||||
usdc().clone(),
|
||||
)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x0000000000000000000000000000000000000000".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: usdc(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
usdc().clone(),
|
||||
dai().clone(),
|
||||
)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x0000000000000000000000000000000000000000".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: wbtc(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
dai().clone(),
|
||||
wbtc().clone(),
|
||||
)
|
||||
.build(),
|
||||
];
|
||||
|
||||
let solution = Solution {
|
||||
@@ -1035,42 +999,37 @@ mod tests {
|
||||
// (some of the pool addresses in this test are fake)
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
weth(),
|
||||
dai(),
|
||||
)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x0000000000000000000000000000000000000000".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: weth(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
dai(),
|
||||
weth(),
|
||||
)
|
||||
.split(0.5)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0x0000000000000000000000000000000000000000".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
dai(),
|
||||
weth(),
|
||||
)
|
||||
.build(),
|
||||
];
|
||||
|
||||
let solution = Solution {
|
||||
@@ -1093,30 +1052,26 @@ mod tests {
|
||||
// (some of the pool addresses in this test are fake)
|
||||
let encoder = get_tycho_router_encoder(UserTransferType::TransferFrom);
|
||||
let swaps = vec![
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: weth(),
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
id: "0x0000000000000000000000000000000000000000".to_string(),
|
||||
weth(),
|
||||
dai(),
|
||||
)
|
||||
.build(),
|
||||
SwapBuilder::new(
|
||||
ProtocolComponent {
|
||||
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
token_in: dai(),
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
dai(),
|
||||
weth(),
|
||||
)
|
||||
.build(),
|
||||
];
|
||||
|
||||
let solution = Solution {
|
||||
@@ -1150,7 +1105,7 @@ mod tests {
|
||||
use tycho_common::{models::protocol::ProtocolComponent, Bytes};
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::models::{Solution, Swap};
|
||||
use crate::encoding::models::Solution;
|
||||
|
||||
#[test]
|
||||
fn test_executor_encoder_encode() {
|
||||
@@ -1160,18 +1115,16 @@ mod tests {
|
||||
let token_in = weth();
|
||||
let token_out = dai();
|
||||
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
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,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
token_in.clone(),
|
||||
token_out.clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
@@ -1222,17 +1175,16 @@ mod tests {
|
||||
let token_in = weth();
|
||||
let token_out = dai();
|
||||
|
||||
let swap = Swap {
|
||||
component: ProtocolComponent {
|
||||
let swap = SwapBuilder::new(
|
||||
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,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
token_in.clone(),
|
||||
token_out.clone(),
|
||||
)
|
||||
.build();
|
||||
|
||||
let solution = Solution {
|
||||
exact_out: false,
|
||||
|
||||
@@ -78,6 +78,9 @@ pub fn get_static_attribute(swap: &Swap, attribute_name: &str) -> Result<Vec<u8>
|
||||
.to_vec())
|
||||
}
|
||||
|
||||
/// Returns the current Tokio runtime handle, or creates a new one if it doesn't exist.
|
||||
/// It also returns the runtime to prevent it from being dropped before use.
|
||||
/// This is required since tycho-execution does not have a pre-existing runtime.
|
||||
pub fn get_runtime() -> Result<(Handle, Option<Arc<Runtime>>), EncodingError> {
|
||||
match Handle::try_current() {
|
||||
Ok(h) => Ok((h, None)),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use clap::ValueEnum;
|
||||
use num_bigint::BigUint;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -35,7 +37,7 @@ pub enum UserTransferType {
|
||||
/// Represents a solution containing details describing an order, and instructions for filling
|
||||
/// the order.
|
||||
#[derive(Clone, Default, Debug, Deserialize, Serialize)]
|
||||
pub struct Solution<'a> {
|
||||
pub struct Solution {
|
||||
/// Address of the sender.
|
||||
pub sender: Bytes,
|
||||
/// Address of the receiver.
|
||||
@@ -55,7 +57,7 @@ pub struct Solution<'a> {
|
||||
#[serde(with = "biguint_string")]
|
||||
pub checked_amount: BigUint,
|
||||
/// List of swaps to fulfill the solution.
|
||||
pub swaps: Vec<Swap<'a>>,
|
||||
pub swaps: Vec<Swap>,
|
||||
/// If set, the corresponding native action will be executed.
|
||||
pub native_action: Option<NativeAction>,
|
||||
}
|
||||
@@ -74,7 +76,7 @@ pub enum NativeAction {
|
||||
|
||||
/// Represents a swap operation to be performed on a pool.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Swap<'a> {
|
||||
pub struct Swap {
|
||||
/// Protocol component from tycho indexer
|
||||
pub component: ProtocolComponent,
|
||||
/// Token being input into the pool.
|
||||
@@ -88,33 +90,106 @@ pub struct Swap<'a> {
|
||||
pub user_data: Option<Bytes>,
|
||||
/// Optional protocol state used to perform the swap.
|
||||
#[serde(skip)]
|
||||
pub protocol_state: Option<&'a dyn ProtocolSim>,
|
||||
pub protocol_state: Option<Arc<dyn ProtocolSim>>,
|
||||
/// Optional estimated amount in for this Swap. This is necessary for RFQ protocols. This value
|
||||
/// is used to request the quote
|
||||
pub estimated_amount_in: Option<BigUint>,
|
||||
}
|
||||
|
||||
impl<'a> Swap<'a> {
|
||||
impl Swap {
|
||||
pub fn new<T: Into<ProtocolComponent>>(
|
||||
component: T,
|
||||
token_in: Bytes,
|
||||
token_out: Bytes,
|
||||
split: f64,
|
||||
user_data: Option<Bytes>,
|
||||
protocol_state: Option<&'a dyn ProtocolSim>,
|
||||
protocol_state: Option<Arc<dyn ProtocolSim>>,
|
||||
estimated_amount_in: Option<BigUint>,
|
||||
) -> Self {
|
||||
Self { component: component.into(), token_in, token_out, split, user_data, protocol_state }
|
||||
Self {
|
||||
component: component.into(),
|
||||
token_in,
|
||||
token_out,
|
||||
split,
|
||||
user_data,
|
||||
protocol_state,
|
||||
estimated_amount_in,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for Swap<'a> {
|
||||
impl PartialEq for Swap {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.component == other.component &&
|
||||
self.token_in == other.token_in &&
|
||||
self.token_out == other.token_out &&
|
||||
self.split == other.split &&
|
||||
self.user_data == other.user_data
|
||||
self.user_data == other.user_data &&
|
||||
self.estimated_amount_in == other.estimated_amount_in
|
||||
// Skip protocol_state comparison since trait objects don't implement PartialEq
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SwapBuilder {
|
||||
component: ProtocolComponent,
|
||||
token_in: Bytes,
|
||||
token_out: Bytes,
|
||||
split: f64,
|
||||
user_data: Option<Bytes>,
|
||||
protocol_state: Option<Arc<dyn ProtocolSim>>,
|
||||
estimated_amount_in: Option<BigUint>,
|
||||
}
|
||||
|
||||
impl SwapBuilder {
|
||||
pub fn new<T: Into<ProtocolComponent>>(
|
||||
component: T,
|
||||
token_in: Bytes,
|
||||
token_out: Bytes,
|
||||
) -> Self {
|
||||
Self {
|
||||
component: component.into(),
|
||||
token_in,
|
||||
token_out,
|
||||
split: 0.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
estimated_amount_in: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split(mut self, split: f64) -> Self {
|
||||
self.split = split;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn user_data(mut self, user_data: Bytes) -> Self {
|
||||
self.user_data = Some(user_data);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn protocol_state(mut self, protocol_state: Arc<dyn ProtocolSim>) -> Self {
|
||||
self.protocol_state = Some(protocol_state);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn estimated_amount_in(mut self, estimated_amount_in: BigUint) -> Self {
|
||||
self.estimated_amount_in = Some(estimated_amount_in);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Swap {
|
||||
Swap {
|
||||
component: self.component,
|
||||
token_in: self.token_in,
|
||||
token_out: self.token_out,
|
||||
split: self.split,
|
||||
user_data: self.user_data,
|
||||
protocol_state: self.protocol_state,
|
||||
estimated_amount_in: self.estimated_amount_in,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a transaction to be executed.
|
||||
///
|
||||
/// # Fields
|
||||
@@ -262,6 +337,7 @@ mod tests {
|
||||
0.5,
|
||||
user_data.clone(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
assert_eq!(swap.token_in, Bytes::from("0x12"));
|
||||
assert_eq!(swap.token_out, Bytes::from("0x34"));
|
||||
|
||||
Reference in New Issue
Block a user