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
46765
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
46583
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
129633
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
122555
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
151223
149728
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
140880
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
149728
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decreaseLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
116766
115271
Original file line number Diff line number Diff line change
@@ -1 +1 @@
109375
108179
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
133499
Original file line number Diff line number Diff line change
@@ -1 +1 @@
127917
126238
Original file line number Diff line number Diff line change
@@ -1 +1 @@
129482
127987
Original file line number Diff line number Diff line change
@@ -1 +1 @@
152736
151216
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134536
133016
Original file line number Diff line number Diff line change
@@ -1 +1 @@
135432
133919
Original file line number Diff line number Diff line change
@@ -1 +1 @@
171588
170075
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
372837
371260
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
335960
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
344590
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
313942
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
314584
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
240166
Original file line number Diff line number Diff line change
@@ -1 +1 @@
371451
369404
Original file line number Diff line number Diff line change
@@ -1 +1 @@
321537
319960
Original file line number Diff line number Diff line change
@@ -1 +1 @@
417173
415636
61 changes: 33 additions & 28 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 {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol";
Expand All @@ -22,6 +23,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 +41,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 @@ -96,22 +99,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 @@ -120,10 +123,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 @@ -140,8 +143,9 @@ 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 = params.decodeCurrency();

// 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 @@ -157,18 +161,17 @@ 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(bytes calldata params) internal {
Currency currency = params.decodeCurrency();

// 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
/// @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 @@ -185,10 +188,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 @@ -201,21 +206,21 @@ contract PositionManager is
);
}

function _getPositionLiquidity(PositionConfig memory config, uint256 tokenId)
function _getPositionLiquidity(PositionConfig calldata config, uint256 tokenId)
internal
view
returns (uint128 liquidity)
{
// TODO: Calculate positionId with Position.calculatePositionKey in v4-core.
bytes32 positionId =
keccak256(abi.encodePacked(address(this), config.tickLower, config.tickUpper, bytes32(tokenId)));
Position.calculatePositionKey(address(this), config.tickLower, config.tickUpper, bytes32(tokenId));
liquidity = poolManager.getPositionLiquidity(config.poolKey.toId(), positionId);
}

/// @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));
(Currency currency, address to) = params.decodeCurrencyAndAddress();

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
Loading