Skip to content

Commit

Permalink
add from amount support (#384)
Browse files Browse the repository at this point in the history
* add from amount support

* update gas snaps

* add calldata decoder fuzz tests

* use open delta

* mint from deltas

* increase test

* use SafeCast

* add fuzz test, optimize clear

* spearbit comments

* fix FOT fuzz test

* add license

---------

Co-authored-by: marktoda <[email protected]>
Co-authored-by: Alice Henshaw <[email protected]>
Co-authored-by: Alice <[email protected]>
  • Loading branch information
4 people authored Nov 22, 2024
1 parent 58974e3 commit e5829f6
Show file tree
Hide file tree
Showing 38 changed files with 644 additions and 54 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 @@
50479
50540
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 @@
50479
50540
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125652
125712
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125134
125195
Original file line number Diff line number Diff line change
@@ -1 +1 @@
132512
132572
Original file line number Diff line number Diff line change
@@ -1 +1 @@
131994
132055
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
146379
146402
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
154954
154977
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_withClose.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
154954
154977
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_withTakePair.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
154319
154342
Original file line number Diff line number Diff line change
@@ -1 +1 @@
112048
112067
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119835
119858
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119200
119223
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decrease_burnEmpty.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
135308
135388
Original file line number Diff line number Diff line change
@@ -1 +1 @@
128448
128528
Original file line number Diff line number Diff line change
@@ -1 +1 @@
132522
132545
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decrease_take_take.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
120455
120478
Original file line number Diff line number Diff line change
@@ -1 +1 @@
148160
148210
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_native.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
365994
366017
Original file line number Diff line number Diff line change
@@ -1 +1 @@
374512
374535
Original file line number Diff line number Diff line change
@@ -1 +1 @@
373748
373771
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickLower.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
317417
317440
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickUpper.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
318087
318110
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
243656
243679
Original file line number Diff line number Diff line change
@@ -1 +1 @@
418864
418887
Original file line number Diff line number Diff line change
@@ -1 +1 @@
323448
323471
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_withClose.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
419970
419993
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_withSettlePair.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
419040
419063
Original file line number Diff line number Diff line change
@@ -1 +1 @@
455814
455837
71 changes: 70 additions & 1 deletion src/PositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ 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 {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";
import {IPositionDescriptor} from "./interfaces/IPositionDescriptor.sol";
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";

import {IPositionDescriptor} from "./interfaces/IPositionDescriptor.sol";
import {ERC721Permit_v4} from "./base/ERC721Permit_v4.sol";
import {ReentrancyLock} from "./base/ReentrancyLock.sol";
import {IPositionManager} from "./interfaces/IPositionManager.sol";
Expand All @@ -26,6 +27,7 @@ import {CalldataDecoder} from "./libraries/CalldataDecoder.sol";
import {Permit2Forwarder} from "./base/Permit2Forwarder.sol";
import {SlippageCheck} from "./libraries/SlippageCheck.sol";
import {PositionInfo, PositionInfoLibrary} from "./libraries/PositionInfoLibrary.sol";
import {LiquidityAmounts} from "./libraries/LiquidityAmounts.sol";
import {NativeWrapper} from "./base/NativeWrapper.sol";
import {IWETH9} from "./interfaces/external/IWETH9.sol";

Expand Down Expand Up @@ -199,6 +201,11 @@ contract PositionManager is
params.decodeModifyLiquidityParams();
_increase(tokenId, liquidity, amount0Max, amount1Max, hookData);
return;
} else if (action == Actions.INCREASE_LIQUIDITY_FROM_DELTAS) {
(uint256 tokenId, uint128 amount0Max, uint128 amount1Max, bytes calldata hookData) =
params.decodeIncreaseLiquidityFromDeltasParams();
_increaseFromDeltas(tokenId, amount0Max, amount1Max, hookData);
return;
} else if (action == Actions.DECREASE_LIQUIDITY) {
(uint256 tokenId, uint256 liquidity, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData) =
params.decodeModifyLiquidityParams();
Expand All @@ -217,6 +224,18 @@ contract PositionManager is
) = params.decodeMintParams();
_mint(poolKey, tickLower, tickUpper, liquidity, amount0Max, amount1Max, _mapRecipient(owner), hookData);
return;
} else if (action == Actions.MINT_POSITION_FROM_DELTAS) {
(
PoolKey calldata poolKey,
int24 tickLower,
int24 tickUpper,
uint128 amount0Max,
uint128 amount1Max,
address owner,
bytes calldata hookData
) = params.decodeMintFromDeltasParams();
_mintFromDeltas(poolKey, tickLower, tickUpper, amount0Max, amount1Max, _mapRecipient(owner), hookData);
return;
} else if (action == Actions.BURN_POSITION) {
// Will automatically decrease liquidity to 0 if the position is not already empty.
(uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData) =
Expand Down Expand Up @@ -283,6 +302,31 @@ contract PositionManager is
(liquidityDelta - feesAccrued).validateMaxIn(amount0Max, amount1Max);
}

