diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index e6c798b..4221d79 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -99,7 +99,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 +130,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 +182,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 +203,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 +232,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 +280,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 +300,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 +327,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 +375,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 +423,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 +470,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,10 +520,6 @@ 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) diff --git a/foundry/src/executors/TokenTransfer.sol b/foundry/src/executors/TokenTransfer.sol index 683fedb..0fb1f6d 100644 --- a/foundry/src/executors/TokenTransfer.sol +++ b/foundry/src/executors/TokenTransfer.sol @@ -16,9 +16,17 @@ contract TokenTransfer { // Assume funds are in the TychoRouter - transfer into the pool TRANSFER, // Assume funds are in msg.sender's wallet - transferFrom into the pool - TRANSFERFROM, + TRANSFER_FROM, // Assume funds are in msg.sender's wallet - permit2TransferFrom into the pool - TRANSFERPERMIT2, + TRANSFER_PERMIT2, + // 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_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 (tokenIn == address(0)) { + payable(receiver).transfer(amount); + } else { + IERC20(tokenIn).safeTransfer(receiver, amount); + } + } else if (transferType == TransferType.TRANSFER_FROM) { // 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) { + // Permit2.permit is already called from the TychoRouter + permit2.transferFrom(sender, receiver, uint160(amount), tokenIn); + } else if (transferType == TransferType.TRANSFER_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..adcea98 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,19 @@ 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, + address(this), // irrelevant attribute + amountIn, + transferType + ); + bytes memory swapData; if (pools.length == 1) { PoolKey memory key = PoolKey({ @@ -138,6 +152,7 @@ contract UniswapV4Executor is IExecutor, V4Router, ICallback { address tokenIn, address tokenOut, bool zeroForOne, + TransferType transferType, UniswapV4Pool[] memory pools ) { @@ -148,10 +163,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..7f9a983 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"7c553846000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000682163b600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9ddbe000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041e9e58e3facf99cd2c64b834d3b646b8cf9377c47540d65b5e180a06bca6f42851cf320a205cf466c7943abe45c2998afa6fd3d870043a108578e71256831ca1c1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d008b0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d231193300f62849f9a0b5bf2913b396098f7c7019b51a820a040000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f400000000000000000000000000000000000000" ); 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"7c5538460000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000c87c939ae635f92dc2379c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000006821689800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9e2a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000412da8d5aab101bbdf256d785a42db176328e8298ee6d0906e0ef1998cfcaa332460f8409d9b298dff73c947796a22c8de21caa17405ea157ced090da2b6cb27431c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d231193301f62849f9a0b5bf2913b396098f7c7019b51a820a056982508145454ce325ddbe47a25d4ec3d23119330061a80001f400000000000000000000000000" ); 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"7c55384600000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e0000000000000000000000000000000000000000000000000000000000682163ee00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067f9ddf60000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416056d925e7906c11b865992ac5c853532f5058bb57b67cd000a53b899503dd8a6fd4c0e5ea44c1ca4137753589bf89f66824796e719e807adee7567a707ee6681b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007300710001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a040000000000000000000000000000000000000000000bb800003c00000000000000000000000000" ); 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,7 +254,7 @@ 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" ); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99889294); @@ -270,7 +270,7 @@ 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" ); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99574171); @@ -286,7 +286,7 @@ 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" ); assertEq(IERC20(USDC_ADDR).balanceOf(ALICE), 99525908); diff --git a/foundry/test/TychoRouterSequentialSwap.t.sol b/foundry/test/TychoRouterSequentialSwap.t.sol index 46a9936..031f66c 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 + : TokenTransfer.TransferType.TRANSFER_FROM; + 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 + ) ); 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 + ) + ); + + // DAI -> USDC + swaps[1] = encodeSequentialSwap( + address(usv2Executor), + encodeUniswapV2Swap( + DAI_ADDR, + DAI_USDC_POOL, + tychoRouterAddr, + true, + TokenTransfer.TransferType.TRANSFER + ) + ); 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 ) ); // 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 + ) ); 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 ); 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 ); bytes[] memory swaps = new bytes[](2); diff --git a/foundry/test/TychoRouterSingleSwap.t.sol b/foundry/test/TychoRouterSingleSwap.t.sol index 246a95c..2c85586 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 ); 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 ); 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 ); 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 ); 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 ); 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 ); 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 + ); 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..2a9d6df 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 + : TokenTransfer.TransferType.TRANSFER_FROM; + // 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 ) ); // 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 + ) ); 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 ); 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 + ); 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 ); 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,13 @@ 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 +504,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 ); 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 ); bytes memory wethUsdcV2OneZeroData = encodeUniswapV2Swap( - WETH_ADDR, USDC_WETH_USV2, tychoRouterAddr, false + WETH_ADDR, + USDC_WETH_USV2, + tychoRouterAddr, + false, + TokenTransfer.TransferType.TRANSFER ); bytes[] memory swaps = new bytes[](3); @@ -547,6 +561,7 @@ contract TychoRouterSplitSwapTest is TychoRouterTestSetup { wethUsdcV2OneZeroData ); tychoRouter.exposedSplitSwap(amountIn, 2, pleEncode(swaps)); + vm.stopPrank(); assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99574171); } @@ -563,15 +578,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 ); 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 ); 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 ); bytes[] memory swaps = new bytes[](3); @@ -610,7 +639,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 ); bytes memory swap = encodeSplitSwap( diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index 961f0f8..d556e35 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -102,7 +102,7 @@ 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 ); @@ -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/UniswapV2Executor.t.sol b/foundry/test/executors/UniswapV2Executor.t.sol index c993ff9..a001a1e 100644 --- a/foundry/test/executors/UniswapV2Executor.t.sol +++ b/foundry/test/executors/UniswapV2Executor.t.sol @@ -176,7 +176,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper { WETH_DAI_POOL, BOB, zeroForOne, - uint8(TokenTransfer.TransferType.TRANSFERFROM) + uint8(TokenTransfer.TransferType.TRANSFER_FROM) ); deal(WETH_ADDR, address(this), amountIn); @@ -197,7 +197,7 @@ contract UniswapV2ExecutorTest is Test, Constants, Permit2TestHelper { WETH_DAI_POOL, ALICE, zeroForOne, - uint8(TokenTransfer.TransferType.TRANSFERPERMIT2) + uint8(TokenTransfer.TransferType.TRANSFER_PERMIT2) ); deal(WETH_ADDR, ALICE, amountIn); diff --git a/foundry/test/executors/UniswapV4Executor.t.sol b/foundry/test/executors/UniswapV4Executor.t.sol index 487e251..912223e 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; 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,13 @@ 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 +128,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"4c9edd5852cd905f086c759e8383e09bff1e68b3dac17f958d2ee523a2206206994597c13d831ec701f62849f9a0b5bf2913b396098f7c7019b51a820a00dac17f958d2ee523a2206206994597c13d831ec7000064000001"; uint256 amountIn = 100 ether; deal(USDE_ADDR, address(uniswapV4Exposed), amountIn); uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager); @@ -151,8 +165,13 @@ 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 +189,7 @@ contract UniswapV4ExecutorTest is Test, Constants { // Generated by the Tycho swap encoder - test_encode_uniswap_v4_sequential_swap bytes memory protocolData = - hex"4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c59901dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c"; + hex"4c9edd5852cd905f086c759e8383e09bff1e68b32260fac5e5542a773aa44fbcfedf7c193bc2c59901f62849f9a0b5bf2913b396098f7c7019b51a820a00dac17f958d2ee523a2206206994597c13d831ec70000640000012260fac5e5542a773aa44fbcfedf7c193bc2c599000bb800003c"; 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..5f13530 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,12 @@ 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 ef44482..2e6ad96 100644 --- a/src/encoding/evm/constants.rs +++ b/src/encoding/evm/constants.rs @@ -26,3 +26,14 @@ pub static IN_TRANSFER_OPTIMIZABLE_PROTOCOLS: LazyLock> = 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("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/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index e5174af..99ee9ae 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -39,6 +39,7 @@ pub struct SingleSwapStrategyEncoder { swap_encoder_registry: SwapEncoderRegistry, permit2: Option, selector: String, + native_address: Bytes, router_address: Bytes, } @@ -57,7 +58,13 @@ 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()?, + router_address, + }) } /// Encodes information necessary for performing a single hop against a given executor for @@ -117,6 +124,7 @@ impl StrategyEncoder for SingleSwapStrategyEncoder { let transfer_type = self.get_transfer_method( swap.clone(), solution.given_token.clone(), + self.native_address.clone(), self.permit2.clone().is_some(), ); @@ -289,6 +297,7 @@ impl StrategyEncoder for SequentialSwapStrategyEncoder { let transfer_type = self.get_transfer_method( swap.clone(), solution.given_token.clone(), + self.native_address.clone(), self.permit2.clone().is_some(), ); @@ -515,6 +524,7 @@ impl StrategyEncoder for SplitSwapStrategyEncoder { let transfer_type = self.get_transfer_method( swap.clone(), solution.given_token.clone(), + self.native_address.clone(), self.permit2.clone().is_some(), ); @@ -857,6 +867,7 @@ mod tests { "0000000000000000000000000000", // padding )); let hex_calldata = encode(&calldata); + println!("{}", hex_calldata); assert_eq!(hex_calldata[..456], expected_input); assert_eq!(hex_calldata[1224..], expected_swap); @@ -1354,9 +1365,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 @@ -1366,6 +1377,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 @@ -1374,7 +1386,7 @@ mod tests { "6982508145454ce325ddbe47a25d4ec3d2311933", // intermediary token (PEPE) "0061a8", // fee "0001f4", // tick spacing - "0000000000000000" // padding + "00000000000000" // padding )); let hex_calldata = encode(&calldata); diff --git a/src/encoding/evm/strategy_encoder/transfer_optimizations.rs b/src/encoding/evm/strategy_encoder/transfer_optimizations.rs index e3692ec..8d63612 100644 --- a/src/encoding/evm/strategy_encoder/transfer_optimizations.rs +++ b/src/encoding/evm/strategy_encoder/transfer_optimizations.rs @@ -1,26 +1,47 @@ use tycho_common::Bytes; use crate::encoding::{ - evm::constants::IN_TRANSFER_OPTIMIZABLE_PROTOCOLS, + 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. - /// - /// If the swap is for the in token of the solution and the protocol supports transferring - /// straight from the user, it will return `TransferType::Permit2Transfer` or - /// `TransferType::TransferFrom`. - fn get_transfer_method(&self, swap: Swap, given_token: Bytes, permit2: bool) -> TransferType { - let optimize_in_transfer = + fn get_transfer_method( + &self, + swap: Swap, + given_token: Bytes, + native_token: Bytes, + permit2: bool, + ) -> TransferType { + let send_funds_to_pool: bool = IN_TRANSFER_OPTIMIZABLE_PROTOCOLS.contains(&swap.component.protocol_system.as_str()); - if (swap.token_in == given_token) && optimize_in_transfer { - if permit2 { + let funds_expected_in_router: bool = + PROTOCOLS_EXPECTING_FUNDS_IN_ROUTER.contains(&swap.component.protocol_system.as_str()); + + if (swap.token_in == given_token) && send_funds_to_pool { + if swap.token_in == native_token { + // Funds are already in router. Transfer from router to pool. + TransferType::Transfer + } else if permit2 { + // Transfer from swapper to pool using permit2. TransferType::Permit2Transfer } else { + // Transfer from swapper to pool. TransferType::TransferFrom } + } else if (swap.token_in == given_token) && funds_expected_in_router { + if swap.token_in == native_token { + // Funds already in router. Do nothing. + TransferType::None + } else if permit2 { + // Transfer from swapper to router using permit2. + TransferType::Permit2TransferToRouter + } else { + // Transfer from swapper to router. + TransferType::TransferToRouter + } } else { TransferType::Transfer } @@ -41,6 +62,10 @@ mod tests { Bytes::from(hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2").to_vec()) } + fn eth() -> Bytes { + Bytes::from(hex!("0000000000000000000000000000000000000000").to_vec()) + } + fn dai() -> Bytes { Bytes::from(hex!("6b175474e89094c44da98b954eedeac495271d0f").to_vec()) } @@ -57,7 +82,7 @@ mod tests { split: 0f64, }; let strategy = MockStrategy {}; - let transfer_method = strategy.get_transfer_method(swap.clone(), weth(), true); + let transfer_method = strategy.get_transfer_method(swap.clone(), weth(), eth(), true); assert_eq!(transfer_method, TransferType::Permit2Transfer); } @@ -73,7 +98,7 @@ mod tests { split: 0f64, }; let strategy = MockStrategy {}; - let transfer_method = strategy.get_transfer_method(swap.clone(), weth(), false); + let transfer_method = strategy.get_transfer_method(swap.clone(), weth(), eth(), false); assert_eq!(transfer_method, TransferType::TransferFrom); } @@ -89,7 +114,7 @@ mod tests { split: 0f64, }; let strategy = MockStrategy {}; - let transfer_method = strategy.get_transfer_method(swap.clone(), dai(), false); + let transfer_method = strategy.get_transfer_method(swap.clone(), dai(), eth(), false); assert_eq!(transfer_method, TransferType::Transfer); } } diff --git a/src/encoding/evm/swap_encoder/swap_encoders.rs b/src/encoding/evm/swap_encoder/swap_encoders.rs index b05199a..89662ce 100644 --- a/src/encoding/evm/swap_encoder/swap_encoders.rs +++ b/src/encoding/evm/swap_encoder/swap_encoders.rs @@ -202,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()) } @@ -786,6 +792,8 @@ mod tests { "dac17f958d2ee523a2206206994597c13d831ec7", // zero for one "01", + // transfer type + "00", // pool params: // - intermediary token "dac17f958d2ee523a2206206994597c13d831ec7", @@ -942,6 +950,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!( @@ -951,6 +960,8 @@ mod tests { "2260fac5e5542a773aa44fbcfedf7c193bc2c599", // zero for one "01", + // transfer type + "00", // pool params: // - intermediary token USDT "dac17f958d2ee523a2206206994597c13d831ec7", diff --git a/src/encoding/evm/tycho_encoders.rs b/src/encoding/evm/tycho_encoders.rs index 6ec4e36..40beb97 100644 --- a/src/encoding/evm/tycho_encoders.rs +++ b/src/encoding/evm/tycho_encoders.rs @@ -1048,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 7f33009..9d5fc47 100644 --- a/src/encoding/models.rs +++ b/src/encoding/models.rs @@ -103,6 +103,8 @@ pub struct Transaction { /// * `Transfer`: Transfer the token from the router into the pool. /// * `TransferFrom`: Transfer the token from the swapper to the pool. /// * `Permit2Transfer`: Transfer the token from the sender to the pool using Permit2. +/// * `TransferToRouter`: Transfer the token from the swapper to the router. +/// * `Permit2TransferToRouter`: Transfer the token from the swapper to the router using Permit2. /// * `None`: No transfer is needed. Tokens are already in the pool. #[repr(u8)] #[derive(Clone, Debug, PartialEq)] @@ -110,7 +112,9 @@ pub enum TransferType { Transfer = 0, TransferFrom = 1, Permit2Transfer = 2, - None = 3, + TransferToRouter = 3, + Permit2TransferToRouter = 4, + None = 5, } /// Represents necessary attributes for encoding an order.