From 4d67df40965414caff94f8660c70f4acad51482f Mon Sep 17 00:00:00 2001 From: royvardhan Date: Thu, 6 Mar 2025 22:00:25 +0530 Subject: [PATCH] feat: add cyclicSwapAmountOut tracker in _swap, add split cylic tests --- foundry/src/TychoRouter.sol | 11 ++-- foundry/test/Constants.sol | 1 + foundry/test/TychoRouter.t.sol | 99 +++++++++++++++++++++++----------- 3 files changed, 75 insertions(+), 36 deletions(-) diff --git a/foundry/src/TychoRouter.sol b/foundry/src/TychoRouter.sol index 59ca036..4e16c70 100644 --- a/foundry/src/TychoRouter.sol +++ b/foundry/src/TychoRouter.sol @@ -329,6 +329,7 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { uint256[] memory remainingAmounts = new uint256[](nTokens); uint256[] memory amounts = new uint256[](nTokens); + uint256 cyclicSwapAmountOut = 0; amounts[0] = amountIn; remainingAmounts[0] = amountIn; @@ -345,13 +346,15 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard { currentAmountOut = _callExecutor( swapData.executor(), currentAmountIn, swapData.protocolData() ); - amounts[tokenOutIndex] = tokenOutIndex == 0 - ? currentAmountOut - : amounts[tokenOutIndex] + currentAmountOut; + if (tokenOutIndex == 0) { + cyclicSwapAmountOut += currentAmountOut; + } else { + amounts[tokenOutIndex] += currentAmountOut; + } remainingAmounts[tokenOutIndex] += currentAmountOut; remainingAmounts[tokenInIndex] -= currentAmountIn; } - return amounts[tokenOutIndex]; + return tokenOutIndex == 0 ? cyclicSwapAmountOut : amounts[tokenOutIndex]; } /** diff --git a/foundry/test/Constants.sol b/foundry/test/Constants.sol index 8677b87..9149dcf 100644 --- a/foundry/test/Constants.sol +++ b/foundry/test/Constants.sol @@ -45,6 +45,7 @@ contract Constants is Test, BaseConstants { address DAI_USDC_POOL = 0xAE461cA67B15dc8dc81CE7615e0320dA1A9aB8D5; address WETH_WBTC_POOL = 0xBb2b8038a1640196FbE3e38816F3e67Cba72D940; address USDC_WBTC_POOL = 0x004375Dff511095CC5A197A54140a24eFEF3A416; + address USDC_WETH_USV2 = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc; // Uniswap v3 address USV3_FACTORY_ETHEREUM = 0x1F98431c8aD98523631AE4a59f267346ea31F984; diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index 0eb5bae..5fe98b1 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -1195,26 +1195,26 @@ contract TychoRouterTest is TychoRouterTestSetup { function testCyclicSequentialSwap() public { // This test has start and end tokens that are the same // The flow is: - // USDC -> WETH -> USDC -> WETH -> USDC using two pools, and four swaps + // USDC -> WETH -> USDC using two pools uint256 amountIn = 100 * 10 ** 6; deal(USDC_ADDR, tychoRouterAddr, amountIn); - bytes memory usdcWethPoolOneZeroForOneData = encodeUniswapV3Swap( + bytes memory usdcWethV3Pool1ZeroOneData = encodeUniswapV3Swap( USDC_ADDR, WETH_ADDR, tychoRouterAddr, USDC_WETH_USV3, true ); - bytes memory usdcWethPoolTwoOneForZeroData = encodeUniswapV3Swap( + bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap( WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3_2, false ); - bytes[] memory swaps = new bytes[](4); + bytes[] memory swaps = new bytes[](2); // USDC -> WETH swaps[0] = encodeSwap( uint8(0), uint8(1), uint24(0), address(usv3Executor), - usdcWethPoolOneZeroForOneData + usdcWethV3Pool1ZeroOneData ); // WETH -> USDC swaps[1] = encodeSwap( @@ -1222,30 +1222,14 @@ contract TychoRouterTest is TychoRouterTestSetup { uint8(0), uint24(0), address(usv3Executor), - usdcWethPoolTwoOneForZeroData - ); - // USDC -> WETH - swaps[2] = encodeSwap( - uint8(0), - uint8(1), - uint24(0), - address(usv3Executor), - usdcWethPoolOneZeroForOneData - ); - // WETH -> USDC - swaps[3] = encodeSwap( - uint8(1), - uint8(0), - uint24(0), - address(usv3Executor), - usdcWethPoolTwoOneForZeroData + usdcWethV3Pool2OneZeroData ); tychoRouter.exposedSwap(amountIn, 2, pleEncode(swaps)); - assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99778590); + assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99889294); } - function testCyclicSplitSwap() public { + function testSplitInputCyclicSwap() public { // This test has start and end tokens that are the same // The flow is: // ┌─── WETH (Pool 1) ───┐ @@ -1259,18 +1243,18 @@ contract TychoRouterTest is TychoRouterTestSetup { uint256 amountIn = 100 * 10 ** 6; deal(USDC_ADDR, tychoRouterAddr, amountIn); - bytes memory usdcWethPoolOneZeroForOneData = encodeUniswapV3Swap( + bytes memory usdcWethV3Pool1ZeroOneData = encodeUniswapV3Swap( USDC_ADDR, WETH_ADDR, tychoRouterAddr, USDC_WETH_USV3, true ); - bytes memory usdcWethPoolOneOneForZeroData = encodeUniswapV3Swap( + bytes memory usdcWethV3Pool1OneZeroData = encodeUniswapV3Swap( WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3, false ); - bytes memory usdcWethPoolTwoZeroForOneData = encodeUniswapV3Swap( + bytes memory usdcWethV3Pool2ZeroOneData = encodeUniswapV3Swap( USDC_ADDR, WETH_ADDR, tychoRouterAddr, USDC_WETH_USV3_2, true ); - bytes memory usdcWethPoolTwoOneForZeroData = encodeUniswapV3Swap( + bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap( WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3_2, false ); @@ -1281,7 +1265,7 @@ contract TychoRouterTest is TychoRouterTestSetup { uint8(1), (0xffffff * 60) / 100, // 60% address(usv3Executor), - usdcWethPoolOneZeroForOneData + usdcWethV3Pool1ZeroOneData ); // WETH -> USDC swaps[1] = encodeSwap( @@ -1289,7 +1273,7 @@ contract TychoRouterTest is TychoRouterTestSetup { uint8(0), uint24(0), address(usv3Executor), - usdcWethPoolTwoOneForZeroData + usdcWethV3Pool2OneZeroData ); // USDC -> WETH @@ -1298,7 +1282,7 @@ contract TychoRouterTest is TychoRouterTestSetup { uint8(1), uint24(0), address(usv3Executor), - usdcWethPoolTwoZeroForOneData + usdcWethV3Pool2ZeroOneData ); // WETH -> USDC @@ -1307,12 +1291,63 @@ contract TychoRouterTest is TychoRouterTestSetup { uint8(0), uint24(0), address(usv3Executor), - usdcWethPoolOneOneForZeroData + usdcWethV3Pool1OneZeroData ); tychoRouter.exposedSwap(amountIn, 2, pleEncode(swaps)); assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99345512); } + function testSplitOutputCyclicSwap() public { + // This test has start and end tokens that are the same + // The flow is: + // ┌─── WETH (Pool 2, 60% split) ───┐ + // │ │ + // USDC ----─┤ ├─> USDC + // │ │ + // └─── WETH (Pool 2, the rest) ───┘ + // + uint256 amountIn = 100 * 10 ** 6; + deal(USDC_ADDR, tychoRouterAddr, amountIn); + + bytes memory usdcWethV2Data = encodeUniswapV2Swap( + USDC_ADDR, USDC_WETH_USV2, tychoRouterAddr, true + ); + + bytes memory usdcWethV3Pool1OneZeroData = encodeUniswapV3Swap( + WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3, false + ); + + bytes memory usdcWethV3Pool2OneZeroData = encodeUniswapV3Swap( + WETH_ADDR, USDC_ADDR, tychoRouterAddr, USDC_WETH_USV3_2, false + ); + + bytes[] memory swaps = new bytes[](3); + // USDC -> WETH + swaps[0] = encodeSwap( + uint8(0), uint8(1), uint24(0), address(usv2Executor), usdcWethV2Data + ); + // WETH -> USDC + swaps[1] = encodeSwap( + uint8(1), + uint8(0), + (0xffffff * 60) / 100, + address(usv3Executor), + usdcWethV3Pool1OneZeroData + ); + + // WETH -> USDC + swaps[2] = encodeSwap( + uint8(1), + uint8(0), + uint24(0), + address(usv3Executor), + usdcWethV3Pool2OneZeroData + ); + + tychoRouter.exposedSwap(amountIn, 2, pleEncode(swaps)); + assertEq(IERC20(USDC_ADDR).balanceOf(tychoRouterAddr), 99525908); + } + // Base Network Tests // Make sure to set the RPC_URL to base network function testSwapSingleBase() public {