From 35e706d6ea1a657164410a050a3d2950acec8e70 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Thu, 27 Feb 2025 22:38:14 +0530 Subject: [PATCH 01/17] test: add GasTest to compare with Universal Router gas usage --- foundry/interfaces/IUniversalRouter.sol | 25 +++++ foundry/test/Constants.sol | 6 ++ foundry/test/GasTest.t.sol | 117 ++++++++++++++++++++++++ foundry/test/TychoRouter.t.sol | 14 +-- foundry/test/TychoRouterTestSetup.sol | 7 +- 5 files changed, 158 insertions(+), 11 deletions(-) create mode 100644 foundry/interfaces/IUniversalRouter.sol create mode 100644 foundry/test/GasTest.t.sol diff --git a/foundry/interfaces/IUniversalRouter.sol b/foundry/interfaces/IUniversalRouter.sol new file mode 100644 index 0000000..472e579 --- /dev/null +++ b/foundry/interfaces/IUniversalRouter.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.24; + +interface IUniversalRouter { + /// @notice Thrown when a required command has failed + error ExecutionFailed(uint256 commandIndex, bytes message); + + /// @notice Thrown when attempting to send ETH directly to the contract + error ETHNotAccepted(); + + /// @notice Thrown when executing commands with an expired deadline + error TransactionDeadlinePassed(); + + /// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided + error LengthMismatch(); + + // @notice Thrown when an address that isn't WETH tries to send ETH to the router without calldata + error InvalidEthSender(); + + /// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired. + /// @param commands A set of concatenated commands, each 1 byte in length + /// @param inputs An array of byte strings containing abi encoded inputs for each command + /// @param deadline The deadline by which the transaction must be executed + function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable; +} \ No newline at end of file diff --git a/foundry/test/Constants.sol b/foundry/test/Constants.sol index 6f552f8..98eb77d 100644 --- a/foundry/test/Constants.sol +++ b/foundry/test/Constants.sol @@ -50,6 +50,12 @@ contract Constants is Test, BaseConstants { address USV3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984; address DAI_WETH_USV3 = 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8; + // universal router + address UNIVERSAL_ROUTER = 0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af; + + // permit2 + address PERMIT2_ADDRESS = 0x000000000022D473030F116dDEE9F6B43aC78BA3; + /** * @dev Deploys a dummy contract with non-empty bytecode */ diff --git a/foundry/test/GasTest.t.sol b/foundry/test/GasTest.t.sol new file mode 100644 index 0000000..847ec68 --- /dev/null +++ b/foundry/test/GasTest.t.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.24; + +import {IUniversalRouter} from "../interfaces/IUniversalRouter.sol"; +import {IPermit2} from "../lib/permit2/src/interfaces/IPermit2.sol"; +import {Constants} from "./Constants.sol"; +import {Actions} from "../lib/v4-periphery/src/libraries/Actions.sol"; +import {PoolKey} from "../lib/v4-core/src/types/PoolKey.sol"; +import {IV4Router} from "../lib/v4-periphery/src/interfaces/IV4Router.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {Currency} from "../lib/v4-core/src/types/Currency.sol"; +import {IHooks} from "../lib/v4-core/src/interfaces/IHooks.sol"; +import "forge-std/Test.sol"; + +contract Commands { + uint256 constant V2_SWAP_EXACT_IN = 0x08; + uint256 constant V3_SWAP_EXACT_IN = 0x00; + uint256 constant V4_SWAP = 0x10; +} + +// A gas test to compare the gas usage of the UniversalRouter with the TychoRouter +// The gas usage quoted from the TychoRouter is without any asserts after the swap +// The path executed on TychoRouter is the same as the path executed on UniversalRouter +contract GasTest is Commands, Test, Constants { + IUniversalRouter universalRouter = IUniversalRouter(UNIVERSAL_ROUTER); + IPermit2 permit2 = IPermit2(PERMIT2_ADDRESS); + + function setUp() public { + uint256 forkBlock = 21817316; + vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); + } + + // Gas usage: 248717 + // TychoRouter:testSwapSimple costs 255123 + function testUniversalRouterUniswapV2() public { + uint256 amountIn = 10 ** 18; + + bytes memory commands = + abi.encodePacked(uint8(Commands.V2_SWAP_EXACT_IN)); + + address[] memory path = new address[](2); + path[0] = WETH_ADDR; + path[1] = DAI_ADDR; + + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, false); + + deal(WETH_ADDR, address(universalRouter), amountIn); + universalRouter.execute(commands, inputs, block.timestamp + 1000); + } + + // Gas usage: 251900 + // TychoRouter:testSwapSingleUSV3 costs 264195 + function testUniversalRouterUniswapV3() public { + uint256 amountIn = 10 ** 18; + + bytes memory commands = + abi.encodePacked(uint8(Commands.V3_SWAP_EXACT_IN)); + + uint24 poolFee = 3000; + bytes memory path = abi.encodePacked(WETH_ADDR, poolFee, DAI_ADDR); + + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, false); + + deal(WETH_ADDR, address(universalRouter), amountIn); + universalRouter.execute(commands, inputs, block.timestamp + 1000); + } + + // Gas usage: 299427 + // TychoRouter:testSwapSingleUSV4Callback costs 286025 + function testUniversalRouterUniswapV4() public { + uint128 amountIn = uint128(100 ether); + uint128 amountOutMinimum = uint128(0); + uint256 deadline = block.timestamp + 1000; + + bytes memory commands = abi.encodePacked(uint8(Commands.V4_SWAP)); + + bytes memory actions = abi.encodePacked( + uint8(Actions.SWAP_EXACT_IN_SINGLE), + uint8(Actions.SETTLE_ALL), + uint8(Actions.TAKE_ALL) + ); + + PoolKey memory key = PoolKey({ + currency0: Currency.wrap(USDE_ADDR), + currency1: Currency.wrap(USDT_ADDR), + fee: 100, + tickSpacing: int24(1), + hooks: IHooks(address(0)) + }); + + bytes[] memory params = new bytes[](3); + params[0] = abi.encode( + IV4Router.ExactInputSingleParams({ + poolKey: key, + zeroForOne: true, + amountIn: amountIn, + amountOutMinimum: amountOutMinimum, + hookData: bytes("") + }) + ); + + params[1] = abi.encode(key.currency0, amountIn); + params[2] = abi.encode(key.currency1, amountOutMinimum); + + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(actions, params); + + deal(USDE_ADDR, address(this), amountIn); + IERC20(USDE_ADDR).approve(PERMIT2_ADDRESS, amountIn); + permit2.approve( + USDE_ADDR, address(universalRouter), amountIn, uint48(deadline) + ); + universalRouter.execute(commands, inputs, deadline); + } +} diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 5c7caee..d23e276 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -650,7 +650,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); - IERC20(WETH_ADDR).approve(address(permit2Address), type(uint256).max); + IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_swap_strategy_encoder_simple` // but manually replacing the executor address // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test @@ -688,7 +688,7 @@ contract TychoRouterTest is TychoRouterTestSetup { assertEq(balancerAfter - balancerBefore, 2659881924818443699787); } - function testUSV4Integration4() public { + function testUSV4Integration() public { // Test created with calldata from our router encoder. // Performs a sequential swap from USDC to PEPE though ETH using two @@ -701,7 +701,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); - IERC20(USDC_ADDR).approve(address(permit2Address), type(uint256).max); + IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_encoding_strategy_usv4` // and ensuring that the encoded executor address is the one in this test // `f62849f9a0b5bf2913b396098f7c7019b51a820a` @@ -754,7 +754,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); - IERC20(USDC_ADDR).approve(address(permit2Address), type(uint256).max); + IERC20(USDC_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_encoding_strategy_usv4_eth_out` // and ensuring that the encoded executor address is the one in this test @@ -783,7 +783,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); - // IERC20(WETH_ADDR).approve(address(permit2Address), type(uint256).max); + // IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using // `test_split_swap_strategy_encoder_simple_route_wrap` // but manually replacing the executor address @@ -812,7 +812,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); - IERC20(DAI_ADDR).approve(address(permit2Address), type(uint256).max); + IERC20(DAI_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using // `test_split_swap_strategy_encoder_simple_route_unwrap` // but manually replacing the executor address @@ -844,7 +844,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); - IERC20(WETH_ADDR).approve(address(permit2Address), type(uint256).max); + IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_swap_strategy_encoder_complex` // but manually replacing the executor address // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index 59264a5..2609fa4 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -34,7 +34,6 @@ contract TychoRouterExposed is TychoRouter { contract TychoRouterTestSetup is Test, Constants { TychoRouterExposed tychoRouter; address tychoRouterAddr; - address permit2Address = address(0x000000000022D473030F116dDEE9F6B43aC78BA3); UniswapV2Executor public usv2Executor; UniswapV3Executor public usv3Executor; UniswapV4Executor public usv4Executor; @@ -48,7 +47,7 @@ contract TychoRouterTestSetup is Test, Constants { address factoryV3 = USV3_FACTORY; address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90; IPoolManager poolManager = IPoolManager(poolManagerAddress); - tychoRouter = new TychoRouterExposed(permit2Address, WETH_ADDR); + tychoRouter = new TychoRouterExposed(PERMIT2_ADDRESS, WETH_ADDR); tychoRouterAddr = address(tychoRouter); tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER); tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER); @@ -107,7 +106,7 @@ contract TychoRouterTestSetup is Test, Constants { internal returns (IAllowanceTransfer.PermitSingle memory, bytes memory) { - IERC20(tokenIn).approve(permit2Address, amount_in); + IERC20(tokenIn).approve(PERMIT2_ADDRESS, amount_in); IAllowanceTransfer.PermitSingle memory permitSingle = IAllowanceTransfer .PermitSingle({ details: IAllowanceTransfer.PermitDetails({ @@ -147,7 +146,7 @@ contract TychoRouterTestSetup is Test, Constants { ), keccak256("Permit2"), block.chainid, - permit2Address + PERMIT2_ADDRESS ) ); bytes32 detailsHash = From 9014fc73837c30ee32d00a66525438a4efae5b93 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Fri, 28 Feb 2025 00:39:20 +0530 Subject: [PATCH 02/17] test: add permit2 tests for simple swaps in TychoRouter and GasTest --- foundry/test/Constants.sol | 10 +-- foundry/test/GasTest.t.sol | 65 ++++++++++++++-- foundry/test/TychoRouter.t.sol | 134 +++++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 12 deletions(-) diff --git a/foundry/test/Constants.sol b/foundry/test/Constants.sol index 98eb77d..1f0762d 100644 --- a/foundry/test/Constants.sol +++ b/foundry/test/Constants.sol @@ -7,7 +7,7 @@ contract BaseConstants { address BASE_USDC = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; address BASE_MAG7 = 0x9E6A46f294bB67c20F1D1E7AfB0bBEf614403B55; - // uniswap v2 + // Uniswap v2 address USDC_MAG7_POOL = 0x739c2431670A12E2cF8e11E3603eB96e6728a789; } @@ -40,20 +40,20 @@ contract Constants is Test, BaseConstants { address USDT_ADDR = address(0xdAC17F958D2ee523a2206206994597C13D831ec7); address PEPE_ADDR = address(0x6982508145454Ce325dDbE47a25d4ec3d2311933); - // uniswap v2 + // Uniswap v2 address WETH_DAI_POOL = 0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11; address DAI_USDC_POOL = 0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5; address WETH_WBTC_POOL = 0xBb2b8038a1640196FbE3e38816F3e67Cba72D940; address USDC_WBTC_POOL = 0x004375Dff511095CC5A197A54140a24eFEF3A416; - // uniswap v3 + // Uniswap v3 address USV3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984; address DAI_WETH_USV3 = 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8; - // universal router + // Uniswap universal router address UNIVERSAL_ROUTER = 0x66a9893cC07D91D95644AEDD05D03f95e1dBA8Af; - // permit2 + // Permit2 address PERMIT2_ADDRESS = 0x000000000022D473030F116dDEE9F6B43aC78BA3; /** diff --git a/foundry/test/GasTest.t.sol b/foundry/test/GasTest.t.sol index 847ec68..90217ab 100644 --- a/foundry/test/GasTest.t.sol +++ b/foundry/test/GasTest.t.sol @@ -30,8 +30,8 @@ contract GasTest is Commands, Test, Constants { vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); } - // Gas usage: 248717 - // TychoRouter:testSwapSimple costs 255123 + // Gas usage: 248511 + // TychoRouter:testSwapSimple costs 113647 function testUniversalRouterUniswapV2() public { uint256 amountIn = 10 ** 18; @@ -49,8 +49,34 @@ contract GasTest is Commands, Test, Constants { universalRouter.execute(commands, inputs, block.timestamp + 1000); } - // Gas usage: 251900 - // TychoRouter:testSwapSingleUSV3 costs 264195 + // Gas usage: 296248 + // TychoRouter:testSwapSimplePermit2 costs 184993 + function testUniversalRouterUniswapV2Permit2() public { + uint256 amountIn = 10 ** 18; + + bytes memory commands = + abi.encodePacked(uint8(Commands.V2_SWAP_EXACT_IN)); + + address[] memory path = new address[](2); + path[0] = WETH_ADDR; + path[1] = DAI_ADDR; + + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, true); + + deal(WETH_ADDR, address(this), amountIn); + IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, amountIn); + permit2.approve( + WETH_ADDR, + address(universalRouter), + uint160(amountIn), + uint48(block.timestamp + 1000) + ); + universalRouter.execute(commands, inputs, block.timestamp + 1000); + } + + // Gas usage: 252003 + // TychoRouter:testSwapSingleUSV3 costs 126181 function testUniversalRouterUniswapV3() public { uint256 amountIn = 10 ** 18; @@ -67,9 +93,34 @@ contract GasTest is Commands, Test, Constants { universalRouter.execute(commands, inputs, block.timestamp + 1000); } - // Gas usage: 299427 - // TychoRouter:testSwapSingleUSV4Callback costs 286025 - function testUniversalRouterUniswapV4() public { + // Gas usage: 299036 + // TychoRouter:testSwapSingleUSV3Permit2 costs 192780 + function testUniversalRouterUniswapV3Permit2() public { + uint256 amountIn = 10 ** 18; + + bytes memory commands = + abi.encodePacked(uint8(Commands.V3_SWAP_EXACT_IN)); + + uint24 poolFee = 3000; + bytes memory path = abi.encodePacked(WETH_ADDR, poolFee, DAI_ADDR); + + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, true); + + deal(WETH_ADDR, address(this), amountIn); + IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, amountIn); + permit2.approve( + WETH_ADDR, + address(universalRouter), + uint160(amountIn), + uint48(block.timestamp + 1000) + ); + universalRouter.execute(commands, inputs, block.timestamp + 1000); + } + + // Gas usage: 299523 + // TychoRouter:testSwapSingleUSV4CallbackPermit2 costs 217751 + function testUniversalRouterUniswapV4Permit2() public { uint128 amountIn = uint128(100 ether); uint128 amountOutMinimum = uint128(0); uint256 deadline = block.timestamp + 1000; diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index d23e276..fd80824 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -231,6 +231,50 @@ contract TychoRouterTest is TychoRouterTestSetup { assertEq(IERC20(WETH_ADDR).balanceOf(tychoRouterAddr), 0); } + function testSwapSimplePermit2() public { + // Trade 1 WETH for DAI with 1 swap on Uniswap V2 using Permit2 + // 1 WETH -> DAI + // (USV2) + vm.startPrank(ALICE); + + uint256 amountIn = 1 ether; + deal(WETH_ADDR, ALICE, amountIn); + ( + IAllowanceTransfer.PermitSingle memory permitSingle, + bytes memory signature + ) = handlePermit2Approval(WETH_ADDR, amountIn); + + bytes memory protocolData = encodeUniswapV2Swap( + WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + ); + + bytes memory swap = encodeSwap( + uint8(0), uint8(1), uint24(0), address(usv2Executor), protocolData + ); + bytes[] memory swaps = new bytes[](1); + swaps[0] = swap; + + tychoRouter.swapPermit2( + amountIn, + WETH_ADDR, + DAI_ADDR, + 0, + false, + false, + 2, + ALICE, + permitSingle, + signature, + pleEncode(swaps) + ); + + uint256 daiBalance = IERC20(DAI_ADDR).balanceOf(ALICE); + assertEq(daiBalance, 2659881924818443699787); + assertEq(IERC20(WETH_ADDR).balanceOf(ALICE), 0); + + vm.stopPrank(); + } + function testSwapMultipleHops() public { // Trade 1 WETH for USDC through DAI with 2 swaps on Uniswap V2 // 1 WETH -> DAI -> USDC @@ -633,6 +677,50 @@ contract TychoRouterTest is TychoRouterTestSetup { assertGe(finalBalance, expAmountOut); } + function testSwapSingleUSV3Permit2() public { + // Trade 1 WETH for DAI with 1 swap on Uniswap V3 using Permit2 + // 1 WETH -> DAI + // (USV3) + vm.startPrank(ALICE); + uint256 amountIn = 10 ** 18; + deal(WETH_ADDR, ALICE, amountIn); + ( + IAllowanceTransfer.PermitSingle memory permitSingle, + bytes memory signature + ) = handlePermit2Approval(WETH_ADDR, amountIn); + + 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 + ); + bytes memory swap = encodeSwap( + uint8(0), uint8(1), uint24(0), address(usv3Executor), protocolData + ); + + bytes[] memory swaps = new bytes[](1); + swaps[0] = swap; + + tychoRouter.swapPermit2( + amountIn, + WETH_ADDR, + DAI_ADDR, + 0, + false, + false, + 2, + ALICE, + permitSingle, + signature, + pleEncode(swaps) + ); + + uint256 finalBalance = IERC20(DAI_ADDR).balanceOf(ALICE); + assertGe(finalBalance, expAmountOut); + + vm.stopPrank(); + } + function testEmptySwapsRevert() public { uint256 amountIn = 10 ** 18; bytes memory swaps = ""; @@ -944,6 +1032,52 @@ contract TychoRouterTest is TychoRouterTestSetup { assertEq(IERC20(USDT_ADDR).balanceOf(tychoRouterAddr), 99943852); } + function testSwapSingleUSV4CallbackPermit2() public { + vm.startPrank(ALICE); + uint256 amountIn = 100 ether; + deal(USDE_ADDR, ALICE, amountIn); + ( + IAllowanceTransfer.PermitSingle memory permitSingle, + bytes memory signature + ) = handlePermit2Approval(USDE_ADDR, amountIn); + + UniswapV4Executor.UniswapV4Pool[] memory pools = + new UniswapV4Executor.UniswapV4Pool[](1); + pools[0] = UniswapV4Executor.UniswapV4Pool({ + intermediaryToken: USDT_ADDR, + fee: uint24(100), + tickSpacing: int24(1) + }); + + bytes memory protocolData = UniswapV4Utils.encodeExactInput( + USDE_ADDR, USDT_ADDR, true, address(usv4Executor), pools + ); + + bytes memory swap = encodeSwap( + uint8(0), uint8(1), uint24(0), address(usv4Executor), protocolData + ); + + bytes[] memory swaps = new bytes[](1); + swaps[0] = swap; + + tychoRouter.swapPermit2( + amountIn, + USDE_ADDR, + USDT_ADDR, + 0, + false, + false, + 2, + ALICE, + permitSingle, + signature, + pleEncode(swaps) + ); + + assertEq(IERC20(USDT_ADDR).balanceOf(ALICE), 99943852); + vm.stopPrank(); + } + function testSwapMultipleUSV4Callback() public { // This test has two uniswap v4 hops that will be executed inside of the V4 pool manager // USDE -> USDT -> WBTC From 5db95781e5afdf8d0270676d7d2845927efedcd2 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Fri, 28 Feb 2025 10:11:04 +0530 Subject: [PATCH 03/17] test: add isPermit2 bool in GasTest for readability --- foundry/test/GasTest.t.sol | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/foundry/test/GasTest.t.sol b/foundry/test/GasTest.t.sol index 90217ab..629b5e1 100644 --- a/foundry/test/GasTest.t.sol +++ b/foundry/test/GasTest.t.sol @@ -33,6 +33,7 @@ contract GasTest is Commands, Test, Constants { // Gas usage: 248511 // TychoRouter:testSwapSimple costs 113647 function testUniversalRouterUniswapV2() public { + bool isPermit2 = false; uint256 amountIn = 10 ** 18; bytes memory commands = @@ -43,7 +44,7 @@ contract GasTest is Commands, Test, Constants { path[1] = DAI_ADDR; bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, false); + inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, isPermit2); deal(WETH_ADDR, address(universalRouter), amountIn); universalRouter.execute(commands, inputs, block.timestamp + 1000); @@ -52,6 +53,7 @@ contract GasTest is Commands, Test, Constants { // Gas usage: 296248 // TychoRouter:testSwapSimplePermit2 costs 184993 function testUniversalRouterUniswapV2Permit2() public { + bool isPermit2 = true; uint256 amountIn = 10 ** 18; bytes memory commands = @@ -62,7 +64,7 @@ contract GasTest is Commands, Test, Constants { path[1] = DAI_ADDR; bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, true); + inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, isPermit2); deal(WETH_ADDR, address(this), amountIn); IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, amountIn); @@ -78,6 +80,7 @@ contract GasTest is Commands, Test, Constants { // Gas usage: 252003 // TychoRouter:testSwapSingleUSV3 costs 126181 function testUniversalRouterUniswapV3() public { + bool isPermit2 = false; uint256 amountIn = 10 ** 18; bytes memory commands = @@ -87,7 +90,7 @@ contract GasTest is Commands, Test, Constants { bytes memory path = abi.encodePacked(WETH_ADDR, poolFee, DAI_ADDR); bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, false); + inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, isPermit2); deal(WETH_ADDR, address(universalRouter), amountIn); universalRouter.execute(commands, inputs, block.timestamp + 1000); @@ -96,6 +99,7 @@ contract GasTest is Commands, Test, Constants { // Gas usage: 299036 // TychoRouter:testSwapSingleUSV3Permit2 costs 192780 function testUniversalRouterUniswapV3Permit2() public { + bool isPermit2 = true; uint256 amountIn = 10 ** 18; bytes memory commands = @@ -105,7 +109,7 @@ contract GasTest is Commands, Test, Constants { bytes memory path = abi.encodePacked(WETH_ADDR, poolFee, DAI_ADDR); bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, true); + inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, isPermit2); deal(WETH_ADDR, address(this), amountIn); IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, amountIn); From 6f69748ba42c0de3b0b45c2d4e199aa9db371e24 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Fri, 28 Feb 2025 22:02:21 +0530 Subject: [PATCH 04/17] test: update gas usage numbers in GasTest --- foundry/test/GasTest.t.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/foundry/test/GasTest.t.sol b/foundry/test/GasTest.t.sol index 629b5e1..a47aa4f 100644 --- a/foundry/test/GasTest.t.sol +++ b/foundry/test/GasTest.t.sol @@ -30,7 +30,7 @@ contract GasTest is Commands, Test, Constants { vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); } - // Gas usage: 248511 + // Gas usage: 72783 // TychoRouter:testSwapSimple costs 113647 function testUniversalRouterUniswapV2() public { bool isPermit2 = false; @@ -50,7 +50,7 @@ contract GasTest is Commands, Test, Constants { universalRouter.execute(commands, inputs, block.timestamp + 1000); } - // Gas usage: 296248 + // Gas usage: 74822 // TychoRouter:testSwapSimplePermit2 costs 184993 function testUniversalRouterUniswapV2Permit2() public { bool isPermit2 = true; @@ -77,7 +77,7 @@ contract GasTest is Commands, Test, Constants { universalRouter.execute(commands, inputs, block.timestamp + 1000); } - // Gas usage: 252003 + // Gas usage: 75917 // TychoRouter:testSwapSingleUSV3 costs 126181 function testUniversalRouterUniswapV3() public { bool isPermit2 = false; @@ -96,7 +96,7 @@ contract GasTest is Commands, Test, Constants { universalRouter.execute(commands, inputs, block.timestamp + 1000); } - // Gas usage: 299036 + // Gas usage: 77962 // TychoRouter:testSwapSingleUSV3Permit2 costs 192780 function testUniversalRouterUniswapV3Permit2() public { bool isPermit2 = true; @@ -122,7 +122,7 @@ contract GasTest is Commands, Test, Constants { universalRouter.execute(commands, inputs, block.timestamp + 1000); } - // Gas usage: 299523 + // Gas usage: 76112 // TychoRouter:testSwapSingleUSV4CallbackPermit2 costs 217751 function testUniversalRouterUniswapV4Permit2() public { uint128 amountIn = uint128(100 ether); From ec8d59eeae75b4c1cdab5efd2a1d22a1921dd1d9 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Sat, 1 Mar 2025 00:09:15 +0530 Subject: [PATCH 05/17] test: remove gas usage numbers from GasTest --- foundry/test/GasTest.t.sol | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/foundry/test/GasTest.t.sol b/foundry/test/GasTest.t.sol index a47aa4f..33c95a6 100644 --- a/foundry/test/GasTest.t.sol +++ b/foundry/test/GasTest.t.sol @@ -19,8 +19,7 @@ contract Commands { } // A gas test to compare the gas usage of the UniversalRouter with the TychoRouter -// The gas usage quoted from the TychoRouter is without any asserts after the swap -// The path executed on TychoRouter is the same as the path executed on UniversalRouter + contract GasTest is Commands, Test, Constants { IUniversalRouter universalRouter = IUniversalRouter(UNIVERSAL_ROUTER); IPermit2 permit2 = IPermit2(PERMIT2_ADDRESS); @@ -30,8 +29,6 @@ contract GasTest is Commands, Test, Constants { vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); } - // Gas usage: 72783 - // TychoRouter:testSwapSimple costs 113647 function testUniversalRouterUniswapV2() public { bool isPermit2 = false; uint256 amountIn = 10 ** 18; @@ -50,8 +47,6 @@ contract GasTest is Commands, Test, Constants { universalRouter.execute(commands, inputs, block.timestamp + 1000); } - // Gas usage: 74822 - // TychoRouter:testSwapSimplePermit2 costs 184993 function testUniversalRouterUniswapV2Permit2() public { bool isPermit2 = true; uint256 amountIn = 10 ** 18; @@ -77,8 +72,6 @@ contract GasTest is Commands, Test, Constants { universalRouter.execute(commands, inputs, block.timestamp + 1000); } - // Gas usage: 75917 - // TychoRouter:testSwapSingleUSV3 costs 126181 function testUniversalRouterUniswapV3() public { bool isPermit2 = false; uint256 amountIn = 10 ** 18; @@ -96,8 +89,6 @@ contract GasTest is Commands, Test, Constants { universalRouter.execute(commands, inputs, block.timestamp + 1000); } - // Gas usage: 77962 - // TychoRouter:testSwapSingleUSV3Permit2 costs 192780 function testUniversalRouterUniswapV3Permit2() public { bool isPermit2 = true; uint256 amountIn = 10 ** 18; @@ -122,8 +113,6 @@ contract GasTest is Commands, Test, Constants { universalRouter.execute(commands, inputs, block.timestamp + 1000); } - // Gas usage: 76112 - // TychoRouter:testSwapSingleUSV4CallbackPermit2 costs 217751 function testUniversalRouterUniswapV4Permit2() public { uint128 amountIn = uint128(100 ether); uint128 amountOutMinimum = uint128(0); From db9c8cde5aaa6cb32dbe74df228fb65d358687a3 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Mon, 3 Mar 2025 18:33:22 +0000 Subject: [PATCH 06/17] fix: Make permit2 permit an action in the universal router --- don't change below this line --- ENG-4286 Took 1 hour 35 minutes --- foundry/test/GasTest.t.sol | 135 ++++++++++++++++++++++++++++--------- 1 file changed, 103 insertions(+), 32 deletions(-) diff --git a/foundry/test/GasTest.t.sol b/foundry/test/GasTest.t.sol index 33c95a6..2bf9bdd 100644 --- a/foundry/test/GasTest.t.sol +++ b/foundry/test/GasTest.t.sol @@ -11,11 +11,13 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Currency} from "../lib/v4-core/src/types/Currency.sol"; import {IHooks} from "../lib/v4-core/src/interfaces/IHooks.sol"; import "forge-std/Test.sol"; +import "@permit2/src/interfaces/IAllowanceTransfer.sol"; contract Commands { uint256 constant V2_SWAP_EXACT_IN = 0x08; uint256 constant V3_SWAP_EXACT_IN = 0x00; uint256 constant V4_SWAP = 0x10; + uint256 constant PERMIT2_PERMIT = 0x0a; } // A gas test to compare the gas usage of the UniversalRouter with the TychoRouter @@ -51,24 +53,26 @@ contract GasTest is Commands, Test, Constants { bool isPermit2 = true; uint256 amountIn = 10 ** 18; - bytes memory commands = - abi.encodePacked(uint8(Commands.V2_SWAP_EXACT_IN)); + bytes memory commands = abi.encodePacked( + uint8(Commands.PERMIT2_PERMIT), uint8(Commands.V2_SWAP_EXACT_IN) + ); + + vm.startPrank(ALICE); + ( + IAllowanceTransfer.PermitSingle memory permitSingle, + bytes memory signature + ) = handlePermit2Approval(WETH_ADDR, amountIn); address[] memory path = new address[](2); path[0] = WETH_ADDR; path[1] = DAI_ADDR; - bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, isPermit2); + bytes[] memory inputs = new bytes[](2); + inputs[0] = abi.encode(permitSingle, signature); + inputs[1] = abi.encode(ALICE, amountIn, uint256(0), path, isPermit2); + + deal(WETH_ADDR, ALICE, amountIn); - deal(WETH_ADDR, address(this), amountIn); - IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, amountIn); - permit2.approve( - WETH_ADDR, - address(universalRouter), - uint160(amountIn), - uint48(block.timestamp + 1000) - ); universalRouter.execute(commands, inputs, block.timestamp + 1000); } @@ -93,23 +97,25 @@ contract GasTest is Commands, Test, Constants { bool isPermit2 = true; uint256 amountIn = 10 ** 18; - bytes memory commands = - abi.encodePacked(uint8(Commands.V3_SWAP_EXACT_IN)); + bytes memory commands = abi.encodePacked( + uint8(Commands.PERMIT2_PERMIT), uint8(Commands.V3_SWAP_EXACT_IN) + ); + + vm.startPrank(ALICE); + ( + IAllowanceTransfer.PermitSingle memory permitSingle, + bytes memory signature + ) = handlePermit2Approval(WETH_ADDR, amountIn); uint24 poolFee = 3000; bytes memory path = abi.encodePacked(WETH_ADDR, poolFee, DAI_ADDR); - bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, isPermit2); + bytes[] memory inputs = new bytes[](2); + inputs[0] = abi.encode(permitSingle, signature); + inputs[1] = abi.encode(ALICE, amountIn, uint256(0), path, isPermit2); + + deal(WETH_ADDR, ALICE, amountIn); - deal(WETH_ADDR, address(this), amountIn); - IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, amountIn); - permit2.approve( - WETH_ADDR, - address(universalRouter), - uint160(amountIn), - uint48(block.timestamp + 1000) - ); universalRouter.execute(commands, inputs, block.timestamp + 1000); } @@ -118,7 +124,9 @@ contract GasTest is Commands, Test, Constants { uint128 amountOutMinimum = uint128(0); uint256 deadline = block.timestamp + 1000; - bytes memory commands = abi.encodePacked(uint8(Commands.V4_SWAP)); + bytes memory commands = abi.encodePacked( + uint8(Commands.PERMIT2_PERMIT), uint8(Commands.V4_SWAP) + ); bytes memory actions = abi.encodePacked( uint8(Actions.SWAP_EXACT_IN_SINGLE), @@ -126,6 +134,12 @@ contract GasTest is Commands, Test, Constants { uint8(Actions.TAKE_ALL) ); + vm.startPrank(ALICE); + ( + IAllowanceTransfer.PermitSingle memory permitSingle, + bytes memory signature + ) = handlePermit2Approval(USDE_ADDR, amountIn); + PoolKey memory key = PoolKey({ currency0: Currency.wrap(USDE_ADDR), currency1: Currency.wrap(USDT_ADDR), @@ -148,14 +162,71 @@ contract GasTest is Commands, Test, Constants { params[1] = abi.encode(key.currency0, amountIn); params[2] = abi.encode(key.currency1, amountOutMinimum); - bytes[] memory inputs = new bytes[](1); - inputs[0] = abi.encode(actions, params); + bytes[] memory inputs = new bytes[](2); + inputs[0] = abi.encode(permitSingle, signature); + inputs[1] = abi.encode(actions, params); + + deal(USDE_ADDR, ALICE, amountIn); - deal(USDE_ADDR, address(this), amountIn); - IERC20(USDE_ADDR).approve(PERMIT2_ADDRESS, amountIn); - permit2.approve( - USDE_ADDR, address(universalRouter), amountIn, uint48(deadline) - ); universalRouter.execute(commands, inputs, deadline); } + + function handlePermit2Approval(address tokenIn, uint256 amount_in) + internal + returns (IAllowanceTransfer.PermitSingle memory, bytes memory) + { + IERC20(tokenIn).approve(PERMIT2_ADDRESS, amount_in); + IAllowanceTransfer.PermitSingle memory permitSingle = IAllowanceTransfer + .PermitSingle({ + details: IAllowanceTransfer.PermitDetails({ + token: tokenIn, + amount: uint160(amount_in), + expiration: uint48(block.timestamp + 1 days), + nonce: 0 + }), + spender: UNIVERSAL_ROUTER, + sigDeadline: block.timestamp + 1 days + }); + + bytes memory signature = signPermit2(permitSingle, ALICE_PK); + return (permitSingle, signature); + } + + function signPermit2( + IAllowanceTransfer.PermitSingle memory permit, + uint256 privateKey + ) internal view returns (bytes memory) { + bytes32 _PERMIT_DETAILS_TYPEHASH = keccak256( + "PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)" + ); + bytes32 _PERMIT_SINGLE_TYPEHASH = keccak256( + "PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)" + ); + bytes32 domainSeparator = keccak256( + abi.encode( + keccak256( + "EIP712Domain(string name,uint256 chainId,address verifyingContract)" + ), + keccak256("Permit2"), + block.chainid, + PERMIT2_ADDRESS + ) + ); + bytes32 detailsHash = + keccak256(abi.encode(_PERMIT_DETAILS_TYPEHASH, permit.details)); + bytes32 permitHash = keccak256( + abi.encode( + _PERMIT_SINGLE_TYPEHASH, + detailsHash, + permit.spender, + permit.sigDeadline + ) + ); + + bytes32 digest = + keccak256(abi.encodePacked("\x19\x01", domainSeparator, permitHash)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + + return abi.encodePacked(r, s, v); + } } From 6f421eb374b798e9521a2a345558fae53f77dae3 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Wed, 5 Mar 2025 00:35:51 +0530 Subject: [PATCH 07/17] fix: inequality check for amountConsumed and amountIn --- foundry/src/TychoRouter.sol | 7 +++---- foundry/test/TychoRouter.t.sol | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index d250695..8897435 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -59,7 +59,7 @@ error TychoRouter__AddressZero(); error TychoRouter__AmountZero(); error TychoRouter__EmptySwaps(); error TychoRouter__NegativeSlippage(uint256 amount, uint256 minAmount); -error TychoRouter__AmountInNotFullySpent(uint256 leftoverAmount); +error TychoRouter__AmountInDiffersFromConsumed(uint256 amountConsumed); error TychoRouter__MessageValueMismatch(uint256 value, uint256 amount); error TychoRouter__InvalidDataLength(); @@ -163,9 +163,8 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { uint256 amountConsumed = initialBalance - currentBalance; - if (amountConsumed < amountIn) { - uint256 leftoverAmount = amountIn - amountConsumed; - revert TychoRouter__AmountInNotFullySpent(leftoverAmount); + if (amountConsumed != amountIn) { + revert TychoRouter__AmountInDiffersFromConsumed(amountConsumed); } if (fee > 0) { diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 5c7caee..1717ddb 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -895,7 +895,8 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.expectRevert( abi.encodeWithSelector( - TychoRouter__AmountInNotFullySpent.selector, 400000000000000000 + TychoRouter__AmountInDiffersFromConsumed.selector, + 600000000000000000 ) ); From a3bffd4f75e8644997970a45c6a8f2b896a30394 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Wed, 5 Mar 2025 00:45:22 +0530 Subject: [PATCH 08/17] fix: add amountIn in error TychoRouter__AmountInDiffersFromConsumed --- foundry/src/TychoRouter.sol | 8 ++++++-- foundry/test/TychoRouter.t.sol | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 8897435..3194c2e 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -59,7 +59,9 @@ error TychoRouter__AddressZero(); error TychoRouter__AmountZero(); error TychoRouter__EmptySwaps(); error TychoRouter__NegativeSlippage(uint256 amount, uint256 minAmount); -error TychoRouter__AmountInDiffersFromConsumed(uint256 amountConsumed); +error TychoRouter__AmountInDiffersFromConsumed( + uint256 amountIn, uint256 amountConsumed +); error TychoRouter__MessageValueMismatch(uint256 value, uint256 amount); error TychoRouter__InvalidDataLength(); @@ -164,7 +166,9 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { uint256 amountConsumed = initialBalance - currentBalance; if (amountConsumed != amountIn) { - revert TychoRouter__AmountInDiffersFromConsumed(amountConsumed); + revert TychoRouter__AmountInDiffersFromConsumed( + amountIn, amountConsumed + ); } if (fee > 0) { diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 1717ddb..08a069e 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -896,6 +896,7 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.expectRevert( abi.encodeWithSelector( TychoRouter__AmountInDiffersFromConsumed.selector, + 1000000000000000000, 600000000000000000 ) ); From 2ad846ed271dd491ad0b972a8bd5f6d6430f92f6 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 4 Mar 2025 19:23:30 +0000 Subject: [PATCH 09/17] chore(release): 0.58.1 [skip ci] ## [0.58.1](https://github.com/propeller-heads/tycho-execution/compare/0.58.0...0.58.1) (2025-03-04) ### Bug Fixes * add amountIn in error TychoRouter__AmountInDiffersFromConsumed ([a3bffd4](https://github.com/propeller-heads/tycho-execution/commit/a3bffd4f75e8644997970a45c6a8f2b896a30394)) * inequality check for amountConsumed and amountIn ([6f421eb](https://github.com/propeller-heads/tycho-execution/commit/6f421eb374b798e9521a2a345558fae53f77dae3)) --- CHANGELOG.md | 8 ++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 999e12e..0dce5ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [0.58.1](https://github.com/propeller-heads/tycho-execution/compare/0.58.0...0.58.1) (2025-03-04) + + +### Bug Fixes + +* add amountIn in error TychoRouter__AmountInDiffersFromConsumed ([a3bffd4](https://github.com/propeller-heads/tycho-execution/commit/a3bffd4f75e8644997970a45c6a8f2b896a30394)) +* inequality check for amountConsumed and amountIn ([6f421eb](https://github.com/propeller-heads/tycho-execution/commit/6f421eb374b798e9521a2a345558fae53f77dae3)) + ## [0.58.0](https://github.com/propeller-heads/tycho-execution/compare/0.57.0...0.58.0) (2025-03-03) diff --git a/Cargo.lock b/Cargo.lock index fa3491a..30d948b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4340,7 +4340,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.58.0" +version = "0.58.1" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 4f8fe68..fba29e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.58.0" +version = "0.58.1" edition = "2021" [[bin]] From 16c57f6aa6472ca750817b951dd7fef096c2a511 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 5 Mar 2025 16:08:56 +0000 Subject: [PATCH 10/17] chore(release): 0.58.2 [skip ci] ## [0.58.2](https://github.com/propeller-heads/tycho-execution/compare/0.58.1...0.58.2) (2025-03-05) ### Bug Fixes * Make permit2 permit an action in the universal router ([db9c8cd](https://github.com/propeller-heads/tycho-execution/commit/db9c8cde5aaa6cb32dbe74df228fb65d358687a3)) --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dce5ec..dcc19d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.58.2](https://github.com/propeller-heads/tycho-execution/compare/0.58.1...0.58.2) (2025-03-05) + + +### Bug Fixes + +* Make permit2 permit an action in the universal router ([db9c8cd](https://github.com/propeller-heads/tycho-execution/commit/db9c8cde5aaa6cb32dbe74df228fb65d358687a3)) + ## [0.58.1](https://github.com/propeller-heads/tycho-execution/compare/0.58.0...0.58.1) (2025-03-04) diff --git a/Cargo.lock b/Cargo.lock index 30d948b..4bfa7f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4340,7 +4340,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.58.1" +version = "0.58.2" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index fba29e9..3d0a84d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.58.1" +version = "0.58.2" edition = "2021" [[bin]] From f853739a3dafb57dc12c77d97a30703a6d65445a Mon Sep 17 00:00:00 2001 From: royvardhan Date: Wed, 5 Mar 2025 21:25:00 +0530 Subject: [PATCH 11/17] feat: add transferFrom in swap and move core swap logic inside _swapChecked --- foundry/src/TychoRouter.sol | 163 ++++++++++++++++++++------------- foundry/test/TychoRouter.t.sol | 47 +++++++++- 2 files changed, 141 insertions(+), 69 deletions(-) diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 3194c2e..b742c0b 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -56,7 +56,6 @@ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; // ✷✷✷✷✷✷ ✷✷✷✷✷ ✷✷✷✷✷✷✷✷ ✷✷✷✷✷✷ ✷✷✷✷✷✷ ✷✷✷✷✷✷✷✷ error TychoRouter__AddressZero(); -error TychoRouter__AmountZero(); error TychoRouter__EmptySwaps(); error TychoRouter__NegativeSlippage(uint256 amount, uint256 minAmount); error TychoRouter__AmountInDiffersFromConsumed( @@ -144,6 +143,104 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { address receiver, bytes calldata swaps ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { + // Transfer the tokenIn token to the router + IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn); + return _swapChecked( + amountIn, + tokenIn, + tokenOut, + minAmountOut, + wrapEth, + unwrapEth, + nTokens, + receiver, + swaps + ); + } + + /** + * @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. + * + * @dev + * - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token. + * - If `unwrapEth` is true, the contract converts the resulting WETH back into native ETH before sending it to the receiver. + * - For ERC20 tokens, Permit2 is used to approve and transfer tokens from the caller to the router. + * - Swaps are executed sequentially using the `_swap` function. + * - A fee is deducted from the output token if `fee > 0`, and the remaining amount is sent to the receiver. + * - Reverts with `TychoRouter__NegativeSlippage` if the output amount is less than `minAmountOut` and `minAmountOut` is greater than 0. + * + * @param amountIn The input token amount to be swapped. + * @param tokenIn The address of the input token. Use `address(0)` for native ETH + * @param tokenOut The address of the output token. Use `address(0)` for native ETH + * @param minAmountOut The minimum acceptable amount of the output token. Reverts if this condition is not met. If it's 0, no check is performed. + * @param wrapEth If true, wraps the input token (native ETH) into WETH. + * @param unwrapEth If true, unwraps the resulting WETH into native ETH and sends it to the receiver. + * @param nTokens The total number of tokens involved in the swap graph (used to initialize arrays for internal calculations). + * @param receiver The address to receive the output tokens. + * @param permitSingle A Permit2 structure containing token approval details for the input token. Ignored if `wrapEth` is true. + * @param signature A valid signature authorizing the Permit2 approval. Ignored if `wrapEth` is true. + * @param swaps Encoded swap graph data containing details of each swap. + * + * @return amountOut The total amount of the output token received by the receiver, after deducting fees if applicable. + */ + function swapPermit2( + uint256 amountIn, + address tokenIn, + address tokenOut, + uint256 minAmountOut, + bool wrapEth, + bool unwrapEth, + uint256 nTokens, + address receiver, + IAllowanceTransfer.PermitSingle calldata permitSingle, + 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. + if (tokenIn != address(0)) { + permit2.permit(msg.sender, permitSingle, signature); + permit2.transferFrom( + msg.sender, + address(this), + uint160(amountIn), + permitSingle.details.token + ); + } + + return _swapChecked( + amountIn, + tokenIn, + tokenOut, + minAmountOut, + wrapEth, + unwrapEth, + nTokens, + receiver, + swaps + ); + } + + /** + * @notice Internal implementation of the core swap logic shared between swap() and swapPermit2(). + * + * @notice This function centralizes the swap execution logic. + * @notice For detailed documentation on parameters and behavior, see the documentation for + * swap() and swapPermit2() functions. + * + */ + function _swapChecked( + uint256 amountIn, + address tokenIn, + address tokenOut, + uint256 minAmountOut, + bool wrapEth, + bool unwrapEth, + uint256 nTokens, + address receiver, + bytes calldata swaps + ) internal returns (uint256 amountOut) { if (receiver == address(0)) { revert TychoRouter__AddressZero(); } @@ -191,70 +288,6 @@ 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. - * - * @dev - * - If `wrapEth` is true, the contract wraps the provided native ETH into WETH and uses it as the sell token. - * - If `unwrapEth` is true, the contract converts the resulting WETH back into native ETH before sending it to the receiver. - * - For ERC20 tokens, Permit2 is used to approve and transfer tokens from the caller to the router. - * - Swaps are executed sequentially using the `_swap` function. - * - A fee is deducted from the output token if `fee > 0`, and the remaining amount is sent to the receiver. - * - Reverts with `TychoRouter__NegativeSlippage` if the output amount is less than `minAmountOut` and `minAmountOut` is greater than 0. - * - * @param amountIn The input token amount to be swapped. - * @param tokenIn The address of the input token. Use `address(0)` for native ETH - * @param tokenOut The address of the output token. Use `address(0)` for native ETH - * @param minAmountOut The minimum acceptable amount of the output token. Reverts if this condition is not met. If it's 0, no check is performed. - * @param wrapEth If true, wraps the input token (native ETH) into WETH. - * @param unwrapEth If true, unwraps the resulting WETH into native ETH and sends it to the receiver. - * @param nTokens The total number of tokens involved in the swap graph (used to initialize arrays for internal calculations). - * @param receiver The address to receive the output tokens. - * @param permitSingle A Permit2 structure containing token approval details for the input token. Ignored if `wrapEth` is true. - * @param signature A valid signature authorizing the Permit2 approval. Ignored if `wrapEth` is true. - * @param swaps Encoded swap graph data containing details of each swap. - * - * @return amountOut The total amount of the output token received by the receiver, after deducting fees if applicable. - */ - function swapPermit2( - uint256 amountIn, - address tokenIn, - address tokenOut, - uint256 minAmountOut, - bool wrapEth, - bool unwrapEth, - uint256 nTokens, - address receiver, - IAllowanceTransfer.PermitSingle calldata permitSingle, - bytes calldata signature, - bytes calldata swaps - ) external payable whenNotPaused returns (uint256 amountOut) { - // For native ETH, assume funds already in our router. Else, transfer and handle approval. - if (tokenIn != address(0)) { - permit2.permit(msg.sender, permitSingle, signature); - permit2.transferFrom( - msg.sender, - address(this), - uint160(amountIn), - permitSingle.details.token - ); - } - - return swap( - amountIn, - tokenIn, - tokenOut, - minAmountOut, - wrapEth, - unwrapEth, - nTokens, - receiver, - swaps - ); - } - /** * @dev Executes sequential swaps as defined by the provided swap graph. * diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 571a060..8af50ad 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -420,10 +420,10 @@ contract TychoRouterTest is TychoRouterTestSetup { // Checks amount out at the end uint256 amountIn = 1 ether; - // Assume Alice has already transferred tokens to the TychoRouter - deal(WETH_ADDR, tychoRouterAddr, amountIn); - + // Approve the tokenIn to be transferred to the router + deal(WETH_ADDR, ALICE, amountIn); vm.startPrank(ALICE); + IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); bytes memory protocolData = encodeUniswapV2Swap( WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false @@ -457,6 +457,43 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } + function testSwapCheckedLessApprovalFailure() public { + // Trade 1 WETH for DAI with 1 swap on Uniswap V2 + // Checks amount out at the end + uint256 amountIn = 1 ether; + + // Approve less than the amountIn + deal(WETH_ADDR, ALICE, amountIn); + vm.startPrank(ALICE); + IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn - 1); + + bytes memory protocolData = encodeUniswapV2Swap( + WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + ); + + bytes memory swap = encodeSwap( + uint8(0), uint8(1), uint24(0), address(usv2Executor), protocolData + ); + bytes[] memory swaps = new bytes[](1); + swaps[0] = swap; + + uint256 minAmountOut = 2600 * 1e18; + vm.expectRevert(); + uint256 amountOut = tychoRouter.swap( + amountIn, + WETH_ADDR, + DAI_ADDR, + minAmountOut, + false, + false, + 2, + ALICE, + pleEncode(swaps) + ); + + vm.stopPrank(); + } + function testSwapCheckedFailure() public { // Trade 1 WETH for DAI with 1 swap on Uniswap V2 // Does permit2 token approval and transfer @@ -760,7 +797,9 @@ contract TychoRouterTest is TychoRouterTestSetup { // address with the USV2 executor address. // Tests swapping WETH -> DAI on a USV2 pool without permit2 - deal(WETH_ADDR, tychoRouterAddr, 1 ether); + deal(WETH_ADDR, ALICE, 1 ether); + vm.startPrank(ALICE); + IERC20(WETH_ADDR).approve(address(tychoRouterAddr), 1 ether); uint256 balancerBefore = IERC20(DAI_ADDR).balanceOf(ALICE); // Encoded solution generated using `test_split_swap_strategy_encoder_simple_route_no_permit2` // but manually replacing the executor address From 208648489844b785476bf7f9ec4873590f405102 Mon Sep 17 00:00:00 2001 From: Harsh Vardhan Roy <42067944+royvardhan@users.noreply.github.com> Date: Wed, 5 Mar 2025 21:40:26 +0530 Subject: [PATCH 12/17] docs: remove unnecessary comment from swap Co-authored-by: Tamara --- foundry/src/TychoRouter.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index b742c0b..1c7bd0a 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -143,7 +143,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { address receiver, bytes calldata swaps ) public payable whenNotPaused nonReentrant returns (uint256 amountOut) { - // Transfer the tokenIn token to the router IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn); return _swapChecked( amountIn, From 783308642524cf917b9870188707318959407a10 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Wed, 5 Mar 2025 22:20:16 +0530 Subject: [PATCH 13/17] fix: TychoRouter swap check test naming and docs --- foundry/test/TychoRouter.t.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 8af50ad..c1795c0 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -420,9 +420,9 @@ contract TychoRouterTest is TychoRouterTestSetup { // Checks amount out at the end uint256 amountIn = 1 ether; - // Approve the tokenIn to be transferred to the router deal(WETH_ADDR, ALICE, amountIn); vm.startPrank(ALICE); + // Approve the tokenIn to be transferred to the router IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn); bytes memory protocolData = encodeUniswapV2Swap( @@ -459,12 +459,12 @@ contract TychoRouterTest is TychoRouterTestSetup { function testSwapCheckedLessApprovalFailure() public { // Trade 1 WETH for DAI with 1 swap on Uniswap V2 - // Checks amount out at the end + // Fails while transferring the tokenIn to the router due to insufficient approval uint256 amountIn = 1 ether; - // Approve less than the amountIn deal(WETH_ADDR, ALICE, amountIn); vm.startPrank(ALICE); + // Approve less than the amountIn IERC20(WETH_ADDR).approve(address(tychoRouterAddr), amountIn - 1); bytes memory protocolData = encodeUniswapV2Swap( @@ -494,7 +494,7 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } - function testSwapCheckedFailure() public { + function testSwapCheckedNegativeSlippageFailure() public { // Trade 1 WETH for DAI with 1 swap on Uniswap V2 // Does permit2 token approval and transfer // Checks amount out at the end and fails From 9eb10e63df14597bb01fb16d115acd8a2a2b96b7 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 5 Mar 2025 17:08:55 +0000 Subject: [PATCH 14/17] chore(release): 0.59.0 [skip ci] ## [0.59.0](https://github.com/propeller-heads/tycho-execution/compare/0.58.2...0.59.0) (2025-03-05) ### Features * add transferFrom in swap and move core swap logic inside _swapChecked ([f853739](https://github.com/propeller-heads/tycho-execution/commit/f853739a3dafb57dc12c77d97a30703a6d65445a)) ### Bug Fixes * TychoRouter swap check test naming and docs ([7833086](https://github.com/propeller-heads/tycho-execution/commit/783308642524cf917b9870188707318959407a10)) --- CHANGELOG.md | 12 ++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcc19d8..dac44cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## [0.59.0](https://github.com/propeller-heads/tycho-execution/compare/0.58.2...0.59.0) (2025-03-05) + + +### Features + +* add transferFrom in swap and move core swap logic inside _swapChecked ([f853739](https://github.com/propeller-heads/tycho-execution/commit/f853739a3dafb57dc12c77d97a30703a6d65445a)) + + +### Bug Fixes + +* TychoRouter swap check test naming and docs ([7833086](https://github.com/propeller-heads/tycho-execution/commit/783308642524cf917b9870188707318959407a10)) + ## [0.58.2](https://github.com/propeller-heads/tycho-execution/compare/0.58.1...0.58.2) (2025-03-05) diff --git a/Cargo.lock b/Cargo.lock index 4bfa7f9..8def08a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4340,7 +4340,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.58.2" +version = "0.59.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 3d0a84d..5fd22ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.58.2" +version = "0.59.0" edition = "2021" [[bin]] From 5c28d77f1d92bb7fba5fa7495e77fcbb5e077eb8 Mon Sep 17 00:00:00 2001 From: Diana Carvalho Date: Wed, 5 Mar 2025 17:49:41 +0000 Subject: [PATCH 15/17] feat: Check min amount out is not zero Update tests --- don't change below this line --- ENG-4286 Took 57 minutes Took 20 seconds --- foundry/src/TychoRouter.sol | 5 +++ foundry/test/TychoRouter.t.sol | 35 +++++++++---------- .../evm/strategy_encoder/strategy_encoders.rs | 26 +++++++------- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 1c7bd0a..a84a1ba 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -63,6 +63,7 @@ error TychoRouter__AmountInDiffersFromConsumed( ); error TychoRouter__MessageValueMismatch(uint256 value, uint256 amount); error TychoRouter__InvalidDataLength(); +error TychoRouter__UndefinedMinAmountOut(); contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { IAllowanceTransfer public immutable permit2; @@ -243,6 +244,10 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { if (receiver == address(0)) { revert TychoRouter__AddressZero(); } + if (minAmountOut == 0) { + revert TychoRouter__UndefinedMinAmountOut(); + } + // Assume funds are already in the router. if (wrapEth) { _wrapETH(amountIn); diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index c1795c0..a95d06d 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -258,7 +258,7 @@ contract TychoRouterTest is TychoRouterTestSetup { amountIn, WETH_ADDR, DAI_ADDR, - 0, + 2659881924818443699786, false, false, 2, @@ -576,7 +576,7 @@ contract TychoRouterTest is TychoRouterTestSetup { amountIn, WETH_ADDR, DAI_ADDR, - 0, + 2633283105570259262780, false, false, 2, @@ -628,7 +628,7 @@ contract TychoRouterTest is TychoRouterTestSetup { amountIn, address(0), DAI_ADDR, - 0, + 2659881924818443699780, true, false, 2, @@ -672,7 +672,7 @@ contract TychoRouterTest is TychoRouterTestSetup { amountIn, DAI_ADDR, address(0), - 0, + 1120007305574805920, false, true, 2, @@ -742,7 +742,7 @@ contract TychoRouterTest is TychoRouterTestSetup { amountIn, WETH_ADDR, DAI_ADDR, - 0, + expAmountOut - 1, false, false, 2, @@ -781,7 +781,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067e4225a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9c620000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000411fdbe0ac6bdafd51044f24b158235effa29797f468cd4684efa379053d3d15d47ed8b8206e3f6e7349f40aad231cc7e04ed25cbea1ac659b575be8cc168fc2361c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067e4225a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9c620000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000411fdbe0ac6bdafd51044f24b158235effa29797f468cd4684efa379053d3d15d47ed8b8206e3f6e7349f40aad231cc7e04ed25cbea1ac659b575be8cc168fc2361c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" ); vm.stopPrank(); @@ -833,7 +833,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // and ensuring that the encoded executor address is the one in this test // `f62849f9a0b5bf2913b396098f7c7019b51a820a` (bool success,) = tychoRouterAddr.call( - hex"d499aa88000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000067e4237600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9d7e00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004166b5d3bb274c323e08eeba45d308cc9c11216f9aaafad2a22e94b94fec39293e5480f65f6238d7c8f1e8177f39118373e1041b0ab3a674d3041d119bdb6bc39c1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008c008a0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d231193300f62849f9a0b5bf2913b396098f7c7019b51a820a0000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000" + hex"d499aa88000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000005064ff624d54346285543f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000067e4237600000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9d7e00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004166b5d3bb274c323e08eeba45d308cc9c11216f9aaafad2a22e94b94fec39293e5480f65f6238d7c8f1e8177f39118373e1041b0ab3a674d3041d119bdb6bc39c1b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008c008a0001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb486982508145454ce325ddbe47a25d4ec3d231193300f62849f9a0b5bf2913b396098f7c7019b51a820a0000000000000000000000000000000000000000000bb800003c6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000000000000000" ); vm.stopPrank(); @@ -858,7 +858,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // and ensuring that the encoded executor address is the one in this test // `f62849f9a0b5bf2913b396098f7c7019b51a820a` (bool success,) = tychoRouterAddr.call{value: 1 ether}( - hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d23119330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067e423f900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9e0100000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004191fb870eca5e2339fd38cd274ca75c2fbb42ffe47a04106d53f22a51c983c5e41e8d2c33be7c4d9e5220e87a42af0853c4cfc264f7ed7363a71b3d1ed89941ce1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007200700001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d231193301f62849f9a0b5bf2913b396098f7c7019b51a820a6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933000000000000000000000000000000000000000000c87c939ae635f92dc2379c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067f01a7800000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067c894800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000416642950b804a47a0abcb17d81cc2a7967d606e00e8de470e0e7827347658160a28b9892f147248b9bf31aad8faa06181aee0c4a612151e9ef4889991b9930b791b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007200700001000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000006982508145454ce325ddbe47a25d4ec3d231193301f62849f9a0b5bf2913b396098f7c7019b51a820a6982508145454ce325ddbe47a25d4ec3d23119330061a80001f40000000000000000000000000000" ); vm.stopPrank(); @@ -887,7 +887,7 @@ contract TychoRouterTest is TychoRouterTestSetup { // and ensuring that the encoded executor address is the one in this test // `f62849f9a0b5bf2913b396098f7c7019b51a820a` (bool success,) = tychoRouterAddr.call( - hex"d499aa8800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e000000000000000000000000000000000000000000000000000000000067e4245900000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9e610000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000415f73f0c9f3edc7ca941874d734f96310db5f1c68d7df17cf00ad0d51915dadf727651a1436920869f7431dda753a8fc9c86ad57b3bbd1c7e86a2416917362a9b1c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007200700001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a0000000000000000000000000000000000000000000bb800003c0000000000000000000000000000" + hex"d499aa8800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f81490b4f29aade000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000b2d05e000000000000000000000000000000000000000000000000000000000067f01af000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067c894f80000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000417811cd10b02278128a9e4df9ef2e099cff6ad46ec6ead5ba0b70dd1db5749d573cf4a8821a524bd6cc5b61ce0faf69d1d4b1f9233b93a4b203e79668f250b1a71c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007200700001000000f62849f9a0b5bf2913b396098f7c7019b51a820aa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a0000000000000000000000000000000000000000000bb800003c0000000000000000000000000000" ); vm.stopPrank(); @@ -910,14 +910,13 @@ contract TychoRouterTest is TychoRouterTestSetup { // Approve permit2 vm.startPrank(ALICE); - // IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, 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 + // `f6c5be66fff9dc69962d73da0a617a827c382329` with the one in this test // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call{value: 1 ether}( - hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067e424b300000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9ebb0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000419db5448f5a0665118d9ea3552572c0d733c3886142d930eda1beb979891fd74612771b3809c4a569b2b2b91fe72bc8214d736eb1fb6cff2f33d1bc9947f1efe91b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000903146e5f6c59c064b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067f0192a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067c893320000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000419849ede1f59ad3092a3d8f6b5d7a4d3d854c8013d0a728b8556dc9744ddeed6c7edc4987c7724c280d493ca8dd55dd5aa5f5a66a66d85683f8a5b744908752a21b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600020000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395000000000000000000" ); vm.stopPrank(); @@ -943,10 +942,10 @@ contract TychoRouterTest is TychoRouterTestSetup { // Encoded solution generated using // `test_split_swap_strategy_encoder_simple_route_unwrap` // but manually replacing the executor address - // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test + // `f6c5be66fff9dc69962d73da0a617a827c382329` with the one in this test // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000067e4250200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9f0a000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041a94c89ae0335fecf539e5b343c84e6e44aff78de119a407512035c8f0d79005d3bdddcb8b6152ab93dc6e338a4af49cdda382273011178a82eaa100e3dbf04a51b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000" + hex"d499aa880000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dbd2fc137a30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc20000000000000000000000006b175474e89094c44da98b954eedeac495271d0f0000000000000000000000000000000000000000000000a2a15d09519be000000000000000000000000000000000000000000000000000000000000067f017d700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067c891df00000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000004181b0d9c7bbf7bd3270e22a7ff337b019b006ea60d9e357035b622bfc8e48126343fa9c1342383d3d072c2ddea2072fd5e447e7b6a4b56f5e7973963d18664e5d1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fa478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d01395010000000000000000" ); vm.stopPrank(); @@ -974,10 +973,10 @@ contract TychoRouterTest is TychoRouterTestSetup { IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, type(uint256).max); // Encoded solution generated using `test_split_swap_strategy_encoder_complex` // but manually replacing the executor address - // `5c2f5a71f67c01775180adc06909288b4c329308` with the one in this test + // `f6c5be66FFf9DC69962d73da0A617a827c382329` with the one in this test // `5615deb798bb3e4dfa0139dfa1b3d433cc23b72f` (bool success,) = tychoRouterAddr.call( - hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067e425a200000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067bc9faa0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000000412cfd5fbb0477fae3b9521a5528afebfe1bffed7b2f5da65d83e8ab6a7e175b1f390705dc7ec3d884b606a3a579b8d735996375fbe6a26987dc236aeaa9736de31b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160005600028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d0139500005602030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d0139501005601030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d0139501" + hex"d499aa880000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000018f61ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000cd09f75e2bf2a4d11f3ab23f1389fcc1621c0cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000000000000067f0198700000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ede3eca2a72b3aecc820e955b36f38437d013950000000000000000000000000000000000000000000000000000000067c8938f000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000041308a3ba881e23ac794deca324cfd959b808c86bb239b81c9db8873c8392382411f87902e6ceb8e59636d8d6fab4ead1863727f9a2168246c93b678f3ae4ae37b1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000160005600028000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a478c2975ab1ea89e8196811f51a7b7ade33eb113ede3eca2a72b3aecc820e955b36f38437d0139500005600010000005615deb798bb3e4dfa0139dfa1b3d433cc23b72fc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2bb2b8038a1640196fbe3e38816f3e67cba72d9403ede3eca2a72b3aecc820e955b36f38437d0139500005602030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f6b175474e89094c44da98b954eedeac495271d0fae461ca67b15dc8dc81ce7615e0320da1a9ab8d53ede3eca2a72b3aecc820e955b36f38437d0139501005601030000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f2260fac5e5542a773aa44fbcfedf7c193bc2c599004375dff511095cc5a197a54140a24efef3a4163ede3eca2a72b3aecc820e955b36f38437d0139501" ); vm.stopPrank(); @@ -1032,7 +1031,7 @@ contract TychoRouterTest is TychoRouterTestSetup { amountIn, WETH_ADDR, DAI_ADDR, - 0, + 1, false, false, 2, @@ -1105,7 +1104,7 @@ contract TychoRouterTest is TychoRouterTestSetup { amountIn, USDE_ADDR, USDT_ADDR, - 0, + 99943850, false, false, 2, diff --git a/src/encoding/evm/strategy_encoder/strategy_encoders.rs b/src/encoding/evm/strategy_encoder/strategy_encoders.rs index 704316b..919ae6f 100644 --- a/src/encoding/evm/strategy_encoder/strategy_encoders.rs +++ b/src/encoding/evm/strategy_encoder/strategy_encoders.rs @@ -564,8 +564,8 @@ mod tests { #[case::with_check_no_slippage( None, None, - Some(BigUint::from_str("3_000_000000000000000000").unwrap()), - U256::from_str("3_000_000000000000000000").unwrap(), + Some(BigUint::from_str("2659881924818443699787").unwrap()), + U256::from_str("2659881924818443699787").unwrap(), )] #[case::no_check_with_slippage( Some(BigUint::from_str("3_000_000000000000000000").unwrap()), @@ -714,8 +714,8 @@ mod tests { given_token: eth(), given_amount: BigUint::from_str("1_000000000000000000").unwrap(), checked_token: dai, - expected_amount: Some(BigUint::from_str("3_000_000000000000000000").unwrap()), - checked_amount: None, + expected_amount: None, + checked_amount: Some(BigUint::from_str("2659881924818443699787").unwrap()), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), @@ -853,8 +853,8 @@ mod tests { given_token: weth, given_amount: BigUint::from_str("1_000000000000000000").unwrap(), checked_token: usdc, - expected_amount: Some(BigUint::from_str("3_000_000000").unwrap()), - checked_amount: None, + expected_amount: None, + checked_amount: Some(BigUint::from_str("26173932").unwrap()), sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), router_address: Bytes::from_str("0x3Ede3eCa2a72B3aeCC820E955B36f38437D01395").unwrap(), @@ -934,8 +934,8 @@ mod tests { given_token: usdc, given_amount: BigUint::from_str("1000_000000").unwrap(), checked_token: pepe, - expected_amount: Some(BigUint::from_str("105_152_000000000000000000").unwrap()), - checked_amount: None, + expected_amount: None, + checked_amount: Some(BigUint::from_str("97191013220606467325121599").unwrap()), slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), @@ -953,7 +953,7 @@ mod tests { "000000000000000000000000000000000000000000000000000000003b9aca00", // amount in "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", // token in "0000000000000000000000006982508145454ce325ddbe47a25d4ec3d2311933", // token out - "0000000000000000000000000000000000000000000000000000000000000000", // min amount out + "0000000000000000000000000000000000000000005064ff624d54346285543f", // min amount out "0000000000000000000000000000000000000000000000000000000000000000", // wrap "0000000000000000000000000000000000000000000000000000000000000000", // unwrap // tokens length (not including intermediary tokens of USV4-optimized swaps) @@ -1135,8 +1135,8 @@ mod tests { given_token: eth, given_amount: BigUint::from_str("1_000000000000000000").unwrap(), checked_token: pepe, - expected_amount: Some(BigUint::from_str("300_000_000000000000000000").unwrap()), - checked_amount: None, + expected_amount: None, + checked_amount: Some(BigUint::from_str("242373460199848577067005852").unwrap()), slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), @@ -1199,8 +1199,8 @@ mod tests { given_token: usdc, given_amount: BigUint::from_str("3000_000000").unwrap(), checked_token: eth, - expected_amount: Some(BigUint::from_str("1_000000000000000000").unwrap()), - checked_amount: None, + expected_amount: None, + checked_amount: Some(BigUint::from_str("1117254495486192350").unwrap()), slippage: None, sender: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), receiver: Bytes::from_str("0xcd09f75E2BF2A4d11F3AB23f1389FcC1621c0cc2").unwrap(), From 09208b342a0bdd790f07fcec0bb9c44aa1a787ee Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Wed, 5 Mar 2025 13:06:06 -0500 Subject: [PATCH 16/17] test: Add testSwapCheckedUndefinedMinAmount To check if the swap actually reverts when min amount is 0. --- foundry/test/TychoRouter.t.sol | 43 ++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index a95d06d..3676b59 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -164,7 +164,6 @@ contract TychoRouterTest is TychoRouterTestSetup { assertEq(tychoRouter.paused(), false); tychoRouter.pause(); assertEq(tychoRouter.paused(), true); - // TODO: test swap calls when implemeted vm.stopPrank(); vm.startPrank(UNPAUSER); @@ -415,6 +414,46 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } + function testSwapCheckedUndefinedMinAmount() public { + // Min amount should always be non-zero. If zero, swap attempt should revert. + + uint256 amountIn = 1 ether; + deal(WETH_ADDR, ALICE, amountIn); + + vm.startPrank(ALICE); + ( + IAllowanceTransfer.PermitSingle memory permitSingle, + bytes memory signature + ) = handlePermit2Approval(WETH_ADDR, amountIn); + + bytes memory protocolData = encodeUniswapV2Swap( + WETH_ADDR, WETH_DAI_POOL, tychoRouterAddr, false + ); + + bytes memory swap = encodeSwap( + uint8(0), uint8(1), uint24(0), address(usv2Executor), protocolData + ); + bytes[] memory swaps = new bytes[](1); + swaps[0] = swap; + uint256 minAmountOut = 0; + + vm.expectRevert(TychoRouter__UndefinedMinAmountOut.selector); + tychoRouter.swapPermit2( + amountIn, + WETH_ADDR, + DAI_ADDR, + minAmountOut, + false, + false, + 2, + ALICE, + permitSingle, + signature, + pleEncode(swaps) + ); + vm.stopPrank(); + } + function testSwapCheckedNoPermit2() public { // Trade 1 WETH for DAI with 1 swap on Uniswap V2 // Checks amount out at the end @@ -479,7 +518,7 @@ contract TychoRouterTest is TychoRouterTestSetup { uint256 minAmountOut = 2600 * 1e18; vm.expectRevert(); - uint256 amountOut = tychoRouter.swap( + tychoRouter.swap( amountIn, WETH_ADDR, DAI_ADDR, From fa47c6057aba9b885eceee1abe2d7ba1706a8a8f Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 5 Mar 2025 18:10:22 +0000 Subject: [PATCH 17/17] chore(release): 0.60.0 [skip ci] ## [0.60.0](https://github.com/propeller-heads/tycho-execution/compare/0.59.0...0.60.0) (2025-03-05) ### Features * Check min amount out is not zero ([5c28d77](https://github.com/propeller-heads/tycho-execution/commit/5c28d77f1d92bb7fba5fa7495e77fcbb5e077eb8)) --- CHANGELOG.md | 7 +++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dac44cf..4805675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [0.60.0](https://github.com/propeller-heads/tycho-execution/compare/0.59.0...0.60.0) (2025-03-05) + + +### Features + +* Check min amount out is not zero ([5c28d77](https://github.com/propeller-heads/tycho-execution/commit/5c28d77f1d92bb7fba5fa7495e77fcbb5e077eb8)) + ## [0.59.0](https://github.com/propeller-heads/tycho-execution/compare/0.58.2...0.59.0) (2025-03-05) diff --git a/Cargo.lock b/Cargo.lock index 8def08a..41413d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4340,7 +4340,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.59.0" +version = "0.60.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 5fd22ee..9f388ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.59.0" +version = "0.60.0" edition = "2021" [[bin]]