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

@@ -58,6 +58,9 @@ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
error TychoRouter__AddressZero(); error TychoRouter__AddressZero();
error TychoRouter__EmptySwaps(); error TychoRouter__EmptySwaps();
error TychoRouter__NegativeSlippage(uint256 amount, uint256 minAmount); error TychoRouter__NegativeSlippage(uint256 amount, uint256 minAmount);
error TychoRouter__AmountOutNotFullyReceived(
uint256 amountIn, uint256 amountConsumed
);
error TychoRouter__MessageValueMismatch(uint256 value, uint256 amount); error TychoRouter__MessageValueMismatch(uint256 value, uint256 amount);
error TychoRouter__InvalidDataLength(); error TychoRouter__InvalidDataLength();
error TychoRouter__UndefinedMinAmountOut(); error TychoRouter__UndefinedMinAmountOut();
@@ -408,9 +411,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address receiver, address receiver,
bytes calldata swaps bytes calldata swaps
) internal returns (uint256 amountOut) { ) internal returns (uint256 amountOut) {
if (receiver == address(0)) {
revert TychoRouter__AddressZero();
}
if (minAmountOut == 0) { if (minAmountOut == 0) {
revert TychoRouter__UndefinedMinAmountOut(); revert TychoRouter__UndefinedMinAmountOut();
} }
@@ -420,6 +420,8 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
_wrapETH(amountIn); _wrapETH(amountIn);
tokenIn = address(_weth); tokenIn = address(_weth);
} }
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
amountOut = _splitSwap(amountIn, nTokens, swaps); amountOut = _splitSwap(amountIn, nTokens, swaps);
if (amountOut < minAmountOut) { if (amountOut < minAmountOut) {
@@ -428,11 +430,13 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
if (unwrapEth) { if (unwrapEth) {
_unwrapETH(amountOut); _unwrapETH(amountOut);
}
if (tokenOut == address(0)) {
Address.sendValue(payable(receiver), amountOut); Address.sendValue(payable(receiver), amountOut);
} else { }
IERC20(tokenOut).safeTransfer(receiver, amountOut);
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
if (userAmount != amountOut) {
revert TychoRouter__AmountOutNotFullyReceived(userAmount, amountOut);
} }
} }
@@ -454,9 +458,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address receiver, address receiver,
bytes calldata swap_ bytes calldata swap_
) internal returns (uint256 amountOut) { ) internal returns (uint256 amountOut) {
if (receiver == address(0)) {
revert TychoRouter__AddressZero();
}
if (minAmountOut == 0) { if (minAmountOut == 0) {
revert TychoRouter__UndefinedMinAmountOut(); revert TychoRouter__UndefinedMinAmountOut();
} }
@@ -470,6 +471,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
(address executor, bytes calldata protocolData) = (address executor, bytes calldata protocolData) =
swap_.decodeSingleSwap(); swap_.decodeSingleSwap();
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
amountOut = _callExecutor(executor, amountIn, protocolData); amountOut = _callExecutor(executor, amountIn, protocolData);
if (amountOut < minAmountOut) { if (amountOut < minAmountOut) {
@@ -478,11 +480,14 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
if (unwrapEth) { if (unwrapEth) {
_unwrapETH(amountOut); _unwrapETH(amountOut);
}
if (tokenOut == address(0)) {
Address.sendValue(payable(receiver), amountOut); Address.sendValue(payable(receiver), amountOut);
} else { }
IERC20(tokenOut).safeTransfer(receiver, amountOut);
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
if (userAmount != amountOut) {
revert TychoRouter__AmountOutNotFullyReceived(userAmount, amountOut);
} }
} }
@@ -504,9 +509,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
address receiver, address receiver,
bytes calldata swaps bytes calldata swaps
) internal returns (uint256 amountOut) { ) internal returns (uint256 amountOut) {
if (receiver == address(0)) {
revert TychoRouter__AddressZero();
}
if (minAmountOut == 0) { if (minAmountOut == 0) {
revert TychoRouter__UndefinedMinAmountOut(); revert TychoRouter__UndefinedMinAmountOut();
} }
@@ -517,7 +519,9 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
tokenIn = address(_weth); tokenIn = address(_weth);
} }
uint256 initialBalanceTokenOut = _balanceOf(tokenOut, receiver);
amountOut = _sequentialSwap(amountIn, swaps); amountOut = _sequentialSwap(amountIn, swaps);
uint256 currentBalanceTokenIn = _balanceOf(tokenIn, address(this));
if (amountOut < minAmountOut) { if (amountOut < minAmountOut) {
revert TychoRouter__NegativeSlippage(amountOut, minAmountOut); revert TychoRouter__NegativeSlippage(amountOut, minAmountOut);
@@ -525,11 +529,13 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
if (unwrapEth) { if (unwrapEth) {
_unwrapETH(amountOut); _unwrapETH(amountOut);
}
if (tokenOut == address(0)) {
Address.sendValue(payable(receiver), amountOut); Address.sendValue(payable(receiver), amountOut);
} else { }
IERC20(tokenOut).safeTransfer(receiver, amountOut); uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
if (userAmount != amountOut) {
revert TychoRouter__AmountOutNotFullyReceived(userAmount, amountOut);
} }
} }
@@ -764,4 +770,13 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
_handleCallback(data); _handleCallback(data);
return ""; return "";
} }
function _balanceOf(address token, address owner)
internal
view
returns (uint256)
{
return
token == address(0) ? owner.balance : IERC20(token).balanceOf(owner);
}
} }