/// @dev The liquidity delta is derived from open deltas in the pool manager.
function _increaseFromDeltas(uint256 tokenId, uint128 amount0Max, uint128 amount1Max, bytes calldata hookData)
internal
onlyIfApproved(msgSender(), tokenId)
{
(PoolKey memory poolKey, PositionInfo info) = getPoolAndPositionInfo(tokenId);

(uint160 sqrtPriceX96,,,) = poolManager.getSlot0(poolKey.toId());

// Use the credit on the pool manager as the amounts for the mint.
uint256 liquidity = LiquidityAmounts.getLiquidityForAmounts(
sqrtPriceX96,
TickMath.getSqrtPriceAtTick(info.tickLower()),
TickMath.getSqrtPriceAtTick(info.tickUpper()),
_getFullCredit(poolKey.currency0),
_getFullCredit(poolKey.currency1)
);

// Note: The tokenId is used as the salt for this position, so every minted position has unique storage in the pool manager.
(BalanceDelta liquidityDelta, BalanceDelta feesAccrued) =
_modifyLiquidity(info, poolKey, liquidity.toInt256(), bytes32(tokenId), hookData);
// Slippage checks should be done on the principal liquidityDelta which is the liquidityDelta - feesAccrued
(liquidityDelta - feesAccrued).validateMaxIn(amount0Max, amount1Max);
}

/// @dev Calling decrease with 0 liquidity will credit the caller with any underlying fees of the position
function _decrease(
uint256 tokenId,
Expand Down Expand Up @@ -335,6 +379,29 @@ contract PositionManager is
liquidityDelta.validateMaxIn(amount0Max, amount1Max);
}

function _mintFromDeltas(
PoolKey calldata poolKey,
int24 tickLower,
int24 tickUpper,
uint128 amount0Max,
uint128 amount1Max,
address owner,
bytes calldata hookData
) internal {
(uint160 sqrtPriceX96,,,) = poolManager.getSlot0(poolKey.toId());

// Use the credit on the pool manager as the amounts for the mint.
uint256 liquidity = LiquidityAmounts.getLiquidityForAmounts(
sqrtPriceX96,
TickMath.getSqrtPriceAtTick(tickLower),
TickMath.getSqrtPriceAtTick(tickUpper),
_getFullCredit(poolKey.currency0),
_getFullCredit(poolKey.currency1)
);

_mint(poolKey, tickLower, tickUpper, liquidity, amount0Max, amount1Max, owner, hookData);
}

/// @dev this is overloaded with ERC721Permit_v4._burn
function _burn(uint256 tokenId, uint128 amount0Min, uint128 amount1Min, bytes calldata hookData)
internal
Expand Down Expand Up @@ -389,8 +456,10 @@ contract PositionManager is

