Skip to content

Commit

Permalink
Decode posm params in calldata (#226)
Browse files Browse the repository at this point in the history
* Decode posm params in calldata

* calculate config id in assembly

* use Position library

* fuzz toId

* fuzz calldata decoder

* 2 more helper functions

* decode before call

* natspec in calldata decoder
  • Loading branch information
hensha256 authored Jul 31, 2024
1 parent 463d030 commit db359ca
Show file tree
Hide file tree
Showing 32 changed files with 341 additions and 98 deletions.
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_burn_empty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
47248
46761
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 @@
47066
46579
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_burn_nonEmpty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
130117
129621
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 @@
123039
122543
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
151223
149718
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
140870
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
149718
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decreaseLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
116766
115261
Original file line number Diff line number Diff line change
@@ -1 +1 @@
109375
108171
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decrease_burnEmpty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
135156
133487
Original file line number Diff line number Diff line change
@@ -1 +1 @@
127895
126226
Original file line number Diff line number Diff line change
@@ -1 +1 @@
129482
127977
Original file line number Diff line number Diff line change
@@ -1 +1 @@
152736
151197
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134536
132997
Original file line number Diff line number Diff line change
@@ -1 +1 @@
135432
133924
Original file line number Diff line number Diff line change
@@ -1 +1 @@
171588
170080
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
372837
371232
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
337537
335932
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_nativeWithSweep.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
346399
344562
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickLower.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
315519
313914
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickUpper.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
316161
314556
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
241743
240138
Original file line number Diff line number Diff line change
@@ -1 +1 @@
371451
369400
Original file line number Diff line number Diff line change
@@ -1 +1 @@
321537
319932
Original file line number Diff line number Diff line change
@@ -1 +1 @@
417173
415608
82 changes: 40 additions & 42 deletions src/PositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol";
import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
import {Position} from "@uniswap/v4-core/src/libraries/Position.sol";
import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
import {TransientStateLibrary} from "@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
import {Position} from "@uniswap/v4-core/src/libraries/Position.sol";
Expand All @@ -23,6 +24,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 @@ -40,6 +42,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 @@ -74,20 +77,30 @@ contract PositionManager is

function _handleAction(uint256 action, bytes calldata params) internal override {
if (action == Actions.INCREASE_LIQUIDITY) {
_increase(params);
(uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes calldata hookData) =
params.decodeModifyLiquidityParams();
_increase(tokenId, config, liquidity, hookData);
} else if (action == Actions.DECREASE_LIQUIDITY) {
_decrease(params);
(uint256 tokenId, PositionConfig calldata config, uint256 liquidity, bytes calldata hookData) =
params.decodeModifyLiquidityParams();
_decrease(tokenId, config, liquidity, hookData);
} else if (action == Actions.MINT_POSITION) {
_mint(params);
(PositionConfig calldata config, uint256 liquidity, address owner, bytes calldata hookData) =
params.decodeMintParams();
_mint(config, liquidity, owner, hookData);
} else if (action == Actions.CLOSE_CURRENCY) {
_close(params);
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.
_burn(params);
(uint256 tokenId, PositionConfig calldata config, bytes calldata hookData) = params.decodeBurnParams();
_burn(tokenId, config, hookData);
} else if (action == Actions.SETTLE_WITH_BALANCE) {
_settleWithBalance(params);
Currency currency = params.decodeCurrency();
_settleWithBalance(currency);
} else if (action == Actions.SWEEP) {
_sweep(params);
(Currency currency, address to) = params.decodeCurrencyAndAddress();
_sweep(currency, to);
} else {
revert UnsupportedAction(action);
}
Expand All @@ -97,35 +110,29 @@ contract PositionManager is
return _getLocker();
}

/// @param params is an encoding of uint256 tokenId, PositionConfig memory 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(uint256 tokenId, PositionConfig calldata config, uint256 liquidity, 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);
}

/// @param params is an encoding of uint256 tokenId, PositionConfig memory 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(uint256 tokenId, PositionConfig calldata config, uint256 liquidity, 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);
}

/// @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));

function _mint(PositionConfig calldata config, uint256 liquidity, address owner, bytes calldata hookData)
internal
{
// mint receipt token
uint256 tokenId;
// tokenId is assigned to current nextTokenId before incrementing it
Expand All @@ -140,9 +147,7 @@ contract PositionManager is
positionConfigs[tokenId] = config.toId();
}

/// @param params is an encoding of the Currency to close
function _close(bytes memory params) internal {
(Currency currency) = abi.decode(params, (Currency));
function _close(Currency currency) internal {
// 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 @@ -156,21 +161,14 @@ contract PositionManager is
}
}

/// @param params is an encoding of Currency
/// @dev uses this addresses balance to settle a negative delta
function _settleWithBalance(bytes memory params) internal {
Currency currency = abi.decode(params, (Currency));

function _settleWithBalance(Currency currency) internal {
// set the payer to this address, performs a transfer.
_settle(currency, address(this), _getFullSettleAmount(currency));
}

/// @param params is an encoding of uint256 tokenId, PositionConfig memory 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(uint256 tokenId, PositionConfig calldata config, 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 @@ -186,10 +184,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 @@ -202,7 +202,7 @@ contract PositionManager is
);
}

function _getPositionLiquidity(PositionConfig memory config, uint256 tokenId)
function _getPositionLiquidity(PositionConfig calldata config, uint256 tokenId)
internal
view
returns (uint128 liquidity)
Expand All @@ -213,9 +213,7 @@ contract PositionManager is
}

/// @notice Sweeps the entire contract balance of specified currency to the recipient
/// @param params an encoding of Currency, address
function _sweep(bytes calldata params) internal {
(Currency currency, address to) = abi.decode(params, (Currency, address));
function _sweep(Currency currency, address to) internal {
uint256 balance = currency.balanceOfSelf();
if (balance > 0) currency.transfer(to, balance);
}
Expand Down
16 changes: 2 additions & 14 deletions src/V4Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,14 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {
}
} else {
if (action == Actions.SETTLE_ALL) {
// equivalent: abi.decode(params, (Currency))
Currency currency;
assembly ("memory-safe") {
currency := calldataload(params.offset)
}

Currency currency = params.decodeCurrency();
uint256 amount = _getFullSettleAmount(currency);

// TODO support address(this) paying too
// TODO should it have a maxAmountOut added slippage protection?
_settle(currency, _msgSender(), amount);
} else if (action == Actions.TAKE_ALL) {
// equivalent: abi.decode(params, (Currency, address))
Currency currency;
address recipient;
assembly ("memory-safe") {
currency := calldataload(params.offset)
recipient := calldataload(add(params.offset, 0x20))
}

(Currency currency, address recipient) = params.decodeCurrencyAndAddress();
uint256 amount = _getFullTakeAmount(currency);

// TODO should _take have a minAmountOut added slippage check?
Expand Down
Loading

0 comments on commit db359ca

Please sign in to comment.