From c20e8940c318dd397492528c4d39fd98536622a7 Mon Sep 17 00:00:00 2001 From: 0x57 Date: Mon, 4 Mar 2024 22:07:17 +0800 Subject: [PATCH 1/5] Update v4-core submodule to use https (#97) Co-authored-by: saucepoint <98790946+saucepoint@users.noreply.github.com> --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 9e4b995c..d2dc450b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,7 +9,7 @@ url = https://github.com/marktoda/forge-gas-snapshot [submodule "lib/v4-core"] path = lib/v4-core - url = git@github.com:Uniswap/v4-core.git + url = https://github.com/Uniswap/v4-core [submodule "lib/solmate"] path = lib/solmate url = https://github.com/transmissions11/solmate From f15995f8dae5be2983d1b9648ea1ae01dd82d484 Mon Sep 17 00:00:00 2001 From: mr-uniswap <144828035+mr-uniswap@users.noreply.github.com> Date: Wed, 6 Mar 2024 14:20:42 +0900 Subject: [PATCH 2/5] chore: add semgrep (#94) --- .github/workflows/semgrep.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/semgrep.yml diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml new file mode 100644 index 00000000..c773069b --- /dev/null +++ b/.github/workflows/semgrep.yml @@ -0,0 +1,22 @@ +name: Semgrep +on: + workflow_dispatch: {} + pull_request: {} + push: + branches: + - main + schedule: + # random HH:MM to avoid a load spike on GitHub Actions at 00:00 + - cron: '35 11 * * *' +jobs: + semgrep: + name: semgrep/ci + runs-on: ubuntu-20.04 + env: + SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} + container: + image: returntocorp/semgrep + if: (github.actor != 'dependabot[bot]') + steps: + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 + - run: semgrep ci From 6616b12db25257ffb3c562f131612ebb2fd89082 Mon Sep 17 00:00:00 2001 From: 0x57 Date: Sat, 16 Mar 2024 06:21:52 +0800 Subject: [PATCH 3/5] [Chore] Update v4-core:latest (#100) * Update v4-core * Update various examples, BaseHook, Quoter and tests * Remove nested locking for LimitOrder * Fix Quoter * update v4-core * fix: remove getLocker as its a bool now * update v4-core: flipped signs, push dynamic fees * fix: flip delta signs * flip delta signs * flip delta signs * flip delta signs * fix getSlot0 calls * snapshots * remove deadcode * remove unused param * update core * update for modifyLiquidity; misc doc updates * correct min int256 * allow for manual fee updates --------- Co-authored-by: saucepoint --- .../FullRangeAddInitialLiquidity.snap | 2 +- .forge-snapshots/FullRangeAddLiquidity.snap | 2 +- .forge-snapshots/FullRangeFirstSwap.snap | 2 +- .forge-snapshots/FullRangeInitialize.snap | 2 +- .../FullRangeRemoveLiquidity.snap | 2 +- .../FullRangeRemoveLiquidityAndRebalance.snap | 2 +- .forge-snapshots/FullRangeSecondSwap.snap | 2 +- .forge-snapshots/FullRangeSwap.snap | 2 +- .forge-snapshots/TWAMMSubmitOrder.snap | 2 +- CONTRIBUTING.md | 2 +- README.md | 12 ++--- contracts/BaseHook.sol | 7 +-- contracts/hooks/examples/FullRange.sol | 46 +++++++--------- contracts/hooks/examples/GeomeanOracle.sol | 8 ++- contracts/hooks/examples/LimitOrder.sol | 54 ++++++++----------- contracts/hooks/examples/TWAMM.sol | 33 +++++------- contracts/hooks/examples/VolatilityOracle.sol | 35 +++++++----- contracts/lens/Quoter.sol | 53 +++++++++--------- lib/v4-core | 2 +- test/FullRange.t.sol | 40 +++++++------- test/GeomeanOracle.t.sol | 20 +++---- test/LimitOrder.t.sol | 10 ++-- test/Quoter.t.sol | 12 ++--- test/TWAMM.t.sol | 2 +- test/utils/HookEnabledSwapRouter.sol | 9 ++-- 25 files changed, 167 insertions(+), 196 deletions(-) diff --git a/.forge-snapshots/FullRangeAddInitialLiquidity.snap b/.forge-snapshots/FullRangeAddInitialLiquidity.snap index fda86345..cfdeb354 100644 --- a/.forge-snapshots/FullRangeAddInitialLiquidity.snap +++ b/.forge-snapshots/FullRangeAddInitialLiquidity.snap @@ -1 +1 @@ -392772 \ No newline at end of file +384735 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeAddLiquidity.snap b/.forge-snapshots/FullRangeAddLiquidity.snap index ff9a3f08..e1efe638 100644 --- a/.forge-snapshots/FullRangeAddLiquidity.snap +++ b/.forge-snapshots/FullRangeAddLiquidity.snap @@ -1 +1 @@ -187139 \ No newline at end of file +179102 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeFirstSwap.snap b/.forge-snapshots/FullRangeFirstSwap.snap index 029a908d..fd04e1b1 100644 --- a/.forge-snapshots/FullRangeFirstSwap.snap +++ b/.forge-snapshots/FullRangeFirstSwap.snap @@ -1 +1 @@ -136542 \ No newline at end of file +128152 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeInitialize.snap b/.forge-snapshots/FullRangeInitialize.snap index 44c69e54..b126274c 100644 --- a/.forge-snapshots/FullRangeInitialize.snap +++ b/.forge-snapshots/FullRangeInitialize.snap @@ -1 +1 @@ -1041060 \ No newline at end of file +1017530 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeRemoveLiquidity.snap b/.forge-snapshots/FullRangeRemoveLiquidity.snap index 6ff7a267..2cdf6c52 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidity.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidity.snap @@ -1 +1 @@ -175903 \ No newline at end of file +169304 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap index 10fb1518..2ccb0b58 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap @@ -1 +1 @@ -363995 \ No newline at end of file +345919 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSecondSwap.snap b/.forge-snapshots/FullRangeSecondSwap.snap index c02e1eae..51e5eb70 100644 --- a/.forge-snapshots/FullRangeSecondSwap.snap +++ b/.forge-snapshots/FullRangeSecondSwap.snap @@ -1 +1 @@ -97295 \ No newline at end of file +89081 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSwap.snap b/.forge-snapshots/FullRangeSwap.snap index 8adf5f54..bd033704 100644 --- a/.forge-snapshots/FullRangeSwap.snap +++ b/.forge-snapshots/FullRangeSwap.snap @@ -1 +1 @@ -134817 \ No newline at end of file +126954 \ No newline at end of file diff --git a/.forge-snapshots/TWAMMSubmitOrder.snap b/.forge-snapshots/TWAMMSubmitOrder.snap index 1ac55f85..9191f9b4 100644 --- a/.forge-snapshots/TWAMMSubmitOrder.snap +++ b/.forge-snapshots/TWAMMSubmitOrder.snap @@ -1 +1 @@ -122753 \ No newline at end of file +122845 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 27bceb4d..1364a2f8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ There are many ways to contribute, but here are a few if you want a place to sta ## Opening an Issue -When opening an [issue](https://github.com/Uniswap/periphery-next/issues/new/choose), choose a template to start from: Bug Report or Feature Improvement. For bug reports, you should be able to reproduce the bug through tests or proof of concept integrations. For feature improvements, please title it with a concise problem statement and check that a similar request is not already open or already in progress. Not all issues may be deemed worth resolving, so please follow through with responding to any questions or comments that others may have regarding the issue. +When opening an [issue](https://github.com/Uniswap/v4-periphery/issues/new/choose), choose a template to start from: Bug Report or Feature Improvement. For bug reports, you should be able to reproduce the bug through tests or proof of concept integrations. For feature improvements, please title it with a concise problem statement and check that a similar request is not already open or already in progress. Not all issues may be deemed worth resolving, so please follow through with responding to any questions or comments that others may have regarding the issue. Feel free to tag the issue as a “good first issue” for any clean-up related issues, or small scoped changes to help encourage pull requests from first time contributors! diff --git a/README.md b/README.md index 245785b4..b3355a10 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Uniswap v4 is a new automated market maker protocol that provides extensibility ## Contributing -If you’re interested in contributing please see the [contribution guidelines](https://github.com/Uniswap/periphery-next/blob/main/CONTRIBUTING.md)! +If you’re interested in contributing please see the [contribution guidelines](https://github.com/Uniswap/v4-periphery/blob/main/CONTRIBUTING.md)! ## Repository Structure @@ -31,24 +31,24 @@ Eventually, some hooks that have been audited and are considered production-read To utilize the contracts and deploy to a local testnet, you can install the code in your repo with forge: ```solidity -forge install https://github.com/Uniswap/periphery-next +forge install https://github.com/Uniswap/v4-periphery ``` If you are building hooks, it may be useful to inherit from the `BaseHook` contract: ```solidity -import {BaseHook} from 'periphery-next/contracts/BaseHook.sol'; +import {BaseHook} from 'v4-periphery/contracts/BaseHook.sol'; contract CoolHook is BaseHook { // Override the hook callbacks you want on your hook - function beforeModifyPosition( + function beforeAddLiquidity( address, IPoolManager.PoolKey calldata key, IPoolManager.ModifyLiquidityParams calldata params ) external override poolManagerOnly returns (bytes4) { // hook logic - return BaseHook.beforeModifyPosition.selector; + return BaseHook.beforeAddLiquidity.selector; } } @@ -56,4 +56,4 @@ contract CoolHook is BaseHook { ## License -The license for Uniswap V4 Periphery is the GNU General Public License (GPL 2.0), see [LICENSE](https://github.com/Uniswap/periphery-next/blob/main/LICENSE). +The license for Uniswap V4 Periphery is the GNU General Public License (GPL 2.0), see [LICENSE](https://github.com/Uniswap/v4-periphery/blob/main/LICENSE). diff --git a/contracts/BaseHook.sol b/contracts/BaseHook.sol index 16fdf684..55670dab 100644 --- a/contracts/BaseHook.sol +++ b/contracts/BaseHook.sol @@ -49,12 +49,7 @@ abstract contract BaseHook is IHooks { Hooks.validateHookPermissions(_this, getHookPermissions()); } - function lockAcquired(address, /*sender*/ bytes calldata data) - external - virtual - poolManagerOnly - returns (bytes memory) - { + function lockAcquired(bytes calldata data) external virtual poolManagerOnly returns (bytes memory) { (bool success, bytes memory returnData) = address(this).call(data); if (success) return returnData; if (returnData.length == 0) revert LockFailure(); diff --git a/contracts/hooks/examples/FullRange.sol b/contracts/hooks/examples/FullRange.sol index a57d9dd0..820d0f93 100644 --- a/contracts/hooks/examples/FullRange.sol +++ b/contracts/hooks/examples/FullRange.sol @@ -98,9 +98,7 @@ contract FullRange is BaseHook, ILockCallback { beforeSwap: true, afterSwap: false, beforeDonate: false, - afterDonate: false, - noOp: false, - accessLock: false + afterDonate: false }); } @@ -119,7 +117,7 @@ contract FullRange is BaseHook, ILockCallback { PoolId poolId = key.toId(); - (uint160 sqrtPriceX96,,) = poolManager.getSlot0(poolId); + (uint160 sqrtPriceX96,,,) = poolManager.getSlot0(poolId); if (sqrtPriceX96 == 0) revert PoolNotInitialized(); @@ -138,7 +136,7 @@ contract FullRange is BaseHook, ILockCallback { if (poolLiquidity == 0 && liquidity <= MINIMUM_LIQUIDITY) { revert LiquidityDoesntMeetMinimum(); } - BalanceDelta addedDelta = modifyPosition( + BalanceDelta addedDelta = modifyLiquidity( key, IPoolManager.ModifyLiquidityParams({ tickLower: MIN_TICK, @@ -155,7 +153,7 @@ contract FullRange is BaseHook, ILockCallback { UniswapV4ERC20(pool.liquidityToken).mint(params.to, liquidity); - if (uint128(addedDelta.amount0()) < params.amount0Min || uint128(addedDelta.amount1()) < params.amount1Min) { + if (uint128(-addedDelta.amount0()) < params.amount0Min || uint128(-addedDelta.amount1()) < params.amount1Min) { revert TooMuchSlippage(); } } @@ -176,13 +174,13 @@ contract FullRange is BaseHook, ILockCallback { PoolId poolId = key.toId(); - (uint160 sqrtPriceX96,,) = poolManager.getSlot0(poolId); + (uint160 sqrtPriceX96,,,) = poolManager.getSlot0(poolId); if (sqrtPriceX96 == 0) revert PoolNotInitialized(); UniswapV4ERC20 erc20 = UniswapV4ERC20(poolInfo[poolId].liquidityToken); - delta = modifyPosition( + delta = modifyLiquidity( key, IPoolManager.ModifyLiquidityParams({ tickLower: MIN_TICK, @@ -247,18 +245,16 @@ contract FullRange is BaseHook, ILockCallback { return IHooks.beforeSwap.selector; } - function modifyPosition(PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params) + function modifyLiquidity(PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params) internal returns (BalanceDelta delta) { - delta = abi.decode( - poolManager.lock(address(this), abi.encode(CallbackData(msg.sender, key, params))), (BalanceDelta) - ); + delta = abi.decode(poolManager.lock(abi.encode(CallbackData(msg.sender, key, params))), (BalanceDelta)); } function _settleDeltas(address sender, PoolKey memory key, BalanceDelta delta) internal { - _settleDelta(sender, key.currency0, uint128(delta.amount0())); - _settleDelta(sender, key.currency1, uint128(delta.amount1())); + _settleDelta(sender, key.currency0, uint128(-delta.amount0())); + _settleDelta(sender, key.currency1, uint128(-delta.amount1())); } function _settleDelta(address sender, Currency currency, uint128 amount) internal { @@ -275,8 +271,8 @@ contract FullRange is BaseHook, ILockCallback { } function _takeDeltas(address sender, PoolKey memory key, BalanceDelta delta) internal { - poolManager.take(key.currency0, sender, uint256(uint128(-delta.amount0()))); - poolManager.take(key.currency1, sender, uint256(uint128(-delta.amount1()))); + poolManager.take(key.currency0, sender, uint256(uint128(delta.amount0()))); + poolManager.take(key.currency1, sender, uint256(uint128(delta.amount1()))); } function _removeLiquidity(PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params) @@ -301,14 +297,12 @@ contract FullRange is BaseHook, ILockCallback { pool.hasAccruedFees = false; } - function lockAcquired(address sender, bytes calldata rawData) + function lockAcquired(bytes calldata rawData) external override(ILockCallback, BaseHook) poolManagerOnly returns (bytes memory) { - // Now that manager can be called by EOAs with a lock target, it's necessary for lockAcquired to check the original sender if it wants to trust the data passed through. - if (sender != address(this)) revert SenderMustBeHook(); CallbackData memory data = abi.decode(rawData, (CallbackData)); BalanceDelta delta; @@ -336,17 +330,17 @@ contract FullRange is BaseHook, ILockCallback { uint160 newSqrtPriceX96 = ( FixedPointMathLib.sqrt( - FullMath.mulDiv(uint128(-balanceDelta.amount1()), FixedPoint96.Q96, uint128(-balanceDelta.amount0())) + FullMath.mulDiv(uint128(balanceDelta.amount1()), FixedPoint96.Q96, uint128(balanceDelta.amount0())) ) * FixedPointMathLib.sqrt(FixedPoint96.Q96) ).toUint160(); - (uint160 sqrtPriceX96,,) = poolManager.getSlot0(poolId); + (uint160 sqrtPriceX96,,,) = poolManager.getSlot0(poolId); poolManager.swap( key, IPoolManager.SwapParams({ zeroForOne: newSqrtPriceX96 < sqrtPriceX96, - amountSpecified: MAX_INT, + amountSpecified: -MAX_INT - 1, // equivalent of type(int256).min sqrtPriceLimitX96: newSqrtPriceX96 }), ZERO_BYTES @@ -356,8 +350,8 @@ contract FullRange is BaseHook, ILockCallback { newSqrtPriceX96, TickMath.getSqrtRatioAtTick(MIN_TICK), TickMath.getSqrtRatioAtTick(MAX_TICK), - uint256(uint128(-balanceDelta.amount0())), - uint256(uint128(-balanceDelta.amount1())) + uint256(uint128(balanceDelta.amount0())), + uint256(uint128(balanceDelta.amount1())) ); BalanceDelta balanceDeltaAfter = poolManager.modifyLiquidity( @@ -371,8 +365,8 @@ contract FullRange is BaseHook, ILockCallback { ); // Donate any "dust" from the sqrtRatio change as fees - uint128 donateAmount0 = uint128(-balanceDelta.amount0() - balanceDeltaAfter.amount0()); - uint128 donateAmount1 = uint128(-balanceDelta.amount1() - balanceDeltaAfter.amount1()); + uint128 donateAmount0 = uint128(balanceDelta.amount0() + balanceDeltaAfter.amount0()); + uint128 donateAmount1 = uint128(balanceDelta.amount1() + balanceDeltaAfter.amount1()); poolManager.donate(key, donateAmount0, donateAmount1, ZERO_BYTES); } diff --git a/contracts/hooks/examples/GeomeanOracle.sol b/contracts/hooks/examples/GeomeanOracle.sol index 9d53fb0a..c0f1c096 100644 --- a/contracts/hooks/examples/GeomeanOracle.sol +++ b/contracts/hooks/examples/GeomeanOracle.sol @@ -71,9 +71,7 @@ contract GeomeanOracle is BaseHook { beforeSwap: true, afterSwap: false, beforeDonate: false, - afterDonate: false, - noOp: false, - accessLock: false + afterDonate: false }); } @@ -105,7 +103,7 @@ contract GeomeanOracle is BaseHook { /// @dev Called before any action that potentially modifies pool price or liquidity, such as swap or modify position function _updatePool(PoolKey calldata key) private { PoolId id = key.toId(); - (, int24 tick,) = poolManager.getSlot0(id); + (, int24 tick,,) = poolManager.getSlot0(id); uint128 liquidity = poolManager.getLiquidity(id); @@ -158,7 +156,7 @@ contract GeomeanOracle is BaseHook { ObservationState memory state = states[id]; - (, int24 tick,) = poolManager.getSlot0(id); + (, int24 tick,,) = poolManager.getSlot0(id); uint128 liquidity = poolManager.getLiquidity(id); diff --git a/contracts/hooks/examples/LimitOrder.sol b/contracts/hooks/examples/LimitOrder.sol index c8d9316f..e6cf8e89 100644 --- a/contracts/hooks/examples/LimitOrder.sol +++ b/contracts/hooks/examples/LimitOrder.sol @@ -84,9 +84,7 @@ contract LimitOrder is BaseHook { beforeSwap: false, afterSwap: true, beforeDonate: false, - afterDonate: false, - noOp: false, - accessLock: false + afterDonate: false }); } @@ -111,7 +109,7 @@ contract LimitOrder is BaseHook { } function getTick(PoolId poolId) private view returns (int24 tick) { - (, tick,) = poolManager.getSlot0(poolId); + (, tick,,) = poolManager.getSlot0(poolId); } function getTickLower(int24 tick, int24 tickSpacing) private pure returns (int24) { @@ -158,13 +156,8 @@ contract LimitOrder is BaseHook { epochInfo.filled = true; - (uint256 amount0, uint256 amount1) = abi.decode( - poolManager.lock( - address(this), - abi.encodeCall(this.lockAcquiredFill, (key, lower, -int256(uint256(epochInfo.liquidityTotal)))) - ), - (uint256, uint256) - ); + (uint256 amount0, uint256 amount1) = + _lockAcquiredFill(key, lower, -int256(uint256(epochInfo.liquidityTotal))); unchecked { epochInfo.token0Total += amount0; @@ -194,9 +187,9 @@ contract LimitOrder is BaseHook { } } - function lockAcquiredFill(PoolKey calldata key, int24 tickLower, int256 liquidityDelta) - external - selfOnly + function _lockAcquiredFill(PoolKey calldata key, int24 tickLower, int256 liquidityDelta) + private + poolManagerOnly returns (uint128 amount0, uint128 amount1) { BalanceDelta delta = poolManager.modifyLiquidity( @@ -209,11 +202,11 @@ contract LimitOrder is BaseHook { ZERO_BYTES ); - if (delta.amount0() < 0) { - poolManager.mint(address(this), key.currency0.toId(), amount0 = uint128(-delta.amount0())); + if (delta.amount0() > 0) { + poolManager.mint(address(this), key.currency0.toId(), amount0 = uint128(delta.amount0())); } - if (delta.amount1() < 0) { - poolManager.mint(address(this), key.currency1.toId(), amount1 = uint128(-delta.amount1())); + if (delta.amount1() > 0) { + poolManager.mint(address(this), key.currency1.toId(), amount1 = uint128(delta.amount1())); } } @@ -224,7 +217,6 @@ contract LimitOrder is BaseHook { if (liquidity == 0) revert ZeroLiquidity(); poolManager.lock( - address(this), abi.encodeCall(this.lockAcquiredPlace, (key, tickLower, zeroForOne, int256(uint256(liquidity)), msg.sender)) ); @@ -270,12 +262,12 @@ contract LimitOrder is BaseHook { ZERO_BYTES ); - if (delta.amount0() > 0) { + if (delta.amount0() < 0) { if (delta.amount1() != 0) revert InRange(); if (!zeroForOne) revert CrossedRange(); // TODO use safeTransferFrom IERC20Minimal(Currency.unwrap(key.currency0)).transferFrom( - owner, address(poolManager), uint256(uint128(delta.amount0())) + owner, address(poolManager), uint256(uint128(-delta.amount0())) ); poolManager.settle(key.currency0); } else { @@ -283,7 +275,7 @@ contract LimitOrder is BaseHook { if (zeroForOne) revert CrossedRange(); // TODO use safeTransferFrom IERC20Minimal(Currency.unwrap(key.currency1)).transferFrom( - owner, address(poolManager), uint256(uint128(delta.amount1())) + owner, address(poolManager), uint256(uint128(-delta.amount1())) ); poolManager.settle(key.currency1); } @@ -306,7 +298,6 @@ contract LimitOrder is BaseHook { uint256 amount1Fee; (amount0, amount1, amount0Fee, amount1Fee) = abi.decode( poolManager.lock( - address(this), abi.encodeCall( this.lockAcquiredKill, (key, tickLower, -int256(uint256(liquidity)), to, liquidity == epochInfo.liquidityTotal) @@ -343,11 +334,11 @@ contract LimitOrder is BaseHook { ZERO_BYTES ); - if (deltaFee.amount0() < 0) { - poolManager.mint(address(this), key.currency0.toId(), amount0Fee = uint128(-deltaFee.amount0())); + if (deltaFee.amount0() > 0) { + poolManager.mint(address(this), key.currency0.toId(), amount0Fee = uint128(deltaFee.amount0())); } - if (deltaFee.amount1() < 0) { - poolManager.mint(address(this), key.currency1.toId(), amount1Fee = uint128(-deltaFee.amount1())); + if (deltaFee.amount1() > 0) { + poolManager.mint(address(this), key.currency1.toId(), amount1Fee = uint128(deltaFee.amount1())); } } @@ -361,11 +352,11 @@ contract LimitOrder is BaseHook { ZERO_BYTES ); - if (delta.amount0() < 0) { - poolManager.take(key.currency0, to, amount0 = uint128(-delta.amount0())); + if (delta.amount0() > 0) { + poolManager.take(key.currency0, to, amount0 = uint128(delta.amount0())); } - if (delta.amount1() < 0) { - poolManager.take(key.currency1, to, amount1 = uint128(-delta.amount1())); + if (delta.amount1() > 0) { + poolManager.take(key.currency1, to, amount1 = uint128(delta.amount1())); } } @@ -388,7 +379,6 @@ contract LimitOrder is BaseHook { epochInfo.liquidityTotal = liquidityTotal - liquidity; poolManager.lock( - address(this), abi.encodeCall(this.lockAcquiredWithdraw, (epochInfo.currency0, epochInfo.currency1, amount0, amount1, to)) ); diff --git a/contracts/hooks/examples/TWAMM.sol b/contracts/hooks/examples/TWAMM.sol index 694c0b2c..85364915 100644 --- a/contracts/hooks/examples/TWAMM.sol +++ b/contracts/hooks/examples/TWAMM.sol @@ -71,9 +71,7 @@ contract TWAMM is BaseHook, ITWAMM { beforeSwap: true, afterSwap: false, beforeDonate: false, - afterDonate: false, - noOp: false, - accessLock: false + afterDonate: false }); } @@ -136,7 +134,7 @@ contract TWAMM is BaseHook, ITWAMM { /// @inheritdoc ITWAMM function executeTWAMMOrders(PoolKey memory key) public { PoolId poolId = key.toId(); - (uint160 sqrtPriceX96,,) = poolManager.getSlot0(poolId); + (uint160 sqrtPriceX96,,,) = poolManager.getSlot0(poolId); State storage twamm = twammStates[poolId]; (bool zeroForOne, uint160 sqrtPriceLimitX96) = _executeTWAMMOrders( @@ -144,9 +142,7 @@ contract TWAMM is BaseHook, ITWAMM { ); if (sqrtPriceLimitX96 != 0 && sqrtPriceLimitX96 != sqrtPriceX96) { - poolManager.lock( - address(this), abi.encode(key, IPoolManager.SwapParams(zeroForOne, type(int256).max, sqrtPriceLimitX96)) - ); + poolManager.lock(abi.encode(key, IPoolManager.SwapParams(zeroForOne, type(int256).max, sqrtPriceLimitX96))); } } @@ -302,32 +298,27 @@ contract TWAMM is BaseHook, ITWAMM { IERC20Minimal(Currency.unwrap(token)).safeTransfer(to, amountTransferred); } - function lockAcquired(address, /*sender*/ bytes calldata rawData) - external - override - poolManagerOnly - returns (bytes memory) - { + function lockAcquired(bytes calldata rawData) external override poolManagerOnly returns (bytes memory) { (PoolKey memory key, IPoolManager.SwapParams memory swapParams) = abi.decode(rawData, (PoolKey, IPoolManager.SwapParams)); BalanceDelta delta = poolManager.swap(key, swapParams, ZERO_BYTES); if (swapParams.zeroForOne) { - if (delta.amount0() > 0) { - key.currency0.transfer(address(poolManager), uint256(uint128(delta.amount0()))); + if (delta.amount0() < 0) { + key.currency0.transfer(address(poolManager), uint256(uint128(-delta.amount0()))); poolManager.settle(key.currency0); } - if (delta.amount1() < 0) { - poolManager.take(key.currency1, address(this), uint256(uint128(-delta.amount1()))); + if (delta.amount1() > 0) { + poolManager.take(key.currency1, address(this), uint256(uint128(delta.amount1()))); } } else { - if (delta.amount1() > 0) { - key.currency1.transfer(address(poolManager), uint256(uint128(delta.amount1()))); + if (delta.amount1() < 0) { + key.currency1.transfer(address(poolManager), uint256(uint128(-delta.amount1()))); poolManager.settle(key.currency1); } - if (delta.amount0() < 0) { - poolManager.take(key.currency0, address(this), uint256(uint128(-delta.amount0()))); + if (delta.amount0() > 0) { + poolManager.take(key.currency0, address(this), uint256(uint128(delta.amount0()))); } } return bytes(""); diff --git a/contracts/hooks/examples/VolatilityOracle.sol b/contracts/hooks/examples/VolatilityOracle.sol index df8bdde5..76a3e8ce 100644 --- a/contracts/hooks/examples/VolatilityOracle.sol +++ b/contracts/hooks/examples/VolatilityOracle.sol @@ -2,25 +2,18 @@ pragma solidity ^0.8.19; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; -import {IDynamicFeeManager} from "@uniswap/v4-core/src/interfaces/IDynamicFeeManager.sol"; import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; -import {FeeLibrary} from "@uniswap/v4-core/src/libraries/FeeLibrary.sol"; +import {SwapFeeLibrary} from "@uniswap/v4-core/src/libraries/SwapFeeLibrary.sol"; import {BaseHook} from "../../BaseHook.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; -contract VolatilityOracle is BaseHook, IDynamicFeeManager { - using FeeLibrary for uint24; +contract VolatilityOracle is BaseHook { + using SwapFeeLibrary for uint24; error MustUseDynamicFee(); uint32 deployTimestamp; - function getFee(address, PoolKey calldata) external view returns (uint24) { - uint24 startingFee = 3000; - uint32 lapsed = _blockTimestamp() - deployTimestamp; - return startingFee + (uint24(lapsed) * 100) / 60; // 100 bps a minute - } - /// @dev For mocking function _blockTimestamp() internal view virtual returns (uint32) { return uint32(block.timestamp); @@ -33,7 +26,7 @@ contract VolatilityOracle is BaseHook, IDynamicFeeManager { function getHookPermissions() public pure override returns (Hooks.Permissions memory) { return Hooks.Permissions({ beforeInitialize: true, - afterInitialize: false, + afterInitialize: true, beforeAddLiquidity: false, beforeRemoveLiquidity: false, afterAddLiquidity: false, @@ -41,9 +34,7 @@ contract VolatilityOracle is BaseHook, IDynamicFeeManager { beforeSwap: false, afterSwap: false, beforeDonate: false, - afterDonate: false, - noOp: false, - accessLock: false + afterDonate: false }); } @@ -56,4 +47,20 @@ contract VolatilityOracle is BaseHook, IDynamicFeeManager { if (!key.fee.isDynamicFee()) revert MustUseDynamicFee(); return VolatilityOracle.beforeInitialize.selector; } + + function setFee(PoolKey calldata key) public { + uint24 startingFee = 3000; + uint32 lapsed = _blockTimestamp() - deployTimestamp; + uint24 fee = startingFee + (uint24(lapsed) * 100) / 60; // 100 bps a minute + poolManager.updateDynamicSwapFee(key, fee); // initial fee 0.30% + } + + function afterInitialize(address, PoolKey calldata key, uint160, int24, bytes calldata) + external + override + returns (bytes4) + { + setFee(key); + return BaseHook.afterInitialize.selector; + } } diff --git a/contracts/lens/Quoter.sol b/contracts/lens/Quoter.sol index 1f9350a8..c039a7b7 100644 --- a/contracts/lens/Quoter.sol +++ b/contracts/lens/Quoter.sol @@ -62,7 +62,7 @@ contract Quoter is IQuoter, ILockCallback { override returns (int128[] memory deltaAmounts, uint160 sqrtPriceX96After, uint32 initializedTicksLoaded) { - try manager.lock(address(this), abi.encodeWithSelector(this._quoteExactInputSingle.selector, params)) {} + try manager.lock(abi.encodeWithSelector(this._quoteExactInputSingle.selector, params)) {} catch (bytes memory reason) { return _handleRevertSingle(reason); } @@ -77,7 +77,7 @@ contract Quoter is IQuoter, ILockCallback { uint32[] memory initializedTicksLoadedList ) { - try manager.lock(address(this), abi.encodeWithSelector(this._quoteExactInput.selector, params)) {} + try manager.lock(abi.encodeWithSelector(this._quoteExactInput.selector, params)) {} catch (bytes memory reason) { return _handleRevert(reason); } @@ -89,7 +89,7 @@ contract Quoter is IQuoter, ILockCallback { override returns (int128[] memory deltaAmounts, uint160 sqrtPriceX96After, uint32 initializedTicksLoaded) { - try manager.lock(address(this), abi.encodeWithSelector(this._quoteExactOutputSingle.selector, params)) {} + try manager.lock(abi.encodeWithSelector(this._quoteExactOutputSingle.selector, params)) {} catch (bytes memory reason) { if (params.sqrtPriceLimitX96 == 0) delete amountOutCached; return _handleRevertSingle(reason); @@ -106,20 +106,17 @@ contract Quoter is IQuoter, ILockCallback { uint32[] memory initializedTicksLoadedList ) { - try manager.lock(address(this), abi.encodeWithSelector(this._quoteExactOutput.selector, params)) {} + try manager.lock(abi.encodeWithSelector(this._quoteExactOutput.selector, params)) {} catch (bytes memory reason) { return _handleRevert(reason); } } /// @inheritdoc ILockCallback - function lockAcquired(address lockCaller, bytes calldata data) external returns (bytes memory) { + function lockAcquired(bytes calldata data) external returns (bytes memory) { if (msg.sender != address(manager)) { revert InvalidLockAcquiredSender(); } - if (lockCaller != address(this)) { - revert InvalidLockCaller(); - } (bool success, bytes memory returnData) = address(this).call(data); if (success) return returnData; @@ -178,23 +175,23 @@ contract Quoter is IQuoter, ILockCallback { for (uint256 i = 0; i < pathLength; i++) { (PoolKey memory poolKey, bool zeroForOne) = params.path[i].getPoolAndSwapDirection(i == 0 ? params.exactCurrency : cache.prevCurrency); - (, cache.tickBefore,) = manager.getSlot0(poolKey.toId()); + (, cache.tickBefore,,) = manager.getSlot0(poolKey.toId()); (cache.curDeltas, cache.sqrtPriceX96After, cache.tickAfter) = _swap( poolKey, zeroForOne, - int256(int128(i == 0 ? params.exactAmount : cache.prevAmount)), + -int256(int128(i == 0 ? params.exactAmount : cache.prevAmount)), 0, params.path[i].hookData ); (cache.deltaIn, cache.deltaOut) = zeroForOne - ? (cache.curDeltas.amount0(), cache.curDeltas.amount1()) - : (cache.curDeltas.amount1(), cache.curDeltas.amount0()); + ? (-cache.curDeltas.amount0(), -cache.curDeltas.amount1()) + : (-cache.curDeltas.amount1(), -cache.curDeltas.amount0()); result.deltaAmounts[i] += cache.deltaIn; result.deltaAmounts[i + 1] += cache.deltaOut; - cache.prevAmount = zeroForOne ? uint128(-cache.curDeltas.amount1()) : uint128(-cache.curDeltas.amount0()); + cache.prevAmount = zeroForOne ? uint128(cache.curDeltas.amount1()) : uint128(cache.curDeltas.amount0()); cache.prevCurrency = params.path[i].intermediateCurrency; result.sqrtPriceX96AfterList[i] = cache.sqrtPriceX96After; result.initializedTicksLoadedList[i] = @@ -209,20 +206,20 @@ contract Quoter is IQuoter, ILockCallback { /// @dev quote an ExactInput swap on a pool, then revert with the result function _quoteExactInputSingle(QuoteExactSingleParams memory params) public selfOnly returns (bytes memory) { - (, int24 tickBefore,) = manager.getSlot0(params.poolKey.toId()); + (, int24 tickBefore,,) = manager.getSlot0(params.poolKey.toId()); (BalanceDelta deltas, uint160 sqrtPriceX96After, int24 tickAfter) = _swap( params.poolKey, params.zeroForOne, - int256(int128(params.exactAmount)), + -int256(int128(params.exactAmount)), params.sqrtPriceLimitX96, params.hookData ); int128[] memory deltaAmounts = new int128[](2); - deltaAmounts[0] = deltas.amount0(); - deltaAmounts[1] = deltas.amount1(); + deltaAmounts[0] = -deltas.amount0(); + deltaAmounts[1] = -deltas.amount1(); uint32 initializedTicksLoaded = PoolTicksCounter.countInitializedTicksLoaded(manager, params.poolKey, tickBefore, tickAfter); @@ -252,20 +249,20 @@ contract Quoter is IQuoter, ILockCallback { params.path[i - 1], i == pathLength ? params.exactCurrency : cache.prevCurrency ); - (, cache.tickBefore,) = manager.getSlot0(poolKey.toId()); + (, cache.tickBefore,,) = manager.getSlot0(poolKey.toId()); (cache.curDeltas, cache.sqrtPriceX96After, cache.tickAfter) = - _swap(poolKey, !oneForZero, -int256(uint256(curAmountOut)), 0, params.path[i - 1].hookData); + _swap(poolKey, !oneForZero, int256(uint256(curAmountOut)), 0, params.path[i - 1].hookData); // always clear because sqrtPriceLimitX96 is set to 0 always delete amountOutCached; (cache.deltaIn, cache.deltaOut) = !oneForZero - ? (cache.curDeltas.amount0(), cache.curDeltas.amount1()) - : (cache.curDeltas.amount1(), cache.curDeltas.amount0()); + ? (-cache.curDeltas.amount0(), -cache.curDeltas.amount1()) + : (-cache.curDeltas.amount1(), -cache.curDeltas.amount0()); result.deltaAmounts[i - 1] += cache.deltaIn; result.deltaAmounts[i] += cache.deltaOut; - cache.prevAmount = !oneForZero ? uint128(cache.curDeltas.amount0()) : uint128(cache.curDeltas.amount1()); + cache.prevAmount = !oneForZero ? uint128(-cache.curDeltas.amount0()) : uint128(-cache.curDeltas.amount1()); cache.prevCurrency = params.path[i - 1].intermediateCurrency; result.sqrtPriceX96AfterList[i - 1] = cache.sqrtPriceX96After; result.initializedTicksLoadedList[i - 1] = @@ -283,11 +280,11 @@ contract Quoter is IQuoter, ILockCallback { // if no price limit has been specified, cache the output amount for comparison in the swap callback if (params.sqrtPriceLimitX96 == 0) amountOutCached = params.exactAmount; - (, int24 tickBefore,) = manager.getSlot0(params.poolKey.toId()); + (, int24 tickBefore,,) = manager.getSlot0(params.poolKey.toId()); (BalanceDelta deltas, uint160 sqrtPriceX96After, int24 tickAfter) = _swap( params.poolKey, params.zeroForOne, - -int256(uint256(params.exactAmount)), + int256(uint256(params.exactAmount)), params.sqrtPriceLimitX96, params.hookData ); @@ -295,8 +292,8 @@ contract Quoter is IQuoter, ILockCallback { if (amountOutCached != 0) delete amountOutCached; int128[] memory deltaAmounts = new int128[](2); - deltaAmounts[0] = deltas.amount0(); - deltaAmounts[1] = deltas.amount1(); + deltaAmounts[0] = -deltas.amount0(); + deltaAmounts[1] = -deltas.amount1(); uint32 initializedTicksLoaded = PoolTicksCounter.countInitializedTicksLoaded(manager, params.poolKey, tickBefore, tickAfter); @@ -325,10 +322,10 @@ contract Quoter is IQuoter, ILockCallback { hookData ); // only exactOut case - if (amountOutCached != 0 && amountOutCached != uint128(zeroForOne ? -deltas.amount1() : -deltas.amount0())) { + if (amountOutCached != 0 && amountOutCached != uint128(zeroForOne ? deltas.amount1() : deltas.amount0())) { revert InsufficientAmountOut(); } - (sqrtPriceX96After, tickAfter,) = manager.getSlot0(poolKey.toId()); + (sqrtPriceX96After, tickAfter,,) = manager.getSlot0(poolKey.toId()); } /// @dev return either the sqrtPriceLimit from user input, or the max/min value possible depending on trade direction diff --git a/lib/v4-core b/lib/v4-core index 4a13732d..f5674e46 160000 --- a/lib/v4-core +++ b/lib/v4-core @@ -1 +1 @@ -Subproject commit 4a13732dc0b9a8c516d3639a78c54af3fc3db8d4 +Subproject commit f5674e46720c0fc4606b287cccc583d56245e724 diff --git a/test/FullRange.t.sol b/test/FullRange.t.sol index 076abab3..f0867ba4 100644 --- a/test/FullRange.t.sol +++ b/test/FullRange.t.sol @@ -127,7 +127,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { emit Initialize(id, testKey.currency0, testKey.currency1, testKey.fee, testKey.tickSpacing, testKey.hooks); snapStart("FullRangeInitialize"); - initializeRouter.initialize(testKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(testKey, SQRT_RATIO_1_1, ZERO_BYTES); snapEnd(); (, address liquidityToken) = fullRange.poolInfo(id); @@ -139,11 +139,11 @@ contract TestFullRange is Test, Deployers, GasSnapshot { PoolKey memory wrongKey = PoolKey(key.currency0, key.currency1, 0, TICK_SPACING + 1, fullRange); vm.expectRevert(FullRange.TickSpacingNotDefault.selector); - initializeRouter.initialize(wrongKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(wrongKey, SQRT_RATIO_1_1, ZERO_BYTES); } function testFullRange_addLiquidity_InitialAddSucceeds() public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); uint256 prevBalance0 = key.currency0.balanceOf(address(this)); uint256 prevBalance1 = key.currency1.balanceOf(address(this)); @@ -169,8 +169,8 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_addLiquidity_InitialAddFuzz(uint256 amount) public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - if (amount < LOCKED_LIQUIDITY) { + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + if (amount <= LOCKED_LIQUIDITY) { vm.expectRevert(FullRange.LiquidityDoesntMeetMinimum.selector); fullRange.addLiquidity( FullRange.AddLiquidityParams( @@ -244,7 +244,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_addLiquidity_SwapThenAddSucceeds() public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); uint256 prevBalance0 = key.currency0.balanceOf(address(this)); uint256 prevBalance1 = key.currency1.balanceOf(address(this)); @@ -265,11 +265,11 @@ contract TestFullRange is Test, Deployers, GasSnapshot { vm.expectEmit(true, true, true, true); emit Swap( - id, address(router), 1 ether, -906610893880149131, 72045250990510446115798809072, 10 ether, -1901, 3000 + id, address(router), -1 ether, 906610893880149131, 72045250990510446115798809072, 10 ether, -1901, 3000 ); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 1 ether, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -1 ether, sqrtPriceLimitX96: SQRT_RATIO_1_2}); HookEnabledSwapRouter.TestSettings memory settings = HookEnabledSwapRouter.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); @@ -298,7 +298,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_addLiquidity_FailsIfTooMuchSlippage() public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); fullRange.addLiquidity( FullRange.AddLiquidityParams( @@ -323,7 +323,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { function testFullRange_swap_TwoSwaps() public { PoolKey memory testKey = key; - initializeRouter.initialize(testKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(testKey, SQRT_RATIO_1_1, ZERO_BYTES); fullRange.addLiquidity( FullRange.AddLiquidityParams( @@ -352,8 +352,8 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_swap_TwoPools() public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - initializeRouter.initialize(key2, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key2, SQRT_RATIO_1_1, ZERO_BYTES); fullRange.addLiquidity( FullRange.AddLiquidityParams( @@ -408,7 +408,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_InitialRemoveFuzz(uint256 amount) public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); fullRange.addLiquidity( FullRange.AddLiquidityParams( @@ -456,7 +456,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_FailsIfNoLiquidity() public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); (, address liquidityToken) = fullRange.poolInfo(id); UniswapV4ERC20(liquidityToken).approve(address(fullRange), type(uint256).max); @@ -468,7 +468,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_SucceedsWithPartial() public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); uint256 prevBalance0 = key.currency0.balanceOfSelf(); uint256 prevBalance1 = key.currency1.balanceOfSelf(); @@ -503,7 +503,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_DiffRatios() public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); uint256 prevBalance0 = key.currency0.balanceOf(address(this)); uint256 prevBalance1 = key.currency1.balanceOf(address(this)); @@ -571,7 +571,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_RemoveAllFuzz(uint256 amount) public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); (, address liquidityToken) = fullRange.poolInfo(id); if (amount <= LOCKED_LIQUIDITY) { @@ -626,7 +626,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { vm.prank(address(2)); token1.approve(address(fullRange), type(uint256).max); - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); (, address liquidityToken) = fullRange.poolInfo(id); // Test contract adds liquidity @@ -704,7 +704,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_SwapRemoveAllFuzz(uint256 amount) public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); (, address liquidityToken) = fullRange.poolInfo(id); if (amount <= LOCKED_LIQUIDITY) { @@ -753,7 +753,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_BeforeModifyPositionFailsWithWrongMsgSender() public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); vm.expectRevert(FullRange.SenderMustBeHook.selector); modifyLiquidityRouter.modifyLiquidity( diff --git a/test/GeomeanOracle.t.sol b/test/GeomeanOracle.t.sol index ec74affc..05255e93 100644 --- a/test/GeomeanOracle.t.sol +++ b/test/GeomeanOracle.t.sol @@ -65,12 +65,12 @@ contract TestGeomeanOracle is Test, Deployers { } function testBeforeInitializeAllowsPoolCreation() public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); } function testBeforeInitializeRevertsIfFee() public { vm.expectRevert(GeomeanOracle.OnlyOneOraclePoolAllowed.selector); - initializeRouter.initialize( + manager.initialize( PoolKey(Currency.wrap(address(token0)), Currency.wrap(address(token1)), 1, MAX_TICK_SPACING, geomeanOracle), SQRT_RATIO_1_1, ZERO_BYTES @@ -79,7 +79,7 @@ contract TestGeomeanOracle is Test, Deployers { function testBeforeInitializeRevertsIfNotMaxTickSpacing() public { vm.expectRevert(GeomeanOracle.OnlyOneOraclePoolAllowed.selector); - initializeRouter.initialize( + manager.initialize( PoolKey(Currency.wrap(address(token0)), Currency.wrap(address(token1)), 0, 60, geomeanOracle), SQRT_RATIO_1_1, ZERO_BYTES @@ -87,7 +87,7 @@ contract TestGeomeanOracle is Test, Deployers { } function testAfterInitializeState() public { - initializeRouter.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); GeomeanOracle.ObservationState memory observationState = geomeanOracle.getState(key); assertEq(observationState.index, 0); assertEq(observationState.cardinality, 1); @@ -95,7 +95,7 @@ contract TestGeomeanOracle is Test, Deployers { } function testAfterInitializeObservation() public { - initializeRouter.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); Oracle.Observation memory observation = geomeanOracle.getObservation(key, 0); assertTrue(observation.initialized); assertEq(observation.blockTimestamp, 1); @@ -104,7 +104,7 @@ contract TestGeomeanOracle is Test, Deployers { } function testAfterInitializeObserve0() public { - initializeRouter.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); uint32[] memory secondsAgo = new uint32[](1); secondsAgo[0] = 0; (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) = @@ -116,7 +116,7 @@ contract TestGeomeanOracle is Test, Deployers { } function testBeforeModifyPositionNoObservations() public { - initializeRouter.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); modifyLiquidityRouter.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams( @@ -138,7 +138,7 @@ contract TestGeomeanOracle is Test, Deployers { } function testBeforeModifyPositionObservation() public { - initializeRouter.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); geomeanOracle.setTime(3); // advance 2 seconds modifyLiquidityRouter.modifyLiquidity( key, @@ -161,7 +161,7 @@ contract TestGeomeanOracle is Test, Deployers { } function testBeforeModifyPositionObservationAndCardinality() public { - initializeRouter.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); geomeanOracle.setTime(3); // advance 2 seconds geomeanOracle.increaseCardinalityNext(key, 2); GeomeanOracle.ObservationState memory observationState = geomeanOracle.getState(key); @@ -199,7 +199,7 @@ contract TestGeomeanOracle is Test, Deployers { } function testPermanentLiquidity() public { - initializeRouter.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); geomeanOracle.setTime(3); // advance 2 seconds modifyLiquidityRouter.modifyLiquidity( key, diff --git a/test/LimitOrder.t.sol b/test/LimitOrder.t.sol index 94cca602..9b9e3116 100644 --- a/test/LimitOrder.t.sol +++ b/test/LimitOrder.t.sol @@ -63,7 +63,7 @@ contract TestLimitOrder is Test, Deployers { function testGetTickLowerLastWithDifferentPrice() public { PoolKey memory differentKey = PoolKey(Currency.wrap(address(token0)), Currency.wrap(address(token1)), 3000, 61, limitOrder); - initializeRouter.initialize(differentKey, SQRT_RATIO_10_1, ZERO_BYTES); + manager.initialize(differentKey, SQRT_RATIO_10_1, ZERO_BYTES); assertEq(limitOrder.getTickLowerLast(differentKey.toId()), 22997); } @@ -103,7 +103,7 @@ contract TestLimitOrder is Test, Deployers { // swapping is free, there's no liquidity in the pool, so we only need to specify 1 wei router.swap( key, - IPoolManager.SwapParams(false, 1 ether, SQRT_RATIO_1_1 + 1), + IPoolManager.SwapParams(false, -1 ether, SQRT_RATIO_1_1 + 1), HookEnabledSwapRouter.TestSettings(true, true), ZERO_BYTES ); @@ -129,7 +129,7 @@ contract TestLimitOrder is Test, Deployers { // swapping is free, there's no liquidity in the pool, so we only need to specify 1 wei router.swap( key, - IPoolManager.SwapParams(true, 1 ether, SQRT_RATIO_1_1 - 1), + IPoolManager.SwapParams(true, -1 ether, SQRT_RATIO_1_1 - 1), HookEnabledSwapRouter.TestSettings(true, true), ZERO_BYTES ); @@ -191,13 +191,13 @@ contract TestLimitOrder is Test, Deployers { router.swap( key, - IPoolManager.SwapParams(false, 1e18, TickMath.getSqrtRatioAtTick(60)), + IPoolManager.SwapParams(false, -1e18, TickMath.getSqrtRatioAtTick(60)), HookEnabledSwapRouter.TestSettings(true, true), ZERO_BYTES ); assertEq(limitOrder.getTickLowerLast(id), 60); - (, int24 tick,) = manager.getSlot0(id); + (, int24 tick,,) = manager.getSlot0(id); assertEq(tick, 60); (bool filled,,, uint256 token0Total, uint256 token1Total,) = limitOrder.epochInfos(Epoch.wrap(1)); diff --git a/test/Quoter.t.sol b/test/Quoter.t.sol index 87de52d5..f3d2ceb1 100644 --- a/test/Quoter.t.sol +++ b/test/Quoter.t.sol @@ -121,9 +121,9 @@ contract QuoterTest is Test, Deployers { // nested self-call into lockAcquired reverts function testQuoter_callLockAcquired_reverts() public { - vm.expectRevert(IQuoter.InvalidLockAcquiredSender.selector); + vm.expectRevert(IQuoter.LockFailure.selector); vm.prank(address(manager)); - quoter.lockAcquired(address(quoter), abi.encodeWithSelector(quoter.lockAcquired.selector, address(this), "0x")); + quoter.lockAcquired(abi.encodeWithSelector(quoter.lockAcquired.selector, address(this), "0x")); } function testQuoter_quoteExactInput_0to2_2TicksLoaded() public { @@ -542,7 +542,7 @@ contract QuoterTest is Test, Deployers { } function setupPool(PoolKey memory poolKey) internal { - initializeRouter.initialize(poolKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(poolKey, SQRT_RATIO_1_1, ZERO_BYTES); MockERC20(Currency.unwrap(poolKey.currency0)).approve(address(positionManager), type(uint256).max); MockERC20(Currency.unwrap(poolKey.currency1)).approve(address(positionManager), type(uint256).max); positionManager.modifyLiquidity( @@ -557,7 +557,7 @@ contract QuoterTest is Test, Deployers { } function setupPoolMultiplePositions(PoolKey memory poolKey) internal { - initializeRouter.initialize(poolKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(poolKey, SQRT_RATIO_1_1, ZERO_BYTES); MockERC20(Currency.unwrap(poolKey.currency0)).approve(address(positionManager), type(uint256).max); MockERC20(Currency.unwrap(poolKey.currency1)).approve(address(positionManager), type(uint256).max); positionManager.modifyLiquidity( @@ -587,9 +587,9 @@ contract QuoterTest is Test, Deployers { function setupPoolWithZeroTickInitialized(PoolKey memory poolKey) internal { PoolId poolId = poolKey.toId(); - (uint160 sqrtPriceX96,,) = manager.getSlot0(poolId); + (uint160 sqrtPriceX96,,,) = manager.getSlot0(poolId); if (sqrtPriceX96 == 0) { - initializeRouter.initialize(poolKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(poolKey, SQRT_RATIO_1_1, ZERO_BYTES); } MockERC20(Currency.unwrap(poolKey.currency0)).approve(address(positionManager), type(uint256).max); diff --git a/test/TWAMM.t.sol b/test/TWAMM.t.sol index fdcf81d2..96941963 100644 --- a/test/TWAMM.t.sol +++ b/test/TWAMM.t.sol @@ -93,7 +93,7 @@ contract TWAMMTest is Test, Deployers, GasSnapshot { assertEq(twamm.lastVirtualOrderTimestamp(initId), 0); vm.warp(10000); - initializeRouter.initialize(initKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(initKey, SQRT_RATIO_1_1, ZERO_BYTES); assertEq(twamm.lastVirtualOrderTimestamp(initId), 10000); } diff --git a/test/utils/HookEnabledSwapRouter.sol b/test/utils/HookEnabledSwapRouter.sol index 54832b4a..4311439c 100644 --- a/test/utils/HookEnabledSwapRouter.sol +++ b/test/utils/HookEnabledSwapRouter.sol @@ -36,15 +36,14 @@ contract HookEnabledSwapRouter is PoolTestBase { bytes memory hookData ) external payable returns (BalanceDelta delta) { delta = abi.decode( - manager.lock(address(this), abi.encode(CallbackData(msg.sender, testSettings, key, params, hookData))), - (BalanceDelta) + manager.lock(abi.encode(CallbackData(msg.sender, testSettings, key, params, hookData))), (BalanceDelta) ); uint256 ethBalance = address(this).balance; if (ethBalance > 0) CurrencyLibrary.NATIVE.transfer(msg.sender, ethBalance); } - function lockAcquired(address, /*sender*/ bytes calldata rawData) external returns (bytes memory) { + function lockAcquired(bytes calldata rawData) external returns (bytes memory) { require(msg.sender == address(manager)); CallbackData memory data = abi.decode(rawData, (CallbackData)); @@ -56,12 +55,12 @@ contract HookEnabledSwapRouter is PoolTestBase { if (data.params.zeroForOne) { _settle(data.key.currency0, data.sender, delta.amount0(), data.testSettings.settleUsingTransfer); - if (delta.amount1() < 0) { + if (delta.amount1() > 0) { _take(data.key.currency1, data.sender, delta.amount1(), data.testSettings.withdrawTokens); } } else { _settle(data.key.currency1, data.sender, delta.amount1(), data.testSettings.settleUsingTransfer); - if (delta.amount0() < 0) { + if (delta.amount0() > 0) { _take(data.key.currency0, data.sender, delta.amount0(), data.testSettings.withdrawTokens); } } From ad7976af2181a80af0d381f4a8e9e234b880cc8f Mon Sep 17 00:00:00 2001 From: saucepoint <98790946+saucepoint@users.noreply.github.com> Date: Thu, 23 May 2024 13:21:47 -0400 Subject: [PATCH 4/5] chore: update v4-core:latest (#105) * update core * rename lockAcquired to unlockCallback * update core; temporary path hack in remappings * update v4-core; remove remapping * wip: fix compatibility * update core; fix renaming of swap fee to lp fee * update core; fix events * update core; address liquidity salt and modify liquidity return values * fix incorrect delta accounting when modifying liquidity * fix todo, use CurrencySettleTake * remove deadcode * update core; use StateLibrary; update sqrtRatio to sqrtPrice * fix beforeSwap return signatures * forge fmt; remove commented out code * update core (wow gas savings) * update core * update core * update core; hook flags LSB * update core * update core --- .../FullRangeAddInitialLiquidity.snap | 2 +- .forge-snapshots/FullRangeAddLiquidity.snap | 2 +- .forge-snapshots/FullRangeFirstSwap.snap | 2 +- .forge-snapshots/FullRangeInitialize.snap | 2 +- .../FullRangeRemoveLiquidity.snap | 2 +- .../FullRangeRemoveLiquidityAndRebalance.snap | 2 +- .forge-snapshots/FullRangeSecondSwap.snap | 2 +- .forge-snapshots/FullRangeSwap.snap | 2 +- .forge-snapshots/TWAMMSubmitOrder.snap | 2 +- contracts/BaseHook.sol | 11 ++- contracts/hooks/examples/FullRange.sol | 70 +++++++------- contracts/hooks/examples/GeomeanOracle.sol | 13 ++- contracts/hooks/examples/LimitOrder.sol | 93 ++++++++++--------- contracts/hooks/examples/TWAMM.sol | 43 +++++---- contracts/hooks/examples/VolatilityOracle.sol | 12 ++- contracts/interfaces/IQuoter.sol | 2 +- contracts/lens/Quoter.sol | 22 +++-- contracts/libraries/PoolGetters.sol | 9 +- contracts/libraries/PoolTicksCounter.sol | 11 +-- lib/v4-core | 2 +- test/FullRange.t.sol | 72 +++++++------- test/GeomeanOracle.t.sol | 30 +++--- test/LimitOrder.t.sol | 27 +++--- test/Quoter.t.sol | 35 ++++--- test/TWAMM.t.sol | 12 +-- test/utils/HookEnabledSwapRouter.sol | 26 ++++-- 26 files changed, 282 insertions(+), 226 deletions(-) diff --git a/.forge-snapshots/FullRangeAddInitialLiquidity.snap b/.forge-snapshots/FullRangeAddInitialLiquidity.snap index cfdeb354..78823d57 100644 --- a/.forge-snapshots/FullRangeAddInitialLiquidity.snap +++ b/.forge-snapshots/FullRangeAddInitialLiquidity.snap @@ -1 +1 @@ -384735 \ No newline at end of file +311243 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeAddLiquidity.snap b/.forge-snapshots/FullRangeAddLiquidity.snap index e1efe638..cd5941c3 100644 --- a/.forge-snapshots/FullRangeAddLiquidity.snap +++ b/.forge-snapshots/FullRangeAddLiquidity.snap @@ -1 +1 @@ -179102 \ No newline at end of file +123052 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeFirstSwap.snap b/.forge-snapshots/FullRangeFirstSwap.snap index fd04e1b1..7180e028 100644 --- a/.forge-snapshots/FullRangeFirstSwap.snap +++ b/.forge-snapshots/FullRangeFirstSwap.snap @@ -1 +1 @@ -128152 \ No newline at end of file +82356 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeInitialize.snap b/.forge-snapshots/FullRangeInitialize.snap index b126274c..69d68c83 100644 --- a/.forge-snapshots/FullRangeInitialize.snap +++ b/.forge-snapshots/FullRangeInitialize.snap @@ -1 +1 @@ -1017530 \ No newline at end of file +1016091 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeRemoveLiquidity.snap b/.forge-snapshots/FullRangeRemoveLiquidity.snap index 2cdf6c52..c024925a 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidity.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidity.snap @@ -1 +1 @@ -169304 \ No newline at end of file +110705 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap index 2ccb0b58..a9b58592 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap @@ -1 +1 @@ -345919 \ No newline at end of file +240800 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSecondSwap.snap b/.forge-snapshots/FullRangeSecondSwap.snap index 51e5eb70..551d00ec 100644 --- a/.forge-snapshots/FullRangeSecondSwap.snap +++ b/.forge-snapshots/FullRangeSecondSwap.snap @@ -1 +1 @@ -89081 \ No newline at end of file +47770 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSwap.snap b/.forge-snapshots/FullRangeSwap.snap index bd033704..cc58180a 100644 --- a/.forge-snapshots/FullRangeSwap.snap +++ b/.forge-snapshots/FullRangeSwap.snap @@ -1 +1 @@ -126954 \ No newline at end of file +81129 \ No newline at end of file diff --git a/.forge-snapshots/TWAMMSubmitOrder.snap b/.forge-snapshots/TWAMMSubmitOrder.snap index 9191f9b4..16e68302 100644 --- a/.forge-snapshots/TWAMMSubmitOrder.snap +++ b/.forge-snapshots/TWAMMSubmitOrder.snap @@ -1 +1 @@ -122845 \ No newline at end of file +122417 \ No newline at end of file diff --git a/contracts/BaseHook.sol b/contracts/BaseHook.sol index 55670dab..eb75502c 100644 --- a/contracts/BaseHook.sol +++ b/contracts/BaseHook.sol @@ -6,6 +6,7 @@ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {BeforeSwapDelta} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol"; abstract contract BaseHook is IHooks { error NotPoolManager(); @@ -49,7 +50,7 @@ abstract contract BaseHook is IHooks { Hooks.validateHookPermissions(_this, getHookPermissions()); } - function lockAcquired(bytes calldata data) external virtual poolManagerOnly returns (bytes memory) { + function unlockCallback(bytes calldata data) external virtual poolManagerOnly returns (bytes memory) { (bool success, bytes memory returnData) = address(this).call(data); if (success) return returnData; if (returnData.length == 0) revert LockFailure(); @@ -95,7 +96,7 @@ abstract contract BaseHook is IHooks { IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, bytes calldata - ) external virtual returns (bytes4) { + ) external virtual returns (bytes4, BalanceDelta) { revert HookNotImplemented(); } @@ -105,14 +106,14 @@ abstract contract BaseHook is IHooks { IPoolManager.ModifyLiquidityParams calldata, BalanceDelta, bytes calldata - ) external virtual returns (bytes4) { + ) external virtual returns (bytes4, BalanceDelta) { revert HookNotImplemented(); } function beforeSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata) external virtual - returns (bytes4) + returns (bytes4, BeforeSwapDelta, uint24) { revert HookNotImplemented(); } @@ -120,7 +121,7 @@ abstract contract BaseHook is IHooks { function afterSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata) external virtual - returns (bytes4) + returns (bytes4, int128) { revert HookNotImplemented(); } diff --git a/contracts/hooks/examples/FullRange.sol b/contracts/hooks/examples/FullRange.sol index 820d0f93..aa4b606d 100644 --- a/contracts/hooks/examples/FullRange.sol +++ b/contracts/hooks/examples/FullRange.sol @@ -8,10 +8,11 @@ import {BaseHook} from "../../BaseHook.sol"; import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {CurrencyLibrary, Currency} from "@uniswap/v4-core/src/types/Currency.sol"; +import {CurrencySettleTake} from "@uniswap/v4-core/src/libraries/CurrencySettleTake.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {IERC20Minimal} from "@uniswap/v4-core/src/interfaces/external/IERC20Minimal.sol"; -import {ILockCallback} from "@uniswap/v4-core/src/interfaces/callback/ILockCallback.sol"; +import {IUnlockCallback} from "@uniswap/v4-core/src/interfaces/callback/IUnlockCallback.sol"; import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {FullMath} from "@uniswap/v4-core/src/libraries/FullMath.sol"; @@ -20,14 +21,18 @@ import {FixedPoint96} from "@uniswap/v4-core/src/libraries/FixedPoint96.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; +import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol"; import "../../libraries/LiquidityAmounts.sol"; -contract FullRange is BaseHook, ILockCallback { +contract FullRange is BaseHook, IUnlockCallback { using CurrencyLibrary for Currency; + using CurrencySettleTake for Currency; using PoolIdLibrary for PoolKey; using SafeCast for uint256; using SafeCast for uint128; + using StateLibrary for IPoolManager; /// @notice Thrown when trying to interact with a non-initialized pool error PoolNotInitialized(); @@ -98,7 +103,11 @@ contract FullRange is BaseHook, ILockCallback { beforeSwap: true, afterSwap: false, beforeDonate: false, - afterDonate: false + afterDonate: false, + beforeSwapReturnDelta: false, + afterSwapReturnDelta: false, + afterAddLiquidityReturnDelta: false, + afterRemoveLiquidityReturnDelta: false }); } @@ -127,8 +136,8 @@ contract FullRange is BaseHook, ILockCallback { liquidity = LiquidityAmounts.getLiquidityForAmounts( sqrtPriceX96, - TickMath.getSqrtRatioAtTick(MIN_TICK), - TickMath.getSqrtRatioAtTick(MAX_TICK), + TickMath.getSqrtPriceAtTick(MIN_TICK), + TickMath.getSqrtPriceAtTick(MAX_TICK), params.amount0Desired, params.amount1Desired ); @@ -141,7 +150,8 @@ contract FullRange is BaseHook, ILockCallback { IPoolManager.ModifyLiquidityParams({ tickLower: MIN_TICK, tickUpper: MAX_TICK, - liquidityDelta: liquidity.toInt256() + liquidityDelta: liquidity.toInt256(), + salt: 0 }) ); @@ -185,7 +195,8 @@ contract FullRange is BaseHook, ILockCallback { IPoolManager.ModifyLiquidityParams({ tickLower: MIN_TICK, tickUpper: MAX_TICK, - liquidityDelta: -(params.liquidity.toInt256()) + liquidityDelta: -(params.liquidity.toInt256()), + salt: 0 }) ); @@ -233,7 +244,7 @@ contract FullRange is BaseHook, ILockCallback { function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata, bytes calldata) external override - returns (bytes4) + returns (bytes4, BeforeSwapDelta, uint24) { PoolId poolId = key.toId(); @@ -242,32 +253,19 @@ contract FullRange is BaseHook, ILockCallback { pool.hasAccruedFees = true; } - return IHooks.beforeSwap.selector; + return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); } function modifyLiquidity(PoolKey memory key, IPoolManager.ModifyLiquidityParams memory params) internal returns (BalanceDelta delta) { - delta = abi.decode(poolManager.lock(abi.encode(CallbackData(msg.sender, key, params))), (BalanceDelta)); + delta = abi.decode(poolManager.unlock(abi.encode(CallbackData(msg.sender, key, params))), (BalanceDelta)); } function _settleDeltas(address sender, PoolKey memory key, BalanceDelta delta) internal { - _settleDelta(sender, key.currency0, uint128(-delta.amount0())); - _settleDelta(sender, key.currency1, uint128(-delta.amount1())); - } - - function _settleDelta(address sender, Currency currency, uint128 amount) internal { - if (currency.isNative()) { - poolManager.settle{value: amount}(currency); - } else { - if (sender == address(this)) { - currency.transfer(address(poolManager), amount); - } else { - IERC20Minimal(Currency.unwrap(currency)).transferFrom(sender, address(poolManager), amount); - } - poolManager.settle(currency); - } + key.currency0.settle(poolManager, sender, uint256(int256(-delta.amount0())), false); + key.currency1.settle(poolManager, sender, uint256(int256(-delta.amount1())), false); } function _takeDeltas(address sender, PoolKey memory key, BalanceDelta delta) internal { @@ -293,13 +291,13 @@ contract FullRange is BaseHook, ILockCallback { ); params.liquidityDelta = -(liquidityToRemove.toInt256()); - delta = poolManager.modifyLiquidity(key, params, ZERO_BYTES); + (delta,) = poolManager.modifyLiquidity(key, params, ZERO_BYTES); pool.hasAccruedFees = false; } - function lockAcquired(bytes calldata rawData) + function unlockCallback(bytes calldata rawData) external - override(ILockCallback, BaseHook) + override(IUnlockCallback, BaseHook) poolManagerOnly returns (bytes memory) { @@ -310,7 +308,7 @@ contract FullRange is BaseHook, ILockCallback { delta = _removeLiquidity(data.key, data.params); _takeDeltas(data.sender, data.key, delta); } else { - delta = poolManager.modifyLiquidity(data.key, data.params, ZERO_BYTES); + (delta,) = poolManager.modifyLiquidity(data.key, data.params, ZERO_BYTES); _settleDeltas(data.sender, data.key, delta); } return abi.encode(delta); @@ -318,12 +316,13 @@ contract FullRange is BaseHook, ILockCallback { function _rebalance(PoolKey memory key) public { PoolId poolId = key.toId(); - BalanceDelta balanceDelta = poolManager.modifyLiquidity( + (BalanceDelta balanceDelta,) = poolManager.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams({ tickLower: MIN_TICK, tickUpper: MAX_TICK, - liquidityDelta: -(poolManager.getLiquidity(poolId).toInt256()) + liquidityDelta: -(poolManager.getLiquidity(poolId).toInt256()), + salt: 0 }), ZERO_BYTES ); @@ -348,18 +347,19 @@ contract FullRange is BaseHook, ILockCallback { uint128 liquidity = LiquidityAmounts.getLiquidityForAmounts( newSqrtPriceX96, - TickMath.getSqrtRatioAtTick(MIN_TICK), - TickMath.getSqrtRatioAtTick(MAX_TICK), + TickMath.getSqrtPriceAtTick(MIN_TICK), + TickMath.getSqrtPriceAtTick(MAX_TICK), uint256(uint128(balanceDelta.amount0())), uint256(uint128(balanceDelta.amount1())) ); - BalanceDelta balanceDeltaAfter = poolManager.modifyLiquidity( + (BalanceDelta balanceDeltaAfter,) = poolManager.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams({ tickLower: MIN_TICK, tickUpper: MAX_TICK, - liquidityDelta: liquidity.toInt256() + liquidityDelta: liquidity.toInt256(), + salt: 0 }), ZERO_BYTES ); diff --git a/contracts/hooks/examples/GeomeanOracle.sol b/contracts/hooks/examples/GeomeanOracle.sol index c0f1c096..ec8301a5 100644 --- a/contracts/hooks/examples/GeomeanOracle.sol +++ b/contracts/hooks/examples/GeomeanOracle.sol @@ -8,6 +8,8 @@ import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {Oracle} from "../../libraries/Oracle.sol"; import {BaseHook} from "../../BaseHook.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; +import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol"; /// @notice A hook for a pool that allows a Uniswap pool to act as an oracle. Pools that use this hook must have full range /// tick spacing and liquidity is always permanently locked in these pools. This is the suggested configuration @@ -15,6 +17,7 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; contract GeomeanOracle is BaseHook { using Oracle for Oracle.Observation[65535]; using PoolIdLibrary for PoolKey; + using StateLibrary for IPoolManager; /// @notice Oracle pools do not have fees because they exist to serve as an oracle for a pair of tokens error OnlyOneOraclePoolAllowed(); @@ -71,7 +74,11 @@ contract GeomeanOracle is BaseHook { beforeSwap: true, afterSwap: false, beforeDonate: false, - afterDonate: false + afterDonate: false, + beforeSwapReturnDelta: false, + afterSwapReturnDelta: false, + afterAddLiquidityReturnDelta: false, + afterRemoveLiquidityReturnDelta: false }); } @@ -140,10 +147,10 @@ contract GeomeanOracle is BaseHook { external override poolManagerOnly - returns (bytes4) + returns (bytes4, BeforeSwapDelta, uint24) { _updatePool(key); - return GeomeanOracle.beforeSwap.selector; + return (GeomeanOracle.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); } /// @notice Observe the given pool for the timestamps diff --git a/contracts/hooks/examples/LimitOrder.sol b/contracts/hooks/examples/LimitOrder.sol index e6cf8e89..92d6489b 100644 --- a/contracts/hooks/examples/LimitOrder.sol +++ b/contracts/hooks/examples/LimitOrder.sol @@ -10,8 +10,10 @@ import {IERC20Minimal} from "@uniswap/v4-core/src/interfaces/external/IERC20Mini import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {BaseHook} from "../../BaseHook.sol"; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; +import {CurrencySettleTake} from "@uniswap/v4-core/src/libraries/CurrencySettleTake.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; type Epoch is uint232; @@ -31,6 +33,8 @@ contract LimitOrder is BaseHook { using EpochLibrary for Epoch; using PoolIdLibrary for PoolKey; using CurrencyLibrary for Currency; + using CurrencySettleTake for Currency; + using StateLibrary for IPoolManager; error ZeroLiquidity(); error InRange(); @@ -84,7 +88,11 @@ contract LimitOrder is BaseHook { beforeSwap: false, afterSwap: true, beforeDonate: false, - afterDonate: false + afterDonate: false, + beforeSwapReturnDelta: false, + afterSwapReturnDelta: false, + afterAddLiquidityReturnDelta: false, + afterRemoveLiquidityReturnDelta: false }); } @@ -134,9 +142,9 @@ contract LimitOrder is BaseHook { IPoolManager.SwapParams calldata params, BalanceDelta, bytes calldata - ) external override poolManagerOnly returns (bytes4) { + ) external override poolManagerOnly returns (bytes4, int128) { (int24 tickLower, int24 lower, int24 upper) = _getCrossedTicks(key.toId(), key.tickSpacing); - if (lower > upper) return LimitOrder.afterSwap.selector; + if (lower > upper) return (LimitOrder.afterSwap.selector, 0); // note that a zeroForOne swap means that the pool is actually gaining token0, so limit // order fills are the opposite of swap fills, hence the inversion below @@ -146,7 +154,7 @@ contract LimitOrder is BaseHook { } setTickLowerLast(key.toId(), tickLower); - return LimitOrder.afterSwap.selector; + return (LimitOrder.afterSwap.selector, 0); } function _fillEpoch(PoolKey calldata key, int24 lower, bool zeroForOne) internal { @@ -157,7 +165,7 @@ contract LimitOrder is BaseHook { epochInfo.filled = true; (uint256 amount0, uint256 amount1) = - _lockAcquiredFill(key, lower, -int256(uint256(epochInfo.liquidityTotal))); + _unlockCallbackFill(key, lower, -int256(uint256(epochInfo.liquidityTotal))); unchecked { epochInfo.token0Total += amount0; @@ -187,17 +195,18 @@ contract LimitOrder is BaseHook { } } - function _lockAcquiredFill(PoolKey calldata key, int24 tickLower, int256 liquidityDelta) + function _unlockCallbackFill(PoolKey calldata key, int24 tickLower, int256 liquidityDelta) private poolManagerOnly returns (uint128 amount0, uint128 amount1) { - BalanceDelta delta = poolManager.modifyLiquidity( + (BalanceDelta delta,) = poolManager.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams({ tickLower: tickLower, tickUpper: tickLower + key.tickSpacing, - liquidityDelta: liquidityDelta + liquidityDelta: liquidityDelta, + salt: 0 }), ZERO_BYTES ); @@ -216,8 +225,10 @@ contract LimitOrder is BaseHook { { if (liquidity == 0) revert ZeroLiquidity(); - poolManager.lock( - abi.encodeCall(this.lockAcquiredPlace, (key, tickLower, zeroForOne, int256(uint256(liquidity)), msg.sender)) + poolManager.unlock( + abi.encodeCall( + this.unlockCallbackPlace, (key, tickLower, zeroForOne, int256(uint256(liquidity)), msg.sender) + ) ); EpochInfo storage epochInfo; @@ -245,19 +256,20 @@ contract LimitOrder is BaseHook { emit Place(msg.sender, epoch, key, tickLower, zeroForOne, liquidity); } - function lockAcquiredPlace( + function unlockCallbackPlace( PoolKey calldata key, int24 tickLower, bool zeroForOne, int256 liquidityDelta, address owner ) external selfOnly { - BalanceDelta delta = poolManager.modifyLiquidity( + (BalanceDelta delta,) = poolManager.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams({ tickLower: tickLower, tickUpper: tickLower + key.tickSpacing, - liquidityDelta: liquidityDelta + liquidityDelta: liquidityDelta, + salt: 0 }), ZERO_BYTES ); @@ -265,26 +277,15 @@ contract LimitOrder is BaseHook { if (delta.amount0() < 0) { if (delta.amount1() != 0) revert InRange(); if (!zeroForOne) revert CrossedRange(); - // TODO use safeTransferFrom - IERC20Minimal(Currency.unwrap(key.currency0)).transferFrom( - owner, address(poolManager), uint256(uint128(-delta.amount0())) - ); - poolManager.settle(key.currency0); + key.currency0.settle(poolManager, owner, uint256(uint128(-delta.amount0())), false); } else { if (delta.amount0() != 0) revert InRange(); if (zeroForOne) revert CrossedRange(); - // TODO use safeTransferFrom - IERC20Minimal(Currency.unwrap(key.currency1)).transferFrom( - owner, address(poolManager), uint256(uint128(-delta.amount1())) - ); - poolManager.settle(key.currency1); + key.currency1.settle(poolManager, owner, uint256(uint128(-delta.amount1())), false); } } - function kill(PoolKey calldata key, int24 tickLower, bool zeroForOne, address to) - external - returns (uint256 amount0, uint256 amount1) - { + function kill(PoolKey calldata key, int24 tickLower, bool zeroForOne, address to) external { Epoch epoch = getEpoch(key, tickLower, zeroForOne); EpochInfo storage epochInfo = epochInfos[epoch]; @@ -296,14 +297,14 @@ contract LimitOrder is BaseHook { uint256 amount0Fee; uint256 amount1Fee; - (amount0, amount1, amount0Fee, amount1Fee) = abi.decode( - poolManager.lock( + (amount0Fee, amount1Fee) = abi.decode( + poolManager.unlock( abi.encodeCall( - this.lockAcquiredKill, + this.unlockCallbackKill, (key, tickLower, -int256(uint256(liquidity)), to, liquidity == epochInfo.liquidityTotal) ) ), - (uint256, uint256, uint256, uint256) + (uint256, uint256) ); epochInfo.liquidityTotal -= liquidity; unchecked { @@ -314,13 +315,13 @@ contract LimitOrder is BaseHook { emit Kill(msg.sender, epoch, key, tickLower, zeroForOne, liquidity); } - function lockAcquiredKill( + function unlockCallbackKill( PoolKey calldata key, int24 tickLower, int256 liquidityDelta, address to, bool removingAllLiquidity - ) external selfOnly returns (uint256 amount0, uint256 amount1, uint128 amount0Fee, uint128 amount1Fee) { + ) external selfOnly returns (uint128 amount0Fee, uint128 amount1Fee) { int24 tickUpper = tickLower + key.tickSpacing; // because `modifyPosition` includes not just principal value but also fees, we cannot allocate @@ -328,9 +329,14 @@ contract LimitOrder is BaseHook { // could be unfairly diluted by a user sychronously placing then killing a limit order to skim off fees. // to prevent this, we allocate all fee revenue to remaining limit order placers, unless this is the last order. if (!removingAllLiquidity) { - BalanceDelta deltaFee = poolManager.modifyLiquidity( + (, BalanceDelta deltaFee) = poolManager.modifyLiquidity( key, - IPoolManager.ModifyLiquidityParams({tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: 0}), + IPoolManager.ModifyLiquidityParams({ + tickLower: tickLower, + tickUpper: tickUpper, + liquidityDelta: 0, + salt: 0 + }), ZERO_BYTES ); @@ -342,21 +348,22 @@ contract LimitOrder is BaseHook { } } - BalanceDelta delta = poolManager.modifyLiquidity( + (BalanceDelta delta,) = poolManager.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams({ tickLower: tickLower, tickUpper: tickUpper, - liquidityDelta: liquidityDelta + liquidityDelta: liquidityDelta, + salt: 0 }), ZERO_BYTES ); if (delta.amount0() > 0) { - poolManager.take(key.currency0, to, amount0 = uint128(delta.amount0())); + key.currency0.take(poolManager, to, uint256(uint128(delta.amount0())), false); } if (delta.amount1() > 0) { - poolManager.take(key.currency1, to, amount1 = uint128(delta.amount1())); + key.currency1.take(poolManager, to, uint256(uint128(delta.amount1())), false); } } @@ -378,14 +385,16 @@ contract LimitOrder is BaseHook { epochInfo.token1Total -= amount1; epochInfo.liquidityTotal = liquidityTotal - liquidity; - poolManager.lock( - abi.encodeCall(this.lockAcquiredWithdraw, (epochInfo.currency0, epochInfo.currency1, amount0, amount1, to)) + poolManager.unlock( + abi.encodeCall( + this.unlockCallbackWithdraw, (epochInfo.currency0, epochInfo.currency1, amount0, amount1, to) + ) ); emit Withdraw(msg.sender, epoch, liquidity); } - function lockAcquiredWithdraw( + function unlockCallbackWithdraw( Currency currency0, Currency currency1, uint256 token0Amount, diff --git a/contracts/hooks/examples/TWAMM.sol b/contracts/hooks/examples/TWAMM.sol index 85364915..0661dd54 100644 --- a/contracts/hooks/examples/TWAMM.sol +++ b/contracts/hooks/examples/TWAMM.sol @@ -19,10 +19,14 @@ import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {PoolGetters} from "../../libraries/PoolGetters.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; +import {CurrencySettleTake} from "@uniswap/v4-core/src/libraries/CurrencySettleTake.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; +import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol"; contract TWAMM is BaseHook, ITWAMM { using TransferHelper for IERC20Minimal; using CurrencyLibrary for Currency; + using CurrencySettleTake for Currency; using OrderPool for OrderPool.State; using PoolIdLibrary for PoolKey; using TickMath for int24; @@ -30,6 +34,7 @@ contract TWAMM is BaseHook, ITWAMM { using SafeCast for uint256; using PoolGetters for IPoolManager; using TickBitmap for mapping(int16 => uint256); + using StateLibrary for IPoolManager; bytes internal constant ZERO_BYTES = bytes(""); @@ -71,7 +76,11 @@ contract TWAMM is BaseHook, ITWAMM { beforeSwap: true, afterSwap: false, beforeDonate: false, - afterDonate: false + afterDonate: false, + beforeSwapReturnDelta: false, + afterSwapReturnDelta: false, + afterAddLiquidityReturnDelta: false, + afterRemoveLiquidityReturnDelta: false }); } @@ -101,10 +110,10 @@ contract TWAMM is BaseHook, ITWAMM { external override poolManagerOnly - returns (bytes4) + returns (bytes4, BeforeSwapDelta, uint24) { executeTWAMMOrders(key); - return BaseHook.beforeSwap.selector; + return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); } function lastVirtualOrderTimestamp(PoolId key) external view returns (uint256) { @@ -142,7 +151,9 @@ contract TWAMM is BaseHook, ITWAMM { ); if (sqrtPriceLimitX96 != 0 && sqrtPriceLimitX96 != sqrtPriceX96) { - poolManager.lock(abi.encode(key, IPoolManager.SwapParams(zeroForOne, type(int256).max, sqrtPriceLimitX96))); + poolManager.unlock( + abi.encode(key, IPoolManager.SwapParams(zeroForOne, type(int256).max, sqrtPriceLimitX96)) + ); } } @@ -298,7 +309,7 @@ contract TWAMM is BaseHook, ITWAMM { IERC20Minimal(Currency.unwrap(token)).safeTransfer(to, amountTransferred); } - function lockAcquired(bytes calldata rawData) external override poolManagerOnly returns (bytes memory) { + function unlockCallback(bytes calldata rawData) external override poolManagerOnly returns (bytes memory) { (PoolKey memory key, IPoolManager.SwapParams memory swapParams) = abi.decode(rawData, (PoolKey, IPoolManager.SwapParams)); @@ -306,19 +317,17 @@ contract TWAMM is BaseHook, ITWAMM { if (swapParams.zeroForOne) { if (delta.amount0() < 0) { - key.currency0.transfer(address(poolManager), uint256(uint128(-delta.amount0()))); - poolManager.settle(key.currency0); + key.currency0.settle(poolManager, address(this), uint256(uint128(-delta.amount0())), false); } if (delta.amount1() > 0) { - poolManager.take(key.currency1, address(this), uint256(uint128(delta.amount1()))); + key.currency1.take(poolManager, address(this), uint256(uint128(delta.amount1())), false); } } else { if (delta.amount1() < 0) { - key.currency1.transfer(address(poolManager), uint256(uint128(-delta.amount1()))); - poolManager.settle(key.currency1); + key.currency1.settle(poolManager, address(this), uint256(uint128(-delta.amount1())), false); } if (delta.amount0() > 0) { - poolManager.take(key.currency0, address(this), uint256(uint128(delta.amount0()))); + key.currency0.take(poolManager, address(this), uint256(uint128(delta.amount0())), false); } } return bytes(""); @@ -512,8 +521,8 @@ contract TWAMM is BaseHook, ITWAMM { _isCrossingInitializedTick(params.pool, poolManager, poolKey, finalSqrtPriceX96); if (crossingInitializedTick) { - int128 liquidityNetAtTick = poolManager.getPoolTickInfo(poolKey.toId(), tick).liquidityNet; - uint160 initializedSqrtPrice = TickMath.getSqrtRatioAtTick(tick); + (, int128 liquidityNetAtTick) = poolManager.getTickLiquidity(poolKey.toId(), tick); + uint160 initializedSqrtPrice = TickMath.getSqrtPriceAtTick(tick); uint256 swapDelta0 = SqrtPriceMath.getAmount0Delta( params.pool.sqrtPriceX96, initializedSqrtPrice, params.pool.liquidity, true @@ -570,7 +579,7 @@ contract TWAMM is BaseHook, ITWAMM { PoolKey memory poolKey, TickCrossingParams memory params ) private returns (PoolParamsOnExecute memory, uint256) { - uint160 initializedSqrtPrice = params.initializedTick.getSqrtRatioAtTick(); + uint160 initializedSqrtPrice = params.initializedTick.getSqrtPriceAtTick(); uint256 secondsUntilCrossingX96 = TwammMath.calculateTimeBetweenTicks( params.pool.liquidity, @@ -596,7 +605,7 @@ contract TWAMM is BaseHook, ITWAMM { unchecked { // update pool - int128 liquidityNet = poolManager.getPoolTickInfo(poolKey.toId(), params.initializedTick).liquidityNet; + (, int128 liquidityNet) = poolManager.getTickLiquidity(poolKey.toId(), params.initializedTick); if (initializedSqrtPrice < params.pool.sqrtPriceX96) liquidityNet = -liquidityNet; params.pool.liquidity = liquidityNet < 0 ? params.pool.liquidity - uint128(-liquidityNet) @@ -614,8 +623,8 @@ contract TWAMM is BaseHook, ITWAMM { uint160 nextSqrtPriceX96 ) internal view returns (bool crossingInitializedTick, int24 nextTickInit) { // use current price as a starting point for nextTickInit - nextTickInit = pool.sqrtPriceX96.getTickAtSqrtRatio(); - int24 targetTick = nextSqrtPriceX96.getTickAtSqrtRatio(); + nextTickInit = pool.sqrtPriceX96.getTickAtSqrtPrice(); + int24 targetTick = nextSqrtPriceX96.getTickAtSqrtPrice(); bool searchingLeft = nextSqrtPriceX96 < pool.sqrtPriceX96; bool nextTickInitFurtherThanTarget = false; // initialize as false diff --git a/contracts/hooks/examples/VolatilityOracle.sol b/contracts/hooks/examples/VolatilityOracle.sol index 76a3e8ce..ede61bf5 100644 --- a/contracts/hooks/examples/VolatilityOracle.sol +++ b/contracts/hooks/examples/VolatilityOracle.sol @@ -3,12 +3,12 @@ pragma solidity ^0.8.19; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; -import {SwapFeeLibrary} from "@uniswap/v4-core/src/libraries/SwapFeeLibrary.sol"; +import {LPFeeLibrary} from "@uniswap/v4-core/src/libraries/LPFeeLibrary.sol"; import {BaseHook} from "../../BaseHook.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; contract VolatilityOracle is BaseHook { - using SwapFeeLibrary for uint24; + using LPFeeLibrary for uint24; error MustUseDynamicFee(); @@ -34,7 +34,11 @@ contract VolatilityOracle is BaseHook { beforeSwap: false, afterSwap: false, beforeDonate: false, - afterDonate: false + afterDonate: false, + beforeSwapReturnDelta: false, + afterSwapReturnDelta: false, + afterAddLiquidityReturnDelta: false, + afterRemoveLiquidityReturnDelta: false }); } @@ -52,7 +56,7 @@ contract VolatilityOracle is BaseHook { uint24 startingFee = 3000; uint32 lapsed = _blockTimestamp() - deployTimestamp; uint24 fee = startingFee + (uint24(lapsed) * 100) / 60; // 100 bps a minute - poolManager.updateDynamicSwapFee(key, fee); // initial fee 0.30% + poolManager.updateDynamicLPFee(key, fee); // initial fee 0.30% } function afterInitialize(address, PoolKey calldata key, uint160, int24, bytes calldata) diff --git a/contracts/interfaces/IQuoter.sol b/contracts/interfaces/IQuoter.sol index 90a390fc..8774e548 100644 --- a/contracts/interfaces/IQuoter.sol +++ b/contracts/interfaces/IQuoter.sol @@ -11,7 +11,7 @@ import {PathKey} from "../libraries/PathKey.sol"; /// @dev These functions are not marked view because they rely on calling non-view functions and reverting /// to compute the result. They are also not gas efficient and should not be called on-chain. interface IQuoter { - error InvalidLockAcquiredSender(); + error InvalidUnlockCallbackSender(); error InvalidLockCaller(); error InvalidQuoteBatchParams(); error InsufficientAmountOut(); diff --git a/contracts/lens/Quoter.sol b/contracts/lens/Quoter.sol index c039a7b7..9e9bfda2 100644 --- a/contracts/lens/Quoter.sol +++ b/contracts/lens/Quoter.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.20; import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; -import {ILockCallback} from "@uniswap/v4-core/src/interfaces/callback/ILockCallback.sol"; +import {IUnlockCallback} from "@uniswap/v4-core/src/interfaces/callback/IUnlockCallback.sol"; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {Currency} from "@uniswap/v4-core/src/types/Currency.sol"; @@ -13,11 +13,13 @@ import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; import {IQuoter} from "../interfaces/IQuoter.sol"; import {PoolTicksCounter} from "../libraries/PoolTicksCounter.sol"; import {PathKey, PathKeyLib} from "../libraries/PathKey.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; -contract Quoter is IQuoter, ILockCallback { +contract Quoter is IQuoter, IUnlockCallback { using Hooks for IHooks; using PoolIdLibrary for PoolKey; using PathKeyLib for PathKey; + using StateLibrary for IPoolManager; /// @dev cache used to check a safety condition in exact output swaps. uint128 private amountOutCached; @@ -62,7 +64,7 @@ contract Quoter is IQuoter, ILockCallback { override returns (int128[] memory deltaAmounts, uint160 sqrtPriceX96After, uint32 initializedTicksLoaded) { - try manager.lock(abi.encodeWithSelector(this._quoteExactInputSingle.selector, params)) {} + try manager.unlock(abi.encodeWithSelector(this._quoteExactInputSingle.selector, params)) {} catch (bytes memory reason) { return _handleRevertSingle(reason); } @@ -77,7 +79,7 @@ contract Quoter is IQuoter, ILockCallback { uint32[] memory initializedTicksLoadedList ) { - try manager.lock(abi.encodeWithSelector(this._quoteExactInput.selector, params)) {} + try manager.unlock(abi.encodeWithSelector(this._quoteExactInput.selector, params)) {} catch (bytes memory reason) { return _handleRevert(reason); } @@ -89,7 +91,7 @@ contract Quoter is IQuoter, ILockCallback { override returns (int128[] memory deltaAmounts, uint160 sqrtPriceX96After, uint32 initializedTicksLoaded) { - try manager.lock(abi.encodeWithSelector(this._quoteExactOutputSingle.selector, params)) {} + try manager.unlock(abi.encodeWithSelector(this._quoteExactOutputSingle.selector, params)) {} catch (bytes memory reason) { if (params.sqrtPriceLimitX96 == 0) delete amountOutCached; return _handleRevertSingle(reason); @@ -106,16 +108,16 @@ contract Quoter is IQuoter, ILockCallback { uint32[] memory initializedTicksLoadedList ) { - try manager.lock(abi.encodeWithSelector(this._quoteExactOutput.selector, params)) {} + try manager.unlock(abi.encodeWithSelector(this._quoteExactOutput.selector, params)) {} catch (bytes memory reason) { return _handleRevert(reason); } } - /// @inheritdoc ILockCallback - function lockAcquired(bytes calldata data) external returns (bytes memory) { + /// @inheritdoc IUnlockCallback + function unlockCallback(bytes calldata data) external returns (bytes memory) { if (msg.sender != address(manager)) { - revert InvalidLockAcquiredSender(); + revert InvalidUnlockCallbackSender(); } (bool success, bytes memory returnData) = address(this).call(data); @@ -331,7 +333,7 @@ contract Quoter is IQuoter, ILockCallback { /// @dev return either the sqrtPriceLimit from user input, or the max/min value possible depending on trade direction function _sqrtPriceLimitOrDefault(uint160 sqrtPriceLimitX96, bool zeroForOne) private pure returns (uint160) { return sqrtPriceLimitX96 == 0 - ? zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1 + ? zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1 : sqrtPriceLimitX96; } } diff --git a/contracts/libraries/PoolGetters.sol b/contracts/libraries/PoolGetters.sol index e3cb318b..df31f3c1 100644 --- a/contracts/libraries/PoolGetters.sol +++ b/contracts/libraries/PoolGetters.sol @@ -5,6 +5,7 @@ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {Pool} from "@uniswap/v4-core/src/libraries/Pool.sol"; import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; import {BitMath} from "@uniswap/v4-core/src/libraries/BitMath.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; /// @title Helper functions to access pool information /// TODO: Expose other getters on core with extsload. Only use when extsload is available and storage layout is frozen. @@ -13,6 +14,8 @@ library PoolGetters { uint256 constant TICKS_OFFSET = 4; uint256 constant TICK_BITMAP_OFFSET = 5; + using StateLibrary for IPoolManager; + function getNetLiquidityAtTick(IPoolManager poolManager, PoolId poolId, int24 tick) internal view @@ -63,7 +66,8 @@ library PoolGetters { // all the 1s at or to the right of the current bitPos uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); // uint256 masked = self[wordPos] & mask; - uint256 masked = poolManager.getPoolBitmapInfo(poolId, wordPos) & mask; + uint256 tickBitmap = poolManager.getTickBitmap(poolId, wordPos); + uint256 masked = tickBitmap & mask; // if there are no initialized ticks to the right of or at the current tick, return rightmost in the word initialized = masked != 0; @@ -76,7 +80,8 @@ library PoolGetters { (int16 wordPos, uint8 bitPos) = position(compressed + 1); // all the 1s at or to the left of the bitPos uint256 mask = ~((1 << bitPos) - 1); - uint256 masked = poolManager.getPoolBitmapInfo(poolId, wordPos) & mask; + uint256 tickBitmap = poolManager.getTickBitmap(poolId, wordPos); + uint256 masked = tickBitmap & mask; // if there are no initialized ticks to the left of the current tick, return leftmost in the word initialized = masked != 0; diff --git a/contracts/libraries/PoolTicksCounter.sol b/contracts/libraries/PoolTicksCounter.sol index 077ef4a6..60fdbbe5 100644 --- a/contracts/libraries/PoolTicksCounter.sol +++ b/contracts/libraries/PoolTicksCounter.sol @@ -5,9 +5,11 @@ import {PoolGetters} from "./PoolGetters.sol"; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; library PoolTicksCounter { using PoolIdLibrary for PoolKey; + using StateLibrary for IPoolManager; struct TickCache { int16 wordPosLower; @@ -41,15 +43,13 @@ library PoolTicksCounter { // If the initializable tick after the swap is initialized, our original tickAfter is a // multiple of tick spacing, and we are swapping downwards we know that tickAfter is initialized // and we shouldn't count it. - uint256 bmAfter = self.getPoolBitmapInfo(key.toId(), wordPosAfter); - //uint256 bmAfter = PoolGetters.getTickBitmapAtWord(self, key.toId(), wordPosAfter); + uint256 bmAfter = self.getTickBitmap(key.toId(), wordPosAfter); cache.tickAfterInitialized = ((bmAfter & (1 << bitPosAfter)) > 0) && ((tickAfter % key.tickSpacing) == 0) && (tickBefore > tickAfter); // In the case where tickBefore is initialized, we only want to count it if we are swapping upwards. // Use the same logic as above to decide whether we should count tickBefore or not. - uint256 bmBefore = self.getPoolBitmapInfo(key.toId(), wordPos); - //uint256 bmBefore = PoolGetters.getTickBitmapAtWord(self, key.toId(), wordPos); + uint256 bmBefore = self.getTickBitmap(key.toId(), wordPos); cache.tickBeforeInitialized = ((bmBefore & (1 << bitPos)) > 0) && ((tickBefore % key.tickSpacing) == 0) && (tickBefore < tickAfter); @@ -76,8 +76,7 @@ library PoolTicksCounter { mask = mask & (type(uint256).max >> (255 - cache.bitPosHigher)); } - //uint256 bmLower = PoolGetters.getTickBitmapAtWord(self, key.toId(), cache.wordPosLower); - uint256 bmLower = self.getPoolBitmapInfo(key.toId(), cache.wordPosLower); + uint256 bmLower = self.getTickBitmap(key.toId(), cache.wordPosLower); uint256 masked = bmLower & mask; initializedTicksLoaded += countOneBits(masked); cache.wordPosLower++; diff --git a/lib/v4-core b/lib/v4-core index f5674e46..3351c80e 160000 --- a/lib/v4-core +++ b/lib/v4-core @@ -1 +1 @@ -Subproject commit f5674e46720c0fc4606b287cccc583d56245e724 +Subproject commit 3351c80e58e6300cb263d33a4efe75b88ad7b9b2 diff --git a/test/FullRange.t.sol b/test/FullRange.t.sol index f0867ba4..5edec106 100644 --- a/test/FullRange.t.sol +++ b/test/FullRange.t.sol @@ -20,14 +20,16 @@ import {UniswapV4ERC20} from "../contracts/libraries/UniswapV4ERC20.sol"; import {FullMath} from "@uniswap/v4-core/src/libraries/FullMath.sol"; import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol"; import {HookEnabledSwapRouter} from "./utils/HookEnabledSwapRouter.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; contract TestFullRange is Test, Deployers, GasSnapshot { using PoolIdLibrary for PoolKey; using SafeCast for uint256; using CurrencyLibrary for Currency; + using StateLibrary for IPoolManager; event Initialize( - PoolId indexed poolId, + PoolId poolId, Currency indexed currency0, Currency indexed currency1, uint24 fee, @@ -39,7 +41,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { ); event Swap( PoolId indexed id, - address indexed sender, + address sender, int128 amount0, int128 amount1, uint160 sqrtPriceX96, @@ -104,7 +106,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { token1.approve(address(router), type(uint256).max); token2.approve(address(router), type(uint256).max); - initPool(keyWithLiq.currency0, keyWithLiq.currency1, fullRange, 3000, SQRT_RATIO_1_1, ZERO_BYTES); + initPool(keyWithLiq.currency0, keyWithLiq.currency1, fullRange, 3000, SQRT_PRICE_1_1, ZERO_BYTES); fullRange.addLiquidity( FullRange.AddLiquidityParams( keyWithLiq.currency0, @@ -127,7 +129,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { emit Initialize(id, testKey.currency0, testKey.currency1, testKey.fee, testKey.tickSpacing, testKey.hooks); snapStart("FullRangeInitialize"); - manager.initialize(testKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(testKey, SQRT_PRICE_1_1, ZERO_BYTES); snapEnd(); (, address liquidityToken) = fullRange.poolInfo(id); @@ -139,11 +141,11 @@ contract TestFullRange is Test, Deployers, GasSnapshot { PoolKey memory wrongKey = PoolKey(key.currency0, key.currency1, 0, TICK_SPACING + 1, fullRange); vm.expectRevert(FullRange.TickSpacingNotDefault.selector); - manager.initialize(wrongKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(wrongKey, SQRT_PRICE_1_1, ZERO_BYTES); } function testFullRange_addLiquidity_InitialAddSucceeds() public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); uint256 prevBalance0 = key.currency0.balanceOf(address(this)); uint256 prevBalance1 = key.currency1.balanceOf(address(this)); @@ -169,7 +171,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_addLiquidity_InitialAddFuzz(uint256 amount) public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); if (amount <= LOCKED_LIQUIDITY) { vm.expectRevert(FullRange.LiquidityDoesntMeetMinimum.selector); fullRange.addLiquidity( @@ -244,7 +246,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_addLiquidity_SwapThenAddSucceeds() public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); uint256 prevBalance0 = key.currency0.balanceOf(address(this)); uint256 prevBalance1 = key.currency1.balanceOf(address(this)); @@ -269,9 +271,9 @@ contract TestFullRange is Test, Deployers, GasSnapshot { ); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -1 ether, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: -1 ether, sqrtPriceLimitX96: SQRT_PRICE_1_2}); HookEnabledSwapRouter.TestSettings memory settings = - HookEnabledSwapRouter.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); + HookEnabledSwapRouter.TestSettings({takeClaims: false, settleUsingBurn: false}); snapStart("FullRangeSwap"); router.swap(key, params, settings, ZERO_BYTES); @@ -298,7 +300,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_addLiquidity_FailsIfTooMuchSlippage() public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); fullRange.addLiquidity( FullRange.AddLiquidityParams( @@ -307,9 +309,9 @@ contract TestFullRange is Test, Deployers, GasSnapshot { ); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 1000 ether, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 1000 ether, sqrtPriceLimitX96: SQRT_PRICE_1_2}); HookEnabledSwapRouter.TestSettings memory settings = - HookEnabledSwapRouter.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); + HookEnabledSwapRouter.TestSettings({takeClaims: false, settleUsingBurn: false}); router.swap(key, params, settings, ZERO_BYTES); @@ -323,7 +325,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { function testFullRange_swap_TwoSwaps() public { PoolKey memory testKey = key; - manager.initialize(testKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(testKey, SQRT_PRICE_1_1, ZERO_BYTES); fullRange.addLiquidity( FullRange.AddLiquidityParams( @@ -332,9 +334,9 @@ contract TestFullRange is Test, Deployers, GasSnapshot { ); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 1 ether, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 1 ether, sqrtPriceLimitX96: SQRT_PRICE_1_2}); HookEnabledSwapRouter.TestSettings memory settings = - HookEnabledSwapRouter.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); + HookEnabledSwapRouter.TestSettings({takeClaims: false, settleUsingBurn: false}); snapStart("FullRangeFirstSwap"); router.swap(testKey, params, settings, ZERO_BYTES); @@ -352,8 +354,8 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_swap_TwoPools() public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - manager.initialize(key2, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); + manager.initialize(key2, SQRT_PRICE_1_1, ZERO_BYTES); fullRange.addLiquidity( FullRange.AddLiquidityParams( @@ -367,10 +369,10 @@ contract TestFullRange is Test, Deployers, GasSnapshot { ); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 10000000, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 10000000, sqrtPriceLimitX96: SQRT_PRICE_1_2}); HookEnabledSwapRouter.TestSettings memory testSettings = - HookEnabledSwapRouter.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); + HookEnabledSwapRouter.TestSettings({takeClaims: false, settleUsingBurn: false}); router.swap(key, params, testSettings, ZERO_BYTES); router.swap(key2, params, testSettings, ZERO_BYTES); @@ -408,7 +410,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_InitialRemoveFuzz(uint256 amount) public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); fullRange.addLiquidity( FullRange.AddLiquidityParams( @@ -456,7 +458,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_FailsIfNoLiquidity() public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); (, address liquidityToken) = fullRange.poolInfo(id); UniswapV4ERC20(liquidityToken).approve(address(fullRange), type(uint256).max); @@ -468,7 +470,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_SucceedsWithPartial() public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); uint256 prevBalance0 = key.currency0.balanceOfSelf(); uint256 prevBalance1 = key.currency1.balanceOfSelf(); @@ -503,7 +505,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_DiffRatios() public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); uint256 prevBalance0 = key.currency0.balanceOf(address(this)); uint256 prevBalance1 = key.currency1.balanceOf(address(this)); @@ -550,10 +552,10 @@ contract TestFullRange is Test, Deployers, GasSnapshot { (, address liquidityToken) = fullRange.poolInfo(idWithLiq); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 1 ether, sqrtPriceLimitX96: SQRT_RATIO_1_2}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 1 ether, sqrtPriceLimitX96: SQRT_PRICE_1_2}); HookEnabledSwapRouter.TestSettings memory testSettings = - HookEnabledSwapRouter.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); + HookEnabledSwapRouter.TestSettings({takeClaims: false, settleUsingBurn: false}); router.swap(keyWithLiq, params, testSettings, ZERO_BYTES); @@ -571,7 +573,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_RemoveAllFuzz(uint256 amount) public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); (, address liquidityToken) = fullRange.poolInfo(id); if (amount <= LOCKED_LIQUIDITY) { @@ -626,7 +628,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { vm.prank(address(2)); token1.approve(address(fullRange), type(uint256).max); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); (, address liquidityToken) = fullRange.poolInfo(id); // Test contract adds liquidity @@ -677,10 +679,10 @@ contract TestFullRange is Test, Deployers, GasSnapshot { ); IPoolManager.SwapParams memory params = - IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100 ether, sqrtPriceLimitX96: SQRT_RATIO_1_4}); + IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100 ether, sqrtPriceLimitX96: SQRT_PRICE_1_4}); HookEnabledSwapRouter.TestSettings memory testSettings = - HookEnabledSwapRouter.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); + HookEnabledSwapRouter.TestSettings({takeClaims: false, settleUsingBurn: false}); router.swap(key, params, testSettings, ZERO_BYTES); @@ -704,7 +706,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_removeLiquidity_SwapRemoveAllFuzz(uint256 amount) public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); (, address liquidityToken) = fullRange.poolInfo(id); if (amount <= LOCKED_LIQUIDITY) { @@ -731,11 +733,11 @@ contract TestFullRange is Test, Deployers, GasSnapshot { IPoolManager.SwapParams memory params = IPoolManager.SwapParams({ zeroForOne: true, amountSpecified: (FullMath.mulDiv(amount, 1, 4)).toInt256(), - sqrtPriceLimitX96: SQRT_RATIO_1_4 + sqrtPriceLimitX96: SQRT_PRICE_1_4 }); HookEnabledSwapRouter.TestSettings memory testSettings = - HookEnabledSwapRouter.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); + HookEnabledSwapRouter.TestSettings({takeClaims: false, settleUsingBurn: false}); router.swap(key, params, testSettings, ZERO_BYTES); @@ -753,12 +755,12 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_BeforeModifyPositionFailsWithWrongMsgSender() public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); vm.expectRevert(FullRange.SenderMustBeHook.selector); modifyLiquidityRouter.modifyLiquidity( key, - IPoolManager.ModifyLiquidityParams({tickLower: MIN_TICK, tickUpper: MAX_TICK, liquidityDelta: 100}), + IPoolManager.ModifyLiquidityParams({tickLower: MIN_TICK, tickUpper: MAX_TICK, liquidityDelta: 100, salt: 0}), ZERO_BYTES ); } diff --git a/test/GeomeanOracle.t.sol b/test/GeomeanOracle.t.sol index 05255e93..e6ff1695 100644 --- a/test/GeomeanOracle.t.sol +++ b/test/GeomeanOracle.t.sol @@ -65,14 +65,14 @@ contract TestGeomeanOracle is Test, Deployers { } function testBeforeInitializeAllowsPoolCreation() public { - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_1_1, ZERO_BYTES); } function testBeforeInitializeRevertsIfFee() public { vm.expectRevert(GeomeanOracle.OnlyOneOraclePoolAllowed.selector); manager.initialize( PoolKey(Currency.wrap(address(token0)), Currency.wrap(address(token1)), 1, MAX_TICK_SPACING, geomeanOracle), - SQRT_RATIO_1_1, + SQRT_PRICE_1_1, ZERO_BYTES ); } @@ -81,13 +81,13 @@ contract TestGeomeanOracle is Test, Deployers { vm.expectRevert(GeomeanOracle.OnlyOneOraclePoolAllowed.selector); manager.initialize( PoolKey(Currency.wrap(address(token0)), Currency.wrap(address(token1)), 0, 60, geomeanOracle), - SQRT_RATIO_1_1, + SQRT_PRICE_1_1, ZERO_BYTES ); } function testAfterInitializeState() public { - manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_2_1, ZERO_BYTES); GeomeanOracle.ObservationState memory observationState = geomeanOracle.getState(key); assertEq(observationState.index, 0); assertEq(observationState.cardinality, 1); @@ -95,7 +95,7 @@ contract TestGeomeanOracle is Test, Deployers { } function testAfterInitializeObservation() public { - manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_2_1, ZERO_BYTES); Oracle.Observation memory observation = geomeanOracle.getObservation(key, 0); assertTrue(observation.initialized); assertEq(observation.blockTimestamp, 1); @@ -104,7 +104,7 @@ contract TestGeomeanOracle is Test, Deployers { } function testAfterInitializeObserve0() public { - manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_2_1, ZERO_BYTES); uint32[] memory secondsAgo = new uint32[](1); secondsAgo[0] = 0; (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) = @@ -116,11 +116,11 @@ contract TestGeomeanOracle is Test, Deployers { } function testBeforeModifyPositionNoObservations() public { - manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_2_1, ZERO_BYTES); modifyLiquidityRouter.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams( - TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), 1000 + TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), 1000, 0 ), ZERO_BYTES ); @@ -138,12 +138,12 @@ contract TestGeomeanOracle is Test, Deployers { } function testBeforeModifyPositionObservation() public { - manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_2_1, ZERO_BYTES); geomeanOracle.setTime(3); // advance 2 seconds modifyLiquidityRouter.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams( - TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), 1000 + TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), 1000, 0 ), ZERO_BYTES ); @@ -161,7 +161,7 @@ contract TestGeomeanOracle is Test, Deployers { } function testBeforeModifyPositionObservationAndCardinality() public { - manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_2_1, ZERO_BYTES); geomeanOracle.setTime(3); // advance 2 seconds geomeanOracle.increaseCardinalityNext(key, 2); GeomeanOracle.ObservationState memory observationState = geomeanOracle.getState(key); @@ -172,7 +172,7 @@ contract TestGeomeanOracle is Test, Deployers { modifyLiquidityRouter.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams( - TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), 1000 + TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), 1000, 0 ), ZERO_BYTES ); @@ -199,12 +199,12 @@ contract TestGeomeanOracle is Test, Deployers { } function testPermanentLiquidity() public { - manager.initialize(key, SQRT_RATIO_2_1, ZERO_BYTES); + manager.initialize(key, SQRT_PRICE_2_1, ZERO_BYTES); geomeanOracle.setTime(3); // advance 2 seconds modifyLiquidityRouter.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams( - TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), 1000 + TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), 1000, 0 ), ZERO_BYTES ); @@ -213,7 +213,7 @@ contract TestGeomeanOracle is Test, Deployers { modifyLiquidityRouter.modifyLiquidity( key, IPoolManager.ModifyLiquidityParams( - TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), -1000 + TickMath.minUsableTick(MAX_TICK_SPACING), TickMath.maxUsableTick(MAX_TICK_SPACING), -1000, 0 ), ZERO_BYTES ); diff --git a/test/LimitOrder.t.sol b/test/LimitOrder.t.sol index 9b9e3116..17f5aecb 100644 --- a/test/LimitOrder.t.sol +++ b/test/LimitOrder.t.sol @@ -15,9 +15,11 @@ import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {HookEnabledSwapRouter} from "./utils/HookEnabledSwapRouter.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; contract TestLimitOrder is Test, Deployers { using PoolIdLibrary for PoolKey; + using StateLibrary for IPoolManager; uint160 constant SQRT_RATIO_10_1 = 250541448375047931186413801569; @@ -48,7 +50,7 @@ contract TestLimitOrder is Test, Deployers { } // key = PoolKey(currency0, currency1, 3000, 60, limitOrder); - (key, id) = initPoolAndAddLiquidity(currency0, currency1, limitOrder, 3000, SQRT_RATIO_1_1, ZERO_BYTES); + (key, id) = initPoolAndAddLiquidity(currency0, currency1, limitOrder, 3000, SQRT_PRICE_1_1, ZERO_BYTES); token0.approve(address(limitOrder), type(uint256).max); token1.approve(address(limitOrder), type(uint256).max); @@ -82,7 +84,8 @@ contract TestLimitOrder is Test, Deployers { uint128 liquidity = 1000000; limitOrder.place(key, tickLower, zeroForOne, liquidity); assertTrue(EpochLibrary.equals(limitOrder.getEpoch(key, tickLower, zeroForOne), Epoch.wrap(1))); - assertEq(manager.getLiquidity(id, address(limitOrder), tickLower, tickLower + 60), liquidity); + + assertEq(manager.getPosition(id, address(limitOrder), tickLower, tickLower + 60, 0).liquidity, liquidity); } function testZeroForOneLeftBoundaryOfCurrentRange() public { @@ -91,7 +94,7 @@ contract TestLimitOrder is Test, Deployers { uint128 liquidity = 1000000; limitOrder.place(key, tickLower, zeroForOne, liquidity); assertTrue(EpochLibrary.equals(limitOrder.getEpoch(key, tickLower, zeroForOne), Epoch.wrap(1))); - assertEq(manager.getLiquidity(id, address(limitOrder), tickLower, tickLower + 60), liquidity); + assertEq(manager.getPosition(id, address(limitOrder), tickLower, tickLower + 60, 0).liquidity, liquidity); } function testZeroForOneCrossedRangeRevert() public { @@ -103,8 +106,8 @@ contract TestLimitOrder is Test, Deployers { // swapping is free, there's no liquidity in the pool, so we only need to specify 1 wei router.swap( key, - IPoolManager.SwapParams(false, -1 ether, SQRT_RATIO_1_1 + 1), - HookEnabledSwapRouter.TestSettings(true, true), + IPoolManager.SwapParams(false, -1 ether, SQRT_PRICE_1_1 + 1), + HookEnabledSwapRouter.TestSettings(false, false), ZERO_BYTES ); vm.expectRevert(LimitOrder.InRange.selector); @@ -117,7 +120,7 @@ contract TestLimitOrder is Test, Deployers { uint128 liquidity = 1000000; limitOrder.place(key, tickLower, zeroForOne, liquidity); assertTrue(EpochLibrary.equals(limitOrder.getEpoch(key, tickLower, zeroForOne), Epoch.wrap(1))); - assertEq(manager.getLiquidity(id, address(limitOrder), tickLower, tickLower + 60), liquidity); + assertEq(manager.getPosition(id, address(limitOrder), tickLower, tickLower + 60, 0).liquidity, liquidity); } function testNotZeroForOneCrossedRangeRevert() public { @@ -129,8 +132,8 @@ contract TestLimitOrder is Test, Deployers { // swapping is free, there's no liquidity in the pool, so we only need to specify 1 wei router.swap( key, - IPoolManager.SwapParams(true, -1 ether, SQRT_RATIO_1_1 - 1), - HookEnabledSwapRouter.TestSettings(true, true), + IPoolManager.SwapParams(true, -1 ether, SQRT_PRICE_1_1 - 1), + HookEnabledSwapRouter.TestSettings(false, false), ZERO_BYTES ); vm.expectRevert(LimitOrder.InRange.selector); @@ -151,7 +154,7 @@ contract TestLimitOrder is Test, Deployers { limitOrder.place(key, tickLower, zeroForOne, liquidity); vm.stopPrank(); assertTrue(EpochLibrary.equals(limitOrder.getEpoch(key, tickLower, zeroForOne), Epoch.wrap(1))); - assertEq(manager.getLiquidity(id, address(limitOrder), tickLower, tickLower + 60), liquidity * 2); + assertEq(manager.getPosition(id, address(limitOrder), tickLower, tickLower + 60, 0).liquidity, liquidity * 2); ( bool filled, @@ -191,8 +194,8 @@ contract TestLimitOrder is Test, Deployers { router.swap( key, - IPoolManager.SwapParams(false, -1e18, TickMath.getSqrtRatioAtTick(60)), - HookEnabledSwapRouter.TestSettings(true, true), + IPoolManager.SwapParams(false, -1e18, TickMath.getSqrtPriceAtTick(60)), + HookEnabledSwapRouter.TestSettings(false, false), ZERO_BYTES ); @@ -205,7 +208,7 @@ contract TestLimitOrder is Test, Deployers { assertTrue(filled); assertEq(token0Total, 0); assertEq(token1Total, 2996 + 17); // 3013, 2 wei of dust - assertEq(manager.getLiquidity(id, address(limitOrder), tickLower, tickLower + 60), 0); + assertEq(manager.getPosition(id, address(limitOrder), tickLower, tickLower + 60, 0).liquidity, 0); vm.expectEmit(true, true, true, true, address(token1)); emit Transfer(address(manager), new GetSender().sender(), 2996 + 17); diff --git a/test/Quoter.t.sol b/test/Quoter.t.sol index f3d2ceb1..0767cadd 100644 --- a/test/Quoter.t.sol +++ b/test/Quoter.t.sol @@ -19,10 +19,12 @@ import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol"; import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol"; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; +import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; contract QuoterTest is Test, Deployers { using SafeCast for *; using PoolIdLibrary for PoolKey; + using StateLibrary for IPoolManager; // Min tick for full range with tick spacing of 60 int24 internal constant MIN_TICK = -887220; @@ -119,11 +121,11 @@ contract QuoterTest is Test, Deployers { assertEq(initializedTicksLoaded, 2); } - // nested self-call into lockAcquired reverts - function testQuoter_callLockAcquired_reverts() public { + // nested self-call into unlockCallback reverts + function testQuoter_callUnlockCallback_reverts() public { vm.expectRevert(IQuoter.LockFailure.selector); vm.prank(address(manager)); - quoter.lockAcquired(abi.encodeWithSelector(quoter.lockAcquired.selector, address(this), "0x")); + quoter.unlockCallback(abi.encodeWithSelector(quoter.unlockCallback.selector, address(this), "0x")); } function testQuoter_quoteExactInput_0to2_2TicksLoaded() public { @@ -542,7 +544,7 @@ contract QuoterTest is Test, Deployers { } function setupPool(PoolKey memory poolKey) internal { - manager.initialize(poolKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(poolKey, SQRT_PRICE_1_1, ZERO_BYTES); MockERC20(Currency.unwrap(poolKey.currency0)).approve(address(positionManager), type(uint256).max); MockERC20(Currency.unwrap(poolKey.currency1)).approve(address(positionManager), type(uint256).max); positionManager.modifyLiquidity( @@ -550,14 +552,15 @@ contract QuoterTest is Test, Deployers { IPoolManager.ModifyLiquidityParams( MIN_TICK, MAX_TICK, - calculateLiquidityFromAmounts(SQRT_RATIO_1_1, MIN_TICK, MAX_TICK, 1000000, 1000000).toInt256() + calculateLiquidityFromAmounts(SQRT_PRICE_1_1, MIN_TICK, MAX_TICK, 1000000, 1000000).toInt256(), + 0 ), ZERO_BYTES ); } function setupPoolMultiplePositions(PoolKey memory poolKey) internal { - manager.initialize(poolKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(poolKey, SQRT_PRICE_1_1, ZERO_BYTES); MockERC20(Currency.unwrap(poolKey.currency0)).approve(address(positionManager), type(uint256).max); MockERC20(Currency.unwrap(poolKey.currency1)).approve(address(positionManager), type(uint256).max); positionManager.modifyLiquidity( @@ -565,21 +568,22 @@ contract QuoterTest is Test, Deployers { IPoolManager.ModifyLiquidityParams( MIN_TICK, MAX_TICK, - calculateLiquidityFromAmounts(SQRT_RATIO_1_1, MIN_TICK, MAX_TICK, 1000000, 1000000).toInt256() + calculateLiquidityFromAmounts(SQRT_PRICE_1_1, MIN_TICK, MAX_TICK, 1000000, 1000000).toInt256(), + 0 ), ZERO_BYTES ); positionManager.modifyLiquidity( poolKey, IPoolManager.ModifyLiquidityParams( - -60, 60, calculateLiquidityFromAmounts(SQRT_RATIO_1_1, -60, 60, 100, 100).toInt256() + -60, 60, calculateLiquidityFromAmounts(SQRT_PRICE_1_1, -60, 60, 100, 100).toInt256(), 0 ), ZERO_BYTES ); positionManager.modifyLiquidity( poolKey, IPoolManager.ModifyLiquidityParams( - -120, 120, calculateLiquidityFromAmounts(SQRT_RATIO_1_1, -120, 120, 100, 100).toInt256() + -120, 120, calculateLiquidityFromAmounts(SQRT_PRICE_1_1, -120, 120, 100, 100).toInt256(), 0 ), ZERO_BYTES ); @@ -589,7 +593,7 @@ contract QuoterTest is Test, Deployers { PoolId poolId = poolKey.toId(); (uint160 sqrtPriceX96,,,) = manager.getSlot0(poolId); if (sqrtPriceX96 == 0) { - manager.initialize(poolKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(poolKey, SQRT_PRICE_1_1, ZERO_BYTES); } MockERC20(Currency.unwrap(poolKey.currency0)).approve(address(positionManager), type(uint256).max); @@ -599,21 +603,22 @@ contract QuoterTest is Test, Deployers { IPoolManager.ModifyLiquidityParams( MIN_TICK, MAX_TICK, - calculateLiquidityFromAmounts(SQRT_RATIO_1_1, MIN_TICK, MAX_TICK, 1000000, 1000000).toInt256() + calculateLiquidityFromAmounts(SQRT_PRICE_1_1, MIN_TICK, MAX_TICK, 1000000, 1000000).toInt256(), + 0 ), ZERO_BYTES ); positionManager.modifyLiquidity( poolKey, IPoolManager.ModifyLiquidityParams( - 0, 60, calculateLiquidityFromAmounts(SQRT_RATIO_1_1, 0, 60, 100, 100).toInt256() + 0, 60, calculateLiquidityFromAmounts(SQRT_PRICE_1_1, 0, 60, 100, 100).toInt256(), 0 ), ZERO_BYTES ); positionManager.modifyLiquidity( poolKey, IPoolManager.ModifyLiquidityParams( - -120, 0, calculateLiquidityFromAmounts(SQRT_RATIO_1_1, -120, 0, 100, 100).toInt256() + -120, 0, calculateLiquidityFromAmounts(SQRT_PRICE_1_1, -120, 0, 100, 100).toInt256(), 0 ), ZERO_BYTES ); @@ -626,8 +631,8 @@ contract QuoterTest is Test, Deployers { uint256 amount0, uint256 amount1 ) internal pure returns (uint128 liquidity) { - uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(tickLower); - uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(tickUpper); + uint160 sqrtRatioAX96 = TickMath.getSqrtPriceAtTick(tickLower); + uint160 sqrtRatioBX96 = TickMath.getSqrtPriceAtTick(tickUpper); liquidity = LiquidityAmounts.getLiquidityForAmounts(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, amount0, amount1); } diff --git a/test/TWAMM.t.sol b/test/TWAMM.t.sol index 96941963..0f2f82e0 100644 --- a/test/TWAMM.t.sol +++ b/test/TWAMM.t.sol @@ -69,21 +69,21 @@ contract TWAMMTest is Test, Deployers, GasSnapshot { } } - (poolKey, poolId) = initPool(currency0, currency1, twamm, 3000, SQRT_RATIO_1_1, ZERO_BYTES); + (poolKey, poolId) = initPool(currency0, currency1, twamm, 3000, SQRT_PRICE_1_1, ZERO_BYTES); token0.approve(address(modifyLiquidityRouter), 100 ether); token1.approve(address(modifyLiquidityRouter), 100 ether); token0.mint(address(this), 100 ether); token1.mint(address(this), 100 ether); modifyLiquidityRouter.modifyLiquidity( - poolKey, IPoolManager.ModifyLiquidityParams(-60, 60, 10 ether), ZERO_BYTES + poolKey, IPoolManager.ModifyLiquidityParams(-60, 60, 10 ether, 0), ZERO_BYTES ); modifyLiquidityRouter.modifyLiquidity( - poolKey, IPoolManager.ModifyLiquidityParams(-120, 120, 10 ether), ZERO_BYTES + poolKey, IPoolManager.ModifyLiquidityParams(-120, 120, 10 ether, 0), ZERO_BYTES ); modifyLiquidityRouter.modifyLiquidity( poolKey, - IPoolManager.ModifyLiquidityParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), 10 ether), + IPoolManager.ModifyLiquidityParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), 10 ether, 0), ZERO_BYTES ); } @@ -93,7 +93,7 @@ contract TWAMMTest is Test, Deployers, GasSnapshot { assertEq(twamm.lastVirtualOrderTimestamp(initId), 0); vm.warp(10000); - manager.initialize(initKey, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(initKey, SQRT_PRICE_1_1, ZERO_BYTES); assertEq(twamm.lastVirtualOrderTimestamp(initId), 10000); } @@ -363,7 +363,7 @@ contract TWAMMTest is Test, Deployers, GasSnapshot { token0.approve(address(twamm), 100e18); token1.approve(address(twamm), 100e18); modifyLiquidityRouter.modifyLiquidity( - poolKey, IPoolManager.ModifyLiquidityParams(-2400, 2400, 10 ether), ZERO_BYTES + poolKey, IPoolManager.ModifyLiquidityParams(-2400, 2400, 10 ether, 0), ZERO_BYTES ); vm.warp(10000); diff --git a/test/utils/HookEnabledSwapRouter.sol b/test/utils/HookEnabledSwapRouter.sol index 4311439c..15af7aad 100644 --- a/test/utils/HookEnabledSwapRouter.sol +++ b/test/utils/HookEnabledSwapRouter.sol @@ -8,9 +8,11 @@ import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolTestBase} from "@uniswap/v4-core/src/test/PoolTestBase.sol"; import {Test} from "forge-std/Test.sol"; +import {CurrencySettleTake} from "@uniswap/v4-core/src/libraries/CurrencySettleTake.sol"; contract HookEnabledSwapRouter is PoolTestBase { using CurrencyLibrary for Currency; + using CurrencySettleTake for Currency; error NoSwapOccurred(); @@ -25,8 +27,8 @@ contract HookEnabledSwapRouter is PoolTestBase { } struct TestSettings { - bool withdrawTokens; - bool settleUsingTransfer; + bool takeClaims; + bool settleUsingBurn; } function swap( @@ -36,14 +38,14 @@ contract HookEnabledSwapRouter is PoolTestBase { bytes memory hookData ) external payable returns (BalanceDelta delta) { delta = abi.decode( - manager.lock(abi.encode(CallbackData(msg.sender, testSettings, key, params, hookData))), (BalanceDelta) + manager.unlock(abi.encode(CallbackData(msg.sender, testSettings, key, params, hookData))), (BalanceDelta) ); uint256 ethBalance = address(this).balance; if (ethBalance > 0) CurrencyLibrary.NATIVE.transfer(msg.sender, ethBalance); } - function lockAcquired(bytes calldata rawData) external returns (bytes memory) { + function unlockCallback(bytes calldata rawData) external returns (bytes memory) { require(msg.sender == address(manager)); CallbackData memory data = abi.decode(rawData, (CallbackData)); @@ -54,14 +56,22 @@ contract HookEnabledSwapRouter is PoolTestBase { if (BalanceDelta.unwrap(delta) == 0) revert NoSwapOccurred(); if (data.params.zeroForOne) { - _settle(data.key.currency0, data.sender, delta.amount0(), data.testSettings.settleUsingTransfer); + data.key.currency0.settle( + manager, data.sender, uint256(int256(-delta.amount0())), data.testSettings.settleUsingBurn + ); if (delta.amount1() > 0) { - _take(data.key.currency1, data.sender, delta.amount1(), data.testSettings.withdrawTokens); + data.key.currency1.take( + manager, data.sender, uint256(int256(delta.amount1())), data.testSettings.takeClaims + ); } } else { - _settle(data.key.currency1, data.sender, delta.amount1(), data.testSettings.settleUsingTransfer); + data.key.currency1.settle( + manager, data.sender, uint256(int256(-delta.amount1())), data.testSettings.settleUsingBurn + ); if (delta.amount0() > 0) { - _take(data.key.currency0, data.sender, delta.amount0(), data.testSettings.withdrawTokens); + data.key.currency0.take( + manager, data.sender, uint256(int256(delta.amount0())), data.testSettings.takeClaims + ); } } From 508277999222ee20207cd63efe50397760c2abe1 Mon Sep 17 00:00:00 2001 From: 0x57 Date: Fri, 31 May 2024 20:35:51 +0800 Subject: [PATCH 5/5] chore: update v4 core (#115) * Update v4-core * CurrencySettleTake -> CurrencySettler * Snapshots --- .forge-snapshots/FullRangeAddInitialLiquidity.snap | 2 +- .forge-snapshots/FullRangeAddLiquidity.snap | 2 +- .forge-snapshots/FullRangeFirstSwap.snap | 2 +- .forge-snapshots/FullRangeInitialize.snap | 2 +- .forge-snapshots/FullRangeRemoveLiquidity.snap | 2 +- .forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap | 2 +- .forge-snapshots/FullRangeSecondSwap.snap | 2 +- .forge-snapshots/FullRangeSwap.snap | 2 +- .forge-snapshots/TWAMMSubmitOrder.snap | 2 +- contracts/hooks/examples/FullRange.sol | 4 ++-- contracts/hooks/examples/LimitOrder.sol | 4 ++-- contracts/hooks/examples/TWAMM.sol | 4 ++-- lib/v4-core | 2 +- test/utils/HookEnabledSwapRouter.sol | 4 ++-- 14 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.forge-snapshots/FullRangeAddInitialLiquidity.snap b/.forge-snapshots/FullRangeAddInitialLiquidity.snap index 78823d57..cd1e3c37 100644 --- a/.forge-snapshots/FullRangeAddInitialLiquidity.snap +++ b/.forge-snapshots/FullRangeAddInitialLiquidity.snap @@ -1 +1 @@ -311243 \ No newline at end of file +311073 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeAddLiquidity.snap b/.forge-snapshots/FullRangeAddLiquidity.snap index cd5941c3..b3de2b4e 100644 --- a/.forge-snapshots/FullRangeAddLiquidity.snap +++ b/.forge-snapshots/FullRangeAddLiquidity.snap @@ -1 +1 @@ -123052 \ No newline at end of file +122882 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeFirstSwap.snap b/.forge-snapshots/FullRangeFirstSwap.snap index 7180e028..54d0d097 100644 --- a/.forge-snapshots/FullRangeFirstSwap.snap +++ b/.forge-snapshots/FullRangeFirstSwap.snap @@ -1 +1 @@ -82356 \ No newline at end of file +80283 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeInitialize.snap b/.forge-snapshots/FullRangeInitialize.snap index 69d68c83..f81651f8 100644 --- a/.forge-snapshots/FullRangeInitialize.snap +++ b/.forge-snapshots/FullRangeInitialize.snap @@ -1 +1 @@ -1016091 \ No newline at end of file +1015169 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeRemoveLiquidity.snap b/.forge-snapshots/FullRangeRemoveLiquidity.snap index c024925a..265c1dec 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidity.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidity.snap @@ -1 +1 @@ -110705 \ No newline at end of file +110476 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap index a9b58592..dcb62527 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap @@ -1 +1 @@ -240800 \ No newline at end of file +239954 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSecondSwap.snap b/.forge-snapshots/FullRangeSecondSwap.snap index 551d00ec..1bf183ef 100644 --- a/.forge-snapshots/FullRangeSecondSwap.snap +++ b/.forge-snapshots/FullRangeSecondSwap.snap @@ -1 +1 @@ -47770 \ No newline at end of file +45993 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSwap.snap b/.forge-snapshots/FullRangeSwap.snap index cc58180a..5630ac05 100644 --- a/.forge-snapshots/FullRangeSwap.snap +++ b/.forge-snapshots/FullRangeSwap.snap @@ -1 +1 @@ -81129 \ No newline at end of file +79414 \ No newline at end of file diff --git a/.forge-snapshots/TWAMMSubmitOrder.snap b/.forge-snapshots/TWAMMSubmitOrder.snap index 16e68302..b2759d7f 100644 --- a/.forge-snapshots/TWAMMSubmitOrder.snap +++ b/.forge-snapshots/TWAMMSubmitOrder.snap @@ -1 +1 @@ -122417 \ No newline at end of file +122355 \ No newline at end of file diff --git a/contracts/hooks/examples/FullRange.sol b/contracts/hooks/examples/FullRange.sol index aa4b606d..194be803 100644 --- a/contracts/hooks/examples/FullRange.sol +++ b/contracts/hooks/examples/FullRange.sol @@ -8,7 +8,7 @@ import {BaseHook} from "../../BaseHook.sol"; import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol"; import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol"; import {CurrencyLibrary, Currency} from "@uniswap/v4-core/src/types/Currency.sol"; -import {CurrencySettleTake} from "@uniswap/v4-core/src/libraries/CurrencySettleTake.sol"; +import {CurrencySettler} from "@uniswap/v4-core/test/utils/CurrencySettler.sol"; import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {IERC20Minimal} from "@uniswap/v4-core/src/interfaces/external/IERC20Minimal.sol"; @@ -28,7 +28,7 @@ import "../../libraries/LiquidityAmounts.sol"; contract FullRange is BaseHook, IUnlockCallback { using CurrencyLibrary for Currency; - using CurrencySettleTake for Currency; + using CurrencySettler for Currency; using PoolIdLibrary for PoolKey; using SafeCast for uint256; using SafeCast for uint128; diff --git a/contracts/hooks/examples/LimitOrder.sol b/contracts/hooks/examples/LimitOrder.sol index 92d6489b..9ee7a33c 100644 --- a/contracts/hooks/examples/LimitOrder.sol +++ b/contracts/hooks/examples/LimitOrder.sol @@ -10,7 +10,7 @@ import {IERC20Minimal} from "@uniswap/v4-core/src/interfaces/external/IERC20Mini import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {BaseHook} from "../../BaseHook.sol"; import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol"; -import {CurrencySettleTake} from "@uniswap/v4-core/src/libraries/CurrencySettleTake.sol"; +import {CurrencySettler} from "@uniswap/v4-core/test/utils/CurrencySettler.sol"; import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; @@ -33,7 +33,7 @@ contract LimitOrder is BaseHook { using EpochLibrary for Epoch; using PoolIdLibrary for PoolKey; using CurrencyLibrary for Currency; - using CurrencySettleTake for Currency; + using CurrencySettler for Currency; using StateLibrary for IPoolManager; error ZeroLiquidity(); diff --git a/contracts/hooks/examples/TWAMM.sol b/contracts/hooks/examples/TWAMM.sol index 0661dd54..5704d765 100644 --- a/contracts/hooks/examples/TWAMM.sol +++ b/contracts/hooks/examples/TWAMM.sol @@ -19,14 +19,14 @@ import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {PoolGetters} from "../../libraries/PoolGetters.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; -import {CurrencySettleTake} from "@uniswap/v4-core/src/libraries/CurrencySettleTake.sol"; +import {CurrencySettler} from "@uniswap/v4-core/test/utils/CurrencySettler.sol"; import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol"; import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol"; contract TWAMM is BaseHook, ITWAMM { using TransferHelper for IERC20Minimal; using CurrencyLibrary for Currency; - using CurrencySettleTake for Currency; + using CurrencySettler for Currency; using OrderPool for OrderPool.State; using PoolIdLibrary for PoolKey; using TickMath for int24; diff --git a/lib/v4-core b/lib/v4-core index 3351c80e..6e6ce35b 160000 --- a/lib/v4-core +++ b/lib/v4-core @@ -1 +1 @@ -Subproject commit 3351c80e58e6300cb263d33a4efe75b88ad7b9b2 +Subproject commit 6e6ce35b69b15cb61bd8cb8488c7d064fab52886 diff --git a/test/utils/HookEnabledSwapRouter.sol b/test/utils/HookEnabledSwapRouter.sol index 15af7aad..4021f453 100644 --- a/test/utils/HookEnabledSwapRouter.sol +++ b/test/utils/HookEnabledSwapRouter.sol @@ -8,11 +8,11 @@ import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol"; import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol"; import {PoolTestBase} from "@uniswap/v4-core/src/test/PoolTestBase.sol"; import {Test} from "forge-std/Test.sol"; -import {CurrencySettleTake} from "@uniswap/v4-core/src/libraries/CurrencySettleTake.sol"; +import {CurrencySettler} from "@uniswap/v4-core/test/utils/CurrencySettler.sol"; contract HookEnabledSwapRouter is PoolTestBase { using CurrencyLibrary for Currency; - using CurrencySettleTake for Currency; + using CurrencySettler for Currency; error NoSwapOccurred();