diff --git a/foundry/src/executors/UniswapV4Executor.sol b/foundry/src/executors/UniswapV4Executor.sol index ce17196..c05d2f8 100644 --- a/foundry/src/executors/UniswapV4Executor.sol +++ b/foundry/src/executors/UniswapV4Executor.sol @@ -332,6 +332,7 @@ contract UniswapV4Executor is uint128 amountOut = 0; Currency swapCurrencyIn = currencyIn; uint256 swapAmountIn = amountIn; + _settle(currencyIn, amountIn, transferType); unchecked { uint256 pathLength = path.length; PathKey calldata pathKey; @@ -353,12 +354,6 @@ contract UniswapV4Executor is } } - uint256 amount = _getFullDebt(currencyIn); - if (amount > amountIn) { - revert UniswapV4Executor__V4TooMuchRequested(amountIn, amount); - } - _settle(currencyIn, amount, transferType); - _take( swapCurrencyIn, // at the end of the loop this is actually currency out receiver, @@ -409,21 +404,6 @@ contract UniswapV4Executor is amount = uint256(_amount); } - /// @notice Obtain the full amount owed by this contract (negative delta) - /// @param currency Currency to get the delta for - /// @return amount The amount owed by this contract as a uint256 - function _getFullDebt(Currency currency) - internal - view - returns (uint256 amount) - { - int256 _amount = poolManager.currencyDelta(address(this), currency); - // If the amount is positive, it should be taken not settled. - if (_amount > 0) revert UniswapV4Executor__DeltaNotNegative(currency); - // Casting is safe due to limits on the total supply of a pool - amount = uint256(-_amount); - } - /** * @notice Pays and settles a currency to the pool manager. * @dev The implementing contract must ensure that the `payer` is a secure address. diff --git a/foundry/test/protocols/UniswapV4.t.sol b/foundry/test/protocols/UniswapV4.t.sol index 2cd63a7..f0b7a4a 100644 --- a/foundry/test/protocols/UniswapV4.t.sol +++ b/foundry/test/protocols/UniswapV4.t.sol @@ -282,6 +282,7 @@ contract UniswapV4ExecutorTestForEuler is Constants, TestUtils { UniswapV4ExecutorExposed uniswapV4Exposed; IERC20 USDT = IERC20(USDT_ADDR); IERC20 RLUSD = IERC20(RLUSD_ADDR); + IERC20 WBTC = IERC20(WBTC_ADDR); function setUp() public { uint256 forkBlock = 23535338; @@ -322,6 +323,82 @@ contract UniswapV4ExecutorTestForEuler is Constants, TestUtils { ); assertTrue(USDT.balanceOf(ALICE) == amountOut); } + + function testMultipleSwapEulerLowBalance() public { + // RLUSD -(euler)-> USDT -> WBTC + uint256 amountIn = 134187695711754971245517404; + deal(RLUSD_ADDR, address(uniswapV4Exposed), amountIn); + address eulerProxy = 0xe1Ce9AF672f8854845E5474400B6ddC7AE458a10; + uint256 rlusdEulerBalanceBefore = RLUSD.balanceOf(eulerProxy); + + UniswapV4Executor.UniswapV4Pool[] memory pools = + new UniswapV4Executor.UniswapV4Pool[](2); + pools[0] = UniswapV4Executor.UniswapV4Pool({ + intermediaryToken: USDT_ADDR, + fee: uint24(50), + tickSpacing: int24(1), + hook: address(0xF87ACF8428F2f9403AAA0256A7272d6549ECa8A8), + hookData: bytes("") + }); + pools[1] = UniswapV4Executor.UniswapV4Pool({ + intermediaryToken: WBTC_ADDR, + fee: uint24(3000), + tickSpacing: int24(60), + hook: address(0), + hookData: bytes("") + }); + + bytes memory data = UniswapV4Utils.encodeExactInput( + RLUSD_ADDR, + WBTC_ADDR, + true, + RestrictTransferFrom.TransferType.Transfer, + ALICE, + pools + ); + + uint256 amountOut = uniswapV4Exposed.swap(amountIn, data); + assertEq( + RLUSD.balanceOf(eulerProxy), rlusdEulerBalanceBefore + amountIn + ); + assertTrue(WBTC.balanceOf(ALICE) == amountOut); + } + + function testMultipleSwapLastSwapEuler() public { + // USDC -> RLUSD -(euler)- > USDT + // Sanity check to see if a grouped swap with Euler in the last hop works + uint256 amountIn = 134187695711754971245517404; + deal(USDC_ADDR, address(uniswapV4Exposed), amountIn); + + UniswapV4Executor.UniswapV4Pool[] memory pools = + new UniswapV4Executor.UniswapV4Pool[](2); + pools[0] = UniswapV4Executor.UniswapV4Pool({ + intermediaryToken: RLUSD_ADDR, + fee: uint24(500), + tickSpacing: int24(10), + hook: address(0), + hookData: bytes("") + }); + pools[1] = UniswapV4Executor.UniswapV4Pool({ + intermediaryToken: USDT_ADDR, + fee: uint24(50), + tickSpacing: int24(1), + hook: address(0xF87ACF8428F2f9403AAA0256A7272d6549ECa8A8), + hookData: bytes("") + }); + + bytes memory data = UniswapV4Utils.encodeExactInput( + USDC_ADDR, + USDT_ADDR, + false, + RestrictTransferFrom.TransferType.Transfer, + ALICE, + pools + ); + + uint256 amountOut = uniswapV4Exposed.swap(amountIn, data); + assertTrue(USDT.balanceOf(ALICE) == amountOut); + } } contract TychoRouterForBalancerV3Test is TychoRouterTestSetup {