/// @dev integrators may elect to forfeit positive deltas with clear
/// if the forfeit amount exceeds the user-specified max, the amount is taken instead
/// if there is no credit, no call is made.
function _clearOrTake(Currency currency, uint256 amountMax) internal {
uint256 delta = _getFullCredit(currency);
if (delta == 0) return;

// forfeit the delta if its less than or equal to the user-specified limit
if (delta <= amountMax) {
Expand Down
42 changes: 23 additions & 19 deletions src/libraries/Actions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,36 @@ library Actions {
uint256 constant DECREASE_LIQUIDITY = 0x01;
uint256 constant MINT_POSITION = 0x02;
uint256 constant BURN_POSITION = 0x03;
uint256 constant INCREASE_LIQUIDITY_FROM_DELTAS = 0x04;
uint256 constant MINT_POSITION_FROM_DELTAS = 0x05;

// swapping
uint256 constant SWAP_EXACT_IN_SINGLE = 0x04;
uint256 constant SWAP_EXACT_IN = 0x05;
uint256 constant SWAP_EXACT_OUT_SINGLE = 0x06;
uint256 constant SWAP_EXACT_OUT = 0x07;
uint256 constant SWAP_EXACT_IN_SINGLE = 0x06;
uint256 constant SWAP_EXACT_IN = 0x07;
uint256 constant SWAP_EXACT_OUT_SINGLE = 0x08;
uint256 constant SWAP_EXACT_OUT = 0x09;
// donate
uint256 constant DONATE = 0x08;
uint256 constant DONATE = 0x0a;

// closing deltas on the pool manager
// settling
uint256 constant SETTLE = 0x09;
uint256 constant SETTLE_ALL = 0x10;
uint256 constant SETTLE_PAIR = 0x11;
uint256 constant SETTLE = 0x0b;
uint256 constant SETTLE_ALL = 0x0c;
uint256 constant SETTLE_PAIR = 0x0d;
// taking
uint256 constant TAKE = 0x12;
uint256 constant TAKE_ALL = 0x13;
uint256 constant TAKE_PORTION = 0x14;
uint256 constant TAKE_PAIR = 0x15;
uint256 constant TAKE = 0x0e;
uint256 constant TAKE_ALL = 0x0f;
uint256 constant TAKE_PORTION = 0x10;
uint256 constant TAKE_PAIR = 0x11;

uint256 constant CLOSE_CURRENCY = 0x12;
uint256 constant CLEAR_OR_TAKE = 0x13;
uint256 constant SWEEP = 0x14;

uint256 constant CLOSE_CURRENCY = 0x17;
uint256 constant CLEAR_OR_TAKE = 0x18;
uint256 constant SWEEP = 0x19;
uint256 constant WRAP = 0x20;
uint256 constant UNWRAP = 0x21;
uint256 constant WRAP = 0x15;
uint256 constant UNWRAP = 0x16;

// minting/burning 6909s to close deltas
uint256 constant MINT_6909 = 0x22;
uint256 constant BURN_6909 = 0x23;
uint256 constant MINT_6909 = 0x17;
uint256 constant BURN_6909 = 0x18;
}
41 changes: 41 additions & 0 deletions src/libraries/CalldataDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,21 @@ library CalldataDecoder {
hookData = params.toBytes(4);
}

/// @dev equivalent to: abi.decode(params, (uint256, uint128, uint128, bytes)) in calldata
function decodeIncreaseLiquidityFromDeltasParams(bytes calldata params)
internal
pure
returns (uint256 tokenId, uint128 amount0Max, uint128 amount1Max, bytes calldata hookData)
{
assembly ("memory-safe") {
tokenId := calldataload(params.offset)
amount0Max := calldataload(add(params.offset, 0x20))
amount1Max := calldataload(add(params.offset, 0x40))
}

hookData = params.toBytes(3);
}

/// @dev equivalent to: abi.decode(params, (PoolKey, int24, int24, uint256, uint128, uint128, address, bytes)) in calldata
function decodeMintParams(bytes calldata params)
internal
Expand Down Expand Up @@ -113,6 +128,32 @@ library CalldataDecoder {
hookData = params.toBytes(11);
}

/// @dev equivalent to: abi.decode(params, (PoolKey, int24, int24, uint128, uint128, address, bytes)) in calldata
function decodeMintFromDeltasParams(bytes calldata params)
internal
pure
returns (
PoolKey calldata poolKey,
int24 tickLower,
int24 tickUpper,
uint128 amount0Max,
uint128 amount1Max,
address owner,
bytes calldata hookData
)
{
assembly ("memory-safe") {
poolKey := params.offset
tickLower := calldataload(add(params.offset, 0xa0))
tickUpper := calldataload(add(params.offset, 0xc0))
amount0Max := calldataload(add(params.offset, 0xe0))
amount1Max := calldataload(add(params.offset, 0x100))
owner := calldataload(add(params.offset, 0x120))
}

hookData = params.toBytes(10);
}

/// @dev equivalent to: abi.decode(params, (uint256, uint128, uint128, bytes)) in calldata
function decodeBurnParams(bytes calldata params)
internal
Expand Down
Loading

0 comments on commit e5829f6

Please sign in to comment.