From 591d73ba717deb1773f5c10f9085cc1175df2536 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Wed, 12 Feb 2025 19:30:21 +0000 Subject: [PATCH] feat: Support uniswap v4 callback in TychoRouter Make TychoRouter inherit from SafeCallback and then delegatecall to the UniswapV4 executor Add a test for this. I had to update the block of our forked network in the tests. Because of this I had to update all the asserts in previous tests Had to change the optimizer_runs in foundry.toml because of weird Yul errors when compiling --- don't change below this line --- ENG-4223 Took 1 hour 21 minutes Took 7 seconds Took 35 seconds --- foundry/foundry.toml | 2 +- foundry/src/TychoRouter.sol | 30 ++++++++- foundry/test/TychoRouter.t.sol | 62 ++++++++++++++----- foundry/test/TychoRouterTestSetup.sol | 28 ++++++--- .../test/executors/BalancerV2Executor.t.sol | 2 +- .../test/executors/UniswapV2Executor.t.sol | 2 +- foundry/test/executors/UniswapV4Utils.sol | 5 +- 7 files changed, 101 insertions(+), 30 deletions(-) diff --git a/foundry/foundry.toml b/foundry/foundry.toml index e573137..548f699 100644 --- a/foundry/foundry.toml +++ b/foundry/foundry.toml @@ -5,7 +5,7 @@ libs = ['lib'] auto_detect_sol = true evm_version = 'cancun' optimizer = true -optimizer_runs = 1000 +optimizer_runs = 200 via_ir = true [rpc_endpoints] diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 311c916..36e3499 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -15,6 +15,8 @@ import "@uniswap/v3-updated/CallbackValidationV2.sol"; import "./ExecutionDispatcher.sol"; import "./CallbackVerificationDispatcher.sol"; import {LibSwap} from "../lib/LibSwap.sol"; +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; +import {SafeCallback} from "@uniswap/v4-periphery/src/base/SafeCallback.sol"; error TychoRouter__WithdrawalFailed(); error TychoRouter__AddressZero(); @@ -28,7 +30,8 @@ contract TychoRouter is ExecutionDispatcher, CallbackVerificationDispatcher, Pausable, - ReentrancyGuard + ReentrancyGuard, + SafeCallback { IAllowanceTransfer public immutable permit2; IWETH private immutable _weth; @@ -65,7 +68,12 @@ contract TychoRouter is address private immutable _usv3Factory; - constructor(address _permit2, address weth, address usv3Factory) { + constructor( + IPoolManager _poolManager, + address _permit2, + address weth, + address usv3Factory + ) SafeCallback(_poolManager) { if ( _permit2 == address(0) || weth == address(0) || usv3Factory == address(0) @@ -434,4 +442,22 @@ contract TychoRouter is return (amountIn, tokenIn); } + + function _unlockCallback(bytes calldata data) + internal + override + returns (bytes memory) + { + require(data.length >= 20, "Invalid data length"); + bytes4 selector = bytes4(data[data.length - 24:data.length - 20]); + address executor = address(uint160(bytes20(data[data.length - 20:]))); + bytes memory protocolData = data[:data.length - 24]; + + // slither-disable-next-line controlled-delegatecall,low-level-calls + (bool success,) = executor.delegatecall( + abi.encodeWithSelector(selector, protocolData) + ); + require(success, "delegatecall to uniswap v4 callback failed"); + return ""; + } } diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index f7df52a..960e7b9 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1,8 +1,10 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.26; +import "@src/executors/UniswapV4Executor.sol"; import {TychoRouter} from "@src/TychoRouter.sol"; import "./TychoRouterTestSetup.sol"; +import "./executors/UniswapV4Utils.sol"; contract TychoRouterTest is TychoRouterTestSetup { bytes32 public constant EXECUTOR_SETTER_ROLE = @@ -254,7 +256,7 @@ contract TychoRouterTest is TychoRouterTestSetup { tychoRouter.exposedSwap(amountIn, 2, pleEncode(swaps)); uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(tychoRouterAddr); - assertEq(daiBalance, 2630432278145144658455); + assertEq(daiBalance, 2659881924818443699787); assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0); } @@ -291,7 +293,7 @@ contract TychoRouterTest is TychoRouterTestSetup { tychoRouter.exposedSwap(amountIn, 3, pleEncode(swaps)); uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr); - assertEq(usdcBalance, 2610580090); + assertEq(usdcBalance, 2644659787); assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0); } @@ -352,7 +354,7 @@ contract TychoRouterTest is TychoRouterTestSetup { tychoRouter.exposedSwap(amountIn, 4, pleEncode(swaps)); uint256 usdcBalance = IERC20(USDC_ADDR).balanceOf(tychoRouterAddr); - assertEq(usdcBalance, 2581503157); + assertEq(usdcBalance, 2615491639); assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0); } @@ -400,7 +402,7 @@ contract TychoRouterTest is TychoRouterTestSetup { pleEncode(swaps) ); - uint256 expectedAmount = 2630432278145144658455; + uint256 expectedAmount = 2659881924818443699787; assertEq(amountOut, expectedAmount); uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE); assertEq(daiBalance, expectedAmount); @@ -442,7 +444,7 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.expectRevert( abi.encodeWithSelector( TychoRouter__NegativeSlippage.selector, - 2630432278145144658455, // actual amountOut + 2659881924818443699787, // actual amountOut minAmountOut ) ); @@ -511,11 +513,11 @@ contract TychoRouterTest is TychoRouterTestSetup { pleEncode(swaps) ); - uint256 expectedAmount = 2604127955363693211871; + uint256 expectedAmount = 2633283105570259262790; assertEq(amountOut, expectedAmount); uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE); assertEq(daiBalance, expectedAmount); - assertEq(IERC20(DAI_ADDR).balanceOf(FEE_RECEIVER), 26304322781451446584); + assertEq(IERC20(DAI_ADDR).balanceOf(FEE_RECEIVER), 26598819248184436997); vm.stopPrank(); } @@ -567,7 +569,7 @@ contract TychoRouterTest is TychoRouterTestSetup { "", pleEncode(swaps) ); - uint256 expectedAmount = 2630432278145144658455; + uint256 expectedAmount = 2659881924818443699787; assertEq(amountOut, expectedAmount); uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE); assertEq(daiBalance, expectedAmount); @@ -617,7 +619,7 @@ contract TychoRouterTest is TychoRouterTestSetup { pleEncode(swaps) ); - uint256 expectedAmount = 1132829934891544187; // 1.13 ETH + uint256 expectedAmount = 1120007305574805922; // 1.12 ETH assertEq(amountOut, expectedAmount); assertEq(ALICE.balance, expectedAmount); @@ -695,7 +697,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( - hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067c43ba900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000679cb5b10000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415bfd02ffd61c11192d1b54d76e0af125afbb32568aad37ec35f918bd5fb304cd314954213ed77c0d071301ddc45243ad57e86fe18f2905b682acc4f1a43ad8dc1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000000000" + hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067d481bb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067acfbc3000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041f2740fde9662d8bc1f8fe8e8fc29447c1832d625f06f4a56ee5103ad555c12323af5d50eb840f73d17873383ae3b7573956d5df7b2bf76bddba768c2837894a51b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000000000" ); vm.stopPrank(); @@ -717,14 +719,14 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); - IERC20(WETH_ADDR).approve(address(permit2Address), type(uint256).max); + // IERC20(WETH_ADDR).approve(address(permit2Address), type(uint256).max); // Encoded solution generated using // `test_split_swap_strategy_encoder_simple_route_wrap` // but manually replacing the executor address // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call{value: 1 ether}( - hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067c9179300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067a1919b000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041cea77a63613f6a02aaee522c91f9569b8377a7f0200d141fafa3e1c42011e1c668555b49a1e7dd960091d0e33764ad24db6550bc761e228864495b478f1a23721b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000000000" + hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067d4806b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067acfa73000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041c36406a750c499ac7f79f7666650f0d4f20fc27bb49ab68121c0be6554cb5cab6caf90dc3aab2e21083a8fa46976521a1e9df41ce74be59abf03e0d3691541e91c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950000000000" ); vm.stopPrank(); @@ -753,7 +755,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( - hex"4860f9ed0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000067c9185300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067a1925b000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041fd1c3dfce5afcb47988cc68165d5de64186cedbeb7eee6fc9cd087bceeaacdfe1ab799d60e0c628f24edfd9819b94ed60846dd23240c481f1d6e5470a7815a891c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950100000000" + hex"4860f9ed0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000067d4809800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067acfaa000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004146411c70ec7fee0d5d260803cb220f5365792426c5d94f7a0a4d37abb05205752c5418b1fadd059570a71f0911814e546728e1f21876f2a1c6d38d34bd235fd61c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d013950100000000" ); vm.stopPrank(); @@ -784,7 +786,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( - hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067c48ea700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d0139500000000000000000000000000000000000000000000000000000000679d08af00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004197c2ff7801fa573e4e8e4af1df41499045485c2b48d090833dc85be38e002c1a1e7ef354285d79c2dcb40c4837e5156069de9aaf42365aef54fdc4cca2c76ccb1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000170005a00028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d0139500005a02030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d0139501005a01030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000" + hex"4860f9ed0000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067d4810d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067acfb15000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041ecaab75f0791c9683b001ea2f0e01a0a6aaf03e6e49c83e9c8a8e588a38e3be9230d962926628ffbf6a5370cda559ff0e7876a63ed38eebe33dbef5b5e2e46ef1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000170005a00028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500005a00010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625abc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d0139500005a02030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d0139501005a01030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fbd0625ab2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d013950100000000000000000000000000000000" ); vm.stopPrank(); @@ -850,4 +852,36 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } + + function testSwapSingleUSV4Callback() public { + uint256 amountIn = 100 ether; + deal(USDE_ADDR, tychoRouterAddr, amountIn); + + bytes memory protocolData = UniswapV4Utils.encodeExactInputSingle( + USDE_ADDR, USDT_ADDR, 100, true, 1, uint128(amountIn) + ); + + // add executor and selector for callback + bytes memory protocolDataWithCallBack = abi.encodePacked( + protocolData, + SafeCallback.unlockCallback.selector, + address(usv4Executor) + ); + + bytes memory swap = encodeSwap( + uint8(0), + uint8(1), + uint24(0), + address(usv4Executor), + bytes4(0), + protocolDataWithCallBack + ); + + bytes[] memory swaps = new bytes[](1); + swaps[0] = swap; + + tychoRouter.exposedSwap(amountIn, 2, pleEncode(swaps)); + + assertTrue(IERC20(USDT_ADDR).balanceOf(tychoRouterAddr) == 99943852); + } } diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index 2d63fe2..b0bbf2e 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -2,16 +2,22 @@ pragma solidity ^0.8.26; import "../src/executors/UniswapV2Executor.sol"; +import "../src/executors/UniswapV3Executor.sol"; +import "../src/executors/UniswapV4Executor.sol"; import "./Constants.sol"; import "./mock/MockERC20.sol"; import "@src/TychoRouter.sol"; +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {WETH} from "../lib/permit2/lib/solmate/src/tokens/WETH.sol"; -import "../src/executors/UniswapV3Executor.sol"; +import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol"; contract TychoRouterExposed is TychoRouter { - constructor(address _permit2, address weth, address usv3Factory) - TychoRouter(_permit2, weth, usv3Factory) - {} + constructor( + IPoolManager _poolManager, + address _permit2, + address weth, + address usv3Factory + ) TychoRouter(_poolManager, _permit2, weth, usv3Factory) {} function wrapETH(uint256 amount) external payable { return _wrapETH(amount); @@ -36,16 +42,20 @@ contract TychoRouterTestSetup is Test, Constants { address permit2Address = address(0x000000000022D473030F116dDEE9F6B43aC78BA3); UniswapV2Executor public usv2Executor; UniswapV3Executor public usv3Executor; + UniswapV4Executor public usv4Executor; MockERC20[] tokens; function setUp() public { - uint256 forkBlock = 21000000; + uint256 forkBlock = 21817316; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); vm.startPrank(ADMIN); address factoryV3 = address(0x1F98431c8aD98523631AE4a59f267346ea31F984); - tychoRouter = - new TychoRouterExposed(permit2Address, WETH_ADDR, factoryV3); + address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90; + IPoolManager poolManager = IPoolManager(poolManagerAddress); + tychoRouter = new TychoRouterExposed( + poolManager, permit2Address, WETH_ADDR, factoryV3 + ); tychoRouterAddr = address(tychoRouter); tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER); tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER); @@ -59,10 +69,12 @@ contract TychoRouterTestSetup is Test, Constants { usv2Executor = new UniswapV2Executor(); usv3Executor = new UniswapV3Executor(); + usv4Executor = new UniswapV4Executor(poolManager); vm.startPrank(EXECUTOR_SETTER); - address[] memory executors = new address[](2); + address[] memory executors = new address[](3); executors[0] = address(usv2Executor); executors[1] = address(usv3Executor); + executors[2] = address(usv4Executor); tychoRouter.setExecutors(executors); vm.stopPrank(); diff --git a/foundry/test/executors/BalancerV2Executor.t.sol b/foundry/test/executors/BalancerV2Executor.t.sol index 117b153..7581146 100644 --- a/foundry/test/executors/BalancerV2Executor.t.sol +++ b/foundry/test/executors/BalancerV2Executor.t.sol @@ -83,7 +83,7 @@ contract BalancerV2ExecutorTest is assertEq(balanceAfter - balanceBefore, amountOut); } - function testDecodeIntegration() public { + function testDecodeIntegration() public view { // Generated by the SwapEncoder - test_encode_balancer_v2 bytes memory protocolData = hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2ba100000625a3754423978a60c9317c58a424e3d5c6ee304399dbdb9c8ef030ab642b10820db8f560002000000000000000000141d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e01"; diff --git a/foundry/test/executors/UniswapV2Executor.t.sol b/foundry/test/executors/UniswapV2Executor.t.sol index dbcffbc..f377f5a 100644 --- a/foundry/test/executors/UniswapV2Executor.t.sol +++ b/foundry/test/executors/UniswapV2Executor.t.sol @@ -94,7 +94,7 @@ contract UniswapV2ExecutorTest is UniswapV2ExecutorExposed, Test, Constants { assertGe(finalBalance, amountOut); } - function testDecodeIntegration() public { + function testDecodeIntegration() public view { // Generated by the ExecutorStrategyEncoder - test_executor_strategy_encode bytes memory protocolData = hex"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc288e6a0c2ddd26feeb64f039a2c41296fcb3f5640000000000000000000000000000000000000000100"; diff --git a/foundry/test/executors/UniswapV4Utils.sol b/foundry/test/executors/UniswapV4Utils.sol index 093a944..f4d021c 100644 --- a/foundry/test/executors/UniswapV4Utils.sol +++ b/foundry/test/executors/UniswapV4Utils.sol @@ -8,7 +8,6 @@ library UniswapV4Utils { address tokenIn, address tokenOut, uint24 fee, - address receiver, bool zeroForOne, uint24 tickSpacing, uint128 amountIn @@ -24,7 +23,7 @@ library UniswapV4Utils { bytes memory actions = abi.encodePacked( uint8(Actions.SWAP_EXACT_IN_SINGLE), uint8(Actions.SETTLE_ALL), - uint8(Actions.TAKE) + uint8(Actions.TAKE_ALL) ); bytes[] memory params = new bytes[](3); @@ -40,7 +39,7 @@ library UniswapV4Utils { ); params[1] = abi.encode(key.currency0, amountIn); - params[2] = abi.encode(key.currency1, receiver, 0); + params[2] = abi.encode(key.currency1, 0); return abi.encode(actions, params); }