chore: resolve merge conflicts
This commit is contained in:
@@ -187,10 +187,10 @@ mod tests {
|
||||
signers::local::PrivateKeySigner,
|
||||
};
|
||||
use num_bigint::BigUint;
|
||||
use tycho_common::models::Chain as TychoCommonChain;
|
||||
use tycho_common::models::Chain;
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::{evm::encoding_utils::sign_permit, models::Chain};
|
||||
use crate::encoding::evm::encoding_utils::sign_permit;
|
||||
|
||||
// These two implementations are to avoid comparing the expiration and sig_deadline fields
|
||||
// because they are timestamps
|
||||
@@ -224,7 +224,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn eth_chain() -> Chain {
|
||||
TychoCommonChain::Ethereum.into()
|
||||
Chain::Ethereum
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -334,7 +334,7 @@ mod tests {
|
||||
let sol_permit: PermitSingle =
|
||||
PermitSingle::try_from(&permit).expect("Failed to convert to PermitSingle");
|
||||
|
||||
let signature = sign_permit(eth_chain().id, &permit, signer).unwrap();
|
||||
let signature = sign_permit(eth_chain().id(), &permit, signer).unwrap();
|
||||
let encoded =
|
||||
(bytes_to_address(&anvil_account).unwrap(), sol_permit, signature.as_bytes().to_vec())
|
||||
.abi_encode();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use alloy::{primitives::B256, signers::local::PrivateKeySigner};
|
||||
use tycho_common::{models::Chain as TychoCommonChain, Bytes};
|
||||
use tycho_common::{models::Chain, Bytes};
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
@@ -10,7 +10,7 @@ use crate::encoding::{
|
||||
swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
|
||||
tycho_encoders::{TychoExecutorEncoder, TychoRouterEncoder},
|
||||
},
|
||||
models::{Chain, UserTransferType},
|
||||
models::UserTransferType,
|
||||
tycho_encoder::TychoEncoder,
|
||||
};
|
||||
|
||||
@@ -41,8 +41,8 @@ impl TychoRouterEncoderBuilder {
|
||||
user_transfer_type: None,
|
||||
}
|
||||
}
|
||||
pub fn chain(mut self, chain: TychoCommonChain) -> Self {
|
||||
self.chain = Some(chain.into());
|
||||
pub fn chain(mut self, chain: Chain) -> Self {
|
||||
self.chain = Some(chain);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -85,10 +85,10 @@ impl TychoRouterEncoderBuilder {
|
||||
if let Some(address) = self.router_address {
|
||||
tycho_router_address = address;
|
||||
} else {
|
||||
let default_routers: HashMap<String, Bytes> =
|
||||
let default_routers: HashMap<Chain, Bytes> =
|
||||
serde_json::from_str(DEFAULT_ROUTERS_JSON)?;
|
||||
tycho_router_address = default_routers
|
||||
.get(&chain.name)
|
||||
.get(&chain)
|
||||
.ok_or(EncodingError::FatalError(
|
||||
"No default router address found for chain".to_string(),
|
||||
))?
|
||||
@@ -96,7 +96,7 @@ impl TychoRouterEncoderBuilder {
|
||||
}
|
||||
|
||||
let swap_encoder_registry =
|
||||
SwapEncoderRegistry::new(self.executors_file_path.clone(), chain.clone())?;
|
||||
SwapEncoderRegistry::new(self.executors_file_path.clone(), chain)?;
|
||||
|
||||
let signer = if let Some(pk) = self.swapper_pk {
|
||||
let pk = B256::from_str(&pk).map_err(|_| {
|
||||
@@ -141,8 +141,8 @@ impl TychoExecutorEncoderBuilder {
|
||||
pub fn new() -> Self {
|
||||
TychoExecutorEncoderBuilder { chain: None, executors_file_path: None }
|
||||
}
|
||||
pub fn chain(mut self, chain: TychoCommonChain) -> Self {
|
||||
self.chain = Some(chain.into());
|
||||
pub fn chain(mut self, chain: Chain) -> Self {
|
||||
self.chain = Some(chain);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ impl TychoExecutorEncoderBuilder {
|
||||
pub fn build(self) -> Result<Box<dyn TychoEncoder>, EncodingError> {
|
||||
if let Some(chain) = self.chain {
|
||||
let swap_encoder_registry =
|
||||
SwapEncoderRegistry::new(self.executors_file_path.clone(), chain.clone())?;
|
||||
SwapEncoderRegistry::new(self.executors_file_path.clone(), chain)?;
|
||||
Ok(Box::new(TychoExecutorEncoder::new(swap_encoder_registry)?))
|
||||
} else {
|
||||
Err(EncodingError::FatalError(
|
||||
|
||||
@@ -11,20 +11,30 @@ use crate::encoding::{evm::constants::GROUPABLE_PROTOCOLS, models::Swap};
|
||||
/// * `protocol_system`: String, the protocol system of the swaps
|
||||
/// * `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, PartialEq, Debug)]
|
||||
pub struct SwapGroup {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SwapGroup<'a> {
|
||||
pub token_in: Bytes,
|
||||
pub token_out: Bytes,
|
||||
pub protocol_system: String,
|
||||
pub swaps: Vec<Swap>,
|
||||
pub swaps: Vec<Swap<'a>>,
|
||||
pub split: f64,
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for SwapGroup<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.token_in == other.token_in &&
|
||||
self.token_out == other.token_out &&
|
||||
self.protocol_system == other.protocol_system &&
|
||||
self.swaps == other.swaps &&
|
||||
self.split == other.split
|
||||
}
|
||||
}
|
||||
|
||||
/// Group consecutive swaps which can be encoded into one swap execution for gas optimization.
|
||||
///
|
||||
/// 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(swaps: &Vec<Swap>) -> Vec<SwapGroup> {
|
||||
pub fn group_swaps<'a>(swaps: &'a Vec<Swap<'a>>) -> Vec<SwapGroup<'a>> {
|
||||
let mut grouped_swaps: Vec<SwapGroup> = Vec::new();
|
||||
let mut current_group: Option<SwapGroup> = None;
|
||||
let mut last_swap_protocol = "".to_string();
|
||||
@@ -106,6 +116,7 @@ mod tests {
|
||||
// 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 {
|
||||
@@ -116,6 +127,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_usdc_dai = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -126,12 +138,10 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let grouped_swaps = group_swaps(&vec![
|
||||
swap_weth_wbtc.clone(),
|
||||
swap_wbtc_usdc.clone(),
|
||||
swap_usdc_dai.clone(),
|
||||
]);
|
||||
let swaps = vec![swap_weth_wbtc.clone(), swap_wbtc_usdc.clone(), swap_usdc_dai.clone()];
|
||||
let grouped_swaps = group_swaps(&swaps);
|
||||
|
||||
assert_eq!(
|
||||
grouped_swaps,
|
||||
@@ -178,6 +188,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -188,6 +199,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_dai = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -200,6 +212,7 @@ mod tests {
|
||||
// 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 {
|
||||
@@ -210,13 +223,15 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let grouped_swaps = group_swaps(&vec![
|
||||
let swaps = vec![
|
||||
swap_wbtc_weth.clone(),
|
||||
swap_weth_usdc.clone(),
|
||||
swap_weth_dai.clone(),
|
||||
swap_dai_usdc.clone(),
|
||||
]);
|
||||
];
|
||||
let grouped_swaps = group_swaps(&swaps);
|
||||
|
||||
assert_eq!(
|
||||
grouped_swaps,
|
||||
@@ -269,6 +284,7 @@ mod tests {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -279,6 +295,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_weth_dai = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -291,6 +308,7 @@ mod tests {
|
||||
// 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 {
|
||||
@@ -301,14 +319,16 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let grouped_swaps = group_swaps(&vec![
|
||||
let swaps = vec![
|
||||
swap_weth_wbtc.clone(),
|
||||
swap_wbtc_usdc.clone(),
|
||||
swap_weth_dai.clone(),
|
||||
swap_dai_usdc.clone(),
|
||||
]);
|
||||
];
|
||||
let grouped_swaps = group_swaps(&swaps);
|
||||
|
||||
assert_eq!(
|
||||
grouped_swaps,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use alloy::primitives::{aliases::U24, U8};
|
||||
use tycho_common::Bytes;
|
||||
use tycho_common::{models::Chain, Bytes};
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
@@ -14,7 +14,7 @@ use crate::encoding::{
|
||||
swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
|
||||
utils::{get_token_position, percentage_to_uint24, ple_encode},
|
||||
},
|
||||
models::{Chain, EncodedSolution, EncodingContext, NativeAction, Solution, UserTransferType},
|
||||
models::{EncodedSolution, EncodingContext, NativeAction, Solution, UserTransferType},
|
||||
strategy_encoder::StrategyEncoder,
|
||||
swap_encoder::SwapEncoder,
|
||||
};
|
||||
@@ -52,8 +52,8 @@ impl SingleSwapStrategyEncoder {
|
||||
swap_encoder_registry,
|
||||
router_address: router_address.clone(),
|
||||
transfer_optimization: TransferOptimization::new(
|
||||
chain.native_token()?,
|
||||
chain.wrapped_token()?,
|
||||
chain.native_token().address,
|
||||
chain.wrapped_native_token().address,
|
||||
user_transfer_type,
|
||||
router_address,
|
||||
),
|
||||
@@ -186,16 +186,18 @@ impl SequentialSwapStrategyEncoder {
|
||||
"sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,bytes)"
|
||||
|
||||
}.to_string();
|
||||
let native_token_address = chain.native_token().address;
|
||||
let wrapped_token_address = chain.wrapped_native_token().address;
|
||||
Ok(Self {
|
||||
function_signature,
|
||||
swap_encoder_registry,
|
||||
router_address: router_address.clone(),
|
||||
native_address: chain.native_token()?,
|
||||
wrapped_address: chain.wrapped_token()?,
|
||||
native_address: native_token_address.clone(),
|
||||
wrapped_address: wrapped_token_address.clone(),
|
||||
sequential_swap_validator: SequentialSwapValidator,
|
||||
transfer_optimization: TransferOptimization::new(
|
||||
chain.native_token()?,
|
||||
chain.wrapped_token()?,
|
||||
native_token_address,
|
||||
wrapped_token_address,
|
||||
user_transfer_type,
|
||||
router_address,
|
||||
),
|
||||
@@ -226,10 +228,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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +252,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
|
||||
@@ -338,16 +341,18 @@ impl SplitSwapStrategyEncoder {
|
||||
} else {
|
||||
"splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
|
||||
}.to_string();
|
||||
let native_token_address = chain.native_token().address;
|
||||
let wrapped_token_address = chain.wrapped_native_token().address;
|
||||
Ok(Self {
|
||||
function_signature,
|
||||
swap_encoder_registry,
|
||||
native_address: chain.native_token()?,
|
||||
wrapped_address: chain.wrapped_token()?,
|
||||
native_address: native_token_address.clone(),
|
||||
wrapped_address: wrapped_token_address.clone(),
|
||||
split_swap_validator: SplitSwapValidator,
|
||||
router_address: router_address.clone(),
|
||||
transfer_optimization: TransferOptimization::new(
|
||||
chain.native_token()?,
|
||||
chain.wrapped_token()?,
|
||||
native_token_address,
|
||||
wrapped_token_address,
|
||||
user_transfer_type,
|
||||
router_address,
|
||||
),
|
||||
@@ -508,7 +513,7 @@ mod tests {
|
||||
use alloy::{hex::encode, primitives::hex};
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
use tycho_common::{
|
||||
models::{protocol::ProtocolComponent, Chain as TychoCommonChain},
|
||||
models::{protocol::ProtocolComponent, Chain},
|
||||
Bytes,
|
||||
};
|
||||
|
||||
@@ -516,7 +521,7 @@ mod tests {
|
||||
use crate::encoding::models::Swap;
|
||||
|
||||
fn eth_chain() -> Chain {
|
||||
TychoCommonChain::Ethereum.into()
|
||||
Chain::Ethereum
|
||||
}
|
||||
|
||||
fn weth() -> Bytes {
|
||||
@@ -554,6 +559,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SingleSwapStrategyEncoder::new(
|
||||
@@ -615,6 +621,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SingleSwapStrategyEncoder::new(
|
||||
@@ -686,6 +693,7 @@ mod tests {
|
||||
token_out: wbtc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_wbtc_usdc = Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -697,6 +705,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
let encoder = SequentialSwapStrategyEncoder::new(
|
||||
@@ -789,6 +798,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0.6f64, // 60% of input
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// USDC -> WETH (Pool 2) - 40% of input (remaining)
|
||||
@@ -811,6 +821,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None, // Remaining 40%
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
// WETH -> USDC (Pool 2)
|
||||
@@ -833,6 +844,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
@@ -941,6 +953,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_weth_usdc_v3_pool1 = Swap {
|
||||
@@ -962,6 +975,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.6f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_weth_usdc_v3_pool2 = Swap {
|
||||
@@ -983,6 +997,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_encoder_registry = get_swap_encoder_registry();
|
||||
|
||||
@@ -215,6 +215,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
let result = validator.validate_swap_path(&swaps, &weth, &dai, &None, ð, &weth);
|
||||
assert_eq!(result, Ok(()));
|
||||
@@ -238,6 +239,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -249,6 +251,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
let result = validator.validate_swap_path(&swaps, &weth, &usdc, &None, ð, &weth);
|
||||
@@ -275,6 +278,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
// This swap is disconnected from the WETH->DAI path
|
||||
Swap {
|
||||
@@ -287,6 +291,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
let result =
|
||||
@@ -315,6 +320,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -326,6 +332,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -352,6 +359,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 1.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
let result =
|
||||
validator.validate_swap_path(&unreachable_swaps, &weth, &usdc, &None, ð, &weth);
|
||||
@@ -391,6 +399,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
let result = validator.validate_split_percentages(&swaps);
|
||||
assert_eq!(result, Ok(()));
|
||||
@@ -414,6 +423,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -425,6 +435,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.3,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -436,6 +447,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.0, // Remainder (20%)
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
assert!(validator
|
||||
@@ -460,6 +472,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.7,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -471,6 +484,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.3,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
assert!(matches!(
|
||||
@@ -496,6 +510,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -507,6 +522,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
assert!(matches!(
|
||||
@@ -532,6 +548,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.6,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -543,6 +560,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.5,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -554,6 +572,7 @@ mod tests {
|
||||
token_out: dai.clone(),
|
||||
split: 0.0,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
assert!(matches!(
|
||||
@@ -579,6 +598,7 @@ mod tests {
|
||||
token_out: usdc.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
|
||||
let result = validator.validate_swap_path(
|
||||
@@ -609,6 +629,7 @@ mod tests {
|
||||
token_out: weth.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
|
||||
let result = validator.validate_swap_path(
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,6 +184,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}];
|
||||
let swap = SwapGroup {
|
||||
protocol_system: protocol,
|
||||
@@ -203,16 +209,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,
|
||||
) {
|
||||
@@ -241,11 +250,12 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}],
|
||||
})
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tycho_common::models::Chain;
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::swap_encoder::swap_encoders::{
|
||||
@@ -7,7 +9,6 @@ use crate::encoding::{
|
||||
EkuboSwapEncoder, MaverickV2SwapEncoder, UniswapV2SwapEncoder, UniswapV3SwapEncoder,
|
||||
UniswapV4SwapEncoder,
|
||||
},
|
||||
models::Chain,
|
||||
swap_encoder::SwapEncoder,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use std::{collections::HashMap, fs};
|
||||
|
||||
use tycho_common::models::Chain;
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::{
|
||||
constants::{DEFAULT_EXECUTORS_JSON, PROTOCOL_SPECIFIC_CONFIG},
|
||||
swap_encoder::builder::SwapEncoderBuilder,
|
||||
},
|
||||
models::Chain,
|
||||
swap_encoder::SwapEncoder,
|
||||
};
|
||||
|
||||
@@ -30,15 +31,15 @@ impl SwapEncoderRegistry {
|
||||
} else {
|
||||
DEFAULT_EXECUTORS_JSON.to_string()
|
||||
};
|
||||
let config: HashMap<String, HashMap<String, String>> = serde_json::from_str(&config_str)?;
|
||||
let config: HashMap<Chain, HashMap<String, String>> = serde_json::from_str(&config_str)?;
|
||||
let executors = config
|
||||
.get(&chain.name)
|
||||
.get(&chain)
|
||||
.ok_or(EncodingError::FatalError("No executors found for chain".to_string()))?;
|
||||
|
||||
let protocol_specific_config: HashMap<String, HashMap<String, HashMap<String, String>>> =
|
||||
let protocol_specific_config: HashMap<Chain, HashMap<String, HashMap<String, String>>> =
|
||||
serde_json::from_str(PROTOCOL_SPECIFIC_CONFIG)?;
|
||||
let protocol_specific_config = protocol_specific_config
|
||||
.get(&chain.name)
|
||||
.get(&chain)
|
||||
.ok_or(EncodingError::FatalError(
|
||||
"No protocol specific config found for chain".to_string(),
|
||||
))?;
|
||||
@@ -47,7 +48,7 @@ impl SwapEncoderRegistry {
|
||||
let builder = SwapEncoderBuilder::new(
|
||||
protocol,
|
||||
executor_address,
|
||||
chain.clone(),
|
||||
chain,
|
||||
protocol_specific_config
|
||||
.get(protocol)
|
||||
.cloned(),
|
||||
|
||||
@@ -5,7 +5,7 @@ use alloy::{
|
||||
sol_types::SolValue,
|
||||
};
|
||||
use serde_json::from_str;
|
||||
use tycho_common::Bytes;
|
||||
use tycho_common::{models::Chain, Bytes};
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
@@ -13,7 +13,7 @@ use crate::encoding::{
|
||||
approvals::protocol_approvals_manager::ProtocolApprovalsManager,
|
||||
utils::{bytes_to_address, get_static_attribute, pad_to_fixed_size},
|
||||
},
|
||||
models::{Chain, EncodingContext, Swap},
|
||||
models::{EncodingContext, Swap},
|
||||
swap_encoder::SwapEncoder,
|
||||
};
|
||||
|
||||
@@ -407,7 +407,10 @@ impl CurveSwapEncoder {
|
||||
// Some curve pools support both ETH and WETH as tokens.
|
||||
// They do the wrapping/unwrapping inside the pool
|
||||
fn normalize_token(&self, token: Address, coins: &[Address]) -> Result<Address, EncodingError> {
|
||||
let native_token_address = bytes_to_address(&self.native_token_address)?;
|
||||
let native_token_address =
|
||||
Address::from_str(&self.native_token_curve_address).map_err(|_| {
|
||||
EncodingError::FatalError("Invalid native token curve address".to_string())
|
||||
})?;
|
||||
let wrapped_native_token_address = bytes_to_address(&self.wrapped_native_token_address)?;
|
||||
if token == native_token_address && !coins.contains(&token) {
|
||||
Ok(wrapped_native_token_address)
|
||||
@@ -440,7 +443,7 @@ impl CurveSwapEncoder {
|
||||
.iter()
|
||||
.position(|&addr| addr == token_out)
|
||||
.ok_or(EncodingError::FatalError(format!(
|
||||
"Token in address {token_in} not found in curve pool coins"
|
||||
"Token in address {token_out} not found in curve pool coins"
|
||||
)))?;
|
||||
Ok((U8::from(i), U8::from(j)))
|
||||
}
|
||||
@@ -463,9 +466,9 @@ impl SwapEncoder for CurveSwapEncoder {
|
||||
.to_string();
|
||||
Ok(Self {
|
||||
executor_address,
|
||||
native_token_address: chain.native_token()?,
|
||||
native_token_address: chain.native_token().address,
|
||||
native_token_curve_address,
|
||||
wrapped_native_token_address: chain.wrapped_token()?,
|
||||
wrapped_native_token_address: chain.wrapped_native_token().address,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -940,7 +943,7 @@ mod tests {
|
||||
use alloy::hex::encode;
|
||||
use num_bigint::BigInt;
|
||||
use tycho_common::{
|
||||
models::{protocol::ProtocolComponent, Chain as TychoCoreChain},
|
||||
models::{protocol::ProtocolComponent, Chain},
|
||||
Bytes,
|
||||
};
|
||||
|
||||
@@ -964,6 +967,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: Bytes::from("0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e"), // BOB
|
||||
@@ -975,7 +979,7 @@ mod tests {
|
||||
};
|
||||
let encoder = UniswapV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1024,6 +1028,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: Bytes::from("0x0000000000000000000000000000000000000001"),
|
||||
@@ -1035,7 +1040,7 @@ mod tests {
|
||||
};
|
||||
let encoder = UniswapV3SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1085,6 +1090,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1097,7 +1103,7 @@ mod tests {
|
||||
};
|
||||
let encoder = BalancerV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
Some(HashMap::from([(
|
||||
"vault_address".to_string(),
|
||||
"0xba12222222228d8ba445958a75a0704d566bf2c8".to_string(),
|
||||
@@ -1158,6 +1164,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver is ALICE to match the solidity tests
|
||||
@@ -1172,7 +1179,7 @@ mod tests {
|
||||
};
|
||||
let encoder = UniswapV4SwapEncoder::new(
|
||||
String::from("0xF62849F9A0B5Bf2913b396098F7c7019b51A820a"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1231,6 +1238,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoding_context = EncodingContext {
|
||||
@@ -1245,7 +1253,7 @@ mod tests {
|
||||
|
||||
let encoder = UniswapV4SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1328,6 +1336,7 @@ mod tests {
|
||||
token_out: usdt_address.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let second_swap = Swap {
|
||||
@@ -1336,11 +1345,12 @@ mod tests {
|
||||
token_out: wbtc_address.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoder = UniswapV4SwapEncoder::new(
|
||||
String::from("0xF62849F9A0B5Bf2913b396098F7c7019b51A820a"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1413,6 +1423,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoding_context = EncodingContext {
|
||||
@@ -1424,9 +1435,7 @@ mod tests {
|
||||
transfer_type: TransferType::Transfer,
|
||||
};
|
||||
|
||||
let encoder =
|
||||
EkuboSwapEncoder::new(String::default(), TychoCoreChain::Ethereum.into(), None)
|
||||
.unwrap();
|
||||
let encoder = EkuboSwapEncoder::new(String::default(), Chain::Ethereum, None).unwrap();
|
||||
|
||||
let encoded_swap = encoder
|
||||
.encode_swap(&swap, &encoding_context)
|
||||
@@ -1457,9 +1466,7 @@ mod tests {
|
||||
let group_token_out = Bytes::from("0xdAC17F958D2ee523a2206206994597C13D831ec7"); // USDT
|
||||
let intermediary_token = Bytes::from("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC
|
||||
|
||||
let encoder =
|
||||
EkuboSwapEncoder::new(String::default(), TychoCoreChain::Ethereum.into(), None)
|
||||
.unwrap();
|
||||
let encoder = EkuboSwapEncoder::new(String::default(), Chain::Ethereum, None).unwrap();
|
||||
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: RECEIVER.into(),
|
||||
@@ -1486,6 +1493,7 @@ mod tests {
|
||||
token_out: intermediary_token.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let second_swap = Swap {
|
||||
@@ -1502,6 +1510,7 @@ mod tests {
|
||||
token_out: group_token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let first_encoded_swap = encoder
|
||||
@@ -1622,13 +1631,10 @@ mod tests {
|
||||
token_out: Bytes::from(token_out),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::default(),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
curve_config(),
|
||||
)
|
||||
.unwrap();
|
||||
let encoder =
|
||||
CurveSwapEncoder::new(String::default(), Chain::Ethereum, curve_config()).unwrap();
|
||||
let (i, j) = encoder
|
||||
.get_coin_indexes(
|
||||
&swap,
|
||||
@@ -1666,6 +1672,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1678,7 +1685,7 @@ mod tests {
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
curve_config(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1738,6 +1745,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1750,7 +1758,7 @@ mod tests {
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
curve_config(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1811,6 +1819,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1823,7 +1832,7 @@ mod tests {
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
Some(HashMap::from([
|
||||
(
|
||||
"native_token_address".to_string(),
|
||||
@@ -1885,6 +1894,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1897,7 +1907,7 @@ mod tests {
|
||||
};
|
||||
let encoder = BalancerV3SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1943,6 +1953,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
// The receiver was generated with `makeAddr("bob") using forge`
|
||||
@@ -1955,7 +1966,7 @@ mod tests {
|
||||
};
|
||||
let encoder = MaverickV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -2088,6 +2099,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: Some(Bytes::from(user_data)),
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoding_context = EncodingContext {
|
||||
@@ -2101,7 +2113,7 @@ mod tests {
|
||||
|
||||
let encoder = BebopSwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
Some(HashMap::from([(
|
||||
"bebop_settlement_address".to_string(),
|
||||
"0xbbbbbBB520d69a9775E85b458C58c648259FAD5F".to_string(),
|
||||
@@ -2282,6 +2294,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: Some(Bytes::from(user_data)),
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let encoding_context = EncodingContext {
|
||||
@@ -2295,7 +2308,7 @@ mod tests {
|
||||
|
||||
let encoder = BebopSwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
TychoCoreChain::Ethereum.into(),
|
||||
Chain::Ethereum,
|
||||
Some(HashMap::from([(
|
||||
"bebop_settlement_address".to_string(),
|
||||
"0xbbbbbBB520d69a9775E85b458C58c648259FAD5F".to_string(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use alloy::signers::local::PrivateKeySigner;
|
||||
use tycho_common::Bytes;
|
||||
use tycho_common::{models::Chain, Bytes};
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
@@ -16,7 +16,7 @@ use crate::encoding::{
|
||||
swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
|
||||
},
|
||||
models::{
|
||||
Chain, EncodedSolution, EncodingContext, NativeAction, Solution, Transaction, TransferType,
|
||||
EncodedSolution, EncodingContext, NativeAction, Solution, Transaction, TransferType,
|
||||
UserTransferType,
|
||||
},
|
||||
strategy_encoder::StrategyEncoder,
|
||||
@@ -61,19 +61,19 @@ impl TychoRouterEncoder {
|
||||
};
|
||||
Ok(TychoRouterEncoder {
|
||||
single_swap_strategy: SingleSwapStrategyEncoder::new(
|
||||
chain.clone(),
|
||||
chain,
|
||||
swap_encoder_registry.clone(),
|
||||
user_transfer_type.clone(),
|
||||
router_address.clone(),
|
||||
)?,
|
||||
sequential_swap_strategy: SequentialSwapStrategyEncoder::new(
|
||||
chain.clone(),
|
||||
chain,
|
||||
swap_encoder_registry.clone(),
|
||||
user_transfer_type.clone(),
|
||||
router_address.clone(),
|
||||
)?,
|
||||
split_swap_strategy: SplitSwapStrategyEncoder::new(
|
||||
chain.clone(),
|
||||
chain,
|
||||
swap_encoder_registry,
|
||||
user_transfer_type.clone(),
|
||||
router_address.clone(),
|
||||
@@ -153,11 +153,11 @@ impl TychoEncoder for TychoRouterEncoder {
|
||||
let encoded_solution = self.encode_solution(solution)?;
|
||||
|
||||
let transaction = encode_tycho_router_call(
|
||||
self.chain.id,
|
||||
self.chain.id(),
|
||||
encoded_solution,
|
||||
solution,
|
||||
&self.user_transfer_type,
|
||||
&self.chain.native_token()?,
|
||||
&self.chain.native_token().address,
|
||||
self.signer.clone(),
|
||||
)?;
|
||||
|
||||
@@ -186,8 +186,11 @@ impl TychoEncoder for TychoRouterEncoder {
|
||||
if solution.swaps.is_empty() {
|
||||
return Err(EncodingError::FatalError("No swaps found in solution".to_string()));
|
||||
}
|
||||
let native_address = self.chain.native_token()?;
|
||||
let wrapped_address = self.chain.wrapped_token()?;
|
||||
let native_address = self.chain.native_token().address;
|
||||
let wrapped_address = self
|
||||
.chain
|
||||
.wrapped_native_token()
|
||||
.address;
|
||||
if let Some(native_action) = &solution.native_action {
|
||||
if native_action == &NativeAction::Wrap {
|
||||
if solution.given_token != native_address {
|
||||
@@ -387,7 +390,7 @@ mod tests {
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
use tycho_common::models::{protocol::ProtocolComponent, Chain as TychoCommonChain};
|
||||
use tycho_common::models::{protocol::ProtocolComponent, Chain};
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::models::Swap;
|
||||
@@ -419,7 +422,7 @@ 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 {
|
||||
fn swap_usdc_eth_univ4() -> Swap<'static> {
|
||||
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();
|
||||
@@ -437,10 +440,11 @@ mod tests {
|
||||
token_out: eth().clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_eth_pepe_univ4() -> Swap {
|
||||
fn swap_eth_pepe_univ4() -> Swap<'static> {
|
||||
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();
|
||||
@@ -458,6 +462,7 @@ mod tests {
|
||||
token_out: pepe().clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,7 +471,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn eth_chain() -> Chain {
|
||||
TychoCommonChain::Ethereum.into()
|
||||
Chain::Ethereum
|
||||
}
|
||||
|
||||
fn get_swap_encoder_registry() -> SwapEncoderRegistry {
|
||||
@@ -506,6 +511,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -571,6 +577,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let swap_dai_usdc = Swap {
|
||||
@@ -583,6 +590,7 @@ mod tests {
|
||||
token_out: usdc(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -663,6 +671,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -692,6 +701,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -726,6 +736,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -780,6 +791,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -808,6 +820,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -843,6 +856,7 @@ mod tests {
|
||||
token_out: eth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -883,6 +897,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -894,6 +909,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -905,6 +921,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -938,6 +955,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -949,6 +967,7 @@ mod tests {
|
||||
token_out: usdc(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -960,6 +979,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -971,6 +991,7 @@ mod tests {
|
||||
token_out: wbtc(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1011,6 +1032,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -1022,6 +1044,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0.5f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -1033,6 +1056,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1066,6 +1090,7 @@ mod tests {
|
||||
token_out: dai(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
Swap {
|
||||
component: ProtocolComponent {
|
||||
@@ -1077,6 +1102,7 @@ mod tests {
|
||||
token_out: weth(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1131,6 +1157,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
@@ -1191,6 +1218,7 @@ mod tests {
|
||||
token_out: token_out.clone(),
|
||||
split: 0f64,
|
||||
user_data: None,
|
||||
protocol_state: None,
|
||||
};
|
||||
|
||||
let solution = Solution {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use clap::ValueEnum;
|
||||
use hex;
|
||||
use num_bigint::BigUint;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tycho_common::{
|
||||
models::{protocol::ProtocolComponent, Chain as TychoCommonChain},
|
||||
Bytes,
|
||||
models::protocol::ProtocolComponent, simulation::protocol_sim::ProtocolSim, Bytes,
|
||||
};
|
||||
|
||||
use crate::encoding::{errors::EncodingError, serde_primitives::biguint_string};
|
||||
@@ -37,7 +35,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 {
|
||||
pub struct Solution<'a> {
|
||||
/// Address of the sender.
|
||||
pub sender: Bytes,
|
||||
/// Address of the receiver.
|
||||
@@ -57,7 +55,7 @@ pub struct Solution {
|
||||
#[serde(with = "biguint_string")]
|
||||
pub checked_amount: BigUint,
|
||||
/// List of swaps to fulfill the solution.
|
||||
pub swaps: Vec<Swap>,
|
||||
pub swaps: Vec<Swap<'a>>,
|
||||
/// If set, the corresponding native action will be executed.
|
||||
pub native_action: Option<NativeAction>,
|
||||
}
|
||||
@@ -75,8 +73,8 @@ pub enum NativeAction {
|
||||
}
|
||||
|
||||
/// Represents a swap operation to be performed on a pool.
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct Swap {
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Swap<'a> {
|
||||
/// Protocol component from tycho indexer
|
||||
pub component: ProtocolComponent,
|
||||
/// Token being input into the pool.
|
||||
@@ -88,17 +86,32 @@ pub struct Swap {
|
||||
pub split: f64,
|
||||
/// Optional user data to be passed to encoding.
|
||||
pub user_data: Option<Bytes>,
|
||||
/// Optional protocol state used to perform the swap.
|
||||
#[serde(skip)]
|
||||
pub protocol_state: Option<&'a dyn ProtocolSim>,
|
||||
}
|
||||
|
||||
impl Swap {
|
||||
impl<'a> Swap<'a> {
|
||||
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>,
|
||||
) -> Self {
|
||||
Self { component: component.into(), token_in, token_out, split, user_data }
|
||||
Self { component: component.into(), token_in, token_out, split, user_data, protocol_state }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for Swap<'a> {
|
||||
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
|
||||
// Skip protocol_state comparison since trait objects don't implement PartialEq
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,63 +242,6 @@ impl TryFrom<u8> for BebopOrderType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Chain {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl From<TychoCommonChain> for Chain {
|
||||
fn from(chain: TychoCommonChain) -> Self {
|
||||
match chain {
|
||||
TychoCommonChain::Ethereum => Chain { id: 1, name: chain.to_string() },
|
||||
TychoCommonChain::ZkSync => Chain { id: 324, name: chain.to_string() },
|
||||
TychoCommonChain::Arbitrum => Chain { id: 42161, name: chain.to_string() },
|
||||
TychoCommonChain::Starknet => Chain { id: 0, name: chain.to_string() },
|
||||
TychoCommonChain::Base => Chain { id: 8453, name: chain.to_string() },
|
||||
TychoCommonChain::Unichain => Chain { id: 130, name: chain.to_string() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Chain {
|
||||
fn decode_hex(&self, hex_str: &str, err_msg: &str) -> Result<Bytes, EncodingError> {
|
||||
Ok(Bytes::from(
|
||||
hex::decode(hex_str).map_err(|_| EncodingError::FatalError(err_msg.to_string()))?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn native_token(&self) -> Result<Bytes, EncodingError> {
|
||||
let decode_err_msg = "Failed to decode native token";
|
||||
match self.id {
|
||||
1 | 8453 | 42161 => {
|
||||
self.decode_hex("0000000000000000000000000000000000000000", decode_err_msg)
|
||||
}
|
||||
324 => self.decode_hex("000000000000000000000000000000000000800A", decode_err_msg),
|
||||
130 => self.decode_hex("0000000000000000000000000000000000000000", decode_err_msg),
|
||||
_ => Err(EncodingError::InvalidInput(format!(
|
||||
"Native token not set for chain {:?}. Double check the chain is supported.",
|
||||
self.name
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wrapped_token(&self) -> Result<Bytes, EncodingError> {
|
||||
let decode_err_msg = "Failed to decode wrapped token";
|
||||
match self.id {
|
||||
1 => self.decode_hex("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", decode_err_msg),
|
||||
8453 => self.decode_hex("4200000000000000000000000000000000000006", decode_err_msg),
|
||||
324 => self.decode_hex("5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", decode_err_msg),
|
||||
42161 => self.decode_hex("82aF49447D8a07e3bd95BD0d56f35241523fBab1", decode_err_msg),
|
||||
130 => self.decode_hex("4200000000000000000000000000000000000006", decode_err_msg),
|
||||
_ => Err(EncodingError::InvalidInput(format!(
|
||||
"Wrapped token not set for chain {:?}. Double check the chain is supported.",
|
||||
self.name
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -318,8 +274,14 @@ mod tests {
|
||||
protocol_system: "uniswap_v2".to_string(),
|
||||
};
|
||||
let user_data = Some(Bytes::from("0x1234"));
|
||||
let swap =
|
||||
Swap::new(component, Bytes::from("0x12"), Bytes::from("34"), 0.5, user_data.clone());
|
||||
let swap = Swap::new(
|
||||
component,
|
||||
Bytes::from("0x12"),
|
||||
Bytes::from("34"),
|
||||
0.5,
|
||||
user_data.clone(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(swap.token_in, Bytes::from("0x12"));
|
||||
assert_eq!(swap.token_out, Bytes::from("0x34"));
|
||||
assert_eq!(swap.component.protocol_system, "uniswap_v2");
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use tycho_common::models::Chain;
|
||||
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
models::{Chain, EncodingContext, Swap},
|
||||
models::{EncodingContext, Swap},
|
||||
};
|
||||
|
||||
/// A trait for protocol-specific swap encoding, where each implementation should handle the
|
||||
|
||||
Reference in New Issue
Block a user