Skip to content

Commit

Permalink
PR feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
saucepoint committed Jul 31, 2024
1 parent a415237 commit 3b0bd3d
Show file tree
Hide file tree
Showing 29 changed files with 86 additions and 47 deletions.
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_burn_nonEmpty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
129992
130023
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_burn_nonEmpty_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
122913
122944
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
150181
150221
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
141333
141373
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
150181
150221
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decreaseLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
115724
115764
Original file line number Diff line number Diff line change
@@ -1 +1 @@
108541
108573
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decrease_burnEmpty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134142
134174
Original file line number Diff line number Diff line change
@@ -1 +1 @@
126881
126913
Original file line number Diff line number Diff line change
@@ -1 +1 @@
128440
128480
Original file line number Diff line number Diff line change
@@ -1 +1 @@
152319
152359
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134119
134159
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134800
134840
Original file line number Diff line number Diff line change
@@ -1 +1 @@
170956
170996
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
372231
372271
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
336931
336971
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_nativeWithSweep.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
345575
345615
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickLower.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
314913
314953
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickUpper.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
315555
315595
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
241137
241177
Original file line number Diff line number Diff line change
@@ -1 +1 @@
370413
370453
Original file line number Diff line number Diff line change
@@ -1 +1 @@
320931
320971
Original file line number Diff line number Diff line change
@@ -1 +1 @@
416619
416659
16 changes: 6 additions & 10 deletions src/PositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {PositionConfig, PositionConfigLibrary} from "./libraries/PositionConfig.
import {BaseActionsRouter} from "./base/BaseActionsRouter.sol";
import {Actions} from "./libraries/Actions.sol";
import {CalldataDecoder} from "./libraries/CalldataDecoder.sol";
import {SlippageCheckLibrary} from "./libraries/SlippageCheck.sol";

contract PositionManager is
IPositionManager,
Expand All @@ -43,6 +44,7 @@ contract PositionManager is
using TransientStateLibrary for IPoolManager;
using SafeCast for uint256;
using CalldataDecoder for bytes;
using SlippageCheckLibrary for BalanceDelta;

/// @dev The ID of the next token that will be minted. Skips 0
uint256 public nextTokenId = 1;
Expand Down Expand Up @@ -146,8 +148,7 @@ contract PositionManager is
if (positionConfigs[tokenId] != config.toId()) revert IncorrectPositionConfigForTokenId(tokenId);
// Note: The tokenId is used as the salt for this position, so every minted position has unique storage in the pool manager.
BalanceDelta liquidityDelta = _modifyLiquidity(config, liquidity.toInt256(), bytes32(tokenId), hookData);
if (liquidityDelta.amount0() < 0 && amount0Max < uint128(-liquidityDelta.amount0())) revert SlippageExceeded();
if (liquidityDelta.amount1() < 0 && amount1Max < uint128(-liquidityDelta.amount1())) revert SlippageExceeded();
liquidityDelta.validateMaximumIncreaseSlippage(amount0Max, amount1Max);
}

/// @dev Calling decrease with 0 liquidity will credit the caller with any underlying fees of the position
Expand All @@ -164,9 +165,7 @@ contract PositionManager is

// Note: the tokenId is used as the salt.
BalanceDelta liquidityDelta = _modifyLiquidity(config, -(liquidity.toInt256()), bytes32(tokenId), hookData);
if (uint128(liquidityDelta.amount0()) < amount0Min || uint128(liquidityDelta.amount1()) < amount1Min) {
revert SlippageExceeded();
}
liquidityDelta.validateMinimumOut(amount0Min, amount1Min);
}

function _mint(
Expand All @@ -187,8 +186,7 @@ contract PositionManager is

// _beforeModify is not called here because the tokenId is newly minted
BalanceDelta liquidityDelta = _modifyLiquidity(config, liquidity.toInt256(), bytes32(tokenId), hookData);
if (amount0Max < uint128(-liquidityDelta.amount0())) revert SlippageExceeded();
if (amount1Max < uint128(-liquidityDelta.amount1())) revert SlippageExceeded();
liquidityDelta.validateMaximumIn(amount0Max, amount1Max);
positionConfigs[tokenId] = config.toId();
}

