Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

posm slippage #225

Merged
merged 16 commits into from
Aug 2, 2024
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_burn_empty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
46767
47040
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_burn_empty_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
46584
46858
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_burn_nonEmpty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
129412
129816
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 @@
122334
122737
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
149447
149962
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
140599
141114
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
149447
149962
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decreaseLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
115032
115505
Original file line number Diff line number Diff line change
@@ -1 +1 @@
107954
108366
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decrease_burnEmpty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
133172
133849
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125911
126588
Original file line number Diff line number Diff line change
@@ -1 +1 @@
127706
128221
Original file line number Diff line number Diff line change
@@ -1 +1 @@
150926
152100
Original file line number Diff line number Diff line change
@@ -1 +1 @@
132726
133900
Original file line number Diff line number Diff line change
@@ -1 +1 @@
133653
134581
Original file line number Diff line number Diff line change
@@ -1 +1 @@
169809
170737
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
370961
372012
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
335661
336712
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_nativeWithSweep.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
344170
345221
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickLower.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
313643
314694
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickUpper.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
314285
315336
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
239867
240918
Original file line number Diff line number Diff line change
@@ -1 +1 @@
368863
369926
Original file line number Diff line number Diff line change
@@ -1 +1 @@
319661
320712
Original file line number Diff line number Diff line change
@@ -1 +1 @@
415324
416388
94 changes: 72 additions & 22 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 @@ -77,24 +79,48 @@ contract PositionManager is

function _handleAction(uint256 action, bytes calldata params) internal virtual override {
if (action == Actions.INCREASE_LIQUIDITY) {
(uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes calldata hookData) =
params.decodeModifyLiquidityParams();
_increase(tokenId, config, liquidity, hookData);
(
uint256 tokenId,
PositionConfig calldata config,
uint256 liquidity,
uint128 amount0Max,
uint128 amount1Max,
bytes calldata hookData
) = params.decodeModifyLiquidityParams();
_increase(tokenId, config, liquidity, amount0Max, amount1Max, hookData);
} else if (action == Actions.DECREASE_LIQUIDITY) {
(uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes calldata hookData) =
params.decodeModifyLiquidityParams();
_decrease(tokenId, config, liquidity, hookData);
(
uint256 tokenId,
PositionConfig calldata config,
uint256 liquidity,
uint128 amount0Min,
uint128 amount1Min,
bytes calldata hookData
) = params.decodeModifyLiquidityParams();
_decrease(tokenId, config, liquidity, amount0Min, amount1Min, hookData);
} else if (action == Actions.MINT_POSITION) {
(PositionConfig calldata config, uint256 liquidity, address owner, bytes calldata hookData) =
params.decodeMintParams();
_mint(config, liquidity, owner, hookData);
(
PositionConfig calldata config,
uint256 liquidity,
uint128 amount0Max,
uint128 amount1Max,
address owner,
bytes calldata hookData
) = params.decodeMintParams();
_mint(config, liquidity, amount0Max, amount1Max, owner, hookData);
} else if (action == Actions.CLOSE_CURRENCY) {
Currency currency = params.decodeCurrency();
_close(currency);
} else if (action == Actions.BURN_POSITION) {
// Will automatically decrease liquidity to 0 if the position is not already empty.
(uint256 tokenId, PositionConfig calldata config, bytes calldata hookData) = params.decodeBurnParams();
_burn(tokenId, config, hookData);
(
uint256 tokenId,
PositionConfig calldata config,
uint128 amount0Min,
uint128 amount1Min,
bytes calldata hookData
) = params.decodeBurnParams();
_burn(tokenId, config, amount0Min, amount1Min, hookData);
} else if (action == Actions.SETTLE_WITH_BALANCE) {
Currency currency = params.decodeCurrency();
_settleWithBalance(currency);
Expand All @@ -111,28 +137,45 @@ contract PositionManager is
}

/// @dev Calling increase with 0 liquidity will credit the caller with any underlying fees of the position
function _increase(uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes calldata hookData)
internal
{
function _increase(
uint256 tokenId,
PositionConfig calldata config,
uint256 liquidity,
uint128 amount0Max,
uint128 amount1Max,
bytes calldata hookData
) internal {
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);
liquidityDelta.validateMaxInNegative(amount0Max, amount1Max);
}

/// @dev Calling decrease with 0 liquidity will credit the caller with any underlying fees of the position
function _decrease(uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes calldata hookData)
internal
{
function _decrease(
uint256 tokenId,
PositionConfig calldata config,
uint256 liquidity,
uint128 amount0Min,
uint128 amount1Min,
bytes calldata hookData
) internal {
if (!_isApprovedOrOwner(_msgSender(), tokenId)) revert NotApproved(_msgSender());
if (positionConfigs[tokenId] != config.toId()) revert IncorrectPositionConfigForTokenId(tokenId);

// Note: the tokenId is used as the salt.
BalanceDelta liquidityDelta = _modifyLiquidity(config, -(liquidity.toInt256()), bytes32(tokenId), hookData);
liquidityDelta.validateMinOut(amount0Min, amount1Min);
}

function _mint(PositionConfig calldata config, uint256 liquidity, address owner, bytes calldata hookData)
internal
{
function _mint(
PositionConfig calldata config,
uint256 liquidity,
uint128 amount0Max,
uint128 amount1Max,
address owner,
bytes calldata hookData
) internal {
// mint receipt token
uint256 tokenId;
// tokenId is assigned to current nextTokenId before incrementing it
Expand All @@ -143,7 +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);

liquidityDelta.validateMaxIn(amount0Max, amount1Max);
positionConfigs[tokenId] = config.toId();
}

Expand All @@ -168,7 +211,13 @@ contract PositionManager is
}

/// @dev this is overloaded with ERC721Permit._burn
function _burn(uint256 tokenId, PositionConfig calldata config, bytes calldata hookData) internal {
function _burn(
uint256 tokenId,
PositionConfig calldata config,
uint128 amount0Min,
uint128 amount1Min,
bytes calldata hookData
) internal {
if (!_isApprovedOrOwner(_msgSender(), tokenId)) revert NotApproved(_msgSender());
if (positionConfigs[tokenId] != config.toId()) revert IncorrectPositionConfigForTokenId(tokenId);
uint256 liquidity = uint256(_getPositionLiquidity(config, tokenId));
Expand All @@ -177,6 +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);
liquidityDelta.validateMinOut(amount0Min, amount1Min);
}

delete positionConfigs[tokenId];
Expand Down
46 changes: 36 additions & 10 deletions src/libraries/CalldataDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,45 +43,71 @@ library CalldataDecoder {
}
}

