feat: Support out transfer straight to the receiver

- The out transfer is now a responsibility of the Executors -> remove this from router methods
- Also adding a check that the receiver got the full amount out
- In encoding, if it is the last swap, pass the receiver as the trade receiver and not the router address (fix encoding tests)
- Fixed some solidity tests (after rebasing with a PR that is still open, I will fix them all)

TODO: Adapt curve and uniswap v4 to support this

--- don't change below this line ---
ENG-4315 Took 3 hours 7 minutes

Took 20 minutes

Took 59 seconds


Took 7 minutes
This commit is contained in:
Diana Carvalho
2025-04-15 12:26:28 +01:00
parent c10d5874f8
commit 9bcb58e5aa
5 changed files with 139 additions and 104 deletions

View File

@@ -111,16 +111,19 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
NativeAction::Unwrap => unwrap = true,
}
}
let protocol = grouped_swap.protocol_system.clone();
let swap_encoder = self
.get_swap_encoder(&grouped_swap.protocol_system)
.get_swap_encoder(&protocol)
.ok_or_else(|| {
EncodingError::InvalidInput(format!(
"Swap encoder not found for protocol: {}",
grouped_swap.protocol_system
protocol
))
})?;
let receiver =
if !unwrap { solution.receiver.clone() } else { self.router_address.clone() };
let mut grouped_protocol_data: Vec<u8> = vec![];
for swap in grouped_swap.swaps.iter() {
let transfer_type = self.get_transfer_type(
@@ -133,7 +136,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
);
let encoding_context = EncodingContext {
receiver: self.router_address.clone(),
receiver: receiver.clone(),
exact_out: solution.exact_out,
router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.input_token.clone(),
@@ -286,16 +289,23 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
}
let mut swaps = vec![];
for grouped_swap in grouped_swaps.iter() {
for (i, grouped_swap) in grouped_swaps.iter().enumerate() {
let protocol = grouped_swap.protocol_system.clone();
let swap_encoder = self
.get_swap_encoder(&grouped_swap.protocol_system)
.get_swap_encoder(&protocol)
.ok_or_else(|| {
EncodingError::InvalidInput(format!(
"Swap encoder not found for protocol: {}",
grouped_swap.protocol_system
protocol
))
})?;
let receiver = if i == grouped_swaps.len() - 1 && !unwrap {
solution.receiver.clone()
} else {
self.router_address.clone()
};
let mut grouped_protocol_data: Vec<u8> = vec![];
for swap in grouped_swap.swaps.iter() {
let transfer_type = self.get_transfer_type(
@@ -308,7 +318,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
);
let encoding_context = EncodingContext {
receiver: self.router_address.clone(),
receiver: receiver.clone(),
exact_out: solution.exact_out,
router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.input_token.clone(),
@@ -516,15 +526,22 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
let mut swaps = vec![];
for grouped_swap in grouped_swaps.iter() {
let protocol = grouped_swap.protocol_system.clone();
let swap_encoder = self
.get_swap_encoder(&grouped_swap.protocol_system)
.get_swap_encoder(&protocol)
.ok_or_else(|| {
EncodingError::InvalidInput(format!(
"Swap encoder not found for protocol: {}",
grouped_swap.protocol_system
protocol
))
})?;
let receiver = if !wrap && grouped_swap.output_token == solution.checked_token {
solution.receiver.clone()
} else {
self.router_address.clone()
};
let mut grouped_protocol_data: Vec<u8> = vec![];
for swap in grouped_swap.swaps.iter() {
let transfer_type = self.get_transfer_type(
@@ -537,7 +554,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
);
let encoding_context = EncodingContext {
receiver: self.router_address.clone(),
receiver: receiver.clone(),
exact_out: solution.exact_out,
router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.input_token.clone(),
@@ -766,7 +783,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one
"02", // transfer type
"00000000000000", // padding
@@ -869,7 +886,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one
"02", // transfer type
"0000000000000000000000000000", // padding
@@ -1197,9 +1214,6 @@ mod tests {
#[test]
fn test_sequential_swap_strategy_encoder_no_permit2() {
// Note: This test does not assert anything. It is only used to obtain integration test
// data for our router solidity test.
//
// Performs a split swap from WETH to USDC though WBTC and DAI using USV2 pools
//
// WETH ───(USV2)──> WBTC ───(USV2)──> USDC
@@ -1253,8 +1267,41 @@ mod tests {
.encode_strategy(solution)
.unwrap();
let _hex_calldata = encode(&calldata);
println!("{}", _hex_calldata);
let hex_calldata = encode(&calldata);
println!("{}", hex_calldata);
let expected = String::from(concat!(
"e8a980d7", /* function selector */
"0000000000000000000000000000000000000000000000000de0b6b3a7640000", // amount in
"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token ou
"00000000000000000000000000000000000000000000000000000000018f61ec", // min amount out
"0000000000000000000000000000000000000000000000000000000000000000", // wrap
"0000000000000000000000000000000000000000000000000000000000000000", // unwrap
"000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"0000000000000000000000000000000000000000000000000000000000000100", /* length ple
* encode */
"00000000000000000000000000000000000000000000000000000000000000a8",
// swap 1
"0052", // swap length
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"bb2b8038a1640196fbe3e38816f3e67cba72d940", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver (router)
"00", // zero to one
"00", // transfer type
// swap 2
"0052", // swap length
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"2260fac5e5542a773aa44fbcfedf7c193bc2c599", // token in
"004375dff511095cc5a197a54140a24efef3a416", // component id
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver (final user)
"01", // zero to one
"00", // transfer type
"000000000000000000000000000000000000000000000000", // padding
));
assert_eq!(hex_calldata, expected);
}
#[test]
@@ -1529,7 +1576,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one
"01", // transfer type
"0000000000000000000000000000", // padding
@@ -1612,7 +1659,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one
"01", // transfer type
"00000000000000", // padding
@@ -1863,7 +1910,7 @@ mod tests {
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out
"0001f4", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"01", // zero2one
"02", // transfer type
@@ -1874,7 +1921,7 @@ mod tests {
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out
"000bb8", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"00", // zero2one
"00", // transfer type
@@ -2017,7 +2064,7 @@ mod tests {
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out
"0001f4", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"01", // zero2one
"02", // transfer type
@@ -2029,7 +2076,7 @@ mod tests {
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out
"000bb8", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"01", // zero2one
"02", // transfer type
@@ -2040,7 +2087,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address,
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id,
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one
"00", // transfer type
"00000000000000" // padding
@@ -2178,7 +2225,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in
"b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"01", // zero2one
"02", // transfer type
"006e", // ple encoded swaps
@@ -2189,7 +2236,7 @@ mod tests {
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out
"0001f4", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"00", // zero2one
"00", // transfer type
@@ -2201,7 +2248,7 @@ mod tests {
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out
"000bb8", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address
"cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"00", // zero2one
"00", // transfer type