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

add from amount support #384

Merged
merged 16 commits into from
Nov 22, 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 @@
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";
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
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;
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
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
Loading