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