View File

@@ -36,7 +36,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
encodeUniswapV2Swap( encodeUniswapV2Swap(
DAI_ADDR, DAI_ADDR,
DAI_USDC_POOL, DAI_USDC_POOL,
tychoRouterAddr, ALICE,
true, true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
) )
@@ -44,22 +44,6 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup {
return swaps; return swaps;
} }
function testSequentialSwapInternalMethod() public {
// Trade 1 WETH for USDC through DAI - see _getSequentialSwaps for more info
uint256 amountIn = 1 ether;
deal(WETH_ADDR, ALICE, amountIn);
vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSequentialSwaps(false);
tychoRouter.exposedSequentialSwap(amountIn, pleEncode(swaps));
vm.stopPrank();
uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr);
assertEq(usdcBalance, 2644659787);
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
}
function testSequentialSwapPermit2() public { function testSequentialSwapPermit2() public {
// Trade 1 WETH for USDC through DAI - see _getSequentialSwaps for more info // Trade 1 WETH for USDC through DAI - see _getSequentialSwaps for more info
uint256 amountIn = 1 ether; uint256 amountIn = 1 ether;

View File

@@ -21,11 +21,10 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
bytes memory signature bytes memory signature
) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn); ) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData =
WETH_ADDR, encodeUniswapV2Swap(WETH_ADDR,
WETH_DAI_POOL, WETH_DAI_POOL,
tychoRouterAddr, ALICE, false,
false,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL
); );
@@ -62,11 +61,10 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
// Approve the tokenIn to be transferred to the router // Approve the tokenIn to be transferred to the router
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData =
WETH_ADDR, encodeUniswapV2Swap(WETH_ADDR,
WETH_DAI_POOL, WETH_DAI_POOL,
tychoRouterAddr, ALICE, false,
false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
); );
@@ -103,11 +101,10 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
vm.startPrank(ALICE); vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData =
WETH_ADDR, encodeUniswapV2Swap(WETH_ADDR,
WETH_DAI_POOL, WETH_DAI_POOL,
tychoRouterAddr, ALICE, false,
false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
); );
@@ -129,11 +126,10 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
vm.startPrank(ALICE); vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn - 1); IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn - 1);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData =
WETH_ADDR, encodeUniswapV2Swap(WETH_ADDR,
WETH_DAI_POOL, WETH_DAI_POOL,
tychoRouterAddr, ALICE, false,
false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
); );
@@ -164,11 +160,10 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
// Approve the tokenIn to be transferred to the router // Approve the tokenIn to be transferred to the router
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn);
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData =
WETH_ADDR, encodeUniswapV2Swap(WETH_ADDR,
WETH_DAI_POOL, WETH_DAI_POOL,
tychoRouterAddr, ALICE, false,
false,
TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL
); );
@@ -213,11 +208,10 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup {
sigDeadline: 0 sigDeadline: 0
}); });
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData =
WETH_ADDR, encodeUniswapV2Swap(WETH_ADDR,
WETH_DAI_POOL, WETH_DAI_POOL,
tychoRouterAddr, ALICE, false,
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
); );

View File

