feat: Bring back TransferType and simplify encoding logic
Took 1 hour 53 minutes
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
use alloy_primitives::{aliases::U24, Address, U256, U8};
|
||||
use alloy_primitives::{aliases::U24, U256, U8};
|
||||
use alloy_sol_types::SolValue;
|
||||
use tycho_common::Bytes;
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::{
|
||||
approvals::permit2::Permit2,
|
||||
constants::CALLBACK_CONSTRAINED_PROTOCOLS,
|
||||
group_swaps::group_swaps,
|
||||
strategy_encoder::{
|
||||
strategy_validators::{SequentialSwapValidator, SplitSwapValidator, SwapValidator},
|
||||
@@ -52,12 +51,11 @@ impl SingleSwapStrategyEncoder {
|
||||
token_in_already_in_router: bool,
|
||||
) -> Result<Self, EncodingError> {
|
||||
let (permit2, selector) = if let Some(swapper_pk) = swapper_pk {
|
||||
(Some(Permit2::new(swapper_pk, chain.clone())?), "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string())
|
||||
(Some(Permit2::new(swapper_pk, chain.clone())?), "singleSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string())
|
||||
} else {
|
||||
(
|
||||
None,
|
||||
"singleSwap(uint256,address,address,uint256,bool,bool,address,bool,address,bytes)"
|
||||
.to_string(),
|
||||
"singleSwap(uint256,address,address,uint256,bool,bool,address,bytes)".to_string(),
|
||||
)
|
||||
};
|
||||
Ok(Self {
|
||||
@@ -125,17 +123,16 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
|
||||
let swap_receiver =
|
||||
if !unwrap { solution.receiver.clone() } else { self.router_address.clone() };
|
||||
|
||||
let (mut transfer_from_needed, mut token_in_receiver, transfer_needed) = self
|
||||
let transfer = self
|
||||
.transfer_optimization
|
||||
.get_transfers(grouped_swap.clone(), wrap);
|
||||
.get_transfers(grouped_swap.clone(), solution.given_token.clone(), wrap, false);
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: swap_receiver.clone(),
|
||||
exact_out: solution.exact_out,
|
||||
router_address: Some(self.router_address.clone()),
|
||||
group_token_in: grouped_swap.token_in.clone(),
|
||||
group_token_out: grouped_swap.token_out.clone(),
|
||||
transfer_from_needed,
|
||||
transfer_needed,
|
||||
transfer,
|
||||
};
|
||||
|
||||
let mut grouped_protocol_data: Vec<u8> = vec![];
|
||||
@@ -149,21 +146,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
|
||||
.map_err(|_| EncodingError::FatalError("Invalid executor address".to_string()))?,
|
||||
grouped_protocol_data,
|
||||
);
|
||||
if CALLBACK_CONSTRAINED_PROTOCOLS.contains(
|
||||
&grouped_swaps[0]
|
||||
.protocol_system
|
||||
.as_str(),
|
||||
) {
|
||||
// The first swap is from a callback constrained protocol. This means that the in
|
||||
// transfer needs to happen at callback time and not before.
|
||||
transfer_from_needed = false;
|
||||
token_in_receiver = Address::ZERO.to_string();
|
||||
}
|
||||
let token_in_receiver = Address::from_str(&token_in_receiver).map_err(|_| {
|
||||
EncodingError::FatalError(format!(
|
||||
"Invalid funds receiver address: {token_in_receiver}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let method_calldata = if let Some(permit2) = self.permit2.clone() {
|
||||
let (permit, signature) = permit2.get_permit(
|
||||
&self.router_address,
|
||||
@@ -179,8 +162,6 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
|
||||
wrap,
|
||||
unwrap,
|
||||
bytes_to_address(&solution.receiver)?,
|
||||
transfer_from_needed,
|
||||
token_in_receiver,
|
||||
permit,
|
||||
signature.as_bytes().to_vec(),
|
||||
swap_data,
|
||||
@@ -195,8 +176,6 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
|
||||
wrap,
|
||||
unwrap,
|
||||
bytes_to_address(&solution.receiver)?,
|
||||
transfer_from_needed,
|
||||
token_in_receiver,
|
||||
swap_data,
|
||||
)
|
||||
.abi_encode()
|
||||
@@ -250,11 +229,11 @@ impl SequentialSwapStrategyEncoder {
|
||||
token_in_already_in_router: bool,
|
||||
) -> Result<Self, EncodingError> {
|
||||
let (permit2, selector) = if let Some(swapper_pk) = swapper_pk {
|
||||
(Some(Permit2::new(swapper_pk, chain.clone())?), "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string())
|
||||
(Some(Permit2::new(swapper_pk, chain.clone())?), "sequentialSwapPermit2(uint256,address,address,uint256,bool,bool,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string())
|
||||
} else {
|
||||
(
|
||||
None,
|
||||
"sequentialSwap(uint256,address,address,uint256,bool,bool,address,bool,address,bytes)"
|
||||
"sequentialSwap(uint256,address,address,uint256,bool,bool,address,bytes)"
|
||||
.to_string(),
|
||||
)
|
||||
};
|
||||
@@ -309,9 +288,6 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
|
||||
NativeAction::Unwrap => unwrap = true,
|
||||
}
|
||||
}
|
||||
let (mut transfer_from_needed, mut token_in_receiver, transfer_needed) = self
|
||||
.transfer_optimization
|
||||
.get_transfers(grouped_swaps[0].clone(), wrap);
|
||||
|
||||
let mut swaps = vec![];
|
||||
let mut next_in_between_swap_optimization_allowed = true;
|
||||
@@ -332,20 +308,21 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
|
||||
.get_receiver(solution.receiver.clone(), next_swap)?;
|
||||
next_in_between_swap_optimization_allowed = next_swap_optimization;
|
||||
|
||||
let in_between_transfer = if i == 0 {
|
||||
transfer_needed
|
||||
} else {
|
||||
self.transfer_optimization
|
||||
.get_in_between_transfer(&protocol, in_between_swap_optimization_allowed)
|
||||
};
|
||||
let transfer = self
|
||||
.transfer_optimization
|
||||
.get_transfers(
|
||||
grouped_swap.clone(),
|
||||
solution.given_token.clone(),
|
||||
wrap,
|
||||
in_between_swap_optimization_allowed,
|
||||
);
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: swap_receiver.clone(),
|
||||
exact_out: solution.exact_out,
|
||||
router_address: Some(self.router_address.clone()),
|
||||
group_token_in: grouped_swap.token_in.clone(),
|
||||
group_token_out: grouped_swap.token_out.clone(),
|
||||
transfer_from_needed: if i == 0 { transfer_from_needed } else { false },
|
||||
transfer_needed: in_between_transfer,
|
||||
transfer,
|
||||
};
|
||||
|
||||
let mut grouped_protocol_data: Vec<u8> = vec![];
|
||||
@@ -364,21 +341,6 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
|
||||
swaps.push(swap_data);
|
||||
}
|
||||
|
||||
if CALLBACK_CONSTRAINED_PROTOCOLS.contains(
|
||||
&grouped_swaps[0]
|
||||
.protocol_system
|
||||
.as_str(),
|
||||
) {
|
||||
// The first swap is from a callback constrained protocol. This means that the in
|
||||
// transfer needs to happen at callback time and not before.
|
||||
transfer_from_needed = false;
|
||||
token_in_receiver = Address::ZERO.to_string();
|
||||
}
|
||||
let token_in_receiver = Address::from_str(&token_in_receiver).map_err(|_| {
|
||||
EncodingError::FatalError(format!(
|
||||
"Invalid funds receiver address: {token_in_receiver}"
|
||||
))
|
||||
})?;
|
||||
let encoded_swaps = ple_encode(swaps);
|
||||
let method_calldata = if let Some(permit2) = self.permit2.clone() {
|
||||
let (permit, signature) = permit2.get_permit(
|
||||
@@ -395,8 +357,6 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
|
||||
wrap,
|
||||
unwrap,
|
||||
bytes_to_address(&solution.receiver)?,
|
||||
transfer_from_needed,
|
||||
token_in_receiver,
|
||||
permit,
|
||||
signature.as_bytes().to_vec(),
|
||||
encoded_swaps,
|
||||
@@ -411,8 +371,6 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
|
||||
wrap,
|
||||
unwrap,
|
||||
bytes_to_address(&solution.receiver)?,
|
||||
transfer_from_needed,
|
||||
token_in_receiver,
|
||||
encoded_swaps,
|
||||
)
|
||||
.abi_encode()
|
||||
@@ -466,11 +424,11 @@ impl SplitSwapStrategyEncoder {
|
||||
token_in_already_in_router: bool,
|
||||
) -> Result<Self, EncodingError> {
|
||||
let (permit2, selector) = if let Some(swapper_pk) = swapper_pk {
|
||||
(Some(Permit2::new(swapper_pk, chain.clone())?), "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,bool,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string())
|
||||
(Some(Permit2::new(swapper_pk, chain.clone())?), "splitSwapPermit2(uint256,address,address,uint256,bool,bool,uint256,address,((address,uint160,uint48,uint48),address,uint256),bytes,bytes)".to_string())
|
||||
} else {
|
||||
(
|
||||
None,
|
||||
"splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bool,bytes)"
|
||||
"splitSwap(uint256,address,address,uint256,bool,bool,uint256,address,bytes)"
|
||||
.to_string(),
|
||||
)
|
||||
};
|
||||
@@ -574,9 +532,6 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||
tokens.push(solution.checked_token.clone());
|
||||
}
|
||||
|
||||
let (transfer_from_needed, _token_in_receiver, _transfer_needed) = self
|
||||
.transfer_optimization
|
||||
.get_transfers(grouped_swaps[0].clone(), wrap);
|
||||
let mut swaps = vec![];
|
||||
for grouped_swap in grouped_swaps.iter() {
|
||||
let protocol = grouped_swap.protocol_system.clone();
|
||||
@@ -593,17 +548,16 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||
} else {
|
||||
self.router_address.clone()
|
||||
};
|
||||
let transfer_needed = self
|
||||
let transfer = self
|
||||
.transfer_optimization
|
||||
.get_in_between_transfer(&protocol, false);
|
||||
.get_transfers(grouped_swap.clone(), solution.given_token.clone(), wrap, false);
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: swap_receiver.clone(),
|
||||
exact_out: solution.exact_out,
|
||||
router_address: Some(self.router_address.clone()),
|
||||
group_token_in: grouped_swap.token_in.clone(),
|
||||
group_token_out: grouped_swap.token_out.clone(),
|
||||
transfer_from_needed: false,
|
||||
transfer_needed,
|
||||
transfer,
|
||||
};
|
||||
|
||||
let mut grouped_protocol_data: Vec<u8> = vec![];
|
||||
@@ -647,7 +601,6 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||
unwrap,
|
||||
U256::from(tokens_len),
|
||||
bytes_to_address(&solution.receiver)?,
|
||||
transfer_from_needed,
|
||||
permit,
|
||||
signature.as_bytes().to_vec(),
|
||||
encoded_swaps,
|
||||
@@ -663,7 +616,6 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
|
||||
unwrap,
|
||||
U256::from(tokens_len),
|
||||
bytes_to_address(&solution.receiver)?,
|
||||
transfer_from_needed,
|
||||
encoded_swaps,
|
||||
)
|
||||
.abi_encode()
|
||||
@@ -792,7 +744,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount));
|
||||
let expected_input = [
|
||||
"a93aabdf", // Function selector
|
||||
"30ace1b1", // Function selector
|
||||
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
|
||||
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
|
||||
"0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out
|
||||
@@ -800,8 +752,6 @@ mod tests {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
|
||||
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"0000000000000000000000000000000000000000000000000000000000000001", // transfer from
|
||||
"000000000000000000000000a478c2975ab1ea89e8196811f51a7b7ade33eb11", // funds receiver
|
||||
]
|
||||
.join("");
|
||||
|
||||
@@ -817,13 +767,13 @@ mod tests {
|
||||
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"00", // zero2one
|
||||
"00", // transfer false
|
||||
"00", // transfer type TransferFrom
|
||||
"0000000000000000000000000000", // padding
|
||||
));
|
||||
let hex_calldata = encode(&calldata);
|
||||
|
||||
assert_eq!(hex_calldata[..584], expected_input);
|
||||
assert_eq!(hex_calldata[1352..], expected_swap);
|
||||
assert_eq!(hex_calldata[..456], expected_input);
|
||||
assert_eq!(hex_calldata[1224..], expected_swap);
|
||||
if expected_amount.is_some() & slippage.is_some() & checked_amount.is_none() {
|
||||
// only write to file for 1 test case
|
||||
write_calldata_to_file(
|
||||
@@ -884,7 +834,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount));
|
||||
let expected_input = [
|
||||
"cc60c623", // Function selector
|
||||
"20144a07", // Function selector
|
||||
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
|
||||
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
|
||||
"0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out
|
||||
@@ -892,9 +842,7 @@ mod tests {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
|
||||
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"0000000000000000000000000000000000000000000000000000000000000001", // transfer from
|
||||
"000000000000000000000000a478c2975ab1ea89e8196811f51a7b7ade33eb11", // funds receiver
|
||||
"0000000000000000000000000000000000000000000000000000000000000140", // offset of swap bytes
|
||||
"0000000000000000000000000000000000000000000000000000000000000100", // offset of swap bytes
|
||||
"0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding
|
||||
|
||||
// Swap data
|
||||
@@ -903,7 +851,7 @@ mod tests {
|
||||
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"00", // zero2one
|
||||
"00", // transfer false
|
||||
"00", // transfer type TransferFrom
|
||||
"0000000000000000000000000000", // padding
|
||||
]
|
||||
.join("");
|
||||
@@ -968,7 +916,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let expected_min_amount_encoded = hex::encode(U256::abi_encode(&expected_min_amount));
|
||||
let expected_input = [
|
||||
"cc60c623", // Function selector
|
||||
"20144a07", // Function selector
|
||||
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
|
||||
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
|
||||
"0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f", // token out
|
||||
@@ -976,9 +924,7 @@ mod tests {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
|
||||
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // transfer from
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // funds receiver
|
||||
"0000000000000000000000000000000000000000000000000000000000000140", // offset of swap bytes
|
||||
"0000000000000000000000000000000000000000000000000000000000000100", // offset of swap bytes
|
||||
"0000000000000000000000000000000000000000000000000000000000000052", // length of swap bytes without padding
|
||||
|
||||
// Swap data
|
||||
@@ -987,7 +933,7 @@ mod tests {
|
||||
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"00", // zero2one
|
||||
"01", // transfer true
|
||||
"01", // transfer type Transfer
|
||||
"0000000000000000000000000000", // padding
|
||||
]
|
||||
.join("");
|
||||
@@ -1240,7 +1186,7 @@ mod tests {
|
||||
let hex_calldata = encode(&calldata);
|
||||
|
||||
let expected = String::from(concat!(
|
||||
"59e3efbb", /* function selector */
|
||||
"e8a980d7", /* function selector */
|
||||
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
|
||||
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
|
||||
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token ou
|
||||
@@ -1248,9 +1194,7 @@ mod tests {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
|
||||
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"0000000000000000000000000000000000000000000000000000000000000001", /* transfer from */
|
||||
"000000000000000000000000bb2b8038a1640196fbe3e38816f3e67cba72d940", /* funds receiver */
|
||||
"0000000000000000000000000000000000000000000000000000000000000140", /* length ple
|
||||
"0000000000000000000000000000000000000000000000000000000000000100", /* length ple
|
||||
* encode */
|
||||
"00000000000000000000000000000000000000000000000000000000000000a8",
|
||||
// swap 1
|
||||
@@ -1260,7 +1204,7 @@ mod tests {
|
||||
"bb2b8038a1640196fbe3e38816f3e67cba72d940", // component id
|
||||
"004375dff511095cc5a197a54140a24efef3a416", // receiver (next pool)
|
||||
"00", // zero to one
|
||||
"00", // transfer false
|
||||
"00", // transfer type TransferFrom
|
||||
// swap 2
|
||||
"0052", // swap length
|
||||
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
|
||||
@@ -1268,7 +1212,7 @@ mod tests {
|
||||
"004375dff511095cc5a197a54140a24efef3a416", // component id
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver (final user)
|
||||
"01", // zero to one
|
||||
"00", // transfer false
|
||||
"02", // transfer type None
|
||||
"000000000000000000000000000000000000000000000000", // padding
|
||||
));
|
||||
|
||||
@@ -1366,7 +1310,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let hex_calldata = hex::encode(&calldata);
|
||||
let expected_input = [
|
||||
"740bae0c", // selector
|
||||
"51bcc7b6", // selector
|
||||
"0000000000000000000000000000000000000000000000000000000005f5e100", // given amount
|
||||
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token
|
||||
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token
|
||||
@@ -1374,14 +1318,12 @@ mod tests {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // wrap action
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap action
|
||||
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // transfer from
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // funds receiver
|
||||
]
|
||||
.join("");
|
||||
|
||||
let expected_swaps = [
|
||||
"00000000000000000000000000000000000000000000000000000000000000d8", // length of ple encoded swaps without padding
|
||||
"006a", // ple encoded swaps
|
||||
"00000000000000000000000000000000000000000000000000000000000000d6", // length of ple encoded swaps without padding
|
||||
"0069", // ple encoded swaps
|
||||
"2e234dae75c793f67a35089c9d99245e1c58470b", // executor address
|
||||
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in
|
||||
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out
|
||||
@@ -1389,9 +1331,8 @@ mod tests {
|
||||
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
|
||||
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
|
||||
"01", // zero2one
|
||||
"01", // transfer from true
|
||||
"00", // transfer false
|
||||
"006a", // ple encoded swaps
|
||||
"00", // transfer type TransferFrom
|
||||
"0069", // ple encoded swaps
|
||||
"2e234dae75c793f67a35089c9d99245e1c58470b", // executor address
|
||||
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
|
||||
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out
|
||||
@@ -1399,14 +1340,13 @@ mod tests {
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
|
||||
"00", // zero2one
|
||||
"00", // transfer from false
|
||||
"01", // transfer true
|
||||
"0000000000000000", // padding
|
||||
"01", // transfer type Transfer
|
||||
"00000000000000000000", // padding
|
||||
]
|
||||
.join("");
|
||||
|
||||
assert_eq!(hex_calldata[..584], expected_input);
|
||||
assert_eq!(hex_calldata[1352..], expected_swaps);
|
||||
assert_eq!(hex_calldata[..456], expected_input);
|
||||
assert_eq!(hex_calldata[1224..], expected_swaps);
|
||||
write_calldata_to_file("test_sequential_strategy_cyclic_swap", hex_calldata.as_str());
|
||||
}
|
||||
|
||||
@@ -2083,7 +2023,7 @@ mod tests {
|
||||
|
||||
let hex_calldata = hex::encode(&calldata);
|
||||
let expected_input = [
|
||||
"308f3ce0", // selector
|
||||
"7c553846", // selector
|
||||
"0000000000000000000000000000000000000000000000000000000005f5e100", // given amount
|
||||
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token
|
||||
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token
|
||||
@@ -2092,12 +2032,11 @@ mod tests {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap action
|
||||
"0000000000000000000000000000000000000000000000000000000000000002", // tokens length
|
||||
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"0000000000000000000000000000000000000000000000000000000000000001", // transfer from
|
||||
]
|
||||
.join("");
|
||||
let expected_swaps = [
|
||||
"000000000000000000000000000000000000000000000000000000000000013b", // length of ple encoded swaps without padding
|
||||
"006f", // ple encoded swaps
|
||||
"0000000000000000000000000000000000000000000000000000000000000139", // length of ple encoded swaps without padding
|
||||
"006e", // ple encoded swaps
|
||||
"00", // token in index
|
||||
"01", // token out index
|
||||
"999999", // split
|
||||
@@ -2108,9 +2047,8 @@ mod tests {
|
||||
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
|
||||
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
|
||||
"01", // zero2one
|
||||
"00", // transfer from false
|
||||
"01", // transfer true
|
||||
"006f", // ple encoded swaps
|
||||
"00", // transfer type TransferFrom
|
||||
"006e", // ple encoded swaps
|
||||
"00", // token in index
|
||||
"01", // token out index
|
||||
"000000", // split
|
||||
@@ -2121,8 +2059,7 @@ mod tests {
|
||||
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
|
||||
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
|
||||
"01", // zero2one
|
||||
"00", // transfer from false
|
||||
"01", // transfer true
|
||||
"00", // transfer type TransferFrom
|
||||
"0057", // ple encoded swaps
|
||||
"01", // token in index
|
||||
"00", // token out index
|
||||
@@ -2132,12 +2069,12 @@ mod tests {
|
||||
"b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id,
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"00", // zero2one
|
||||
"01", // transfer true
|
||||
"0000000000" // padding
|
||||
"01", // transfer type Transfer
|
||||
"00000000000000" // padding
|
||||
]
|
||||
.join("");
|
||||
assert_eq!(hex_calldata[..584], expected_input);
|
||||
assert_eq!(hex_calldata[1352..], expected_swaps);
|
||||
assert_eq!(hex_calldata[..520], expected_input);
|
||||
assert_eq!(hex_calldata[1288..], expected_swaps);
|
||||
write_calldata_to_file("test_split_input_cyclic_swap", hex_calldata.as_str());
|
||||
}
|
||||
|
||||
@@ -2249,7 +2186,7 @@ mod tests {
|
||||
|
||||
let hex_calldata = hex::encode(&calldata);
|
||||
let expected_input = [
|
||||
"308f3ce0", // selector
|
||||
"7c553846", // selector
|
||||
"0000000000000000000000000000000000000000000000000000000005f5e100", // given amount
|
||||
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // given token
|
||||
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // checked token
|
||||
@@ -2258,12 +2195,11 @@ mod tests {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap action
|
||||
"0000000000000000000000000000000000000000000000000000000000000002", // tokens length
|
||||
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"0000000000000000000000000000000000000000000000000000000000000001", // transfer from
|
||||
]
|
||||
.join("");
|
||||
|
||||
let expected_swaps = [
|
||||
"000000000000000000000000000000000000000000000000000000000000013b", // length of ple encoded swaps without padding
|
||||
"0000000000000000000000000000000000000000000000000000000000000139", // length of ple encoded swaps without padding
|
||||
"0057", // ple encoded swaps
|
||||
"00", // token in index
|
||||
"01", // token out index
|
||||
@@ -2273,8 +2209,8 @@ mod tests {
|
||||
"b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id
|
||||
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
|
||||
"01", // zero2one
|
||||
"01", // transfer true
|
||||
"006f", // ple encoded swaps
|
||||
"00", // transfer type TransferFrom
|
||||
"006e", // ple encoded swaps
|
||||
"01", // token in index
|
||||
"00", // token out index
|
||||
"999999", // split
|
||||
@@ -2285,9 +2221,8 @@ mod tests {
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
|
||||
"00", // zero2one
|
||||
"00", // transfer from false
|
||||
"01", // transfer true
|
||||
"006f", // ple encoded swaps
|
||||
"01", // transfer type Transfer
|
||||
"006e", // ple encoded swaps
|
||||
"01", // token in index
|
||||
"00", // token out index
|
||||
"000000", // split
|
||||
@@ -2298,14 +2233,13 @@ mod tests {
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
|
||||
"00", // zero2one
|
||||
"00", // transfer from false
|
||||
"01", // transfer true
|
||||
"0000000000" // padding
|
||||
"01", // transfer type Transfer
|
||||
"00000000000000" // padding
|
||||
]
|
||||
.join("");
|
||||
|
||||
assert_eq!(hex_calldata[..584], expected_input);
|
||||
assert_eq!(hex_calldata[1352..], expected_swaps);
|
||||
assert_eq!(hex_calldata[..520], expected_input);
|
||||
assert_eq!(hex_calldata[1288..], expected_swaps);
|
||||
write_calldata_to_file("test_split_output_cyclic_swap", hex_calldata.as_str());
|
||||
}
|
||||
}
|
||||
@@ -2653,7 +2587,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let expected_input = [
|
||||
"a93aabdf", // Function selector (single swap)
|
||||
"30ace1b1", // Function selector (single swap)
|
||||
"000000000000000000000000000000000000000000000000000000003b9aca00", // amount in
|
||||
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in
|
||||
"0000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933", // token out
|
||||
@@ -2661,8 +2595,6 @@ mod tests {
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
|
||||
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // transfer from
|
||||
"0000000000000000000000000000000000000000000000000000000000000000", // funds receiver
|
||||
]
|
||||
.join("");
|
||||
|
||||
@@ -2671,15 +2603,14 @@ mod tests {
|
||||
|
||||
let expected_swaps = String::from(concat!(
|
||||
// length of ple encoded swaps without padding
|
||||
"0000000000000000000000000000000000000000000000000000000000000087",
|
||||
"0000000000000000000000000000000000000000000000000000000000000086",
|
||||
// Swap data header
|
||||
"f62849f9a0b5bf2913b396098f7c7019b51a820a", // executor address
|
||||
// Protocol data
|
||||
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // group token in
|
||||
"6982508145454ce325ddbe47a25d4ec3d2311933", // group token in
|
||||
"00", // zero2one
|
||||
"01", // transfer from true
|
||||
"00", // transfer false
|
||||
"00", // transfer type TransferFrom
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
|
||||
// First pool params
|
||||
"0000000000000000000000000000000000000000", // intermediary token (ETH)
|
||||
@@ -2689,13 +2620,13 @@ mod tests {
|
||||
"6982508145454ce325ddbe47a25d4ec3d2311933", // intermediary token (PEPE)
|
||||
"0061a8", // fee
|
||||
"0001f4", // tick spacing
|
||||
"00000000000000000000000000000000000000000000000000" // padding
|
||||
"0000000000000000000000000000000000000000000000000000" // padding
|
||||
));
|
||||
|
||||
let hex_calldata = encode(&calldata);
|
||||
|
||||
assert_eq!(hex_calldata[..584], expected_input);
|
||||
assert_eq!(hex_calldata[1352..], expected_swaps);
|
||||
assert_eq!(hex_calldata[..456], expected_input);
|
||||
assert_eq!(hex_calldata[1224..], expected_swaps);
|
||||
write_calldata_to_file(
|
||||
"test_single_encoding_strategy_usv4_grouped_swap",
|
||||
hex_calldata.as_str(),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ impl SwapEncoder for UniswapV2SwapEncoder {
|
||||
component_id,
|
||||
bytes_to_address(&encoding_context.receiver)?,
|
||||
zero_to_one,
|
||||
encoding_context.transfer_needed,
|
||||
(encoding_context.transfer as u8).to_be_bytes(),
|
||||
);
|
||||
|
||||
Ok(args.abi_encode_packed())
|
||||
@@ -129,8 +129,7 @@ impl SwapEncoder for UniswapV3SwapEncoder {
|
||||
bytes_to_address(&encoding_context.receiver)?,
|
||||
component_id,
|
||||
zero_to_one,
|
||||
encoding_context.transfer_from_needed,
|
||||
encoding_context.transfer_needed,
|
||||
(encoding_context.transfer as u8).to_be_bytes(),
|
||||
);
|
||||
|
||||
Ok(args.abi_encode_packed())
|
||||
@@ -207,8 +206,7 @@ impl SwapEncoder for UniswapV4SwapEncoder {
|
||||
group_token_in_address,
|
||||
group_token_out_address,
|
||||
zero_to_one,
|
||||
encoding_context.transfer_from_needed,
|
||||
encoding_context.transfer_needed,
|
||||
(encoding_context.transfer as u8).to_be_bytes(),
|
||||
bytes_to_address(&encoding_context.receiver)?,
|
||||
pool_params,
|
||||
);
|
||||
@@ -284,6 +282,7 @@ impl SwapEncoder for BalancerV2SwapEncoder {
|
||||
component_id,
|
||||
bytes_to_address(&encoding_context.receiver)?,
|
||||
approval_needed,
|
||||
(encoding_context.transfer as u8).to_be_bytes(),
|
||||
);
|
||||
Ok(args.abi_encode_packed())
|
||||
}
|
||||
@@ -345,8 +344,7 @@ impl SwapEncoder for EkuboSwapEncoder {
|
||||
let mut encoded = vec![];
|
||||
|
||||
if encoding_context.group_token_in == swap.token_in {
|
||||
encoded.extend((encoding_context.transfer_from_needed as u8).to_be_bytes());
|
||||
encoded.extend((encoding_context.transfer_needed as u8).to_be_bytes());
|
||||
encoded.extend((encoding_context.transfer as u8).to_be_bytes());
|
||||
encoded.extend(bytes_to_address(&encoding_context.receiver)?);
|
||||
encoded.extend(bytes_to_address(&swap.token_in)?);
|
||||
}
|
||||
@@ -578,6 +576,7 @@ impl SwapEncoder for CurveSwapEncoder {
|
||||
j.to_be_bytes::<1>(),
|
||||
approval_needed,
|
||||
bytes_to_address(&encoding_context.receiver)?,
|
||||
(encoding_context.transfer as u8).to_be_bytes(),
|
||||
);
|
||||
|
||||
Ok(args.abi_encode_packed())
|
||||
@@ -621,7 +620,7 @@ impl SwapEncoder for MaverickV2SwapEncoder {
|
||||
bytes_to_address(&swap.token_in)?,
|
||||
component_id,
|
||||
bytes_to_address(&encoding_context.receiver)?,
|
||||
encoding_context.transfer_needed,
|
||||
(encoding_context.transfer as u8).to_be_bytes(),
|
||||
);
|
||||
Ok(args.abi_encode_packed())
|
||||
}
|
||||
@@ -646,7 +645,7 @@ mod tests {
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::encoding::evm::utils::write_calldata_to_file;
|
||||
use crate::encoding::{evm::utils::write_calldata_to_file, models::TransferType};
|
||||
|
||||
mod uniswap_v2 {
|
||||
use super::*;
|
||||
@@ -671,8 +670,7 @@ mod tests {
|
||||
router_address: Some(Bytes::zero(20)),
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_needed: true,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::Transfer,
|
||||
};
|
||||
let encoder = UniswapV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
@@ -695,7 +693,7 @@ mod tests {
|
||||
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
|
||||
// zero for one
|
||||
"00",
|
||||
// transfer true
|
||||
// transfer type Transfer
|
||||
"01",
|
||||
))
|
||||
);
|
||||
@@ -731,8 +729,7 @@ mod tests {
|
||||
router_address: Some(Bytes::zero(20)),
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_needed: true,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::Transfer,
|
||||
};
|
||||
let encoder = UniswapV3SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
@@ -759,9 +756,7 @@ mod tests {
|
||||
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
|
||||
// zero for one
|
||||
"00",
|
||||
// transfer from false
|
||||
"00",
|
||||
// transfer true
|
||||
// transfer type Transfer
|
||||
"01",
|
||||
))
|
||||
);
|
||||
@@ -796,8 +791,7 @@ mod tests {
|
||||
router_address: Some(Bytes::zero(20)),
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_needed: false,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::None,
|
||||
};
|
||||
let encoder = BalancerV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
@@ -825,7 +819,9 @@ mod tests {
|
||||
// receiver
|
||||
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
|
||||
// approval needed
|
||||
"01"
|
||||
"01",
|
||||
// transfer type None
|
||||
"02"
|
||||
))
|
||||
);
|
||||
write_calldata_to_file("test_encode_balancer_v2", hex_swap.as_str());
|
||||
@@ -869,8 +865,7 @@ mod tests {
|
||||
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_needed: true,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::Transfer,
|
||||
};
|
||||
let encoder = UniswapV4SwapEncoder::new(
|
||||
String::from("0xF62849F9A0B5Bf2913b396098F7c7019b51A820a"),
|
||||
@@ -892,9 +887,7 @@ mod tests {
|
||||
"dac17f958d2ee523a2206206994597c13d831ec7",
|
||||
// zero for one
|
||||
"01",
|
||||
// transfer from false
|
||||
"00",
|
||||
// transfer true
|
||||
// transfer type Transfer
|
||||
"01",
|
||||
// receiver
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
|
||||
@@ -943,8 +936,7 @@ mod tests {
|
||||
group_token_in: group_token_in.clone(),
|
||||
// Token out is the same as the group token out
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_needed: true,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::Transfer,
|
||||
};
|
||||
|
||||
let encoder = UniswapV4SwapEncoder::new(
|
||||
@@ -987,8 +979,7 @@ mod tests {
|
||||
router_address: Some(router_address.clone()),
|
||||
group_token_in: usde_address.clone(),
|
||||
group_token_out: wbtc_address.clone(),
|
||||
transfer_needed: true,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::Transfer,
|
||||
};
|
||||
|
||||
// Setup - First sequence: USDE -> USDT
|
||||
@@ -1066,9 +1057,7 @@ mod tests {
|
||||
"2260fac5e5542a773aa44fbcfedf7c193bc2c599",
|
||||
// zero for one
|
||||
"01",
|
||||
// transfer from false
|
||||
"00",
|
||||
// transfer true
|
||||
// transfer type Transfer
|
||||
"01",
|
||||
// receiver
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
|
||||
@@ -1125,8 +1114,7 @@ mod tests {
|
||||
group_token_out: token_out.clone(),
|
||||
exact_out: false,
|
||||
router_address: Some(Bytes::default()),
|
||||
transfer_needed: true,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::Transfer,
|
||||
};
|
||||
|
||||
let encoder =
|
||||
@@ -1142,9 +1130,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
hex_swap,
|
||||
concat!(
|
||||
// transfer from false
|
||||
"00",
|
||||
// transfer true
|
||||
// transfer type Transfer
|
||||
"01",
|
||||
// receiver
|
||||
"ca4f73fe97d0b987a0d12b39bbd562c779bab6f6",
|
||||
@@ -1174,8 +1160,7 @@ mod tests {
|
||||
group_token_out: group_token_out.clone(),
|
||||
exact_out: false,
|
||||
router_address: Some(Bytes::default()),
|
||||
transfer_needed: true,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::Transfer,
|
||||
};
|
||||
|
||||
let first_swap = Swap {
|
||||
@@ -1225,9 +1210,7 @@ mod tests {
|
||||
combined_hex,
|
||||
// transfer type
|
||||
concat!(
|
||||
// transfer from false
|
||||
"00",
|
||||
// transfer true
|
||||
// transfer type Transfer
|
||||
"01",
|
||||
// receiver
|
||||
"ca4f73fe97d0b987a0d12b39bbd562c779bab6f6",
|
||||
@@ -1373,8 +1356,7 @@ mod tests {
|
||||
router_address: None,
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_needed: false,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::None,
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
@@ -1405,7 +1387,9 @@ mod tests {
|
||||
// approval needed
|
||||
"01",
|
||||
// receiver,
|
||||
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"
|
||||
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
|
||||
// transfer type None
|
||||
"02",
|
||||
))
|
||||
);
|
||||
}
|
||||
@@ -1442,8 +1426,7 @@ mod tests {
|
||||
router_address: None,
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_needed: false,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::None,
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
@@ -1474,7 +1457,9 @@ mod tests {
|
||||
// approval needed
|
||||
"01",
|
||||
// receiver
|
||||
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"
|
||||
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
|
||||
// transfer type None
|
||||
"02",
|
||||
))
|
||||
);
|
||||
}
|
||||
@@ -1512,8 +1497,7 @@ mod tests {
|
||||
router_address: None,
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_needed: false,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::None,
|
||||
};
|
||||
let encoder = CurveSwapEncoder::new(
|
||||
String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"),
|
||||
@@ -1553,7 +1537,9 @@ mod tests {
|
||||
// approval needed
|
||||
"01",
|
||||
// receiver
|
||||
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e"
|
||||
"1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e",
|
||||
// transfer type None
|
||||
"02",
|
||||
))
|
||||
);
|
||||
}
|
||||
@@ -1582,8 +1568,7 @@ mod tests {
|
||||
router_address: Some(Bytes::default()),
|
||||
group_token_in: token_in.clone(),
|
||||
group_token_out: token_out.clone(),
|
||||
transfer_needed: true,
|
||||
transfer_from_needed: false,
|
||||
transfer: TransferType::Transfer,
|
||||
};
|
||||
let encoder = MaverickV2SwapEncoder::new(
|
||||
String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"),
|
||||
|
||||
@@ -6,14 +6,14 @@ use tycho_common::Bytes;
|
||||
use crate::encoding::{
|
||||
errors::EncodingError,
|
||||
evm::{
|
||||
constants::GROUPABLE_PROTOCOLS,
|
||||
constants::{GROUPABLE_PROTOCOLS, IN_TRANSFER_REQUIRED_PROTOCOLS},
|
||||
group_swaps::group_swaps,
|
||||
strategy_encoder::strategy_encoders::{
|
||||
SequentialSwapStrategyEncoder, SingleSwapStrategyEncoder, SplitSwapStrategyEncoder,
|
||||
},
|
||||
swap_encoder::swap_encoder_registry::SwapEncoderRegistry,
|
||||
},
|
||||
models::{Chain, EncodingContext, NativeAction, Solution, Transaction},
|
||||
models::{Chain, EncodingContext, NativeAction, Solution, Transaction, TransferType},
|
||||
strategy_encoder::StrategyEncoder,
|
||||
tycho_encoder::TychoEncoder,
|
||||
};
|
||||
@@ -273,14 +273,20 @@ impl TychoExecutorEncoder {
|
||||
|
||||
let mut grouped_protocol_data: Vec<u8> = vec![];
|
||||
for swap in grouped_swap.swaps.iter() {
|
||||
let transfer = if IN_TRANSFER_REQUIRED_PROTOCOLS
|
||||
.contains(&swap.component.protocol_system.as_str())
|
||||
{
|
||||
TransferType::Transfer
|
||||
} else {
|
||||
TransferType::None
|
||||
};
|
||||
let encoding_context = EncodingContext {
|
||||
receiver: receiver.clone(),
|
||||
exact_out: solution.exact_out,
|
||||
router_address: None,
|
||||
group_token_in: grouped_swap.token_in.clone(),
|
||||
group_token_out: grouped_swap.token_out.clone(),
|
||||
transfer_needed: true,
|
||||
transfer_from_needed: false,
|
||||
transfer,
|
||||
};
|
||||
let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context.clone())?;
|
||||
grouped_protocol_data.extend(protocol_data);
|
||||
@@ -462,7 +468,7 @@ mod tests {
|
||||
Bytes::from_str("0x3ede3eca2a72b3aecc820e955b36f38437d01395").unwrap()
|
||||
);
|
||||
// single swap selector
|
||||
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "cc60c623");
|
||||
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "20144a07");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -487,7 +493,7 @@ mod tests {
|
||||
let transactions = transactions.unwrap();
|
||||
assert_eq!(transactions.len(), 1);
|
||||
// single swap selector
|
||||
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "cc60c623");
|
||||
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "20144a07");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -534,7 +540,7 @@ mod tests {
|
||||
assert_eq!(transactions.len(), 1);
|
||||
assert_eq!(transactions[0].value, eth_amount_in);
|
||||
// sequential swap selector
|
||||
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "59e3efbb");
|
||||
assert_eq!(&hex::encode(transactions[0].clone().data)[..8], "e8a980d7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1148,9 +1154,7 @@ mod tests {
|
||||
"6982508145454ce325ddbe47a25d4ec3d2311933",
|
||||
// zero for one
|
||||
"00",
|
||||
// transfer from false
|
||||
"00",
|
||||
// transfer true
|
||||
// transfer type Transfer
|
||||
"01",
|
||||
// receiver
|
||||
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2",
|
||||
|
||||
@@ -106,9 +106,7 @@ pub struct Transaction {
|
||||
/// solution does not require router address.
|
||||
/// * `group_token_in`: Token to be used as the input for the group swap.
|
||||
/// * `group_token_out`: Token to be used as the output for the group swap.
|
||||
/// * `transfer_from_needed`: true if the solution requires a transfer from the user to the router
|
||||
/// or pool.
|
||||
/// * `transfer_needed`: true if the solution requires a transfer from the router to the pool.
|
||||
/// * `transfer`: Type of transfer to be performed. See `TransferType` for more details.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EncodingContext {
|
||||
pub receiver: Bytes,
|
||||
@@ -116,8 +114,22 @@ pub struct EncodingContext {
|
||||
pub router_address: Option<Bytes>,
|
||||
pub group_token_in: Bytes,
|
||||
pub group_token_out: Bytes,
|
||||
pub transfer_from_needed: bool,
|
||||
pub transfer_needed: bool,
|
||||
pub transfer: TransferType,
|
||||
}
|
||||
|
||||
/// Represents the type of transfer to be performed into the pool.
|
||||
///
|
||||
/// # Fields
|
||||
///
|
||||
/// * `Transfer`: Transfer the token from the router into the protocol.
|
||||
/// * `TransferFrom`: Transfer the token from the sender to the protocol/router.
|
||||
/// * `None`: No transfer is needed. Tokens are already in the pool.
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum TransferType {
|
||||
TransferFrom = 0,
|
||||
Transfer = 1,
|
||||
None = 2,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
|
||||
Reference in New Issue
Block a user