feat: Bring back TransferType and simplify encoding logic

Took 1 hour 53 minutes
This commit is contained in:
Diana Carvalho
2025-05-16 16:59:35 +01:00
parent 9b59b8b434
commit 38748925b3
6 changed files with 230 additions and 309 deletions

View File

@@ -8,6 +8,7 @@ use crate::encoding::{
constants::{CALLBACK_CONSTRAINED_PROTOCOLS, IN_TRANSFER_REQUIRED_PROTOCOLS},
group_swaps::SwapGroup,
},
models::TransferType,
};
/// A struct that defines how the tokens will be transferred into the given pool given the solution.
@@ -34,54 +35,47 @@ impl TransferOptimization {
}
}
/// Returns the information about the necessary transfers.
/// Returns (bool, String, bool) where:
/// * bool: true if a transfer from the user is needed, false otherwise (it might use regular
/// approvals or permit2)
/// * String: the address to transfer from (if not needed it's the zero address)
/// * bool: true if the tokens are already in the router and there only needs to be a transfer
/// from the router into the pool
pub fn get_transfers(&self, swap: SwapGroup, wrap: bool) -> (bool, String, bool) {
let zero_address = Bytes::from([0u8; 20]).to_string();
/// Returns the transfer type that should be used for the first transfer.
pub fn get_transfers(
&self,
swap: SwapGroup,
given_token: Bytes,
wrap: bool,
in_between_swap_optimization: bool,
) -> TransferType {
let is_first_swap = swap.token_in == given_token;
let in_transfer_required: bool =
IN_TRANSFER_REQUIRED_PROTOCOLS.contains(&swap.protocol_system.as_str());
if swap.token_in == self.native_token {
// Funds are already in router. All protocols currently take care of native transfers.
(false, zero_address, false)
TransferType::None
} else if (swap.token_in == self.wrapped_token) && wrap {
// Wrapping already happened in the router so, we just do a normal transfer.
(false, zero_address, true)
} else if in_transfer_required {
if self.token_in_already_in_router {
// Transfer from router to pool.
(false, zero_address, true)
TransferType::Transfer
} else if is_first_swap {
if in_transfer_required {
if self.token_in_already_in_router {
// Transfer from router to pool.
TransferType::Transfer
} else {
// Transfer from swapper to pool
TransferType::TransferFrom
}
// in transfer is not necessary for these protocols. Only make a transfer from the
// swapper to the router if the tokens are not already in the router
} else if !self.token_in_already_in_router {
// Transfer from swapper to router using.
TransferType::TransferFrom
} else {
// Transfer from swapper to pool
(true, swap.swaps[0].component.id.clone(), false)
TransferType::None
}
// in transfer is not necessary for these protocols. Only make a transfer from the swapper
// to the router if the tokens are not already in the router
} else if !self.token_in_already_in_router {
// Transfer from swapper to router using.
(true, self.router_address.to_string(), false)
} else {
(false, zero_address, false)
}
}
pub fn get_in_between_transfer(
&self,
protocol_system: &str,
in_between_swap_optimization: bool,
) -> bool {
let in_transfer_required: bool = IN_TRANSFER_REQUIRED_PROTOCOLS.contains(protocol_system);
if !in_transfer_required || in_between_swap_optimization {
// all other swaps that not the first one
} else if !in_transfer_required || in_between_swap_optimization {
// funds should already be in the router or in the next pool
false
TransferType::None
} else {
true
TransferType::Transfer
}
}
@@ -149,31 +143,35 @@ mod tests {
Bytes::from("0x5615deb798bb3e4dfa0139dfa1b3d433cc23b72f")
}
fn zero_address() -> String {
Bytes::from([0u8; 20]).to_string()
}
#[rstest]
// First swap tests
// WETH -(univ2)-> DAI we expect a transfer from the user to the protocol
#[case(weth(), "uniswap_v2".to_string(), false, false, true, "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11", false)]
#[case(weth(), weth(), "uniswap_v2".to_string(), false, false,false, TransferType::TransferFrom)]
// Native token swap. No transfer is needed
#[case(eth(), "uniswap_v2".to_string(),false, false, false, zero_address(), false)]
#[case(eth(), eth(), "uniswap_v2".to_string(),false, false,false, TransferType::None)]
// ETH -(wrap)-> WETH -(univ2)-> DAI. Only a transfer from the router into the protocol is
// needed
#[case(weth(), "uniswap_v2".to_string(),true, false, false, zero_address(), true)]
#[case(eth(), weth(), "uniswap_v2".to_string(),true, false,false,TransferType::Transfer)]
// USDC -(univ2)-> DAI and the tokens are already in the router. Only a transfer from the router
// to the protocol is needed
#[case(usdc(), "uniswap_v2".to_string(),false, true, false, zero_address(), true)]
#[case(usdc(), usdc(), "uniswap_v2".to_string(),false, true,false, TransferType::Transfer)]
// USDC -(curve)-> DAI and the tokens are already in the router. No transfer is needed
#[case(usdc(), "vm:curve".to_string(),false, true, false, zero_address(), false)]
#[case(usdc(), usdc(), "vm:curve".to_string(),false, true, false,TransferType::None)]
// other swaps tests
// tokens need to be transferred into the pool
#[case(weth(), usdc(), "uniswap_v2".to_string(), false, false,false, TransferType::Transfer)]
// tokens are already in the pool (optimization)
#[case(weth(), usdc(), "uniswap_v2".to_string(), false, false, true, TransferType::None)]
// tokens are already in the router and don't need a transfer
#[case(weth(), usdc(), "vm:curve".to_string(), false, false, false, TransferType::None)]
fn test_get_transfers(
#[case] token_in: Bytes,
#[case] given_token: Bytes,
#[case] swap_token_in: Bytes,
#[case] protocol: String,
#[case] wrap: bool,
#[case] token_in_already_in_router: bool,
#[case] expected_transfer_from: bool,
#[case] expected_receiver: String,
#[case] expected_transfer: bool,
#[case] in_between_swap_optimization: bool,
#[case] expected_transfer: TransferType,
) {
// The swap token is the same as the given token, which is not the native token
let swaps = vec![Swap {
@@ -182,34 +180,25 @@ mod tests {
id: "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11".to_string(),
..Default::default()
},
token_in: token_in.clone(),
token_in: swap_token_in.clone(),
token_out: dai(),
split: 0f64,
}];
let swap =
SwapGroup { protocol_system: protocol, token_in, token_out: dai(), split: 0f64, swaps };
let swap = SwapGroup {
protocol_system: protocol,
token_in: swap_token_in,
token_out: dai(),
split: 0f64,
swaps,
};
let optimization =
TransferOptimization::new(eth(), weth(), token_in_already_in_router, router_address());
let (transfer_from, receiver, transfer) = optimization.get_transfers(swap.clone(), wrap);
assert_eq!(transfer_from, expected_transfer_from);
assert_eq!(receiver, expected_receiver);
assert_eq!(transfer, expected_transfer);
}
#[rstest]
// tokens need to be transferred into the pool
#[case("uniswap_v2", false, true)]
// tokens are already in the pool (optimization)
#[case("uniswap_v2", true, false)]
// tokens are already in the router and don't need a transfer
#[case("vm:curve", false, false)]
fn test_get_in_between_transfers(
#[case] protocol: &str,
#[case] in_between_swap_optimization: bool,
#[case] expected_transfer: bool,
) {
let optimization = TransferOptimization::new(eth(), weth(), false, router_address());
let transfer = optimization.get_in_between_transfer(protocol, in_between_swap_optimization);
let transfer = optimization.get_transfers(
swap.clone(),
given_token,
wrap,
in_between_swap_optimization,
);
assert_eq!(transfer, expected_transfer);
}