@@ -44,11 +44,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
uint8(2), uint8(2),
uint24(0), uint24(0),
address(usv2Executor), address(usv2Executor),
encodeUniswapV2Swap( encodeUniswapV2Swap(WBTC_ADDR, USDC_WBTC_POOL, ALICE, true,
WBTC_ADDR,
USDC_WBTC_POOL,
tychoRouterAddr,
true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
) )
); );
@@ -72,7 +68,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
encodeUniswapV2Swap( encodeUniswapV2Swap(
DAI_ADDR, DAI_ADDR,
DAI_USDC_POOL, DAI_USDC_POOL,
tychoRouterAddr, ALICE,
true, true,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
) )
@@ -92,7 +88,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
tychoRouter.exposedSplitSwap(amountIn, 4, pleEncode(swaps)); tychoRouter.exposedSplitSwap(amountIn, 4, pleEncode(swaps));
vm.stopPrank(); vm.stopPrank();
uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr); uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(ALICE);
assertEq(usdcBalance, 2615491639); assertEq(usdcBalance, 2615491639);
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0); assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0);
} }
@@ -136,7 +132,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
deal(WETH_ADDR, ALICE, amountIn); deal(WETH_ADDR, ALICE, amountIn);
vm.startPrank(ALICE); vm.startPrank(ALICE);
IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn);
bytes[] memory swaps = _getSplitSwaps(false); bytes[] memory swaps = _getSplitSwaps(false);
@@ -154,7 +150,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(ALICE); uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(ALICE);
assertEq(usdcBalance, 2615491639); assertEq(usdcBalance, 2615491639);
assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0); assertEq(IERC20(WETH_ADDR).balanceOf(ALICE), 0);
} }
function testSplitSwapUndefinedMinAmount() public { function testSplitSwapUndefinedMinAmount() public {
@@ -265,11 +261,10 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
spender: address(0), spender: address(0),
sigDeadline: 0 sigDeadline: 0
}); });
bytes memory protocolData = encodeUniswapV2Swap( bytes memory protocolData =
WETH_ADDR, encodeUniswapV2Swap(WETH_ADDR,
WETH_DAI_POOL, WETH_DAI_POOL,
tychoRouterAddr, ALICE, false,
false,
TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL
); );
@@ -367,7 +362,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup {
bytes memory protocolData = encodeUniswapV3Swap( bytes memory protocolData = encodeUniswapV3Swap(
WETH_ADDR, WETH_ADDR,
DAI_ADDR, DAI_ADDR,
tychoRouterAddr, ALICE,
DAI_WETH_USV3, DAI_WETH_USV3,
zeroForOne, zeroForOne,
TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL

View File

@@ -111,16 +111,19 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
NativeAction::Unwrap => unwrap = true, NativeAction::Unwrap => unwrap = true,
} }
} }
let protocol = grouped_swap.protocol_system.clone();
let swap_encoder = self let swap_encoder = self
.get_swap_encoder(&grouped_swap.protocol_system) .get_swap_encoder(&protocol)
.ok_or_else(|| { .ok_or_else(|| {
EncodingError::InvalidInput(format!( EncodingError::InvalidInput(format!(
"Swap encoder not found for protocol: {}", "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![]; let mut grouped_protocol_data: Vec<u8> = vec![];
for swap in grouped_swap.swaps.iter() { for swap in grouped_swap.swaps.iter() {
let transfer_type = self.get_transfer_type( let transfer_type = self.get_transfer_type(
@@ -133,7 +136,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder {
); );
let encoding_context = EncodingContext { let encoding_context = EncodingContext {
receiver: self.router_address.clone(), receiver: receiver.clone(),
exact_out: solution.exact_out, exact_out: solution.exact_out,
router_address: Some(self.router_address.clone()), router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.input_token.clone(), group_token_in: grouped_swap.input_token.clone(),
@@ -286,16 +289,23 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
} }
let mut swaps = vec![]; 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 let swap_encoder = self
.get_swap_encoder(&grouped_swap.protocol_system) .get_swap_encoder(&protocol)
.ok_or_else(|| { .ok_or_else(|| {
EncodingError::InvalidInput(format!( EncodingError::InvalidInput(format!(
"Swap encoder not found for protocol: {}", "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![]; let mut grouped_protocol_data: Vec<u8> = vec![];
for swap in grouped_swap.swaps.iter() { for swap in grouped_swap.swaps.iter() {
let transfer_type = self.get_transfer_type( let transfer_type = self.get_transfer_type(
@@ -308,7 +318,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder {
); );
let encoding_context = EncodingContext { let encoding_context = EncodingContext {
receiver: self.router_address.clone(), receiver: receiver.clone(),
exact_out: solution.exact_out, exact_out: solution.exact_out,
router_address: Some(self.router_address.clone()), router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.input_token.clone(), group_token_in: grouped_swap.input_token.clone(),
@@ -516,15 +526,22 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
let mut swaps = vec![]; let mut swaps = vec![];
for grouped_swap in grouped_swaps.iter() { for grouped_swap in grouped_swaps.iter() {
let protocol = grouped_swap.protocol_system.clone();
let swap_encoder = self let swap_encoder = self
.get_swap_encoder(&grouped_swap.protocol_system) .get_swap_encoder(&protocol)
.ok_or_else(|| { .ok_or_else(|| {
EncodingError::InvalidInput(format!( EncodingError::InvalidInput(format!(
"Swap encoder not found for protocol: {}", "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![]; let mut grouped_protocol_data: Vec<u8> = vec![];
for swap in grouped_swap.swaps.iter() { for swap in grouped_swap.swaps.iter() {
let transfer_type = self.get_transfer_type( let transfer_type = self.get_transfer_type(
@@ -537,7 +554,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder {
); );
let encoding_context = EncodingContext { let encoding_context = EncodingContext {
receiver: self.router_address.clone(), receiver: receiver.clone(),
exact_out: solution.exact_out, exact_out: solution.exact_out,
router_address: Some(self.router_address.clone()), router_address: Some(self.router_address.clone()),
group_token_in: grouped_swap.input_token.clone(), group_token_in: grouped_swap.input_token.clone(),
@@ -766,7 +783,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one "00", // zero2one
"02", // transfer type "02", // transfer type
"00000000000000", // padding "00000000000000", // padding
@@ -869,7 +886,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one "00", // zero2one
"02", // transfer type "02", // transfer type
"0000000000000000000000000000", // padding "0000000000000000000000000000", // padding
@@ -1197,9 +1214,6 @@ mod tests {
#[test] #[test]
fn test_sequential_swap_strategy_encoder_no_permit2() { 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 // Performs a split swap from WETH to USDC though WBTC and DAI using USV2 pools
// //
// WETH ───(USV2)──> WBTC ───(USV2)──> USDC // WETH ───(USV2)──> WBTC ───(USV2)──> USDC
@@ -1253,8 +1267,41 @@ mod tests {
.encode_strategy(solution) .encode_strategy(solution)
.unwrap(); .unwrap();
let _hex_calldata = encode(&calldata); let hex_calldata = encode(&calldata);
println!("{}", _hex_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] #[test]
@@ -1529,7 +1576,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one "00", // zero2one
"01", // transfer type "01", // transfer type
"0000000000000000000000000000", // padding "0000000000000000000000000000", // padding
@@ -1612,7 +1659,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one "00", // zero2one
"01", // transfer type "01", // transfer type
"00000000000000", // padding "00000000000000", // padding
@@ -1863,7 +1910,7 @@ mod tests {
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out
"0001f4", // pool fee "0001f4", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"01", // zero2one "01", // zero2one
"02", // transfer type "02", // transfer type
@@ -1874,7 +1921,7 @@ mod tests {
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out
"000bb8", // pool fee "000bb8", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"00", // zero2one "00", // zero2one
"00", // transfer type "00", // transfer type
@@ -2017,7 +2064,7 @@ mod tests {
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out
"0001f4", // pool fee "0001f4", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"01", // zero2one "01", // zero2one
"02", // transfer type "02", // transfer type
@@ -2029,7 +2076,7 @@ mod tests {
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token out
"000bb8", // pool fee "000bb8", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"01", // zero2one "01", // zero2one
"02", // transfer type "02", // transfer type
@@ -2040,7 +2087,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address, "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address,
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id, "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id,
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"00", // zero2one "00", // zero2one
"00", // transfer type "00", // transfer type
"00000000000000" // padding "00000000000000" // padding
@@ -2178,7 +2225,7 @@ mod tests {
"5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address "5615deb798bb3e4dfa0139dfa1b3d433cc23b72f", // executor address
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in
"b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
"01", // zero2one "01", // zero2one
"02", // transfer type "02", // transfer type
"006e", // ple encoded swaps "006e", // ple encoded swaps
@@ -2189,7 +2236,7 @@ mod tests {
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out
"0001f4", // pool fee "0001f4", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id
"00", // zero2one "00", // zero2one
"00", // transfer type "00", // transfer type
@@ -2201,7 +2248,7 @@ mod tests {
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in "c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token out
"000bb8", // pool fee "000bb8", // pool fee
"3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2", // receiver
"8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id
"00", // zero2one "00", // zero2one
"00", // transfer type "00", // transfer type