diff --git a/foundry/src/executors/UniswapV4Executor.sol b/foundry/src/executors/UniswapV4Executor.sol index 99f9e0d..1eb4921 100644 --- a/foundry/src/executors/UniswapV4Executor.sol +++ b/foundry/src/executors/UniswapV4Executor.sol @@ -2,27 +2,22 @@ pragma solidity ^0.8.28; 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/interfaces/IPoolManager.sol"; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/types/Currency.sol"; import {PoolKey} from "@uniswap/v4-core/types/PoolKey.sol"; import {BalanceDelta} from "@uniswap/v4-core/types/BalanceDelta.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import {IHooks} from "@uniswap/v4-core/interfaces/IHooks.sol"; -import {IUnlockCallback} from - "@uniswap/v4-core/interfaces/callback/IUnlockCallback.sol"; -import {TransientStateLibrary} from - "@uniswap/v4-core/libraries/TransientStateLibrary.sol"; +import {IUnlockCallback} from "@uniswap/v4-core/interfaces/callback/IUnlockCallback.sol"; +import {TransientStateLibrary} from "@uniswap/v4-core/libraries/TransientStateLibrary.sol"; error UniswapV4Executor__InvalidDataLength(); error UniswapV4Executor__SwapFailed(); error UniswapV4Executor__InsufficientOutput(); error UniswapV4Executor__ManagerMismatch(); -contract UniswapV4Executor is IExecutor, IUnlockCallback { +contract UniswapV4Executor is IExecutor { using SafeERC20 for IERC20; using CurrencyLibrary for Currency; using SafeCast for int128; @@ -41,11 +36,10 @@ contract UniswapV4Executor is IExecutor, IUnlockCallback { address receiver; } - function swap(uint256 amountIn, bytes calldata data) - external - payable - returns (uint256 amountOut) - { + function swap( + uint256 amountIn, + bytes calldata data + ) external payable returns (uint256 amountOut) { ( address tokenIn, address tokenOut, @@ -71,10 +65,6 @@ contract UniswapV4Executor is IExecutor, IUnlockCallback { ) }); - // TODO: Find a better place - IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn); - IERC20(tokenIn).approve(target, amountIn); - SwapCallbackData memory callbackData = SwapCallbackData({ key: key, params: params, @@ -101,124 +91,9 @@ contract UniswapV4Executor is IExecutor, IUnlockCallback { } } - // Dev notes: This is inspired by the Uniswap V4 PoolSwapTest.sol - function unlockCallback(bytes calldata rawData) - external - returns (bytes memory) - { - SwapCallbackData memory data = abi.decode(rawData, (SwapCallbackData)); - - IPoolManager poolManager = IPoolManager(msg.sender); - - // Check initial balances - (,, int256 deltaBefore0) = _fetchBalances( - data.key.currency0, data.receiver, address(this), poolManager - ); - (,, int256 deltaBefore1) = _fetchBalances( - data.key.currency1, data.receiver, address(this), poolManager - ); - - require(deltaBefore0 == 0, "deltaBefore0 not zero"); - require(deltaBefore1 == 0, "deltaBefore1 not zero"); - - BalanceDelta delta = poolManager.swap(data.key, data.params, ""); - - // Check final balances and validate based on swap direction - (,, int256 deltaAfter0) = _fetchBalances( - data.key.currency0, data.receiver, address(this), poolManager - ); - (,, int256 deltaAfter1) = _fetchBalances( - data.key.currency1, data.receiver, address(this), poolManager - ); - - uint256 amountOut; - if (data.params.zeroForOne) { - if (data.params.amountSpecified < 0) { - // exact input, 0 for 1 - require( - deltaAfter0 >= data.params.amountSpecified, - "insufficient input amount" - ); - require(delta.amount0() == deltaAfter0, "delta mismatch"); - require(deltaAfter1 >= 0, "negative output amount"); - amountOut = deltaAfter1 > 0 ? uint256(deltaAfter1) : 0; - } else { - // exact output, 0 for 1 - require(deltaAfter0 <= 0, "positive input amount"); - require(delta.amount1() == deltaAfter1, "delta mismatch"); - require( - deltaAfter1 <= data.params.amountSpecified, - "excessive output amount" - ); - amountOut = uint256((-delta.amount1()).toUint256()); - } - } else { - if (data.params.amountSpecified < 0) { - // exact input, 1 for 0 - require( - deltaAfter1 >= data.params.amountSpecified, - "insufficient input amount" - ); - require(delta.amount1() == deltaAfter1, "delta mismatch"); - require(deltaAfter0 >= 0, "negative output amount"); - amountOut = deltaAfter0 > 0 ? uint256(deltaAfter0) : 0; - } else { - // exact output, 1 for 0 - require(deltaAfter1 <= 0, "positive input amount"); - require(delta.amount0() == deltaAfter0, "delta mismatch"); - require( - deltaAfter0 <= data.params.amountSpecified, - "excessive output amount" - ); - amountOut = uint256((-delta.amount0()).toUint256()); - } - } - - if (deltaAfter0 < 0) { - poolManager.settle{ - value: data.key.currency0.isAddressZero() - ? uint256(-deltaAfter0) - : 0 - }(); - if (!data.key.currency0.isAddressZero()) { - IERC20(Currency.unwrap(data.key.currency0)).transfer( - address(poolManager), uint256(-deltaAfter0) - ); - } - } - if (deltaAfter1 < 0) { - poolManager.settle{ - value: data.key.currency1.isAddressZero() - ? uint256(-deltaAfter1) - : 0 - }(); - if (!data.key.currency1.isAddressZero()) { - IERC20(Currency.unwrap(data.key.currency1)).transfer( - address(poolManager), uint256(-deltaAfter1) - ); - } - } - if (deltaAfter0 > 0) { - poolManager.take( - data.key.currency0, data.receiver, uint256(deltaAfter0) - ); - } - if (deltaAfter1 > 0) { - poolManager.take( - data.key.currency1, data.receiver, uint256(deltaAfter1) - ); - } - - // Handle any remaining ETH balance - uint256 ethBalance = address(this).balance; - if (ethBalance > 0) { - CurrencyLibrary.ADDRESS_ZERO.transfer(data.receiver, ethBalance); - } - - return abi.encode(amountOut); - } - - function _decodeData(bytes calldata data) + function _decodeData( + bytes calldata data + ) internal pure returns ( @@ -241,19 +116,4 @@ contract UniswapV4Executor is IExecutor, IUnlockCallback { target = address(bytes20(data[63:83])); zeroForOne = uint8(data[83]) > 0; } - - function _fetchBalances( - Currency currency, - address user, - address deltaHolder, - IPoolManager manager - ) - internal - view - returns (uint256 userBalance, uint256 poolBalance, int256 delta) - { - userBalance = currency.balanceOf(user); - poolBalance = currency.balanceOf(address(manager)); - delta = manager.currencyDelta(deltaHolder, currency); - } }