From ba45e0f7792ed826ad73f25a07b1dceeab4b7636 Mon Sep 17 00:00:00 2001 From: Qi Wu Date: Mon, 11 Mar 2024 16:36:12 +0800 Subject: [PATCH 01/19] Update v4-core --- lib/v4-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/v4-core b/lib/v4-core index 4a13732d..0e8e6c91 160000 --- a/lib/v4-core +++ b/lib/v4-core @@ -1 +1 @@ -Subproject commit 4a13732dc0b9a8c516d3639a78c54af3fc3db8d4 +Subproject commit 0e8e6c91bfc3695ac4f7481fd812a7262cf04897 From bff607591ef5699b951d76c2e216c2fbefc15862 Mon Sep 17 00:00:00 2001 From: Qi Wu Date: Mon, 11 Mar 2024 16:53:08 +0800 Subject: [PATCH 02/19] Update various examples, BaseHook, Quoter and tests --- .../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 | 7 +--- contracts/hooks/examples/FullRange.sol | 12 ++----- contracts/hooks/examples/GeomeanOracle.sol | 4 +-- contracts/hooks/examples/LimitOrder.sol | 8 +---- contracts/hooks/examples/TWAMM.sol | 15 ++------ contracts/hooks/examples/VolatilityOracle.sol | 4 +-- contracts/lens/Quoter.sol | 13 +++---- test/FullRange.t.sol | 34 +++++++++---------- test/GeomeanOracle.t.sol | 20 +++++------ test/LimitOrder.t.sol | 2 +- test/Quoter.t.sol | 8 ++--- test/TWAMM.t.sol | 2 +- test/utils/HookEnabledSwapRouter.sol | 5 ++- 22 files changed, 59 insertions(+), 93 deletions(-) diff --git a/.forge-snapshots/FullRangeAddInitialLiquidity.snap b/.forge-snapshots/FullRangeAddInitialLiquidity.snap index fda86345..ade39882 100644 --- a/.forge-snapshots/FullRangeAddInitialLiquidity.snap +++ b/.forge-snapshots/FullRangeAddInitialLiquidity.snap @@ -1 +1 @@ -392772 \ No newline at end of file +385077 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeAddLiquidity.snap b/.forge-snapshots/FullRangeAddLiquidity.snap index ff9a3f08..adaace55 100644 --- a/.forge-snapshots/FullRangeAddLiquidity.snap +++ b/.forge-snapshots/FullRangeAddLiquidity.snap @@ -1 +1 @@ -187139 \ No newline at end of file +179444 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeFirstSwap.snap b/.forge-snapshots/FullRangeFirstSwap.snap index 029a908d..fd84f5a0 100644 --- a/.forge-snapshots/FullRangeFirstSwap.snap +++ b/.forge-snapshots/FullRangeFirstSwap.snap @@ -1 +1 @@ -136542 \ No newline at end of file +128741 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeInitialize.snap b/.forge-snapshots/FullRangeInitialize.snap index 44c69e54..8b9a7693 100644 --- a/.forge-snapshots/FullRangeInitialize.snap +++ b/.forge-snapshots/FullRangeInitialize.snap @@ -1 +1 @@ -1041060 \ No newline at end of file +1017451 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeRemoveLiquidity.snap b/.forge-snapshots/FullRangeRemoveLiquidity.snap index 6ff7a267..473e27f2 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidity.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidity.snap @@ -1 +1 @@ -175903 \ No newline at end of file +169775 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap index 10fb1518..b1c4f9f4 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap @@ -1 +1 @@ -363995 \ No newline at end of file +347304 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSecondSwap.snap b/.forge-snapshots/FullRangeSecondSwap.snap index c02e1eae..637cad9d 100644 --- a/.forge-snapshots/FullRangeSecondSwap.snap +++ b/.forge-snapshots/FullRangeSecondSwap.snap @@ -1 +1 @@ -97295 \ No newline at end of file +89494 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSwap.snap b/.forge-snapshots/FullRangeSwap.snap index 8adf5f54..583c0bff 100644 --- a/.forge-snapshots/FullRangeSwap.snap +++ b/.forge-snapshots/FullRangeSwap.snap @@ -1 +1 @@ -134817 \ No newline at end of file +127016 \ No newline at end of file diff --git a/.forge-snapshots/TWAMMSubmitOrder.snap b/.forge-snapshots/TWAMMSubmitOrder.snap index 1ac55f85..8b4d94e0 100644 --- a/.forge-snapshots/TWAMMSubmitOrder.snap +++ b/.forge-snapshots/TWAMMSubmitOrder.snap @@ -1 +1 @@ -122753 \ No newline at end of file +122687 \ No newline at end of file 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..6924d5e7 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 }); } @@ -251,9 +249,7 @@ contract FullRange is BaseHook, ILockCallback { 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 { @@ -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; diff --git a/contracts/hooks/examples/GeomeanOracle.sol b/contracts/hooks/examples/GeomeanOracle.sol index 9d53fb0a..f3e789ef 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 }); } diff --git a/contracts/hooks/examples/LimitOrder.sol b/contracts/hooks/examples/LimitOrder.sol index c8d9316f..ffb35859 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 }); } @@ -160,7 +158,6 @@ contract LimitOrder is BaseHook { (uint256 amount0, uint256 amount1) = abi.decode( poolManager.lock( - address(this), abi.encodeCall(this.lockAcquiredFill, (key, lower, -int256(uint256(epochInfo.liquidityTotal)))) ), (uint256, uint256) @@ -224,7 +221,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)) ); @@ -306,7 +302,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) @@ -388,7 +383,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..655e2ea2 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 }); } @@ -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,12 +298,7 @@ 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)); diff --git a/contracts/hooks/examples/VolatilityOracle.sol b/contracts/hooks/examples/VolatilityOracle.sol index df8bdde5..e9b98f91 100644 --- a/contracts/hooks/examples/VolatilityOracle.sol +++ b/contracts/hooks/examples/VolatilityOracle.sol @@ -41,9 +41,7 @@ contract VolatilityOracle is BaseHook, IDynamicFeeManager { beforeSwap: false, afterSwap: false, beforeDonate: false, - afterDonate: false, - noOp: false, - accessLock: false + afterDonate: false }); } diff --git a/contracts/lens/Quoter.sol b/contracts/lens/Quoter.sol index 1f9350a8..91d21e37 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; diff --git a/test/FullRange.t.sol b/test/FullRange.t.sol index 076abab3..335a9d52 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,7 +169,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { } function testFullRange_addLiquidity_InitialAddFuzz(uint256 amount) public { - initializeRouter.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); if (amount < LOCKED_LIQUIDITY) { vm.expectRevert(FullRange.LiquidityDoesntMeetMinimum.selector); fullRange.addLiquidity( @@ -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)); @@ -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..519ed266 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); } diff --git a/test/Quoter.t.sol b/test/Quoter.t.sol index 87de52d5..b7d35a3d 100644 --- a/test/Quoter.t.sol +++ b/test/Quoter.t.sol @@ -123,7 +123,7 @@ contract QuoterTest is Test, Deployers { function testQuoter_callLockAcquired_reverts() public { vm.expectRevert(IQuoter.InvalidLockAcquiredSender.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( @@ -589,7 +589,7 @@ contract QuoterTest is Test, Deployers { PoolId poolId = poolKey.toId(); (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..b924ed61 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)); From 594999f064cf2841ab7b27bd5dcfa225755f562c Mon Sep 17 00:00:00 2001 From: Qi Wu Date: Mon, 11 Mar 2024 17:56:58 +0800 Subject: [PATCH 03/19] Remove nested locking for LimitOrder --- contracts/hooks/examples/LimitOrder.sol | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/contracts/hooks/examples/LimitOrder.sol b/contracts/hooks/examples/LimitOrder.sol index ffb35859..d2d8e372 100644 --- a/contracts/hooks/examples/LimitOrder.sol +++ b/contracts/hooks/examples/LimitOrder.sol @@ -129,7 +129,7 @@ contract LimitOrder is BaseHook { } function afterSwap( - address, + address sender, PoolKey calldata key, IPoolManager.SwapParams calldata params, BalanceDelta, @@ -142,26 +142,24 @@ contract LimitOrder is BaseHook { // order fills are the opposite of swap fills, hence the inversion below bool zeroForOne = !params.zeroForOne; for (; lower <= upper; lower += key.tickSpacing) { - _fillEpoch(key, lower, zeroForOne); + _fillEpoch(sender, key, lower, zeroForOne); } setTickLowerLast(key.toId(), tickLower); return LimitOrder.afterSwap.selector; } - function _fillEpoch(PoolKey calldata key, int24 lower, bool zeroForOne) internal { + function _fillEpoch(address sender, PoolKey calldata key, int24 lower, bool zeroForOne) internal { Epoch epoch = getEpoch(key, lower, zeroForOne); if (!epoch.equals(EPOCH_DEFAULT)) { EpochInfo storage epochInfo = epochInfos[epoch]; epochInfo.filled = true; - (uint256 amount0, uint256 amount1) = abi.decode( - poolManager.lock( - abi.encodeCall(this.lockAcquiredFill, (key, lower, -int256(uint256(epochInfo.liquidityTotal)))) - ), - (uint256, uint256) - ); + address locker = poolManager.getLocker(); + require(locker == sender, "invalid locker"); + (uint256 amount0, uint256 amount1) = + _lockAcquiredFill(key, lower, -int256(uint256(epochInfo.liquidityTotal))); unchecked { epochInfo.token0Total += amount0; @@ -191,9 +189,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( From 1e7ac5ea0bc8f7ede1b66ef7fe4d5ec01d2f1424 Mon Sep 17 00:00:00 2001 From: Qi Wu Date: Mon, 11 Mar 2024 18:05:58 +0800 Subject: [PATCH 04/19] Fix Quoter --- test/Quoter.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Quoter.t.sol b/test/Quoter.t.sol index b7d35a3d..69c9a026 100644 --- a/test/Quoter.t.sol +++ b/test/Quoter.t.sol @@ -121,7 +121,7 @@ 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(abi.encodeWithSelector(quoter.lockAcquired.selector, address(this), "0x")); } From 0c1b8f0cdc41542f4c4e7255d705ba3685945cd3 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Mar 2024 15:22:00 +0000 Subject: [PATCH 05/19] update v4-core --- lib/v4-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/v4-core b/lib/v4-core index 0e8e6c91..702f547b 160000 --- a/lib/v4-core +++ b/lib/v4-core @@ -1 +1 @@ -Subproject commit 0e8e6c91bfc3695ac4f7481fd812a7262cf04897 +Subproject commit 702f547bb3d2f345264f58d2634067d5c18ceee2 From 15ae6ce556945f408397bdfbcdf985db235c28c3 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Wed, 13 Mar 2024 15:24:04 +0000 Subject: [PATCH 06/19] fix: remove getLocker as its a bool now --- .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/LimitOrder.sol | 6 ++---- 10 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.forge-snapshots/FullRangeAddInitialLiquidity.snap b/.forge-snapshots/FullRangeAddInitialLiquidity.snap index ade39882..cc081c07 100644 --- a/.forge-snapshots/FullRangeAddInitialLiquidity.snap +++ b/.forge-snapshots/FullRangeAddInitialLiquidity.snap @@ -1 +1 @@ -385077 \ No newline at end of file +384810 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeAddLiquidity.snap b/.forge-snapshots/FullRangeAddLiquidity.snap index adaace55..3efefad2 100644 --- a/.forge-snapshots/FullRangeAddLiquidity.snap +++ b/.forge-snapshots/FullRangeAddLiquidity.snap @@ -1 +1 @@ -179444 \ No newline at end of file +179177 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeFirstSwap.snap b/.forge-snapshots/FullRangeFirstSwap.snap index fd84f5a0..dab2490c 100644 --- a/.forge-snapshots/FullRangeFirstSwap.snap +++ b/.forge-snapshots/FullRangeFirstSwap.snap @@ -1 +1 @@ -128741 \ No newline at end of file +128474 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeInitialize.snap b/.forge-snapshots/FullRangeInitialize.snap index 8b9a7693..eba3eeed 100644 --- a/.forge-snapshots/FullRangeInitialize.snap +++ b/.forge-snapshots/FullRangeInitialize.snap @@ -1 +1 @@ -1017451 \ No newline at end of file +1017493 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeRemoveLiquidity.snap b/.forge-snapshots/FullRangeRemoveLiquidity.snap index 473e27f2..3d33afe6 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidity.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidity.snap @@ -1 +1 @@ -169775 \ No newline at end of file +169552 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap index b1c4f9f4..3b896491 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap @@ -1 +1 @@ -347304 \ No newline at end of file +346885 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSecondSwap.snap b/.forge-snapshots/FullRangeSecondSwap.snap index 637cad9d..2ed006ed 100644 --- a/.forge-snapshots/FullRangeSecondSwap.snap +++ b/.forge-snapshots/FullRangeSecondSwap.snap @@ -1 +1 @@ -89494 \ No newline at end of file +89227 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSwap.snap b/.forge-snapshots/FullRangeSwap.snap index 583c0bff..f7af585f 100644 --- a/.forge-snapshots/FullRangeSwap.snap +++ b/.forge-snapshots/FullRangeSwap.snap @@ -1 +1 @@ -127016 \ No newline at end of file +126749 \ No newline at end of file diff --git a/.forge-snapshots/TWAMMSubmitOrder.snap b/.forge-snapshots/TWAMMSubmitOrder.snap index 8b4d94e0..688d8d7c 100644 --- a/.forge-snapshots/TWAMMSubmitOrder.snap +++ b/.forge-snapshots/TWAMMSubmitOrder.snap @@ -1 +1 @@ -122687 \ No newline at end of file +122731 \ No newline at end of file diff --git a/contracts/hooks/examples/LimitOrder.sol b/contracts/hooks/examples/LimitOrder.sol index d2d8e372..17a35579 100644 --- a/contracts/hooks/examples/LimitOrder.sol +++ b/contracts/hooks/examples/LimitOrder.sol @@ -142,22 +142,20 @@ contract LimitOrder is BaseHook { // order fills are the opposite of swap fills, hence the inversion below bool zeroForOne = !params.zeroForOne; for (; lower <= upper; lower += key.tickSpacing) { - _fillEpoch(sender, key, lower, zeroForOne); + _fillEpoch(key, lower, zeroForOne); } setTickLowerLast(key.toId(), tickLower); return LimitOrder.afterSwap.selector; } - function _fillEpoch(address sender, PoolKey calldata key, int24 lower, bool zeroForOne) internal { + function _fillEpoch(PoolKey calldata key, int24 lower, bool zeroForOne) internal { Epoch epoch = getEpoch(key, lower, zeroForOne); if (!epoch.equals(EPOCH_DEFAULT)) { EpochInfo storage epochInfo = epochInfos[epoch]; epochInfo.filled = true; - address locker = poolManager.getLocker(); - require(locker == sender, "invalid locker"); (uint256 amount0, uint256 amount1) = _lockAcquiredFill(key, lower, -int256(uint256(epochInfo.liquidityTotal))); From 9ea7ab165abe7c0227901c1d40ea40974a9928ee Mon Sep 17 00:00:00 2001 From: saucepoint Date: Thu, 14 Mar 2024 11:28:18 +0000 Subject: [PATCH 07/19] update v4-core: flipped signs, push dynamic fees --- lib/v4-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/v4-core b/lib/v4-core index 702f547b..b5b36146 160000 --- a/lib/v4-core +++ b/lib/v4-core @@ -1 +1 @@ -Subproject commit 702f547bb3d2f345264f58d2634067d5c18ceee2 +Subproject commit b5b3614626ccce2b317cfbeead99566388824f95 From 171ad9e9c1e48ed011aaa29eaaffe860a042be3d Mon Sep 17 00:00:00 2001 From: saucepoint Date: Thu, 14 Mar 2024 12:02:12 +0000 Subject: [PATCH 08/19] fix: flip delta signs --- contracts/lens/Quoter.sol | 40 +++++++++++++++++++-------------------- test/Quoter.t.sol | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/contracts/lens/Quoter.sol b/contracts/lens/Quoter.sol index 91d21e37..c039a7b7 100644 --- a/contracts/lens/Quoter.sol +++ b/contracts/lens/Quoter.sol @@ -175,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] = @@ -206,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); @@ -249,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] = @@ -280,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 ); @@ -292,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); @@ -322,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/test/Quoter.t.sol b/test/Quoter.t.sol index 69c9a026..f3d2ceb1 100644 --- a/test/Quoter.t.sol +++ b/test/Quoter.t.sol @@ -587,7 +587,7 @@ 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) { manager.initialize(poolKey, SQRT_RATIO_1_1, ZERO_BYTES); } From f6717d5e7a93e8b2becc777bcb2fd3ba0e3be1b7 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Thu, 14 Mar 2024 12:19:31 +0000 Subject: [PATCH 09/19] flip delta signs --- contracts/hooks/examples/LimitOrder.sol | 32 ++++++++++++------------- test/LimitOrder.t.sol | 8 +++---- test/utils/HookEnabledSwapRouter.sol | 4 ++-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/contracts/hooks/examples/LimitOrder.sol b/contracts/hooks/examples/LimitOrder.sol index 17a35579..fb641444 100644 --- a/contracts/hooks/examples/LimitOrder.sol +++ b/contracts/hooks/examples/LimitOrder.sol @@ -109,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) { @@ -202,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())); } } @@ -262,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 { @@ -275,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); } @@ -334,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())); } } @@ -352,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())); } } diff --git a/test/LimitOrder.t.sol b/test/LimitOrder.t.sol index 519ed266..9b9e3116 100644 --- a/test/LimitOrder.t.sol +++ b/test/LimitOrder.t.sol @@ -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/utils/HookEnabledSwapRouter.sol b/test/utils/HookEnabledSwapRouter.sol index b924ed61..4311439c 100644 --- a/test/utils/HookEnabledSwapRouter.sol +++ b/test/utils/HookEnabledSwapRouter.sol @@ -55,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 bc1685a5caae0e610a1f5b68eff30748cb93ff94 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Thu, 14 Mar 2024 12:31:34 +0000 Subject: [PATCH 10/19] flip delta signs --- contracts/hooks/examples/FullRange.sol | 28 +++++++++++++------------- test/FullRange.t.sol | 6 +++--- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/contracts/hooks/examples/FullRange.sol b/contracts/hooks/examples/FullRange.sol index 6924d5e7..99dbcdb2 100644 --- a/contracts/hooks/examples/FullRange.sol +++ b/contracts/hooks/examples/FullRange.sol @@ -117,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(); @@ -153,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(); } } @@ -174,7 +174,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(); @@ -253,8 +253,8 @@ contract FullRange is BaseHook, ILockCallback { } 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 { @@ -271,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) @@ -330,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, sqrtPriceLimitX96: newSqrtPriceX96 }), ZERO_BYTES @@ -350,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( @@ -365,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/test/FullRange.t.sol b/test/FullRange.t.sol index 335a9d52..f0867ba4 100644 --- a/test/FullRange.t.sol +++ b/test/FullRange.t.sol @@ -170,7 +170,7 @@ contract TestFullRange is Test, Deployers, GasSnapshot { function testFullRange_addLiquidity_InitialAddFuzz(uint256 amount) public { manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - if (amount < LOCKED_LIQUIDITY) { + if (amount <= LOCKED_LIQUIDITY) { vm.expectRevert(FullRange.LiquidityDoesntMeetMinimum.selector); fullRange.addLiquidity( FullRange.AddLiquidityParams( @@ -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}); From e6275adb0ca8fdd8e20e5843676b68e6d53efa0f Mon Sep 17 00:00:00 2001 From: saucepoint Date: Thu, 14 Mar 2024 12:40:51 +0000 Subject: [PATCH 11/19] flip delta signs --- contracts/hooks/examples/TWAMM.sol | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/hooks/examples/TWAMM.sol b/contracts/hooks/examples/TWAMM.sol index 655e2ea2..85364915 100644 --- a/contracts/hooks/examples/TWAMM.sol +++ b/contracts/hooks/examples/TWAMM.sol @@ -134,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( @@ -305,20 +305,20 @@ contract TWAMM is BaseHook, ITWAMM { 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(""); From bd692c422a81c94deee3570bc4c6d45502f6ddfd Mon Sep 17 00:00:00 2001 From: saucepoint Date: Thu, 14 Mar 2024 12:41:13 +0000 Subject: [PATCH 12/19] fix getSlot0 calls --- contracts/hooks/examples/GeomeanOracle.sol | 4 +-- contracts/hooks/examples/VolatilityOracle.sol | 30 ++++++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/contracts/hooks/examples/GeomeanOracle.sol b/contracts/hooks/examples/GeomeanOracle.sol index f3e789ef..c0f1c096 100644 --- a/contracts/hooks/examples/GeomeanOracle.sol +++ b/contracts/hooks/examples/GeomeanOracle.sol @@ -103,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); @@ -156,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/VolatilityOracle.sol b/contracts/hooks/examples/VolatilityOracle.sol index e9b98f91..29d24a6a 100644 --- a/contracts/hooks/examples/VolatilityOracle.sol +++ b/contracts/hooks/examples/VolatilityOracle.sol @@ -2,24 +2,23 @@ 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 - } + // 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) { @@ -33,7 +32,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, @@ -54,4 +53,15 @@ contract VolatilityOracle is BaseHook, IDynamicFeeManager { if (!key.fee.isDynamicFee()) revert MustUseDynamicFee(); return VolatilityOracle.beforeInitialize.selector; } + + function afterInitialize(address, PoolKey calldata key, uint160, int24, bytes calldata) + external + override + returns (bytes4) + { + 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% + } } From 4665485d4c3cdb875876864cae2b70ffedcce60e Mon Sep 17 00:00:00 2001 From: saucepoint Date: Thu, 14 Mar 2024 12:41:26 +0000 Subject: [PATCH 13/19] 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 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.forge-snapshots/FullRangeAddInitialLiquidity.snap b/.forge-snapshots/FullRangeAddInitialLiquidity.snap index cc081c07..cfdeb354 100644 --- a/.forge-snapshots/FullRangeAddInitialLiquidity.snap +++ b/.forge-snapshots/FullRangeAddInitialLiquidity.snap @@ -1 +1 @@ -384810 \ 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 3efefad2..e1efe638 100644 --- a/.forge-snapshots/FullRangeAddLiquidity.snap +++ b/.forge-snapshots/FullRangeAddLiquidity.snap @@ -1 +1 @@ -179177 \ 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 dab2490c..fd04e1b1 100644 --- a/.forge-snapshots/FullRangeFirstSwap.snap +++ b/.forge-snapshots/FullRangeFirstSwap.snap @@ -1 +1 @@ -128474 \ 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 eba3eeed..b126274c 100644 --- a/.forge-snapshots/FullRangeInitialize.snap +++ b/.forge-snapshots/FullRangeInitialize.snap @@ -1 +1 @@ -1017493 \ 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 3d33afe6..2cdf6c52 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidity.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidity.snap @@ -1 +1 @@ -169552 \ 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 3b896491..4f6cc348 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap @@ -1 +1 @@ -346885 \ No newline at end of file +345804 \ No newline at end of file diff --git a/.forge-snapshots/FullRangeSecondSwap.snap b/.forge-snapshots/FullRangeSecondSwap.snap index 2ed006ed..51e5eb70 100644 --- a/.forge-snapshots/FullRangeSecondSwap.snap +++ b/.forge-snapshots/FullRangeSecondSwap.snap @@ -1 +1 @@ -89227 \ 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 f7af585f..bd033704 100644 --- a/.forge-snapshots/FullRangeSwap.snap +++ b/.forge-snapshots/FullRangeSwap.snap @@ -1 +1 @@ -126749 \ 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 688d8d7c..9191f9b4 100644 --- a/.forge-snapshots/TWAMMSubmitOrder.snap +++ b/.forge-snapshots/TWAMMSubmitOrder.snap @@ -1 +1 @@ -122731 \ No newline at end of file +122845 \ No newline at end of file From 33194a60bc5e9f4728eda7e5fe168fd8182ce92e Mon Sep 17 00:00:00 2001 From: saucepoint Date: Thu, 14 Mar 2024 18:35:55 +0000 Subject: [PATCH 14/19] remove deadcode --- contracts/hooks/examples/VolatilityOracle.sol | 6 ------ 1 file changed, 6 deletions(-) diff --git a/contracts/hooks/examples/VolatilityOracle.sol b/contracts/hooks/examples/VolatilityOracle.sol index 29d24a6a..b6507d00 100644 --- a/contracts/hooks/examples/VolatilityOracle.sol +++ b/contracts/hooks/examples/VolatilityOracle.sol @@ -14,12 +14,6 @@ contract VolatilityOracle is BaseHook { 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); From 9fd8a6694413d2d277a4cd43564993402f8f38f6 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Fri, 15 Mar 2024 11:37:03 +0000 Subject: [PATCH 15/19] remove unused param --- contracts/hooks/examples/LimitOrder.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/hooks/examples/LimitOrder.sol b/contracts/hooks/examples/LimitOrder.sol index fb641444..e6cf8e89 100644 --- a/contracts/hooks/examples/LimitOrder.sol +++ b/contracts/hooks/examples/LimitOrder.sol @@ -129,7 +129,7 @@ contract LimitOrder is BaseHook { } function afterSwap( - address sender, + address, PoolKey calldata key, IPoolManager.SwapParams calldata params, BalanceDelta, From f26508ea7cc9ccb98021ca9c4632b091d4b7946d Mon Sep 17 00:00:00 2001 From: saucepoint Date: Fri, 15 Mar 2024 12:15:56 +0000 Subject: [PATCH 16/19] update core --- lib/v4-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/v4-core b/lib/v4-core index b5b36146..f5674e46 160000 --- a/lib/v4-core +++ b/lib/v4-core @@ -1 +1 @@ -Subproject commit b5b3614626ccce2b317cfbeead99566388824f95 +Subproject commit f5674e46720c0fc4606b287cccc583d56245e724 From 0f84f4bd622f18fbab20fb83b1ed8ebd6c05d403 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Fri, 15 Mar 2024 12:16:14 +0000 Subject: [PATCH 17/19] update for modifyLiquidity; misc doc updates --- CONTRIBUTING.md | 2 +- README.md | 12 ++++++------ contracts/hooks/examples/FullRange.sol | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) 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/hooks/examples/FullRange.sol b/contracts/hooks/examples/FullRange.sol index 99dbcdb2..d77ee33c 100644 --- a/contracts/hooks/examples/FullRange.sol +++ b/contracts/hooks/examples/FullRange.sol @@ -136,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, @@ -180,7 +180,7 @@ contract FullRange is BaseHook, ILockCallback { UniswapV4ERC20 erc20 = UniswapV4ERC20(poolInfo[poolId].liquidityToken); - delta = modifyPosition( + delta = modifyLiquidity( key, IPoolManager.ModifyLiquidityParams({ tickLower: MIN_TICK, @@ -245,7 +245,7 @@ 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) { From 3bfede2a10ba1cfcc8b72d8137cf32ad734d49b2 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Fri, 15 Mar 2024 15:45:25 +0000 Subject: [PATCH 18/19] correct min int256 --- .forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap | 2 +- contracts/hooks/examples/FullRange.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap index 4f6cc348..2ccb0b58 100644 --- a/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap +++ b/.forge-snapshots/FullRangeRemoveLiquidityAndRebalance.snap @@ -1 +1 @@ -345804 \ No newline at end of file +345919 \ No newline at end of file diff --git a/contracts/hooks/examples/FullRange.sol b/contracts/hooks/examples/FullRange.sol index d77ee33c..820d0f93 100644 --- a/contracts/hooks/examples/FullRange.sol +++ b/contracts/hooks/examples/FullRange.sol @@ -340,7 +340,7 @@ contract FullRange is BaseHook, ILockCallback { key, IPoolManager.SwapParams({ zeroForOne: newSqrtPriceX96 < sqrtPriceX96, - amountSpecified: -MAX_INT, + amountSpecified: -MAX_INT - 1, // equivalent of type(int256).min sqrtPriceLimitX96: newSqrtPriceX96 }), ZERO_BYTES From 57619405e19d1873b639de0c29a3b77f26dde3f8 Mon Sep 17 00:00:00 2001 From: saucepoint Date: Fri, 15 Mar 2024 15:53:06 +0000 Subject: [PATCH 19/19] allow for manual fee updates --- contracts/hooks/examples/VolatilityOracle.sol | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/contracts/hooks/examples/VolatilityOracle.sol b/contracts/hooks/examples/VolatilityOracle.sol index b6507d00..76a3e8ce 100644 --- a/contracts/hooks/examples/VolatilityOracle.sol +++ b/contracts/hooks/examples/VolatilityOracle.sol @@ -48,14 +48,19 @@ contract VolatilityOracle is BaseHook { 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) { - 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% + setFee(key); + return BaseHook.afterInitialize.selector; } }