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

Decode posm params in calldata #226

Merged
merged 12 commits into from
Jul 31, 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 @@
47271
47690
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 @@
47088
47508
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_burn_nonEmpty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
130139
130555
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 @@
123060
123476
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
151223
150859
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
142375
142011
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
151223
150859
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decreaseLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
116766
116402
Original file line number Diff line number Diff line change
@@ -1 +1 @@
109375
109084
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decrease_burnEmpty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
135178
135328
Original file line number Diff line number Diff line change
@@ -1 +1 @@
127917
128067
Original file line number Diff line number Diff line change
@@ -1 +1 @@
129482
129118
Original file line number Diff line number Diff line change
@@ -1 +1 @@
152726
152337
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134609
134220
Original file line number Diff line number Diff line change
@@ -1 +1 @@
135432
135050
Original file line number Diff line number Diff line change
@@ -1 +1 @@
171588
171206
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
372827
372374
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
337610
337157
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_nativeWithSweep.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
344548
344095
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickLower.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
315509
315056
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickUpper.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
316151
315698
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
241733
241280
Original file line number Diff line number Diff line change
@@ -1 +1 @@
321527
321074
Original file line number Diff line number Diff line change
@@ -1 +1 @@
417163
416750
52 changes: 29 additions & 23 deletions src/PositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {DeltaResolver} from "./base/DeltaResolver.sol";
import {PositionConfig, PositionConfigLibrary} from "./libraries/PositionConfig.sol";
import {BaseActionsRouter} from "./base/BaseActionsRouter.sol";
import {Actions} from "./libraries/Actions.sol";
import {CalldataDecoder} from "./libraries/CalldataDecoder.sol";

contract PositionManager is
IPositionManager,
Expand All @@ -39,6 +40,7 @@ contract PositionManager is
using StateLibrary for IPoolManager;
using TransientStateLibrary for IPoolManager;
using SafeCast for uint256;
using CalldataDecoder for bytes;

/// @dev The ID of the next token that will be minted. Skips 0
uint256 public nextTokenId = 1;
Expand Down Expand Up @@ -92,22 +94,22 @@ contract PositionManager is
return _getLocker();
}

/// @param params is an encoding of uint256 tokenId, PositionConfig memory config, uint256 liquidity, bytes hookData
/// @param params is an encoding of uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes hookData
/// @dev Calling increase with 0 liquidity will credit the caller with any underlying fees of the position
function _increase(bytes memory params) internal {
(uint256 tokenId, PositionConfig memory config, uint256 liquidity, bytes memory hookData) =
abi.decode(params, (uint256, PositionConfig, uint256, bytes));
function _increase(bytes calldata params) internal {
(uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes calldata hookData) =
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
params.decodeModifyLiquidityParams();

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);
}

