|
| 1 | +// SPDX-License-Identifier: UNLICENSED |
| 2 | +pragma solidity ^0.8.24; |
| 3 | + |
| 4 | +import {CurrencyLibrary, Currency} from "@uniswap/v4-core/src/types/Currency.sol"; |
| 5 | +import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; |
| 6 | +import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; |
| 7 | +import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; |
| 8 | +import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; |
| 9 | +import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; |
| 10 | +import {CurrencySettler} from "@uniswap/v4-core/test/utils/CurrencySettler.sol"; |
| 11 | +import {SafeCallback} from "v4-periphery/src/base/SafeCallback.sol"; |
| 12 | +import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; |
| 13 | + |
| 14 | +contract MinimalRouter is SafeCallback { |
| 15 | + using CurrencySettler for Currency; |
| 16 | + |
| 17 | + uint160 public constant MIN_PRICE_LIMIT = TickMath.MIN_SQRT_PRICE + 1; |
| 18 | + uint160 public constant MAX_PRICE_LIMIT = TickMath.MAX_SQRT_PRICE - 1; |
| 19 | + |
| 20 | + constructor(IPoolManager _manager) SafeCallback(_manager) {} |
| 21 | + |
| 22 | + function swap(PoolKey memory key, bool zeroForOne, bool exactInput, uint256 amount, bytes memory hookData) |
| 23 | + external |
| 24 | + payable |
| 25 | + returns (BalanceDelta delta) |
| 26 | + { |
| 27 | + delta = abi.decode( |
| 28 | + poolManager.unlock(abi.encode(msg.sender, key, zeroForOne, exactInput, amount, hookData)), (BalanceDelta) |
| 29 | + ); |
| 30 | + |
| 31 | + uint256 ethBalance = address(this).balance; |
| 32 | + if (ethBalance > 0) CurrencyLibrary.ADDRESS_ZERO.transfer(msg.sender, ethBalance); |
| 33 | + } |
| 34 | + |
| 35 | + function _unlockCallback(bytes calldata data) internal override returns (bytes memory) { |
| 36 | + (address sender, PoolKey memory key, bool zeroForOne, bool exactInput, uint256 amount, bytes memory hookData) = |
| 37 | + abi.decode(data, (address, PoolKey, bool, bool, uint256, bytes)); |
| 38 | + |
| 39 | + // for exact input swaps, send the input first to avoid PoolManager token balance issues |
| 40 | + if (exactInput) { |
| 41 | + zeroForOne |
| 42 | + ? key.currency0.settle(poolManager, sender, amount, false) |
| 43 | + : key.currency1.settle(poolManager, sender, amount, false); |
| 44 | + } |
| 45 | + |
| 46 | + BalanceDelta delta = poolManager.swap( |
| 47 | + key, |
| 48 | + IPoolManager.SwapParams({ |
| 49 | + zeroForOne: zeroForOne, |
| 50 | + amountSpecified: exactInput ? -int256(amount) : int256(amount), |
| 51 | + sqrtPriceLimitX96: zeroForOne ? MIN_PRICE_LIMIT : MAX_PRICE_LIMIT |
| 52 | + }), |
| 53 | + hookData |
| 54 | + ); |
| 55 | + |
| 56 | + if (!exactInput && delta.amount0() < 0) { |
| 57 | + key.currency0.settle(poolManager, sender, uint256(int256(-delta.amount0())), false); |
| 58 | + } else if (delta.amount0() > 0) { |
| 59 | + key.currency0.take(poolManager, sender, uint256(int256(delta.amount0())), false); |
| 60 | + } |
| 61 | + |
| 62 | + if (!exactInput && delta.amount1() < 0) { |
| 63 | + key.currency1.settle(poolManager, sender, uint256(int256(-delta.amount1())), false); |
| 64 | + } else if (delta.amount1() > 0) { |
| 65 | + key.currency1.take(poolManager, sender, uint256(int256(delta.amount1())), false); |
| 66 | + } |
| 67 | + |
| 68 | + return abi.encode(delta); |
| 69 | + } |
| 70 | +} |
0 commit comments