Expand Down Expand Up @@ -228,9 +226,7 @@ contract PositionManager is
// Can only call modify if there is non zero liquidity.
if (liquidity > 0) {
liquidityDelta = _modifyLiquidity(config, -(liquidity.toInt256()), bytes32(tokenId), hookData);
if (uint128(liquidityDelta.amount0()) < amount0Min || uint128(liquidityDelta.amount1()) < amount1Min) {
revert SlippageExceeded();
}
liquidityDelta.validateMinimumOut(amount0Min, amount1Min);
}

delete positionConfigs[tokenId];
Expand Down
1 change: 0 additions & 1 deletion src/interfaces/IPositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";

interface IPositionManager {
error NotApproved(address caller);
error SlippageExceeded();
error DeadlinePassed();
error IncorrectPositionConfigForTokenId(uint256 tokenId);

Expand Down
40 changes: 40 additions & 0 deletions src/libraries/SlippageCheck.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";

/// @title Slippage Check Library
/// @notice a library for checking if a delta exceeds a maximum ceiling or fails to meet a minimum floor
library SlippageCheckLibrary {
error MaximumAmountExceeded();
error MinimumAmountInsufficient();

/// @notice Revert if one or both deltas does not meet a minimum output
/// @dev to be used when removing liquidity to guarantee a minimum output
function validateMinimumOut(BalanceDelta delta, uint128 amount0Min, uint128 amount1Min) internal pure {
if (uint128(delta.amount0()) < amount0Min || uint128(delta.amount1()) < amount1Min) {
revert MinimumAmountInsufficient();
}
}

/// @notice Revert if one or both deltas exceeds a maximum input
/// @dev to be used when minting liquidity to guarantee a maximum input
function validateMaximumIn(BalanceDelta delta, uint128 amount0Max, uint128 amount1Max) internal pure {
if (uint128(-delta.amount0()) > amount0Max || uint128(-delta.amount1()) > amount1Max) {
revert MaximumAmountExceeded();
}
}

/// @notice Revert if one or both deltas exceeds a maximum input
/// @dev When increasing liquidity, delta can be positive when excess fees need to be collected
/// in those cases, slippage checks are not required
function validateMaximumIncreaseSlippage(BalanceDelta delta, uint128 amount0Max, uint128 amount1Max)
internal
pure
{
if (
delta.amount0() < 0 && amount0Max < uint128(-delta.amount0())
|| delta.amount1() < 0 && amount1Max < uint128(-delta.amount1())
) revert MaximumAmountExceeded();
}
}
7 changes: 4 additions & 3 deletions test/position-managers/IncreaseLiquidity.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {IERC20} from "forge-std/interfaces/IERC20.sol";

import {PositionManager} from "../../src/PositionManager.sol";
import {PositionConfig} from "../../src/libraries/PositionConfig.sol";
import {SlippageCheckLibrary} from "../../src/libraries/SlippageCheck.sol";
import {IPositionManager} from "../../src/interfaces/IPositionManager.sol";
import {Actions} from "../../src/libraries/Actions.sol";
import {Planner, Plan} from "../shared/Planner.sol";
Expand Down Expand Up @@ -378,7 +379,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers {

// revert since amount0Max is too low
bytes memory calls = getIncreaseEncoded(tokenId, config, 100e18, 1 wei, type(uint128).max, ZERO_BYTES);
vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MaximumAmountExceeded.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand All @@ -389,7 +390,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers {

// revert since amount1Max is too low
bytes memory calls = getIncreaseEncoded(tokenId, config, 100e18, type(uint128).max, 1 wei, ZERO_BYTES);
vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MaximumAmountExceeded.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand Down Expand Up @@ -437,7 +438,7 @@ contract IncreaseLiquidityTest is Test, PosmTestSetup, Fuzzers {
swap(key, true, -10e18, ZERO_BYTES);

bytes memory calls = getIncreaseEncoded(tokenId, config, newLiquidity, slippage, slippage, ZERO_BYTES);
vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MaximumAmountExceeded.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand Down
4 changes: 3 additions & 1 deletion test/position-managers/PositionManager.gas.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,9 @@ contract PosMGasTest is Test, PosmTestSetup, GasSnapshot {
Actions.DECREASE_LIQUIDITY,
abi.encode(tokenId, config, 10_000 ether, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES)
);
planner.add(Actions.BURN_POSITION, abi.encode(tokenId, config, 0 wei, 0 wei, ZERO_BYTES));
planner.add(
Actions.BURN_POSITION, abi.encode(tokenId, config, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, ZERO_BYTES)
);

// We must include CLOSE commands.
bytes memory calls = planner.finalizeModifyLiquidity(config.poolKey);
Expand Down
19 changes: 10 additions & 9 deletions test/position-managers/PositionManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {IPositionManager} from "../../src/interfaces/IPositionManager.sol";
import {Actions} from "../../src/libraries/Actions.sol";
import {PositionManager} from "../../src/PositionManager.sol";
import {PositionConfig} from "../../src/libraries/PositionConfig.sol";
import {SlippageCheckLibrary} from "../../src/libraries/SlippageCheck.sol";
import {BaseActionsRouter} from "../../src/base/BaseActionsRouter.sol";

import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol";
Expand Down Expand Up @@ -184,15 +185,15 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers {
PositionConfig memory config = PositionConfig({poolKey: key, tickLower: -120, tickUpper: 120});

bytes memory calls = getMintEncoded(config, 1e18, 1 wei, MAX_SLIPPAGE_INCREASE, address(this), ZERO_BYTES);
vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MaximumAmountExceeded.selector);
lpm.modifyLiquidities(calls, _deadline);
}

function test_mint_slippage_revertAmount1() public {
PositionConfig memory config = PositionConfig({poolKey: key, tickLower: -120, tickUpper: 120});

bytes memory calls = getMintEncoded(config, 1e18, MAX_SLIPPAGE_INCREASE, 1 wei, address(this), ZERO_BYTES);
vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MaximumAmountExceeded.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand Down Expand Up @@ -235,7 +236,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers {
// swap to move the price and cause a slippage revert
swap(key, true, -1e18, ZERO_BYTES);

vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MaximumAmountExceeded.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand Down Expand Up @@ -346,7 +347,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers {

bytes memory calls =
getBurnEncoded(tokenId, config, uint128(-delta.amount0()) + 1 wei, MIN_SLIPPAGE_DECREASE, ZERO_BYTES);
vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MinimumAmountInsufficient.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand All @@ -358,7 +359,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers {

bytes memory calls =
getBurnEncoded(tokenId, config, MIN_SLIPPAGE_DECREASE, uint128(-delta.amount1()) + 1 wei, ZERO_BYTES);
vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MinimumAmountInsufficient.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand Down Expand Up @@ -393,7 +394,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers {
// swap to move the price and cause a slippage revert
swap(key, true, -1e18, ZERO_BYTES);

vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MinimumAmountInsufficient.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand Down Expand Up @@ -470,7 +471,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers {
bytes memory calls = getDecreaseEncoded(
tokenId, config, 1e18, uint128(-delta.amount0()) + 1 wei, MIN_SLIPPAGE_DECREASE, ZERO_BYTES
);
vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MinimumAmountInsufficient.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand All @@ -483,7 +484,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers {
bytes memory calls = getDecreaseEncoded(
tokenId, config, 1e18, MIN_SLIPPAGE_DECREASE, uint128(-delta.amount1()) + 1 wei, ZERO_BYTES
);
vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MinimumAmountInsufficient.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand Down Expand Up @@ -519,7 +520,7 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers {
// swap to move the price and cause a slippage revert
swap(key, true, -1e18, ZERO_BYTES);

vm.expectRevert(IPositionManager.SlippageExceeded.selector);
vm.expectRevert(SlippageCheckLibrary.MinimumAmountInsufficient.selector);
lpm.modifyLiquidities(calls, _deadline);
}

Expand Down

0 comments on commit 3b0bd3d

Please sign in to comment.