From b47cff3fc915b8146d62b085a7a5239d85d9d993 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Wed, 12 Feb 2025 17:36:16 +0530 Subject: [PATCH] fix: add equality check, amountInOrOut check, update _decodeData --- foundry/src/executors/UniswapV4Executor.sol | 104 +++++++++++------- .../test/executors/UniswapV4Executor.t.sol | 34 ++++-- 2 files changed, 89 insertions(+), 49 deletions(-) diff --git a/foundry/src/executors/UniswapV4Executor.sol b/foundry/src/executors/UniswapV4Executor.sol index f72b0d9..8627aa2 100644 --- a/foundry/src/executors/UniswapV4Executor.sol +++ b/foundry/src/executors/UniswapV4Executor.sol @@ -2,14 +2,9 @@ pragma solidity ^0.8.26; import "@interfaces/IExecutor.sol"; -import { - IERC20, - SafeERC20 -} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; -import { - Currency, CurrencyLibrary -} from "@uniswap/v4-core/src/types/Currency.sol"; +import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {V4Router} from "@uniswap/v4-periphery/src/V4Router.sol"; @@ -26,98 +21,127 @@ contract UniswapV4Executor is IExecutor, V4Router { constructor(IPoolManager _poolManager) V4Router(_poolManager) {} - function swap(uint256, bytes calldata data) - external - payable - returns (uint256 amountOut) - { - (address tokenOut, address receiver, bool isExactInput, uint256 amount) - = _decodeData(data); + function swap( + uint256, + bytes calldata data + ) external payable returns (uint256 amountInOrOut) { + ( + address tokenIn, + address tokenOut, + address receiver, + bool isExactInput, + uint256 amount + ) = _decodeData(data); - uint256 balanceBefore = IERC20(tokenOut).balanceOf(receiver); + uint256 tokenOutBefore = IERC20(tokenOut).balanceOf(receiver); + uint256 tokenInBefore = IERC20(tokenIn).balanceOf(address(this)); this.executeActions(data); - uint256 balanceAfter = IERC20(tokenOut).balanceOf(receiver); + uint256 tokenOutAfter = IERC20(tokenOut).balanceOf(receiver); + uint256 tokenInAfter = IERC20(tokenIn).balanceOf(address(this)); if (isExactInput) { - amountOut = balanceAfter - balanceBefore; + amountInOrOut = tokenOutAfter - tokenOutBefore; } else { - amountOut = amount; + amountInOrOut = tokenInBefore - tokenInAfter; } - // Checks if the amountOut is not 0. - // Slither does not allow strict equality checks. - if (amountOut < 1) { + // slither-disable-next-line incorrect-equality + if (amountInOrOut == 0) { revert UniswapV4Executor__SwapFailed(); } - return amountOut; + return amountInOrOut; } function executeActions(bytes calldata actions) public { _executeActions(actions); } - function _decodeData(bytes calldata data) + function _decodeData( + bytes calldata data + ) internal pure returns ( + address tokenIn, address tokenOut, address receiver, bool isExactInput, uint256 amount ) { - (bytes memory actions, bytes[] memory params) = - abi.decode(data, (bytes, bytes[])); + (bytes memory actions, bytes[] memory params) = abi.decode( + data, + (bytes, bytes[]) + ); // First byte of actions determines the swap type uint8 action = uint8(bytes1(actions[0])); // Get receiver from params[2] for all cases - (, receiver,) = abi.decode(params[2], (Currency, address, uint256)); + (, receiver, ) = abi.decode(params[2], (Currency, address, uint256)); if (action == uint8(Actions.SWAP_EXACT_IN_SINGLE)) { - IV4Router.ExactInputSingleParams memory swapParams = - abi.decode(params[0], (IV4Router.ExactInputSingleParams)); + IV4Router.ExactInputSingleParams memory swapParams = abi.decode( + params[0], + (IV4Router.ExactInputSingleParams) + ); + tokenIn = swapParams.zeroForOne + ? address(uint160(swapParams.poolKey.currency0.toId())) + : address(uint160(swapParams.poolKey.currency1.toId())); tokenOut = swapParams.zeroForOne ? address(uint160(swapParams.poolKey.currency1.toId())) : address(uint160(swapParams.poolKey.currency0.toId())); isExactInput = true; amount = swapParams.amountIn; } else if (action == uint8(Actions.SWAP_EXACT_OUT_SINGLE)) { - IV4Router.ExactOutputSingleParams memory swapParams = - abi.decode(params[0], (IV4Router.ExactOutputSingleParams)); + IV4Router.ExactOutputSingleParams memory swapParams = abi.decode( + params[0], + (IV4Router.ExactOutputSingleParams) + ); + tokenIn = swapParams.zeroForOne + ? address(uint160(swapParams.poolKey.currency0.toId())) + : address(uint160(swapParams.poolKey.currency1.toId())); tokenOut = swapParams.zeroForOne ? address(uint160(swapParams.poolKey.currency1.toId())) : address(uint160(swapParams.poolKey.currency0.toId())); isExactInput = false; amount = swapParams.amountOut; } else if (action == uint8(Actions.SWAP_EXACT_IN)) { - IV4Router.ExactInputParams memory swapParams = - abi.decode(params[0], (IV4Router.ExactInputParams)); + IV4Router.ExactInputParams memory swapParams = abi.decode( + params[0], + (IV4Router.ExactInputParams) + ); - PathKey memory lastPath = - swapParams.path[swapParams.path.length - 1]; + tokenIn = address(uint160(swapParams.currencyIn.toId())); + PathKey memory lastPath = swapParams.path[ + swapParams.path.length - 1 + ]; tokenOut = address(uint160(lastPath.intermediateCurrency.toId())); isExactInput = true; amount = swapParams.amountIn; } else if (action == uint8(Actions.SWAP_EXACT_OUT)) { - IV4Router.ExactOutputParams memory swapParams = - abi.decode(params[0], (IV4Router.ExactOutputParams)); + IV4Router.ExactOutputParams memory swapParams = abi.decode( + params[0], + (IV4Router.ExactOutputParams) + ); + PathKey memory firstPath = swapParams.path[0]; + tokenIn = address(uint160(firstPath.intermediateCurrency.toId())); tokenOut = address(uint160(swapParams.currencyOut.toId())); isExactInput = false; amount = swapParams.amountOut; } } - function _pay(Currency token, address payer, uint256 amount) - internal - override - { + function _pay( + Currency token, + address payer, + uint256 amount + ) internal override { token.transfer(payer, amount); } diff --git a/foundry/test/executors/UniswapV4Executor.t.sol b/foundry/test/executors/UniswapV4Executor.t.sol index 249c78d..594b986 100644 --- a/foundry/test/executors/UniswapV4Executor.t.sol +++ b/foundry/test/executors/UniswapV4Executor.t.sol @@ -9,10 +9,13 @@ import {console} from "forge-std/console.sol"; contract UniswapV4ExecutorExposed is UniswapV4Executor { constructor(IPoolManager _poolManager) UniswapV4Executor(_poolManager) {} - function decodeData(bytes calldata data) + function decodeData( + bytes calldata data + ) external pure returns ( + address tokenIn, address tokenOut, address receiver, bool isExactInput, @@ -34,8 +37,9 @@ contract UniswapV4ExecutorTest is Test, Constants { function setUp() public { uint256 forkBlock = 21817316; vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); - uniswapV4Exposed = - new UniswapV4ExecutorExposed(IPoolManager(poolManager)); + uniswapV4Exposed = new UniswapV4ExecutorExposed( + IPoolManager(poolManager) + ); } function testDecodeParams() public view { @@ -53,9 +57,15 @@ contract UniswapV4ExecutorTest is Test, Constants { expectedAmount ); - (address tokenOut, address receiver, bool isExactInput, uint256 amount) - = uniswapV4Exposed.decodeData(data); + ( + address tokenIn, + address tokenOut, + address receiver, + bool isExactInput, + uint256 amount + ) = uniswapV4Exposed.decodeData(data); + assertEq(tokenIn, USDE_ADDR); assertEq(tokenOut, USDT_ADDR); assertEq(receiver, expectedReceiver); assertTrue(isExactInput); @@ -63,15 +73,21 @@ contract UniswapV4ExecutorTest is Test, Constants { } function testSwap() public { - vm.startPrank(BOB); uint256 amountIn = 100 ether; deal(USDE_ADDR, address(uniswapV4Exposed), amountIn); uint256 usdeBalanceBeforePool = USDE.balanceOf(poolManager); - uint256 usdeBalanceBeforeSwapExecutor = - USDE.balanceOf(address(uniswapV4Exposed)); + uint256 usdeBalanceBeforeSwapExecutor = USDE.balanceOf( + address(uniswapV4Exposed) + ); bytes memory data = _encodeExactInputSingle( - USDE_ADDR, USDT_ADDR, 100, BOB, true, 1, uint128(amountIn) + USDE_ADDR, + USDT_ADDR, + 100, + BOB, + true, + 1, + uint128(amountIn) ); uint256 amountOut = uniswapV4Exposed.swap(amountIn, data);