/// @param params is an encoding of uint256 tokenId, PositionConfig memory config, uint256 liquidity, bytes hookData
/// @param params is an encoding of uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes hookData
/// @dev Calling decrease with 0 liquidity will credit the caller with any underlying fees of the position
function _decrease(bytes memory params) internal {
(uint256 tokenId, PositionConfig memory config, uint256 liquidity, bytes memory hookData) =
abi.decode(params, (uint256, PositionConfig, uint256, bytes));
function _decrease(bytes calldata params) internal {
(uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes calldata hookData) =
params.decodeModifyLiquidityParams();

if (!_isApprovedOrOwner(_msgSender(), tokenId)) revert NotApproved(_msgSender());
if (positionConfigs[tokenId] != config.toId()) revert IncorrectPositionConfigForTokenId(tokenId);
Expand All @@ -116,10 +118,10 @@ contract PositionManager is
BalanceDelta liquidityDelta = _modifyLiquidity(config, -(liquidity.toInt256()), bytes32(tokenId), hookData);
}

/// @param params is an encoding of PositionConfig memory config, uint256 liquidity, address recipient, bytes hookData where recipient is the receiver / owner of the ERC721
function _mint(bytes memory params) internal {
(PositionConfig memory config, uint256 liquidity, address owner, bytes memory hookData) =
abi.decode(params, (PositionConfig, uint256, address, bytes));
/// @param params is an encoding of PositionConfig calldata config, uint256 liquidity, address recipient, bytes hookData where recipient is the receiver / owner of the ERC721
function _mint(bytes calldata params) internal {
(PositionConfig calldata config, uint256 liquidity, address owner, bytes calldata hookData) =
params.decodeMintParams();

// mint receipt token
uint256 tokenId;
Expand All @@ -136,8 +138,11 @@ contract PositionManager is
}

/// @param params is an encoding of the Currency to close
function _close(bytes memory params) internal {
(Currency currency) = abi.decode(params, (Currency));
function _close(bytes calldata params) internal {
Currency currency;
assembly ("memory-safe") {
currency := calldataload(params.offset)
}
// this address has applied all deltas on behalf of the user/owner
// it is safe to close this entire delta because of slippage checks throughout the batched calls.
int256 currencyDelta = poolManager.currencyDelta(address(this), currency);
Expand All @@ -154,11 +159,10 @@ contract PositionManager is
}
}

/// @param params is an encoding of uint256 tokenId, PositionConfig memory config, bytes hookData
/// @param params is an encoding of uint256 tokenId, PositionConfig calldata config, bytes hookData
/// @dev this is overloaded with ERC721Permit._burn
function _burn(bytes memory params) internal {
(uint256 tokenId, PositionConfig memory config, bytes memory hookData) =
abi.decode(params, (uint256, PositionConfig, bytes));
function _burn(bytes calldata params) internal {
(uint256 tokenId, PositionConfig calldata config, bytes calldata hookData) = params.decodeBurnParams();

if (!_isApprovedOrOwner(_msgSender(), tokenId)) revert NotApproved(_msgSender());
if (positionConfigs[tokenId] != config.toId()) revert IncorrectPositionConfigForTokenId(tokenId);
Expand All @@ -175,10 +179,12 @@ contract PositionManager is
_burn(tokenId);
}

function _modifyLiquidity(PositionConfig memory config, int256 liquidityChange, bytes32 salt, bytes memory hookData)
internal
returns (BalanceDelta liquidityDelta)
{
function _modifyLiquidity(
PositionConfig calldata config,
int256 liquidityChange,
bytes32 salt,
bytes calldata hookData
) internal returns (BalanceDelta liquidityDelta) {
(liquidityDelta,) = poolManager.modifyLiquidity(
config.poolKey,
IPoolManager.ModifyLiquidityParams({
Expand All @@ -191,7 +197,7 @@ contract PositionManager is
);
}

function _getPositionLiquidity(PositionConfig memory config, uint256 tokenId)
function _getPositionLiquidity(PositionConfig calldata config, uint256 tokenId)
internal
view
returns (uint128 liquidity)
Expand Down
80 changes: 80 additions & 0 deletions src/libraries/CalldataDecoder.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import {PositionConfig} from "./PositionConfig.sol";

/// @title Library for abi decoding in calldata
library CalldataDecoder {
using CalldataDecoder for bytes;

error SliceOutOfBounds();

/// @notice equivalent to SliceOutOfBounds.selector
Expand Down Expand Up @@ -40,4 +44,80 @@ library CalldataDecoder {
}
}
}

/// @param params The input bytes string to extract input arrays from
function decodeModifyLiquidityParams(bytes calldata params)
internal
pure
returns (uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes calldata hookData)
{
assembly ("memory-safe") {
tokenId := calldataload(params.offset)
config := add(params.offset, 0x20)
liquidity := calldataload(add(params.offset, 0x100))
}
hookData = params.toBytes(9);
}
hensha256 marked this conversation as resolved.
Show resolved Hide resolved

/// @param params The input bytes string to extract input arrays from
function decodeMintParams(bytes calldata params)
internal
pure
returns (PositionConfig calldata config, uint256 liquidity, address owner, bytes calldata hookData)
{
assembly ("memory-safe") {
config := params.offset
liquidity := calldataload(add(params.offset, 0xe0))
owner := calldataload(add(params.offset, 0x100))
}
hookData = params.toBytes(9);
}

/// @param params The input bytes string to extract input arrays from
function decodeBurnParams(bytes calldata params)
internal
pure
returns (uint256 tokenId, PositionConfig calldata config, bytes calldata hookData)
{
assembly ("memory-safe") {
tokenId := calldataload(params.offset)
config := add(params.offset, 0x20)
}
hookData = params.toBytes(8);
}

/// @notice Decode the `_arg`-th element in `_bytes` as a dynamic array
/// @dev The decoding of `length` and `offset` is universal,
/// whereas the type declaration of `res` instructs the compiler how to read it.
/// @param _bytes The input bytes string to slice
/// @param _arg The index of the argument to extract
/// @return length Length of the array
/// @return offset Pointer to the data part of the array
function toLengthOffset(bytes calldata _bytes, uint256 _arg)
internal
pure
returns (uint256 length, uint256 offset)
{
uint256 relativeOffset;
assembly ("memory-safe") {
// The offset of the `_arg`-th element is `32 * arg`, which stores the offset of the length pointer.
// shl(5, x) is equivalent to mul(32, x)
let lengthPtr := add(_bytes.offset, calldataload(add(_bytes.offset, shl(5, _arg))))
length := calldataload(lengthPtr)
offset := add(lengthPtr, 0x20)
relativeOffset := sub(offset, _bytes.offset)
}
if (_bytes.length < length + relativeOffset) revert SliceOutOfBounds();
}

/// @notice Decode the `_arg`-th element in `_bytes` as `bytes`
/// @param _bytes The input bytes string to extract a bytes string from
/// @param _arg The index of the argument to extract
function toBytes(bytes calldata _bytes, uint256 _arg) internal pure returns (bytes calldata res) {
(uint256 length, uint256 offset) = toLengthOffset(_bytes, _arg);
assembly ("memory-safe") {
res.length := length
res.offset := offset
}
}
}
7 changes: 5 additions & 2 deletions src/libraries/PositionConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ struct PositionConfig {

/// @notice Library for computing the configId given a PositionConfig
library PositionConfigLibrary {
function toId(PositionConfig memory config) internal pure returns (bytes32 id) {
PoolKey memory poolKey = config.poolKey;
function toId(PositionConfig calldata config) internal pure returns (bytes32 id) {
PoolKey calldata poolKey;
assembly ("memory-safe") {
poolKey := config
}
return keccak256(
abi.encodePacked(
poolKey.currency0,
Expand Down
Loading