diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index e6c798b..1b693fd 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -58,9 +58,6 @@ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; error TychoRouter__AddressZero(); error TychoRouter__EmptySwaps(); error TychoRouter__NegativeSlippage(uint256 amount, uint256 minAmount); -error TychoRouter__AmountInDiffersFromConsumed( - uint256 amountIn, uint256 amountConsumed -); error TychoRouter__MessageValueMismatch(uint256 value, uint256 amount); error TychoRouter__InvalidDataLength(); error TychoRouter__UndefinedMinAmountOut(); @@ -99,7 +96,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { /** * @notice Executes a swap operation based on a predefined swap graph, supporting internal token amount splits. * This function enables multi-step swaps, optional ETH wrapping/unwrapping, and validates the output amount - * against a user-specified minimum. This function performs a transferFrom to retrieve tokens from the caller. + * against a user-specified minimum. * * @dev * - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token. @@ -130,11 +127,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { address receiver, bytes calldata swaps ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { - if (address(tokenIn) != address(0)) { - IERC20(tokenIn).safeTransferFrom( - msg.sender, address(this), amountIn - ); - } return _splitSwapChecked( amountIn, tokenIn, @@ -187,15 +179,9 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { bytes calldata signature, bytes calldata swaps ) external payable whenNotPaused nonReentrant returns (uint256 amountOut) { - // For native ETH, assume funds already in our router. Else, transfer and handle approval. + // For native ETH, assume funds already in our router. Else, handle approval. if (tokenIn != address(0)) { permit2.permit(msg.sender, permitSingle, signature); - permit2.transferFrom( - msg.sender, - address(this), - uint160(amountIn), - permitSingle.details.token - ); } return _splitSwapChecked( @@ -214,7 +200,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { /** * @notice Executes a swap operation based on a predefined swap graph with no split routes. * This function enables multi-step swaps, optional ETH wrapping/unwrapping, and validates the output amount - * against a user-specified minimum. This function performs a transferFrom to retrieve tokens from the caller. + * against a user-specified minimum. * * @dev * - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token. @@ -243,7 +229,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { address receiver, bytes calldata swaps ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { - IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn); return _sequentialSwapChecked( amountIn, tokenIn, @@ -292,15 +277,9 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { bytes calldata signature, bytes calldata swaps ) external payable whenNotPaused nonReentrant returns (uint256 amountOut) { - // For native ETH, assume funds already in our router. Else, transfer and handle approval. + // For native ETH, assume funds already in our router. Else, handle approval. if (tokenIn != address(0)) { permit2.permit(msg.sender, permitSingle, signature); - permit2.transferFrom( - msg.sender, - address(this), - uint160(amountIn), - permitSingle.details.token - ); } return _sequentialSwapChecked( @@ -318,7 +297,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { /** * @notice Executes a single swap operation. * This function enables optional ETH wrapping/unwrapping, and validates the output amount against a user-specified minimum. - * This function performs a transferFrom to retrieve tokens from the caller. * * @dev * - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token. @@ -346,7 +324,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { address receiver, bytes calldata swapData ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { - IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn); return _singleSwap( amountIn, tokenIn, @@ -395,15 +372,9 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { bytes calldata signature, bytes calldata swapData ) external payable whenNotPaused nonReentrant returns (uint256 amountOut) { - // For native ETH, assume funds already in our router. Else, transfer and handle approval. + // For native ETH, assume funds already in our router. Else, handle approval. if (tokenIn != address(0)) { permit2.permit(msg.sender, permitSingle, signature); - permit2.transferFrom( - msg.sender, - address(this), - uint160(amountIn), - permitSingle.details.token - ); } return _singleSwap( @@ -449,23 +420,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { _wrapETH(amountIn); tokenIn = address(_weth); } - - uint256 initialBalance = tokenIn == address(0) - ? address(this).balance - : IERC20(tokenIn).balanceOf(address(this)); - amountOut = _splitSwap(amountIn, nTokens, swaps); - uint256 currentBalance = tokenIn == address(0) - ? address(this).balance - : IERC20(tokenIn).balanceOf(address(this)); - - uint256 amountConsumed = initialBalance - currentBalance; - - if (tokenIn != tokenOut && amountConsumed != amountIn) { - revert TychoRouter__AmountInDiffersFromConsumed( - amountIn, amountConsumed - ); - } if (amountOut < minAmountOut) { revert TychoRouter__NegativeSlippage(amountOut, minAmountOut); @@ -512,25 +467,10 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { tokenIn = address(_weth); } - uint256 initialBalance = tokenIn == address(0) - ? address(this).balance - : IERC20(tokenIn).balanceOf(address(this)); - (address executor, bytes calldata protocolData) = swap_.decodeSingleSwap(); amountOut = _callExecutor(executor, amountIn, protocolData); - uint256 currentBalance = tokenIn == address(0) - ? address(this).balance - : IERC20(tokenIn).balanceOf(address(this)); - - uint256 amountConsumed = initialBalance - currentBalance; - - if (amountConsumed != amountIn) { - revert TychoRouter__AmountInDiffersFromConsumed( - amountIn, amountConsumed - ); - } if (amountOut < minAmountOut) { revert TychoRouter__NegativeSlippage(amountOut, minAmountOut); @@ -577,24 +517,8 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { tokenIn = address(_weth); } - uint256 initialBalance = tokenIn == address(0) - ? address(this).balance - : IERC20(tokenIn).balanceOf(address(this)); - amountOut = _sequentialSwap(amountIn, swaps); - uint256 currentBalance = tokenIn == address(0) - ? address(this).balance - : IERC20(tokenIn).balanceOf(address(this)); - - uint256 amountConsumed = initialBalance - currentBalance; - - if (tokenIn != tokenOut && amountConsumed != amountIn) { - revert TychoRouter__AmountInDiffersFromConsumed( - amountIn, amountConsumed - ); - } - if (amountOut < minAmountOut) { revert TychoRouter__NegativeSlippage(amountOut, minAmountOut); } diff --git a/foundry/src/executors/BalancerV2Executor.sol b/foundry/src/executors/BalancerV2Executor.sol index 0f69fcd..817c98b 100644 --- a/foundry/src/executors/BalancerV2Executor.sol +++ b/foundry/src/executors/BalancerV2Executor.sol @@ -10,14 +10,17 @@ import { import {IAsset} from "@balancer-labs/v2-interfaces/contracts/vault/IAsset.sol"; // slither-disable-next-line solc-version import {IVault} from "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol"; +import {TokenTransfer} from "./TokenTransfer.sol"; error BalancerV2Executor__InvalidDataLength(); -contract BalancerV2Executor is IExecutor { +contract BalancerV2Executor is IExecutor, TokenTransfer { using SafeERC20 for IERC20; address private constant VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; + constructor(address _permit2) TokenTransfer(_permit2) {} + // slither-disable-next-line locked-ether function swap(uint256 givenAmount, bytes calldata data) external @@ -29,9 +32,20 @@ contract BalancerV2Executor is IExecutor { IERC20 tokenOut, bytes32 poolId, address receiver, - bool needsApproval + bool needsApproval, + TransferType transferType ) = _decodeData(data); + _transfer( + address(tokenIn), + msg.sender, + // Receiver can never be the pool, since the pool expects funds in the router contract + // Thus, this call will only ever be used to transfer funds from the user into the router. + address(this), + givenAmount, + transferType + ); + if (needsApproval) { // slither-disable-next-line unused-return tokenIn.approve(VAULT, type(uint256).max); @@ -67,10 +81,11 @@ contract BalancerV2Executor is IExecutor { IERC20 tokenOut, bytes32 poolId, address receiver, - bool needsApproval + bool needsApproval, + TransferType transferType ) { - if (data.length != 93) { + if (data.length != 94) { revert BalancerV2Executor__InvalidDataLength(); } @@ -79,5 +94,6 @@ contract BalancerV2Executor is IExecutor { poolId = bytes32(data[40:72]); receiver = address(bytes20(data[72:92])); needsApproval = uint8(data[92]) > 0; + transferType = TransferType(uint8(data[93])); } } diff --git a/foundry/src/executors/CurveExecutor.sol b/foundry/src/executors/CurveExecutor.sol index 569072f..933077e 100644 --- a/foundry/src/executors/CurveExecutor.sol +++ b/foundry/src/executors/CurveExecutor.sol @@ -3,8 +3,10 @@ pragma solidity ^0.8.26; import "@interfaces/IExecutor.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "./TokenTransfer.sol"; error CurveExecutor__AddressZero(); +error CurveExecutor__InvalidDataLength(); interface CryptoPool { // slither-disable-next-line naming-convention @@ -32,12 +34,14 @@ interface CryptoPoolETH { // slither-disable-end naming-convention } -contract CurveExecutor is IExecutor { +contract CurveExecutor is IExecutor, TokenTransfer { using SafeERC20 for IERC20; address public immutable nativeToken; - constructor(address _nativeToken) { + constructor(address _nativeToken, address _permit2) + TokenTransfer(_permit2) + { if (_nativeToken == address(0)) { revert CurveExecutor__AddressZero(); } @@ -50,6 +54,8 @@ contract CurveExecutor is IExecutor { payable returns (uint256) { + if (data.length != 65) revert CurveExecutor__InvalidDataLength(); + ( address tokenIn, address tokenOut, @@ -57,9 +63,20 @@ contract CurveExecutor is IExecutor { uint8 poolType, int128 i, int128 j, - bool tokenApprovalNeeded + bool tokenApprovalNeeded, + TransferType transferType ) = _decodeData(data); + _transfer( + tokenIn, + msg.sender, + // Receiver can never be the pool, since the pool expects funds in the router contract + // Thus, this call will only ever be used to transfer funds from the user into the router. + address(this), + amountIn, + transferType + ); + if (tokenApprovalNeeded && tokenIn != nativeToken) { // slither-disable-next-line unused-return IERC20(tokenIn).approve(address(pool), type(uint256).max); @@ -105,7 +122,8 @@ contract CurveExecutor is IExecutor { uint8 poolType, int128 i, int128 j, - bool tokenApprovalNeeded + bool tokenApprovalNeeded, + TransferType transferType ) { tokenIn = address(bytes20(data[0:20])); @@ -115,6 +133,7 @@ contract CurveExecutor is IExecutor { i = int128(uint128(uint8(data[61]))); j = int128(uint128(uint8(data[62]))); tokenApprovalNeeded = data[63] != 0; + transferType = TransferType(uint8(data[64])); } receive() external payable { diff --git a/foundry/src/executors/TokenTransfer.sol b/foundry/src/executors/TokenTransfer.sol index f529b69..b5f8629 100644 --- a/foundry/src/executors/TokenTransfer.sol +++ b/foundry/src/executors/TokenTransfer.sol @@ -14,11 +14,19 @@ contract TokenTransfer { enum TransferType { // Assume funds are in the TychoRouter - transfer into the pool - TRANSFER, + TRANSFER_TO_PROTOCOL, // Assume funds are in msg.sender's wallet - transferFrom into the pool - TRANSFERFROM, + TRANSFER_FROM_TO_PROTOCOL, // Assume funds are in msg.sender's wallet - permit2TransferFrom into the pool - TRANSFERPERMIT2, + TRANSFER_PERMIT2_TO_PROTOCOL, + // Assume funds are in msg.sender's wallet - but the pool requires it to be + // in the router contract when calling swap - transferFrom into the router + // contract + TRANSFER_FROM_TO_ROUTER, + // Assume funds are in msg.sender's wallet - but the pool requires it to be + // in the router contract when calling swap - transferFrom into the router + // contract using permit2 + TRANSFER_PERMIT2_TO_ROUTER, // Assume funds have already been transferred into the pool. Do nothing. NONE } @@ -31,21 +39,31 @@ contract TokenTransfer { } function _transfer( - IERC20 tokenIn, + address tokenIn, address sender, address receiver, uint256 amount, TransferType transferType ) internal { - if (transferType == TransferType.TRANSFER) { - tokenIn.safeTransfer(receiver, amount); - } else if (transferType == TransferType.TRANSFERFROM) { + if (transferType == TransferType.TRANSFER_TO_PROTOCOL) { + if (tokenIn == address(0)) { + payable(receiver).transfer(amount); + } else { + IERC20(tokenIn).safeTransfer(receiver, amount); + } + } else if (transferType == TransferType.TRANSFER_FROM_TO_PROTOCOL) { // slither-disable-next-line arbitrary-send-erc20 - tokenIn.safeTransferFrom(sender, receiver, amount); - } else if (transferType == TransferType.TRANSFERPERMIT2) { + IERC20(tokenIn).safeTransferFrom(sender, receiver, amount); + } else if (transferType == TransferType.TRANSFER_PERMIT2_TO_PROTOCOL) { + // Permit2.permit is already called from the TychoRouter + permit2.transferFrom(sender, receiver, uint160(amount), tokenIn); + } else if (transferType == TransferType.TRANSFER_FROM_TO_ROUTER) { + // slither-disable-next-line arbitrary-send-erc20 + IERC20(tokenIn).safeTransferFrom(sender, address(this), amount); + } else if (transferType == TransferType.TRANSFER_PERMIT2_TO_ROUTER) { // Permit2.permit is already called from the TychoRouter permit2.transferFrom( - sender, receiver, uint160(amount), address(tokenIn) + sender, address(this), uint160(amount), tokenIn ); } } diff --git a/foundry/src/executors/UniswapV2Executor.sol b/foundry/src/executors/UniswapV2Executor.sol index 2ddcd97..863216d 100644 --- a/foundry/src/executors/UniswapV2Executor.sol +++ b/foundry/src/executors/UniswapV2Executor.sol @@ -50,7 +50,9 @@ contract UniswapV2Executor is IExecutor, TokenTransfer { _verifyPairAddress(target); calculatedAmount = _getAmountOut(target, givenAmount, zeroForOne); - _transfer(tokenIn, msg.sender, target, givenAmount, transferType); + _transfer( + address(tokenIn), msg.sender, target, givenAmount, transferType + ); IUniswapV2Pair pool = IUniswapV2Pair(target); if (zeroForOne) { diff --git a/foundry/src/executors/UniswapV3Executor.sol b/foundry/src/executors/UniswapV3Executor.sol index dbc794e..4609bc1 100644 --- a/foundry/src/executors/UniswapV3Executor.sol +++ b/foundry/src/executors/UniswapV3Executor.sol @@ -111,7 +111,7 @@ contract UniswapV3Executor is IExecutor, ICallback, TokenTransfer { uint256 amountOwed = amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta); - _transfer(IERC20(tokenIn), sender, msg.sender, amountOwed, transferType); + _transfer(tokenIn, sender, msg.sender, amountOwed, transferType); return abi.encode(amountOwed, tokenIn); } diff --git a/foundry/src/executors/UniswapV4Executor.sol b/foundry/src/executors/UniswapV4Executor.sol index 694937f..aafbd7d 100644 --- a/foundry/src/executors/UniswapV4Executor.sol +++ b/foundry/src/executors/UniswapV4Executor.sol @@ -17,10 +17,11 @@ import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol"; import {IV4Router} from "@uniswap/v4-periphery/src/interfaces/IV4Router.sol"; import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol"; import {ICallback} from "@interfaces/ICallback.sol"; +import {TokenTransfer} from "./TokenTransfer.sol"; error UniswapV4Executor__InvalidDataLength(); -contract UniswapV4Executor is IExecutor, V4Router, ICallback { +contract UniswapV4Executor is IExecutor, V4Router, ICallback, TokenTransfer { using SafeERC20 for IERC20; using CurrencyLibrary for Currency; @@ -30,7 +31,10 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback { int24 tickSpacing; } - constructor(IPoolManager _poolManager) V4Router(_poolManager) {} + constructor(IPoolManager _poolManager, address _permit2) + V4Router(_poolManager) + TokenTransfer(_permit2) + {} function swap(uint256 amountIn, bytes calldata data) external @@ -41,9 +45,21 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback { address tokenIn, address tokenOut, bool zeroForOne, + TransferType transferType, UniswapV4Executor.UniswapV4Pool[] memory pools ) = _decodeData(data); + // TODO move this into callback when we implement callback transfer type support + _transfer( + tokenIn, + msg.sender, + // Receiver can never be the pool, since the pool expects funds in the router contract + // Thus, this call will only ever be used to transfer funds from the user into the router. + address(this), + amountIn, + transferType + ); + bytes memory swapData; if (pools.length == 1) { PoolKey memory key = PoolKey({ @@ -138,6 +154,7 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback { address tokenIn, address tokenOut, bool zeroForOne, + TransferType transferType, UniswapV4Pool[] memory pools ) { @@ -148,10 +165,11 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback { tokenIn = address(bytes20(data[0:20])); tokenOut = address(bytes20(data[20:40])); zeroForOne = (data[40] != 0); + transferType = TransferType(uint8(data[41])); - uint256 poolsLength = (data.length - 41) / 26; // 26 bytes per pool object + uint256 poolsLength = (data.length - 42) / 26; // 26 bytes per pool object pools = new UniswapV4Pool[](poolsLength); - bytes memory poolsData = data[41:]; + bytes memory poolsData = data[42:]; uint256 offset = 0; for (uint256 i = 0; i < poolsLength; i++) { address intermediaryToken; diff --git a/foundry/test/TychoRouterIntegration.t.sol b/foundry/test/TychoRouterIntegration.t.sol index 29f2cb9..0f25861 100644 --- a/foundry/test/TychoRouterIntegration.t.sol +++ b/foundry/test/TychoRouterIntegration.t.sol @@ -12,7 +12,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { uint256 balancerBefore = IERC20(DAI_ADDR).balanceOf(ALICE); // Encoded solution generated using `test_split_swap_strategy_encoder_no_permit2` (bool success,) = tychoRouterAddr.call( - hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae37400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000059005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" + hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae37400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000059005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000100000000000000" ); vm.stopPrank(); @@ -37,7 +37,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_encoding_strategy_usv4` (bool success,) = tychoRouterAddr.call( - hex"7c553846000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000681f1d6200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f7976a0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416af8ab519cbe8f466aed6188c8966ca4910d72d40d2f4360f62dbe75864b77ce5ec168870bb1540673b3f720c4f2bfaa5de2a79813c857ad5cc49b20217c65041c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007800760001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000" + hex"7c553846000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000006824c2ae00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067fd3cb6000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041850e1add80e90f19d7b1ac70721b5dec8a6bdcb57999ea957f831ea57c4ce1a116bec18d2cae2559421ead48186b6985176fee7f8d2e3452c1518dc2635224b41c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007900770001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d231193300040000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f400000000000000" ); vm.stopPrank(); @@ -60,7 +60,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { // Encoded solution generated using `test_split_encoding_strategy_usv4_eth_in` (bool success,) = tychoRouterAddr.call{value: 1 ether}( - hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000c87c939ae635f92dc2379c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681f1d7100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f79779000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c9ad125cc7c7188d1f0cd61dd8ee0d752d45c89ade1df275c5e0ce17525c6ff137bd976ebd0d17223c007e1f0a2806d086b4c7015460ebd21fef8a6caf8368d51c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e005c0001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933016982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000" + hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000c87c939ae635f92dc2379c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006824c2cb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067fd3cd3000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041a97e156e8c7a48a968efe2f057700ce8458ecfbfb53e0319fc9223b2364aba20227d2ee00226b42e2f28aeb002a242576d75152b270d73a8926f595190828f3d1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f005d0001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d231193301056982508145454ce325ddbe47a25d4ec3d23119330061a80001f400" ); vm.stopPrank(); @@ -87,7 +87,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { // Encoded solution generated using `test_split_encoding_strategy_usv4_eth_out` (bool success,) = tychoRouterAddr.call( - hex"7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e0000000000000000000000000000000000000000000000000000000000681f1d7e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f797860000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000412beedebe0200a3d4ca03f34697af8ec027fe6c164e3d3b78e17d1c327dcfc8241ce8bda513f092e54b35856637e5e485a06cc8c1c6287f15f469d47e84fd91341b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005e005c0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000bb800003c0000" + hex"7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000006824c2da00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067fd3ce20000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416b224d97fe1a35f2698380969c22854cf7ed92881ea484df7adb438f8d8e78e66c8617131f6712c5f3b4fa07725725081b2ebe27cd44acd11332a4a72bded96c1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f005d0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000040000000000000000000000000000000000000000000bb800003c00" ); vm.stopPrank(); @@ -109,7 +109,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { vm.startPrank(ALICE); // Encoded solution generated using `test_split_swap_strategy_encoder_wrap` (bool success,) = tychoRouterAddr.call{value: 1 ether}( - hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681e435000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6bd580000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415563c90eb0f8a79e234e300520b50879242b42622cabd5d01c19e67aba4854a723e0bd8333774062ae03a22b8c5de4a0dfd70bffd9197f56b2063f390610e5891b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059005700020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" + hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006821640400000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de0c000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041b34e1f3d4e78942b2429b776073a5dfab1420f763de7d7e2a2296ca8abf684f923f7ae7945e824d8a084b9610d33ed49246a36e8e0efbce8ae210b0474f9fe3a1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059005700020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" ); vm.stopPrank(); @@ -131,7 +131,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { IERC20(DAI_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_swap_strategy_encoder_unwrap` (bool success,) = tychoRouterAddr.call( - hex"7c5538460000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be0000000000000000000000000000000000000000000000000000000000000681e436b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6bd730000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000411150ac5d21c61fcd35b486034a433e44d045950a52e991a7ad4035f3ad52beee5d9ecbc5ed9c370730cd2631a050fbe1219dd606e39c8aca40d588aa3eab03411c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000" + hex"7c5538460000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be00000000000000000000000000000000000000000000000000000000000006821641200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de1a00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004181d23336d0cacd47a4d228590a825a1d92a48378cd481ff308b6d235e14b925c584f31e420682879bea58363ca4aa44a3b79557b15a9f73078a4696e00f55f911b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000059005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395010200000000000000" ); vm.stopPrank(); @@ -183,7 +183,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_swap_strategy_encoder_complex` (bool success,) = tychoRouterAddr.call( - hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681e2d8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6a793000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041e9868ce97a4c09488710ce748362ca38418bda44148bb5faf7820445d47efaff66f8f3667a0b3091c9d67d240db4cc2c95db27bdf019e4d5ff76b7b6620514691b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950000005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d013950100005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000000000000000000000000000000000000000000000" + hex"7c5538460000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006821643700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de3f00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004112f41a590796702b322fa5a9ee1602daef9b22d732e4fd8f122f072b65dda325271f630759db500db8a42bd4f41ddc18ddda63650deaf36228dca702e28eefd31b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000164005700028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950002005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d013950002005702030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d013950100005701030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000000000000000000000000000000000000000000000" ); vm.stopPrank(); @@ -210,7 +210,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_sequential_swap_strategy_encoder_complex_route` (bool success,) = tychoRouterAddr.call( - hex"51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000681e493d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6c34500000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000004189757b528c9874627ca7ecd64bd662db5fd7855837ec98cca5cff2cbd599f9af15baff1d389b5de4ca0dc1106b9d3795a1695934cd5fa1158fffcc8d6495dfaa1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d01395000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000000000000000000000" + hex"51bcc7b60000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006821644a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de5200000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000041530bdcde0c687eacb51a339f30c7b3eff7c078a3bbd4bc852519568dcdf271bb4c6ac05583f32c4a8d1a99be3a2817fe86c15ad2a06c5cf938bde9c22bc80f301c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d01395000200525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000000000000000000000" ); vm.stopPrank(); @@ -234,7 +234,7 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { IERC20(WETH_ADDR).approve(tychoRouterAddr, type(uint256).max); // Encoded solution generated using `test_sequential_swap_strategy_encoder_no_permit2` (bool success,) = tychoRouterAddr.call( - hex"e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d01395000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000000000000000000000" + hex"e8a980d70000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a800525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d01395000100525615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000000000000000000000" ); vm.stopPrank(); @@ -254,9 +254,10 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_cyclic_sequential_swap` (bool success,) = tychoRouterAddr.call( - hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681e4a0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6c40a0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416a11f27f0546e9cc9d27e4383a3f860d9822f0d7d0117e73abbf03007b3e235b36c2288ff083a04f51285092ff23422b3e00a5a292d5627172dfd031308fc3a11b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000" + hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f4308e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006821647000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9de780000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000413c8b048dc7b7614106a5aa1fa13e48c02a6a9714dfa07d2c424f68b81a5f828c39ace62f2dd57d7bfad10910ae44f77d68aec5c079fce456028b1bd7f72053151c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400102006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80000" ); + assertTrue(success, "Call Failed"); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99889294); vm.stopPrank(); @@ -270,9 +271,10 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_input_cyclic_swap` (bool success,) = tychoRouterAddr.call( - hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681e2d1600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6a71e00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004129478d2bffac3e378054d024ca3461f61e55552e2e8b0c7e0869f38ee834462f157f761e1a67c713f5749a6f6210dc4ac998a0521dda8dcb780b35d774250a141c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400100006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80100005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" + hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005ef619b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000000000000000000006821659d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9dfa5000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041dd84c5cdc51719e377598eccd8eac0aae036e7e0745a7c65b5d44cc817071a7460ccc73934363f33cc7af71dc07545aeff1d92f8c2f0b2973e1fc37e7b2de3551c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139006e00019999992e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400102006e00010000002e234dae75c793f67a35089c9d99245e1c58470ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb48c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d80102005701000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" ); + assertTrue(success, "Call Failed"); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99574171); vm.stopPrank(); @@ -286,9 +288,10 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_output_cyclic_swap` (bool success,) = tychoRouterAddr.call( - hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000681e2d3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f6a738000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041196745237299bfab79f038c0c12decec9c46b264621e1f00bbef32b15a22f15543defe95694dd1616aa8785bbb6f91a1557eebc97c0f5ca968c99f250da1b2ce1c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950100006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400000006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000" + hex"7c5538460000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005eea514000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000682165ac00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9dfb400000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004107f2b0f9c2e4e308ab43b288d69de30d84b10c8075e4dd9a2cf66594f97a52fb34de2534b89bf1887da74c92fd03464f45baff700dd32e213e3add1a3f351e891b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000139005700010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48b4e16d0168e52d35cacd2c6185b44281ec28c9dc3ede3eca2a72b3aecc820e955b36f38437d013950102006e01009999992e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f43ede3eca2a72b3aecc820e955b36f38437d0139588e6a0c2ddd26feeb64f039a2c41296fcb3f56400000006e01000000002e234dae75c793f67a35089c9d99245e1c58470bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000bb83ede3eca2a72b3aecc820e955b36f38437d013958ad599c3a0ff1de082011efddc58f1908eb6e6d8000000000000000000" ); + assertTrue(success, "Call Failed"); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99525908); vm.stopPrank(); @@ -301,9 +304,10 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { IERC20(UWU_ADDR).approve(tychoRouterAddr, type(uint256).max); // Encoded solution generated using `test_split_encoding_strategy_curve` (bool success,) = tychoRouterAddr.call( - hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005b005900010000001d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e71020100010000000000" + hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000055c08ca52497e2f1534b59e2917bf524d4765257000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005c005a00010000001d1499e622d69689cdf9004d05ec547d650ff21155c08ca52497e2f1534b59e2917bf524d4765257c02aaa39b223fe8d0a0e5c4f27ead9083c756cc277146b0a1d08b6844376df6d9da99ba7f1b19e71020100010300000000" ); + assertTrue(success, "Call Failed"); assertEq(IERC20(WETH_ADDR).balanceOf(ALICE), 4691958787921); vm.stopPrank(); @@ -315,9 +319,10 @@ contract TychoRouterTestIntegration is TychoRouterTestSetup { vm.startPrank(ALICE); // Encoded solution generated using `test_split_encoding_strategy_curve_st_eth` (bool success,) = tychoRouterAddr.call{value: 1 ether}( - hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe840000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005b005900010000001d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f67022010001000000000000" + hex"79b9b93b0000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ae7ab96520de3a18e5e111b5eaab095312d7fe840000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000005c005a00010000001d1499e622d69689cdf9004d05ec547d650ff211eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeae7ab96520de3a18e5e111b5eaab095312d7fe84dc24316b9ae028f1497c275eb9192a3ea0f67022010001000500000000" ); + assertTrue(success, "Call Failed"); assertEq(IERC20(STETH_ADDR).balanceOf(ALICE), 1000754689941529590); vm.stopPrank(); diff --git a/foundry/test/TychoRouterSequentialSwap.t.sol b/foundry/test/TychoRouterSequentialSwap.t.sol index 46a9936..9efd39b 100644 --- a/foundry/test/TychoRouterSequentialSwap.t.sol +++ b/foundry/test/TychoRouterSequentialSwap.t.sol @@ -8,23 +8,38 @@ import "./executors/UniswapV4Utils.sol"; import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol"; contract TychoRouterSequentialSwapTest is TychoRouterTestSetup { - function _getSequentialSwaps() internal view returns (bytes[] memory) { + function _getSequentialSwaps(bool permit2) + internal + view + returns (bytes[] memory) + { // Trade 1 WETH for USDC through DAI with 2 swaps on Uniswap V2 // 1 WETH -> DAI -> USDC // (univ2) (univ2) + + TokenTransfer.TransferType transferType = permit2 + ? TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL + : TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL; + bytes[] memory swaps = new bytes[](2); // WETH -> DAI swaps[0] = encodeSequentialSwap( address(usv2Executor), encodeUniswapV2Swap( - WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false, transferType ) ); // DAI -> USDC swaps[1] = encodeSequentialSwap( address(usv2Executor), - encodeUniswapV2Swap(DAI_ADDR, DAI_USDC_POOL, tychoRouterAddr, true) + encodeUniswapV2Swap( + DAI_ADDR, + DAI_USDC_POOL, + tychoRouterAddr, + true, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL + ) ); return swaps; } @@ -32,10 +47,13 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup { function testSequentialSwapInternalMethod() public { // Trade 1 WETH for USDC through DAI - see _getSequentialSwaps for more info uint256 amountIn = 1 ether; - deal(WETH_ADDR, tychoRouterAddr, amountIn); + deal(WETH_ADDR, ALICE, amountIn); + vm.startPrank(ALICE); + IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn); - bytes[] memory swaps = _getSequentialSwaps(); + bytes[] memory swaps = _getSequentialSwaps(false); tychoRouter.exposedSequentialSwap(amountIn, pleEncode(swaps)); + vm.stopPrank(); uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr); assertEq(usdcBalance, 2644659787); @@ -53,7 +71,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup { bytes memory signature ) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn); - bytes[] memory swaps = _getSequentialSwaps(); + bytes[] memory swaps = _getSequentialSwaps(true); tychoRouter.sequentialSwapPermit2( amountIn, WETH_ADDR, @@ -80,7 +98,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn); - bytes[] memory swaps = _getSequentialSwaps(); + bytes[] memory swaps = _getSequentialSwaps(false); tychoRouter.sequentialSwap( amountIn, WETH_ADDR, @@ -105,7 +123,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn); - bytes[] memory swaps = _getSequentialSwaps(); + bytes[] memory swaps = _getSequentialSwaps(false); vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector); tychoRouter.sequentialSwap( amountIn, @@ -127,7 +145,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(WETH_ADDR).approve(tychoRouterAddr, amountIn - 1); - bytes[] memory swaps = _getSequentialSwaps(); + bytes[] memory swaps = _getSequentialSwaps(false); vm.expectRevert(); tychoRouter.sequentialSwap( amountIn, @@ -152,7 +170,7 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup { bytes memory signature ) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn); - bytes[] memory swaps = _getSequentialSwaps(); + bytes[] memory swaps = _getSequentialSwaps(true); uint256 minAmountOut = 3000 * 1e18; @@ -195,7 +213,30 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup { sigDeadline: 0 }); - bytes[] memory swaps = _getSequentialSwaps(); + bytes[] memory swaps = new bytes[](2); + // WETH -> DAI + swaps[0] = encodeSequentialSwap( + address(usv2Executor), + encodeUniswapV2Swap( + WETH_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL + ) + ); + + // DAI -> USDC + swaps[1] = encodeSequentialSwap( + address(usv2Executor), + encodeUniswapV2Swap( + DAI_ADDR, + DAI_USDC_POOL, + tychoRouterAddr, + true, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL + ) + ); uint256 amountOut = tychoRouter.sequentialSwapPermit2{value: amountIn}( amountIn, @@ -237,14 +278,24 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup { swaps[0] = encodeSequentialSwap( address(usv2Executor), encodeUniswapV2Swap( - USDC_ADDR, DAI_USDC_POOL, tychoRouterAddr, false + USDC_ADDR, + DAI_USDC_POOL, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL ) ); // DAI -> WETH swaps[1] = encodeSequentialSwap( address(usv2Executor), - encodeUniswapV2Swap(DAI_ADDR, WETH_DAI_POOL, tychoRouterAddr, true) + encodeUniswapV2Swap( + DAI_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + true, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL + ) ); uint256 amountOut = tychoRouter.sequentialSwapPermit2( @@ -275,11 +326,21 @@ contract TychoRouterSequentialSwapTest is TychoRouterTestSetup { deal(USDC_ADDR, tychoRouterAddr, amountIn); bytes memory usdcWethV3Pool1ZeroOneData = encodeUniswapV3Swap( - USDC_ADDR, WETH_ADDR, tychoRouterAddr, USDC_WETH_USV3, true + USDC_ADDR, + WETH_ADDR, + tychoRouterAddr, + USDC_WETH_USV3, + true, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap( - WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3_2, false + WETH_ADDR, + USDC_ADDR, + tychoRouterAddr, + USDC_WETH_USV3_2, + false, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); bytes[] memory swaps = new bytes[](2); diff --git a/foundry/test/TychoRouterSingleSwap.t.sol b/foundry/test/TychoRouterSingleSwap.t.sol index 246a95c..8a8a4d3 100644 --- a/foundry/test/TychoRouterSingleSwap.t.sol +++ b/foundry/test/TychoRouterSingleSwap.t.sol @@ -22,7 +22,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup { ) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn); bytes memory protocolData = encodeUniswapV2Swap( - WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + WETH_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL ); bytes memory swap = @@ -59,7 +63,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup { IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); bytes memory protocolData = encodeUniswapV2Swap( - WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + WETH_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL ); bytes memory swap = @@ -96,7 +104,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup { IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); bytes memory protocolData = encodeUniswapV2Swap( - WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + WETH_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL ); bytes memory swap = @@ -118,7 +130,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup { IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn - 1); bytes memory protocolData = encodeUniswapV2Swap( - WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + WETH_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL ); bytes memory swap = @@ -149,7 +165,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup { IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); bytes memory protocolData = encodeUniswapV2Swap( - WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + WETH_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL ); bytes memory swap = @@ -194,7 +214,11 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup { }); bytes memory protocolData = encodeUniswapV2Swap( - WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + WETH_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); bytes memory swap = @@ -232,8 +256,13 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup { bytes memory signature ) = handlePermit2Approval(DAI_ADDR, tychoRouterAddr, amountIn); - bytes memory protocolData = - encodeUniswapV2Swap(DAI_ADDR, WETH_DAI_POOL, tychoRouterAddr, true); + bytes memory protocolData = encodeUniswapV2Swap( + DAI_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + true, + TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL + ); bytes memory swap = encodeSingleSwap(address(usv2Executor), protocolData); @@ -267,7 +296,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup { IERC20(WETH_ADDR).approve(tychoRouterAddr, type(uint256).max); // Encoded solution generated using `test_single_swap_strategy_encoder_no_permit2` (bool success,) = tychoRouterAddr.call( - hex"20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae3740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000" + hex"20144a070000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000008f1d5c1cae3740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500010000000000000000000000000000" ); vm.stopPrank(); @@ -286,7 +315,7 @@ contract TychoRouterSingleSwapTest is TychoRouterTestSetup { IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_single_swap_strategy_encoder` (bool success,) = tychoRouterAddr.call( - hex"30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006817833200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067effd3a00000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000417efea09004d5d40d8d072e1ce0a425507717ea485c765eb90c170859197d362b502fb039b4f5cdce57318ecfe3ab276d1ac87771eb5d017b253a8f4107e6a20b1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000" + hex"30ace1b10000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000682169c100000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9e3c900000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000412d3f0fee3fc61987512f024f20b1448eb934f82105a91653dd169179c693aaf95d09ef666ce1d38be70b8156fa6e4ea3e8717204e02fe7ba99a1fc4e5a26e6e11b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000525615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500020000000000000000000000000000" ); vm.stopPrank(); diff --git a/foundry/test/TychoRouterSplitSwap.t.sol b/foundry/test/TychoRouterSplitSwap.t.sol index a9c593a..00f8406 100644 --- a/foundry/test/TychoRouterSplitSwap.t.sol +++ b/foundry/test/TychoRouterSplitSwap.t.sol @@ -8,13 +8,22 @@ import "./executors/UniswapV4Utils.sol"; import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol"; contract TychoRouterSplitSwapTest is TychoRouterTestSetup { - function _getSplitSwaps() private view returns (bytes[] memory) { + function _getSplitSwaps(bool permit2) + private + view + returns (bytes[] memory) + { // Trade 1 WETH for USDC through DAI and WBTC with 4 swaps on Uniswap V2 // -> DAI -> // 1 WETH USDC // -> WBTC -> // (univ2) (univ2) bytes[] memory swaps = new bytes[](4); + + TokenTransfer.TransferType inTransferType = permit2 + ? TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL + : TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL; + // WETH -> WBTC (60%) swaps[0] = encodeSplitSwap( uint8(0), @@ -22,7 +31,11 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { (0xffffff * 60) / 100, // 60% address(usv2Executor), encodeUniswapV2Swap( - WETH_ADDR, WETH_WBTC_POOL, tychoRouterAddr, false + WETH_ADDR, + WETH_WBTC_POOL, + tychoRouterAddr, + false, + inTransferType ) ); // WBTC -> USDC @@ -32,7 +45,11 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { uint24(0), address(usv2Executor), encodeUniswapV2Swap( - WBTC_ADDR, USDC_WBTC_POOL, tychoRouterAddr, true + WBTC_ADDR, + USDC_WBTC_POOL, + tychoRouterAddr, + true, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ) ); // WETH -> DAI @@ -42,7 +59,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { uint24(0), address(usv2Executor), encodeUniswapV2Swap( - WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false, inTransferType ) ); @@ -52,7 +69,13 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { uint8(2), uint24(0), address(usv2Executor), - encodeUniswapV2Swap(DAI_ADDR, DAI_USDC_POOL, tychoRouterAddr, true) + encodeUniswapV2Swap( + DAI_ADDR, + DAI_USDC_POOL, + tychoRouterAddr, + true, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL + ) ); return swaps; @@ -62,9 +85,12 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { // Trade 1 WETH for USDC through DAI and WBTC - see _getSplitSwaps for more info uint256 amountIn = 1 ether; - deal(WETH_ADDR, tychoRouterAddr, amountIn); - bytes[] memory swaps = _getSplitSwaps(); + deal(WETH_ADDR, ALICE, amountIn); + vm.startPrank(ALICE); + IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); + bytes[] memory swaps = _getSplitSwaps(false); tychoRouter.exposedSplitSwap(amountIn, 4, pleEncode(swaps)); + vm.stopPrank(); uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr); assertEq(usdcBalance, 2615491639); @@ -83,7 +109,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { bytes memory signature ) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn); - bytes[] memory swaps = _getSplitSwaps(); + bytes[] memory swaps = _getSplitSwaps(true); tychoRouter.splitSwapPermit2( amountIn, @@ -112,7 +138,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); - bytes[] memory swaps = _getSplitSwaps(); + bytes[] memory swaps = _getSplitSwaps(false); tychoRouter.splitSwap( amountIn, @@ -139,7 +165,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { vm.startPrank(ALICE); IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); - bytes[] memory swaps = _getSplitSwaps(); + bytes[] memory swaps = _getSplitSwaps(false); vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector); tychoRouter.splitSwap( @@ -164,7 +190,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { vm.startPrank(ALICE); // Approve less than the amountIn IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn - 1); - bytes[] memory swaps = _getSplitSwaps(); + bytes[] memory swaps = _getSplitSwaps(false); vm.expectRevert(); tychoRouter.splitSwap( @@ -193,7 +219,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { bytes memory signature ) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn); - bytes[] memory swaps = _getSplitSwaps(); + bytes[] memory swaps = _getSplitSwaps(true); uint256 minAmountOut = 3000 * 1e18; @@ -240,7 +266,11 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { sigDeadline: 0 }); bytes memory protocolData = encodeUniswapV2Swap( - WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + WETH_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); bytes memory swap = encodeSplitSwap( @@ -284,8 +314,13 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { bytes memory signature ) = handlePermit2Approval(DAI_ADDR, tychoRouterAddr, amountIn); - bytes memory protocolData = - encodeUniswapV2Swap(DAI_ADDR, WETH_DAI_POOL, tychoRouterAddr, true); + bytes memory protocolData = encodeUniswapV2Swap( + DAI_ADDR, + WETH_DAI_POOL, + tychoRouterAddr, + true, + TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL + ); bytes memory swap = encodeSplitSwap( uint8(0), uint8(1), uint24(0), address(usv2Executor), protocolData @@ -330,7 +365,12 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { uint256 expAmountOut = 1205_128428842122129186; //Swap 1 WETH for 1205.12 DAI bool zeroForOne = false; bytes memory protocolData = encodeUniswapV3Swap( - WETH_ADDR, DAI_ADDR, tychoRouterAddr, DAI_WETH_USV3, zeroForOne + WETH_ADDR, + DAI_ADDR, + tychoRouterAddr, + DAI_WETH_USV3, + zeroForOne, + TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL ); bytes memory swap = encodeSplitSwap( uint8(0), uint8(1), uint24(0), address(usv3Executor), protocolData @@ -366,59 +406,6 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { tychoRouter.exposedSplitSwap(amountIn, 2, swaps); } - function testSplitSwapAmountInNotFullySpent() public { - // Trade 1 WETH for DAI with 1 swap on Uniswap V2 - // Has invalid data as input! There is only one swap with 60% of the input amount - uint256 amountIn = 1 ether; - deal(WETH_ADDR, ALICE, amountIn); - - vm.startPrank(ALICE); - - ( - IAllowanceTransfer.PermitSingle memory permitSingle, - bytes memory signature - ) = handlePermit2Approval(WETH_ADDR, tychoRouterAddr, amountIn); - - bytes memory protocolData = encodeUniswapV2Swap( - WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false - ); - - bytes memory swap = encodeSplitSwap( - uint8(0), - uint8(1), - (0xffffff * 60) / 100, // 60% - address(usv2Executor), - protocolData - ); - - bytes[] memory swaps = new bytes[](1); - swaps[0] = swap; - - vm.expectRevert( - abi.encodeWithSelector( - TychoRouter__AmountInDiffersFromConsumed.selector, - 1000000000000000000, - 600000000000000000 - ) - ); - - tychoRouter.splitSwapPermit2( - amountIn, - WETH_ADDR, - DAI_ADDR, - 1, - false, - false, - 2, - ALICE, - permitSingle, - signature, - pleEncode(swaps) - ); - - vm.stopPrank(); - } - function testSplitSwapSingleUSV4CallbackPermit2() public { vm.startPrank(ALICE); uint256 amountIn = 100 ether; @@ -436,8 +423,13 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { tickSpacing: int24(1) }); - bytes memory protocolData = - UniswapV4Utils.encodeExactInput(USDE_ADDR, USDT_ADDR, true, pools); + bytes memory protocolData = UniswapV4Utils.encodeExactInput( + USDE_ADDR, + USDT_ADDR, + true, + TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_ROUTER, + pools + ); bytes memory swap = encodeSplitSwap( uint8(0), uint8(1), uint24(0), address(usv4Executor), protocolData @@ -483,8 +475,9 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { tickSpacing: int24(60) }); - bytes memory protocolData = - UniswapV4Utils.encodeExactInput(USDE_ADDR, WBTC_ADDR, true, pools); + bytes memory protocolData = UniswapV4Utils.encodeExactInput( + USDE_ADDR, WBTC_ADDR, true, TokenTransfer.TransferType.NONE, pools + ); bytes memory swap = encodeSplitSwap( uint8(0), uint8(1), uint24(0), address(usv4Executor), protocolData @@ -507,18 +500,35 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { // │ │ // └─ (USV3, 40% split) ──> WETH ─┘ uint256 amountIn = 100 * 10 ** 6; - deal(USDC_ADDR, tychoRouterAddr, amountIn); + deal(USDC_ADDR, ALICE, amountIn); + vm.startPrank(ALICE); + // Approve the TychoRouter to spend USDC + IERC20(USDC_ADDR).approve(tychoRouterAddr, amountIn); bytes memory usdcWethV3Pool1ZeroOneData = encodeUniswapV3Swap( - USDC_ADDR, WETH_ADDR, tychoRouterAddr, USDC_WETH_USV3, true + USDC_ADDR, + WETH_ADDR, + tychoRouterAddr, + USDC_WETH_USV3, + true, + TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL ); bytes memory usdcWethV3Pool2ZeroOneData = encodeUniswapV3Swap( - USDC_ADDR, WETH_ADDR, tychoRouterAddr, USDC_WETH_USV3_2, true + USDC_ADDR, + WETH_ADDR, + tychoRouterAddr, + USDC_WETH_USV3_2, + true, + TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL ); bytes memory wethUsdcV2OneZeroData = encodeUniswapV2Swap( - WETH_ADDR, USDC_WETH_USV2, tychoRouterAddr, false + WETH_ADDR, + USDC_WETH_USV2, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); bytes[] memory swaps = new bytes[](3); @@ -547,6 +557,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { wethUsdcV2OneZeroData ); tychoRouter.exposedSplitSwap(amountIn, 2, pleEncode(swaps)); + vm.stopPrank(); assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99574171); } @@ -563,15 +574,29 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { deal(USDC_ADDR, tychoRouterAddr, amountIn); bytes memory usdcWethV2Data = encodeUniswapV2Swap( - USDC_ADDR, USDC_WETH_USV2, tychoRouterAddr, true + USDC_ADDR, + USDC_WETH_USV2, + tychoRouterAddr, + true, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); bytes memory usdcWethV3Pool1OneZeroData = encodeUniswapV3Swap( - WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3, false + WETH_ADDR, + USDC_ADDR, + tychoRouterAddr, + USDC_WETH_USV3, + false, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap( - WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3_2, false + WETH_ADDR, + USDC_ADDR, + tychoRouterAddr, + USDC_WETH_USV3_2, + false, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); bytes[] memory swaps = new bytes[](3); @@ -610,7 +635,11 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { deal(BASE_USDC, tychoRouterAddr, amountIn); bytes memory protocolData = encodeUniswapV2Swap( - BASE_USDC, USDC_MAG7_POOL, tychoRouterAddr, true + BASE_USDC, + USDC_MAG7_POOL, + tychoRouterAddr, + true, + TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL ); bytes memory swap = encodeSplitSwap( diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index 961f0f8..83b665f 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -102,13 +102,13 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper { new UniswapV2Executor(factoryV2, initCodeV2, PERMIT2_ADDRESS); usv3Executor = new UniswapV3Executor(factoryV3, initCodeV3, PERMIT2_ADDRESS); - usv4Executor = new UniswapV4Executor(poolManager); + usv4Executor = new UniswapV4Executor(poolManager, PERMIT2_ADDRESS); pancakev3Executor = new UniswapV3Executor( factoryPancakeV3, initCodePancakeV3, PERMIT2_ADDRESS ); - balancerv2Executor = new BalancerV2Executor(); + balancerv2Executor = new BalancerV2Executor(PERMIT2_ADDRESS); ekuboExecutor = new EkuboExecutor(ekuboCore); - curveExecutor = new CurveExecutor(ETH_ADDR_FOR_CURVE); + curveExecutor = new CurveExecutor(ETH_ADDR_FOR_CURVE, PERMIT2_ADDRESS); address[] memory executors = new address[](7); executors[0] = address(usv2Executor); @@ -178,15 +178,11 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper { address tokenIn, address target, address receiver, - bool zero2one + bool zero2one, + TokenTransfer.TransferType transferType ) internal pure returns (bytes memory) { - return abi.encodePacked( - tokenIn, - target, - receiver, - zero2one, - TokenTransfer.TransferType.TRANSFER - ); + return + abi.encodePacked(tokenIn, target, receiver, zero2one, transferType); } function encodeUniswapV3Swap( @@ -194,7 +190,8 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper { address tokenOut, address receiver, address target, - bool zero2one + bool zero2one, + TokenTransfer.TransferType transferType ) internal view returns (bytes memory) { IUniswapV3Pool pool = IUniswapV3Pool(target); return abi.encodePacked( @@ -204,7 +201,7 @@ contract TychoRouterTestSetup is Constants, Permit2TestHelper { receiver, target, zero2one, - TokenTransfer.TransferType.TRANSFER + transferType ); } } diff --git a/foundry/test/executors/BalancerV2Executor.t.sol b/foundry/test/executors/BalancerV2Executor.t.sol index 9187187..78b435b 100644 --- a/foundry/test/executors/BalancerV2Executor.t.sol +++ b/foundry/test/executors/BalancerV2Executor.t.sol @@ -6,6 +6,8 @@ import {Test} from "../../lib/forge-std/src/Test.sol"; import {Constants} from "../Constants.sol"; contract BalancerV2ExecutorExposed is BalancerV2Executor { + constructor(address _permit2) BalancerV2Executor(_permit2) {} + function decodeParams(bytes calldata data) external pure @@ -14,18 +16,15 @@ contract BalancerV2ExecutorExposed is BalancerV2Executor { IERC20 tokenOut, bytes32 poolId, address receiver, - bool needsApproval + bool needsApproval, + TransferType transferType ) { return _decodeData(data); } } -contract BalancerV2ExecutorTest is - BalancerV2ExecutorExposed, - Test, - Constants -{ +contract BalancerV2ExecutorTest is Test, Constants { using SafeERC20 for IERC20; BalancerV2ExecutorExposed balancerV2Exposed; @@ -37,12 +36,17 @@ contract BalancerV2ExecutorTest is function setUp() public { uint256 forkBlock = 17323404; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - balancerV2Exposed = new BalancerV2ExecutorExposed(); + balancerV2Exposed = new BalancerV2ExecutorExposed(PERMIT2_ADDRESS); } function testDecodeParams() public view { bytes memory params = abi.encodePacked( - WETH_ADDR, BAL_ADDR, WETH_BAL_POOL_ID, address(2), true + WETH_ADDR, + BAL_ADDR, + WETH_BAL_POOL_ID, + address(2), + true, + TokenTransfer.TransferType.NONE ); ( @@ -50,7 +54,8 @@ contract BalancerV2ExecutorTest is IERC20 tokenOut, bytes32 poolId, address receiver, - bool needsApproval + bool needsApproval, + TokenTransfer.TransferType transferType ) = balancerV2Exposed.decodeParams(params); assertEq(address(tokenIn), WETH_ADDR); @@ -58,6 +63,7 @@ contract BalancerV2ExecutorTest is assertEq(poolId, WETH_BAL_POOL_ID); assertEq(receiver, address(2)); assertEq(needsApproval, true); + assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE)); } function testDecodeParamsInvalidDataLength() public { @@ -70,8 +76,14 @@ contract BalancerV2ExecutorTest is function testSwap() public { uint256 amountIn = 10 ** 18; - bytes memory protocolData = - abi.encodePacked(WETH_ADDR, BAL_ADDR, WETH_BAL_POOL_ID, BOB, true); + bytes memory protocolData = abi.encodePacked( + WETH_ADDR, + BAL_ADDR, + WETH_BAL_POOL_ID, + BOB, + true, + TokenTransfer.TransferType.NONE + ); deal(WETH_ADDR, address(balancerV2Exposed), amountIn); uint256 balanceBefore = BAL.balanceOf(BOB); @@ -86,14 +98,15 @@ contract BalancerV2ExecutorTest is function testDecodeIntegration() public view { // Generated by the SwapEncoder - test_encode_balancer_v2 bytes memory protocolData = - hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e01"; + hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0105"; ( IERC20 tokenIn, IERC20 tokenOut, bytes32 poolId, address receiver, - bool needsApproval + bool needsApproval, + TokenTransfer.TransferType transferType ) = balancerV2Exposed.decodeParams(protocolData); assertEq(address(tokenIn), WETH_ADDR); @@ -101,12 +114,13 @@ contract BalancerV2ExecutorTest is assertEq(poolId, WETH_BAL_POOL_ID); assertEq(receiver, BOB); assertEq(needsApproval, true); + assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE)); } function testSwapIntegration() public { // Generated by the SwapEncoder - test_encode_balancer_v2 bytes memory protocolData = - hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e01"; + hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e0105"; uint256 amountIn = 10 ** 18; deal(WETH_ADDR, address(balancerV2Exposed), amountIn); diff --git a/foundry/test/executors/CurveExecutor.t.sol b/foundry/test/executors/CurveExecutor.t.sol index cd15079..a49a2dc 100644 --- a/foundry/test/executors/CurveExecutor.t.sol +++ b/foundry/test/executors/CurveExecutor.t.sol @@ -22,7 +22,9 @@ interface MetaRegistry { } contract CurveExecutorExposed is CurveExecutor { - constructor(address _nativeToken) CurveExecutor(_nativeToken) {} + constructor(address _nativeToken, address _permit2) + CurveExecutor(_nativeToken, _permit2) + {} function decodeData(bytes calldata data) external @@ -34,7 +36,8 @@ contract CurveExecutorExposed is CurveExecutor { uint8 poolType, int128 i, int128 j, - bool tokenApprovalNeeded + bool tokenApprovalNeeded, + TokenTransfer.TransferType transferType ) { return _decodeData(data); @@ -50,7 +53,8 @@ contract CurveExecutorTest is Test, Constants { function setUp() public { uint256 forkBlock = 22031795; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - curveExecutorExposed = new CurveExecutorExposed(ETH_ADDR_FOR_CURVE); + curveExecutorExposed = + new CurveExecutorExposed(ETH_ADDR_FOR_CURVE, PERMIT2_ADDRESS); metaRegistry = MetaRegistry(CURVE_META_REGISTRY); } @@ -62,7 +66,8 @@ contract CurveExecutorTest is Test, Constants { uint8(3), uint8(2), uint8(0), - true + true, + TokenTransfer.TransferType.NONE ); ( @@ -72,7 +77,8 @@ contract CurveExecutorTest is Test, Constants { uint8 poolType, int128 i, int128 j, - bool tokenApprovalNeeded + bool tokenApprovalNeeded, + TokenTransfer.TransferType transferType ) = curveExecutorExposed.decodeData(data); assertEq(tokenIn, WETH_ADDR); @@ -82,6 +88,7 @@ contract CurveExecutorTest is Test, Constants { assertEq(i, 2); assertEq(j, 0); assertEq(tokenApprovalNeeded, true); + assertEq(uint8(transferType), uint8(TokenTransfer.TransferType.NONE)); } function testTriPool() public { @@ -311,7 +318,8 @@ contract CurveExecutorTest is Test, Constants { poolType, uint8(uint256(uint128(i))), uint8(uint256(uint128(j))), - true + true, + TokenTransfer.TransferType.NONE ); } diff --git a/foundry/test/executors/UniswapV2Executor.t.sol b/foundry/test/executors/UniswapV2Executor.t.sol index c993ff9..64be1d2 100644 --- a/foundry/test/executors/UniswapV2Executor.t.sol +++ b/foundry/test/executors/UniswapV2Executor.t.sol @@ -84,7 +84,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper { address(2), address(3), false, - TokenTransfer.TransferType.TRANSFER + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); ( @@ -100,7 +100,8 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper { assertEq(receiver, address(3)); assertEq(zeroForOne, false); assertEq( - uint8(TokenTransfer.TransferType.TRANSFER), uint8(transferType) + uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL), + uint8(transferType) ); } @@ -157,7 +158,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper { WETH_DAI_POOL, BOB, zeroForOne, - uint8(TokenTransfer.TransferType.TRANSFER) + uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL) ); deal(WETH_ADDR, address(uniswapV2Exposed), amountIn); @@ -176,7 +177,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper { WETH_DAI_POOL, BOB, zeroForOne, - uint8(TokenTransfer.TransferType.TRANSFERFROM) + uint8(TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL) ); deal(WETH_ADDR, address(this), amountIn); @@ -197,7 +198,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper { WETH_DAI_POOL, ALICE, zeroForOne, - uint8(TokenTransfer.TransferType.TRANSFERPERMIT2) + uint8(TokenTransfer.TransferType.TRANSFER_PERMIT2_TO_PROTOCOL) ); deal(WETH_ADDR, ALICE, amountIn); @@ -283,7 +284,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper { fakePool, BOB, zeroForOne, - uint8(TokenTransfer.TransferType.TRANSFER) + uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL) ); deal(WETH_ADDR, address(uniswapV2Exposed), amountIn); @@ -303,7 +304,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper { USDC_MAG7_POOL, BOB, zeroForOne, - uint8(TokenTransfer.TransferType.TRANSFER) + uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL) ); deal(BASE_USDC, address(uniswapV2Exposed), amountIn); diff --git a/foundry/test/executors/UniswapV3Executor.t.sol b/foundry/test/executors/UniswapV3Executor.t.sol index 24bfd22..cd77697 100644 --- a/foundry/test/executors/UniswapV3Executor.t.sol +++ b/foundry/test/executors/UniswapV3Executor.t.sol @@ -71,7 +71,7 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper { address(2), address(3), false, - TokenTransfer.TransferType.TRANSFER + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); ( @@ -91,7 +91,8 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper { assertEq(target, address(3)); assertEq(zeroForOne, false); assertEq( - uint8(transferType), uint8(TokenTransfer.TransferType.TRANSFER) + uint8(transferType), + uint8(TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL) ); } @@ -123,7 +124,11 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper { vm.startPrank(DAI_WETH_USV3); bytes memory protocolData = abi.encodePacked( - WETH_ADDR, DAI_ADDR, poolFee, TokenTransfer.TransferType.TRANSFER + WETH_ADDR, + DAI_ADDR, + poolFee, + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL, + address(uniswapV3Exposed) ); uint256 dataOffset = 3; // some offset uint256 dataLength = protocolData.length; @@ -134,8 +139,7 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper { int256(0), // amount1Delta dataOffset, dataLength, - protocolData, - address(uniswapV3Exposed) // transferFrom sender (irrelevant in this case) + protocolData ); uniswapV3Exposed.handleCallback(callbackData); vm.stopPrank(); @@ -157,7 +161,7 @@ contract UniswapV3ExecutorTest is Test, Constants, Permit2TestHelper { address(this), fakePool, zeroForOne, - TokenTransfer.TransferType.TRANSFER + TokenTransfer.TransferType.TRANSFER_TO_PROTOCOL ); vm.expectRevert(UniswapV3Executor__InvalidTarget.selector); diff --git a/foundry/test/executors/UniswapV4Executor.t.sol b/foundry/test/executors/UniswapV4Executor.t.sol index 487e251..62f61c9 100644 --- a/foundry/test/executors/UniswapV4Executor.t.sol +++ b/foundry/test/executors/UniswapV4Executor.t.sol @@ -7,9 +7,12 @@ import "@src/executors/UniswapV4Executor.sol"; import {Constants} from "../Constants.sol"; import {Test} from "../../lib/forge-std/src/Test.sol"; import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol"; +import "@src/executors/TokenTransfer.sol"; contract UniswapV4ExecutorExposed is UniswapV4Executor { - constructor(IPoolManager _poolManager) UniswapV4Executor(_poolManager) {} + constructor(IPoolManager _poolManager, address _permit2) + UniswapV4Executor(_poolManager, _permit2) + {} function decodeData(bytes calldata data) external @@ -18,6 +21,7 @@ contract UniswapV4ExecutorExposed is UniswapV4Executor { address tokenIn, address tokenOut, bool zeroForOne, + TokenTransfer.TransferType transferType, UniswapV4Pool[] memory pools ) { @@ -36,8 +40,9 @@ contract UniswapV4ExecutorTest is Test, Constants { function setUp() public { uint256 forkBlock = 21817316; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - uniswapV4Exposed = - new UniswapV4ExecutorExposed(IPoolManager(poolManager)); + uniswapV4Exposed = new UniswapV4ExecutorExposed( + IPoolManager(poolManager), PERMIT2_ADDRESS + ); } function testDecodeParams() public view { @@ -46,6 +51,8 @@ contract UniswapV4ExecutorTest is Test, Constants { int24 tickSpacing1 = 60; uint24 pool2Fee = 1000; int24 tickSpacing2 = -10; + TokenTransfer.TransferType transferType = + TokenTransfer.TransferType.TRANSFER_FROM_TO_PROTOCOL; UniswapV4Executor.UniswapV4Pool[] memory pools = new UniswapV4Executor.UniswapV4Pool[](2); @@ -61,19 +68,21 @@ contract UniswapV4ExecutorTest is Test, Constants { }); bytes memory data = UniswapV4Utils.encodeExactInput( - USDE_ADDR, USDT_ADDR, zeroForOne, pools + USDE_ADDR, USDT_ADDR, zeroForOne, transferType, pools ); ( address tokenIn, address tokenOut, bool zeroForOneDecoded, + TokenTransfer.TransferType transferTypeDecoded, UniswapV4Executor.UniswapV4Pool[] memory decodedPools ) = uniswapV4Exposed.decodeData(data); assertEq(tokenIn, USDE_ADDR); assertEq(tokenOut, USDT_ADDR); assertEq(zeroForOneDecoded, zeroForOne); + assertEq(uint8(transferTypeDecoded), uint8(transferType)); assertEq(decodedPools.length, 2); assertEq(decodedPools[0].intermediaryToken, USDT_ADDR); assertEq(decodedPools[0].fee, pool1Fee); @@ -98,8 +107,9 @@ contract UniswapV4ExecutorTest is Test, Constants { tickSpacing: int24(1) }); - bytes memory data = - UniswapV4Utils.encodeExactInput(USDE_ADDR, USDT_ADDR, true, pools); + bytes memory data = UniswapV4Utils.encodeExactInput( + USDE_ADDR, USDT_ADDR, true, TokenTransfer.TransferType.NONE, pools + ); uint256 amountOut = uniswapV4Exposed.swap(amountIn, data); assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn); @@ -114,7 +124,7 @@ contract UniswapV4ExecutorTest is Test, Constants { // USDE -> USDT // Generated by the Tycho swap encoder - test_encode_uniswap_v4_simple_swap bytes memory protocolData = - hex"4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec701dac17f958d2ee523a2206206994597c13d831ec7000064000001"; + hex"4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec70100dac17f958d2ee523a2206206994597c13d831ec7000064000001"; uint256 amountIn = 100 ether; deal(USDE_ADDR, address(uniswapV4Exposed), amountIn); uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager); @@ -151,8 +161,9 @@ contract UniswapV4ExecutorTest is Test, Constants { tickSpacing: int24(60) }); - bytes memory data = - UniswapV4Utils.encodeExactInput(USDE_ADDR, WBTC_ADDR, true, pools); + bytes memory data = UniswapV4Utils.encodeExactInput( + USDE_ADDR, WBTC_ADDR, true, TokenTransfer.TransferType.NONE, pools + ); uint256 amountOut = uniswapV4Exposed.swap(amountIn, data); assertEq(USDE.balanceOf(poolManager), usdeBalanceBeforePool + amountIn); @@ -170,7 +181,7 @@ contract UniswapV4ExecutorTest is Test, Constants { // Generated by the Tycho swap encoder - test_encode_uniswap_v4_sequential_swap bytes memory protocolData = - hex"4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c59901dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c"; + hex"4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c5990100dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c"; uint256 amountIn = 100 ether; deal(USDE_ADDR, address(uniswapV4Exposed), amountIn); diff --git a/foundry/test/executors/UniswapV4Utils.sol b/foundry/test/executors/UniswapV4Utils.sol index 67c7c7f..1ccc29c 100644 --- a/foundry/test/executors/UniswapV4Utils.sol +++ b/foundry/test/executors/UniswapV4Utils.sol @@ -8,6 +8,7 @@ library UniswapV4Utils { address tokenIn, address tokenOut, bool zeroForOne, + UniswapV4Executor.TransferType transferType, UniswapV4Executor.UniswapV4Pool[] memory pools ) public pure returns (bytes memory) { bytes memory encodedPools; @@ -21,6 +22,8 @@ library UniswapV4Utils { ); } - return abi.encodePacked(tokenIn, tokenOut, zeroForOne, encodedPools); + return abi.encodePacked( + tokenIn, tokenOut, zeroForOne, transferType, encodedPools + ); } } diff --git a/src/encoding/evm/constants.rs b/src/encoding/evm/constants.rs index feba1cc..4eadb35 100644 --- a/src/encoding/evm/constants.rs +++ b/src/encoding/evm/constants.rs @@ -17,3 +17,23 @@ pub static GROUPABLE_PROTOCOLS: LazyLock> = LazyLock::new( set.insert("ekubo_v2"); set }); + +/// These protocols support the optimization of transferring straight from the user. +pub static IN_TRANSFER_OPTIMIZABLE_PROTOCOLS: LazyLock> = + LazyLock::new(|| { + let mut set = HashSet::new(); + set.insert("uniswap_v2"); + set.insert("uniswap_v3"); + set + }); + +/// These protocols expect funds to be in the router at the time of swap. +pub static PROTOCOLS_EXPECTING_FUNDS_IN_ROUTER: LazyLock> = + LazyLock::new(|| { + let mut set = HashSet::new(); + set.insert("vm:curve"); + set.insert("balancer_v2"); + // TODO remove uniswap_v4 when we add callback support for transfer optimizations + set.insert("uniswap_v4"); + set + }); diff --git a/src/encoding/evm/strategy_encoder/mod.rs b/src/encoding/evm/strategy_encoder/mod.rs index 8f53524..260436b 100644 --- a/src/encoding/evm/strategy_encoder/mod.rs +++ b/src/encoding/evm/strategy_encoder/mod.rs @@ -1,2 +1,4 @@ pub mod strategy_encoders; mod strategy_validators; + +mod transfer_optimizations; diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 3151034..2536812 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -9,8 +9,9 @@ use crate::encoding::{ evm::{ approvals::permit2::Permit2, group_swaps::group_swaps, - strategy_encoder::strategy_validators::{ - SequentialSwapValidator, SplitSwapValidator, SwapValidator, + strategy_encoder::{ + strategy_validators::{SequentialSwapValidator, SplitSwapValidator, SwapValidator}, + transfer_optimizations::TransferOptimization, }, swap_encoder::swap_encoder_registry::SwapEncoderRegistry, utils::{ @@ -38,6 +39,8 @@ pub struct SingleSwapStrategyEncoder { swap_encoder_registry: SwapEncoderRegistry, permit2: Option, selector: String, + native_address: Bytes, + wrapped_address: Bytes, router_address: Bytes, } @@ -56,7 +59,14 @@ impl SingleSwapStrategyEncoder { "singleSwap(uint256,address,address,uint256,bool,bool,address,bytes)".to_string(), ) }; - Ok(Self { permit2, selector, swap_encoder_registry, router_address }) + Ok(Self { + permit2, + selector, + swap_encoder_registry, + native_address: chain.native_token()?, + wrapped_address: chain.wrapped_token()?, + router_address, + }) } /// Encodes information necessary for performing a single hop against a given executor for @@ -69,6 +79,8 @@ impl SingleSwapStrategyEncoder { } } +impl TransferOptimization for SingleSwapStrategyEncoder {} + impl StrategyEncoder for SingleSwapStrategyEncoder { fn encode_strategy(&self, solution: Solution) -> Result<(Vec, Bytes), EncodingError> { let grouped_swaps = group_swaps(solution.clone().swaps); @@ -111,12 +123,22 @@ impl StrategyEncoder for SingleSwapStrategyEncoder { let mut grouped_protocol_data: Vec = vec![]; for swap in grouped_swap.swaps.iter() { + let transfer_type = self.get_transfer_type( + swap.clone(), + solution.given_token.clone(), + self.native_address.clone(), + self.wrapped_address.clone(), + self.permit2.clone().is_some(), + wrap, + ); + let encoding_context = EncodingContext { receiver: self.router_address.clone(), exact_out: solution.exact_out, router_address: Some(self.router_address.clone()), group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), + transfer_type: transfer_type.clone(), }; let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context.clone())?; grouped_protocol_data.extend(protocol_data); @@ -199,6 +221,8 @@ pub struct SequentialSwapStrategyEncoder { sequential_swap_validator: SequentialSwapValidator, } +impl TransferOptimization for SequentialSwapStrategyEncoder {} + impl SequentialSwapStrategyEncoder { pub fn new( chain: Chain, @@ -274,12 +298,22 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder { let mut grouped_protocol_data: Vec = vec![]; for swap in grouped_swap.swaps.iter() { + let transfer_type = self.get_transfer_type( + swap.clone(), + solution.given_token.clone(), + self.native_address.clone(), + self.wrapped_address.clone(), + self.permit2.clone().is_some(), + wrap, + ); + let encoding_context = EncodingContext { receiver: self.router_address.clone(), exact_out: solution.exact_out, router_address: Some(self.router_address.clone()), group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), + transfer_type: transfer_type.clone(), }; let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context.clone())?; @@ -415,6 +449,8 @@ impl SplitSwapStrategyEncoder { } } +impl TransferOptimization for SplitSwapStrategyEncoder {} + impl StrategyEncoder for SplitSwapStrategyEncoder { fn encode_strategy(&self, solution: Solution) -> Result<(Vec, Bytes), EncodingError> { self.split_swap_validator @@ -491,12 +527,22 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { let mut grouped_protocol_data: Vec = vec![]; for swap in grouped_swap.swaps.iter() { + let transfer_type = self.get_transfer_type( + swap.clone(), + solution.given_token.clone(), + self.native_address.clone(), + self.wrapped_address.clone(), + self.permit2.clone().is_some(), + wrap, + ); + let encoding_context = EncodingContext { receiver: self.router_address.clone(), exact_out: solution.exact_out, router_address: Some(self.router_address.clone()), group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), + transfer_type: transfer_type.clone(), }; let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context.clone())?; @@ -722,7 +768,7 @@ mod tests { "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver "00", // zero2one - "00", // transfer type + "02", // transfer type "00000000000000", // padding )); let hex_calldata = encode(&calldata); @@ -825,11 +871,11 @@ mod tests { "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver "00", // zero2one - "00", // exact out - "00", // transfer type - "00000000000000000000000000", // padding + "02", // transfer type + "0000000000000000000000000000", // padding )); let hex_calldata = encode(&calldata); + println!("{}", hex_calldata); assert_eq!(hex_calldata[..456], expected_input); assert_eq!(hex_calldata[1224..], expected_swap); @@ -1327,9 +1373,9 @@ mod tests { let expected_swaps = String::from(concat!( // length of ple encoded swaps without padding - "0000000000000000000000000000000000000000000000000000000000000078", + "0000000000000000000000000000000000000000000000000000000000000079", // ple encoded swaps - "0076", // Swap length + "0077", // Swap length "00", // token in index "01", // token out index "000000", // split @@ -1339,6 +1385,7 @@ mod tests { "a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // group token in "6982508145454ce325ddbe47a25d4ec3d2311933", // group token in "00", // zero2one + "04", // transfer type (transfer to router) // First pool params "0000000000000000000000000000000000000000", // intermediary token (ETH) "000bb8", // fee @@ -1347,7 +1394,7 @@ mod tests { "6982508145454ce325ddbe47a25d4ec3d2311933", // intermediary token (PEPE) "0061a8", // fee "0001f4", // tick spacing - "0000000000000000" // padding + "00000000000000" // padding )); let hex_calldata = encode(&calldata); @@ -1484,9 +1531,8 @@ mod tests { "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver "00", // zero2one - "00", // exact out - "00", // transfer type - "00000000000000000000000000", // padding + "01", // transfer type + "0000000000000000000000000000", // padding ] .join(""); @@ -1568,9 +1614,8 @@ mod tests { "a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id "3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver "00", // zero2one - "00", // exact out - "00", // transfer type - "000000000000", // padding + "01", // transfer type + "00000000000000", // padding ] .join(""); @@ -1821,7 +1866,7 @@ mod tests { "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id "01", // zero2one - "00", // transfer type + "02", // transfer type "006e", // ple encoded swaps "01", // token in index "00000000", // split @@ -1975,7 +2020,7 @@ mod tests { "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "88e6a0c2ddd26feeb64f039a2c41296fcb3f5640", // component id "01", // zero2one - "00", // transfer type + "02", // transfer type "006e", // ple encoded swaps "00", // token in index "01", // token out index @@ -1987,7 +2032,7 @@ mod tests { "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "8ad599c3a0ff1de082011efddc58f1908eb6e6d8", // component id "01", // zero2one - "00", // transfer type + "02", // transfer type "0057", // ple encoded swaps "01", // token in index "00", // token out index @@ -2135,7 +2180,7 @@ mod tests { "b4e16d0168e52d35cacd2c6185b44281ec28c9dc", // component id "3ede3eca2a72b3aecc820e955b36f38437d01395", // router address "01", // zero2one - "00", // transfer type + "02", // transfer type "006e", // ple encoded swaps "01", // token in index "00", // token out index diff --git a/src/encoding/evm/strategy_encoder/transfer_optimizations.rs b/src/encoding/evm/strategy_encoder/transfer_optimizations.rs new file mode 100644 index 0000000..5188986 --- /dev/null +++ b/src/encoding/evm/strategy_encoder/transfer_optimizations.rs @@ -0,0 +1,173 @@ +use tycho_common::Bytes; + +use crate::encoding::{ + evm::constants::{IN_TRANSFER_OPTIMIZABLE_PROTOCOLS, PROTOCOLS_EXPECTING_FUNDS_IN_ROUTER}, + models::{Swap, TransferType}, +}; + +/// A trait that defines how the tokens will be transferred into the given pool given the solution. +pub trait TransferOptimization { + /// Returns the transfer method that should be used for the given swap and solution. + fn get_transfer_type( + &self, + swap: Swap, + given_token: Bytes, + native_token: Bytes, + wrapped_token: Bytes, + permit2: bool, + wrap: bool, + ) -> TransferType { + let send_funds_to_pool: bool = + IN_TRANSFER_OPTIMIZABLE_PROTOCOLS.contains(&swap.component.protocol_system.as_str()); + let funds_expected_in_router: bool = + PROTOCOLS_EXPECTING_FUNDS_IN_ROUTER.contains(&swap.component.protocol_system.as_str()); + + // In the case of wrapping, check if the swap's token in is the wrapped token to + // determine if it's the first swap. Otherwise, compare to the given token. + let is_first_swap = + (swap.token_in == given_token) || ((swap.token_in == wrapped_token) && wrap); + + if swap.token_in == native_token { + // Funds are already in router. All protocols currently take care of native transfers. + TransferType::None + } else if is_first_swap && send_funds_to_pool { + if permit2 { + // Transfer from swapper to pool using permit2. + TransferType::TransferPermit2ToProtocol + } else { + // Transfer from swapper to pool. + TransferType::TransferFromToProtocol + } + } else if is_first_swap && funds_expected_in_router { + if permit2 { + // Transfer from swapper to router using permit2. + TransferType::TransferPermit2ToRouter + } else { + // Transfer from swapper to router. + TransferType::TransferFromToRouter + } + } else { + TransferType::TransferToProtocol + } + } +} + +#[cfg(test)] +mod tests { + use alloy_primitives::hex; + use tycho_common::{models::protocol::ProtocolComponent, Bytes}; + + use super::*; + + struct MockStrategy {} + impl TransferOptimization for MockStrategy {} + + fn weth() -> Bytes { + Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec()) + } + + fn eth() -> Bytes { + Bytes::from(hex!("0000000000000000000000000000000000000000").to_vec()) + } + + fn dai() -> Bytes { + Bytes::from(hex!("6b175474e89094c44da98b954eedeac495271d0f").to_vec()) + } + + fn usdc() -> Bytes { + Bytes::from(hex!("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48").to_vec()) + } + + #[test] + fn test_first_swap_transfer_from_permit2() { + // The swap token is the same as the given token, which is not the native token + let swap = Swap { + component: ProtocolComponent { + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth(), + token_out: dai(), + split: 0f64, + }; + let strategy = MockStrategy {}; + let transfer_method = + strategy.get_transfer_type(swap.clone(), weth(), eth(), weth(), true, false); + assert_eq!(transfer_method, TransferType::TransferPermit2ToProtocol); + } + + #[test] + fn test_first_swap_transfer_from() { + // The swap token is the same as the given token, which is not the native token + let swap = Swap { + component: ProtocolComponent { + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth(), + token_out: dai(), + split: 0f64, + }; + let strategy = MockStrategy {}; + let transfer_method = + strategy.get_transfer_type(swap.clone(), weth(), eth(), weth(), false, false); + assert_eq!(transfer_method, TransferType::TransferFromToProtocol); + } + + #[test] + fn test_first_swap_native() { + // The swap token is the same as the given token, and it's the native token. + // No transfer action is needed. + let swap = Swap { + component: ProtocolComponent { + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: eth(), + token_out: dai(), + split: 0f64, + }; + let strategy = MockStrategy {}; + let transfer_method = + strategy.get_transfer_type(swap.clone(), eth(), eth(), weth(), false, false); + assert_eq!(transfer_method, TransferType::None); + } + + #[test] + fn test_first_swap_wrapped() { + // The swap token is NOT the same as the given token, but we are wrapping. + // Since the swap's token in is the wrapped token - this is the first swap. + let swap = Swap { + component: ProtocolComponent { + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: weth(), + token_out: dai(), + split: 0f64, + }; + let strategy = MockStrategy {}; + let transfer_method = + strategy.get_transfer_type(swap.clone(), eth(), eth(), weth(), false, true); + assert_eq!(transfer_method, TransferType::TransferFromToProtocol); + } + + #[test] + fn test_not_first_swap() { + // The swap token is NOT the same as the given token, and we are NOT wrapping. + // Thus, this is not the first swap. + let swap = Swap { + component: ProtocolComponent { + protocol_system: "uniswap_v2".to_string(), + ..Default::default() + }, + token_in: usdc(), + token_out: dai(), + split: 0f64, + }; + let strategy = MockStrategy {}; + let transfer_method = + strategy.get_transfer_type(swap.clone(), weth(), eth(), weth(), false, false); + assert_eq!(transfer_method, TransferType::TransferToProtocol); + } +} diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index bdc170e..0ec212e 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -22,15 +22,6 @@ use crate::encoding::{ swap_encoder::SwapEncoder, }; -#[allow(dead_code)] -#[repr(u8)] -pub enum TransferType { - Transfer = 0, - TransferFrom = 1, - Permit2Transfer = 2, - None = 3, -} - /// Encodes a swap on a Uniswap V2 pool through the given executor address. /// /// # Fields @@ -75,7 +66,7 @@ impl SwapEncoder for UniswapV2SwapEncoder { component_id, bytes_to_address(&encoding_context.receiver)?, zero_to_one, - (TransferType::Transfer as u8).to_be_bytes(), + (encoding_context.transfer_type as u8).to_be_bytes(), ); Ok(args.abi_encode_packed()) @@ -138,7 +129,7 @@ impl SwapEncoder for UniswapV3SwapEncoder { bytes_to_address(&encoding_context.receiver)?, component_id, zero_to_one, - (TransferType::Transfer as u8).to_be_bytes(), + (encoding_context.transfer_type as u8).to_be_bytes(), ); Ok(args.abi_encode_packed()) @@ -211,7 +202,13 @@ impl SwapEncoder for UniswapV4SwapEncoder { let pool_params = (token_out_address, pool_fee_u24, pool_tick_spacing_u24).abi_encode_packed(); - let args = (group_token_in_address, group_token_out_address, zero_to_one, pool_params); + let args = ( + group_token_in_address, + group_token_out_address, + zero_to_one, + (encoding_context.transfer_type as u8).to_be_bytes(), + pool_params, + ); Ok(args.abi_encode_packed()) } @@ -284,6 +281,7 @@ impl SwapEncoder for BalancerV2SwapEncoder { component_id, bytes_to_address(&encoding_context.receiver)?, approval_needed, + (encoding_context.transfer_type as u8).to_be_bytes(), ); Ok(args.abi_encode_packed()) } @@ -548,6 +546,7 @@ impl SwapEncoder for CurveSwapEncoder { i.to_be_bytes::<1>(), j.to_be_bytes::<1>(), approval_needed, + (encoding_context.transfer_type as u8).to_be_bytes(), ); Ok(args.abi_encode_packed()) @@ -573,6 +572,7 @@ mod tests { }; use super::*; + use crate::encoding::models::TransferType; #[test] fn test_encode_uniswap_v2() { @@ -595,6 +595,7 @@ mod tests { router_address: Some(Bytes::zero(20)), group_token_in: token_in.clone(), group_token_out: token_out.clone(), + transfer_type: TransferType::TransferToProtocol, }; let encoder = UniswapV2SwapEncoder::new( String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"), @@ -648,6 +649,7 @@ mod tests { router_address: Some(Bytes::zero(20)), group_token_in: token_in.clone(), group_token_out: token_out.clone(), + transfer_type: TransferType::TransferToProtocol, }; let encoder = UniswapV3SwapEncoder::new( String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"), @@ -702,6 +704,7 @@ mod tests { router_address: Some(Bytes::zero(20)), group_token_in: token_in.clone(), group_token_out: token_out.clone(), + transfer_type: TransferType::None, }; let encoder = BalancerV2SwapEncoder::new( String::from("0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"), @@ -729,7 +732,9 @@ mod tests { // receiver "1d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e", // approval needed - "01" + "01", + // transfer type + "05" )) ); } @@ -768,6 +773,7 @@ mod tests { group_token_in: token_in.clone(), group_token_out: token_out.clone(), + transfer_type: TransferType::TransferToProtocol, }; let encoder = UniswapV4SwapEncoder::new( String::from("0xF62849F9A0B5Bf2913b396098F7c7019b51A820a"), @@ -790,6 +796,8 @@ mod tests { "dac17f958d2ee523a2206206994597c13d831ec7", // zero for one "01", + // transfer type + "00", // pool params: // - intermediary token "dac17f958d2ee523a2206206994597c13d831ec7", @@ -834,6 +842,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_type: TransferType::TransferToProtocol, }; let encoder = UniswapV4SwapEncoder::new( @@ -876,6 +885,7 @@ mod tests { router_address: Some(router_address.clone()), group_token_in: usde_address.clone(), group_token_out: wbtc_address.clone(), + transfer_type: TransferType::TransferToProtocol, }; // Setup - First sequence: USDE -> USDT @@ -944,6 +954,7 @@ mod tests { let combined_hex = format!("{}{}", encode(&initial_encoded_swap), encode(&second_encoded_swap)); + println!("{}", combined_hex); assert_eq!( combined_hex, String::from(concat!( @@ -953,6 +964,8 @@ mod tests { "2260fac5e5542a773aa44fbcfedf7c193bc2c599", // zero for one "01", + // transfer type + "00", // pool params: // - intermediary token USDT "dac17f958d2ee523a2206206994597c13d831ec7", @@ -1004,6 +1017,7 @@ mod tests { group_token_out: token_out.clone(), exact_out: false, router_address: Some(Bytes::default()), + transfer_type: TransferType::TransferToProtocol, }; let encoder = @@ -1046,6 +1060,7 @@ mod tests { group_token_out: group_token_out.clone(), exact_out: false, router_address: Some(Bytes::default()), + transfer_type: TransferType::TransferToProtocol, }; let first_swap = Swap { @@ -1222,6 +1237,7 @@ mod tests { router_address: None, group_token_in: token_in.clone(), group_token_out: token_out.clone(), + transfer_type: TransferType::None, }; let encoder = CurveSwapEncoder::new( String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"), @@ -1251,6 +1267,8 @@ mod tests { "01", // approval needed "01", + // transfer type + "05", )) ); } @@ -1287,6 +1305,7 @@ mod tests { router_address: None, group_token_in: token_in.clone(), group_token_out: token_out.clone(), + transfer_type: TransferType::None, }; let encoder = CurveSwapEncoder::new( String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"), @@ -1316,6 +1335,8 @@ mod tests { "00", // approval needed "01", + // transfer type + "05", )) ); } @@ -1353,6 +1374,7 @@ mod tests { router_address: None, group_token_in: token_in.clone(), group_token_out: token_out.clone(), + transfer_type: TransferType::None, }; let encoder = CurveSwapEncoder::new( String::from("0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f"), @@ -1391,6 +1413,8 @@ mod tests { "01", // approval needed "01", + // transfer type + "05", )) ); } diff --git a/src/encoding/evm/tycho_encoders.rs b/src/encoding/evm/tycho_encoders.rs index 92706fc..1ecf41f 100644 --- a/src/encoding/evm/tycho_encoders.rs +++ b/src/encoding/evm/tycho_encoders.rs @@ -12,7 +12,7 @@ use crate::encoding::{ }, 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, }; @@ -201,9 +201,10 @@ impl TychoEncoder for TychoRouterEncoder { } } -/// Represents an encoder for one swap to be executed directly against an Executor. This is useful -/// when you want to bypass the Tycho Router, use your own Router contract and just need the -/// calldata for a particular swap. +/// Represents an encoder for one swap to be executed directly against an Executor. +/// +/// This is useful when you want to bypass the Tycho Router, use your own Router contract and +/// just need the calldata for a particular swap. /// /// # Fields /// * `swap_encoder_registry`: Registry of swap encoders @@ -259,6 +260,7 @@ impl TychoExecutorEncoder { router_address: None, group_token_in: grouped_swap.input_token.clone(), group_token_out: grouped_swap.output_token.clone(), + transfer_type: TransferType::TransferToProtocol, }; let protocol_data = swap_encoder.encode_swap(swap.clone(), encoding_context.clone())?; grouped_protocol_data.extend(protocol_data); @@ -1046,6 +1048,8 @@ mod tests { "6982508145454ce325ddbe47a25d4ec3d2311933", // zero for one "00", + // transfer type + "00", // first pool intermediary token (ETH) "0000000000000000000000000000000000000000", // fee diff --git a/src/encoding/models.rs b/src/encoding/models.rs index 9da64dd..052df36 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -96,6 +96,27 @@ pub struct Transaction { pub data: Vec, } +/// Represents the type of transfer to be performed into the pool. +/// +/// # Fields +/// +/// * `TransferToProtocol`: Transfer the token from the router into the protocol. +/// * `TransferFromToProtocol`: Transfer the token from the sender to the protocol. +/// * `TransferPermit2ToProtocol`: Transfer the token from the sender to the protocol using Permit2. +/// * `TransferFromToRouter`: Transfer the token from the sender to the router. +/// * `TransferPermit2ToRouter`: Transfer the token from the sender to the router using Permit2. +/// * `None`: No transfer is needed. Tokens are already in the pool. +#[repr(u8)] +#[derive(Clone, Debug, PartialEq)] +pub enum TransferType { + TransferToProtocol = 0, + TransferFromToProtocol = 1, + TransferPermit2ToProtocol = 2, + TransferFromToRouter = 3, + TransferPermit2ToRouter = 4, + None = 5, +} + /// Represents necessary attributes for encoding an order. /// /// # Fields @@ -113,6 +134,7 @@ pub struct EncodingContext { pub router_address: Option, pub group_token_in: Bytes, pub group_token_out: Bytes, + pub transfer_type: TransferType, } #[derive(Clone, PartialEq, Eq, Hash)]