/// @dev equivalent to: abi.decode(params, (uint256, PositionConfig, uint256, bytes)) in calldata
/// @dev equivalent to: abi.decode(params, (uint256, PositionConfig, uint256, uint128, uint128, bytes)) in calldata
function decodeModifyLiquidityParams(bytes calldata params)
internal
pure
returns (uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes calldata hookData)
returns (
uint256 tokenId,
PositionConfig calldata config,
uint256 liquidity,
uint128 amount0,
uint128 amount1,
bytes calldata hookData
)
{
assembly ("memory-safe") {
tokenId := calldataload(params.offset)
config := add(params.offset, 0x20)
liquidity := calldataload(add(params.offset, 0x100))
amount0 := calldataload(add(params.offset, 0x120))
amount1 := calldataload(add(params.offset, 0x140))
}
hookData = params.toBytes(9);
hookData = params.toBytes(11);
}

/// @dev equivalent to: abi.decode(params, (PositionConfig, uint256, address, bytes)) in calldata
/// @dev equivalent to: abi.decode(params, (PositionConfig, uint256, uint128, uint128, address, bytes)) in calldata
function decodeMintParams(bytes calldata params)
internal
pure
returns (PositionConfig calldata config, uint256 liquidity, address owner, bytes calldata hookData)
returns (
PositionConfig calldata config,
uint256 liquidity,
uint128 amount0Max,
uint128 amount1Max,
address owner,
bytes calldata hookData
)
{
assembly ("memory-safe") {
config := params.offset
liquidity := calldataload(add(params.offset, 0xe0))
owner := calldataload(add(params.offset, 0x100))
amount0Max := calldataload(add(params.offset, 0x100))
amount1Max := calldataload(add(params.offset, 0x120))
owner := calldataload(add(params.offset, 0x140))
}
hookData = params.toBytes(9);
hookData = params.toBytes(11);
}

/// @dev equivalent to: abi.decode(params, (uint256, PositionConfig, bytes)) in calldata
/// @dev equivalent to: abi.decode(params, (uint256, PositionConfig, uint128, uint128, bytes)) in calldata
function decodeBurnParams(bytes calldata params)
internal
pure
returns (uint256 tokenId, PositionConfig calldata config, bytes calldata hookData)
returns (
uint256 tokenId,
PositionConfig calldata config,
uint128 amount0Min,
uint128 amount1Min,
bytes calldata hookData
)
{
assembly ("memory-safe") {
tokenId := calldataload(params.offset)
config := add(params.offset, 0x20)
amount0Min := calldataload(add(params.offset, 0x100))
amount1Min := calldataload(add(params.offset, 0x120))
}
hookData = params.toBytes(8);
hookData = params.toBytes(10);
}

/// @dev equivalent to: abi.decode(params, (Currency)) in calldata
Expand Down
Loading
Loading