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:
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user