Skip to content

Commit

Permalink
posm: Native Token (#189)
Browse files Browse the repository at this point in the history
* payable modifyLiquidities

* wip scaffold

* test for minting with native tokens

* test burn and receive native token

* test increase liquidity with native token

* test overpaying on increase liquidity

* test for decreasing liquidity native tokens

* test collecting native tokens

* regenerate snapshots

* gas snapshots for native token operations

* add gas check for sweep; move sweep logic to an internal function

* fix

* optimize sweep

---------

Co-authored-by: gretzke <daniel@gretzke.de>
  • Loading branch information
saucepoint and gretzke authored Jul 24, 2024
1 parent 6f094c3 commit a266de8
Show file tree
Hide file tree
Showing 23 changed files with 461 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
162386
162362
1 change: 1 addition & 0 deletions .forge-snapshots/PositionManager_collect_native.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
153577
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_collect_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
162386
162362
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_decreaseLiquidity.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
127764
127740
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
119120
Original file line number Diff line number Diff line change
@@ -1 +1 @@
140645
140621
Original file line number Diff line number Diff line change
@@ -1 +1 @@
157182
157204
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
142500
Original file line number Diff line number Diff line change
@@ -1 +1 @@
148692
148668
Original file line number Diff line number Diff line change
@@ -1 +1 @@
184956
184932
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
418192
418214
1 change: 1 addition & 0 deletions .forge-snapshots/PositionManager_mint_native.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
366282
1 change: 1 addition & 0 deletions .forge-snapshots/PositionManager_mint_nativeWithSweep.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
373221
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickLower.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
360874
360896
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_onSameTickUpper.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
361516
361538
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_mint_sameRange.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
287098
287120
Original file line number Diff line number Diff line change
@@ -1 +1 @@
366892
366914
Original file line number Diff line number Diff line change
@@ -1 +1 @@
462111
462133
11 changes: 11 additions & 0 deletions src/PositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ contract PositionManager is IPositionManager, ERC721Permit, PoolInitializer, Mul
/// @return returnData is the endocing of each actions return information
function modifyLiquidities(bytes calldata unlockData, uint256 deadline)
external
payable
checkDeadline(deadline)
returns (bytes[] memory)
{
Expand Down Expand Up @@ -155,6 +156,9 @@ contract PositionManager is IPositionManager, ERC721Permit, PoolInitializer, Mul
// the sender is the payer or receiver
if (currencyDelta < 0) {
currency.settle(poolManager, sender, uint256(-int256(currencyDelta)), false);

// if there are native tokens left over after settling, return to sender
if (currency.isNative()) _sweepNativeToken(sender);
} else if (currencyDelta > 0) {
currency.take(poolManager, sender, uint256(int256(currencyDelta)), false);
}
Expand Down Expand Up @@ -189,6 +193,13 @@ contract PositionManager is IPositionManager, ERC721Permit, PoolInitializer, Mul
);
}

/// @dev Send excess native tokens back to the recipient (sender)
/// @param recipient the receiver of the excess native tokens. Should be the caller, the one that sent the native tokens
function _sweepNativeToken(address recipient) internal {
uint256 nativeBalance = address(this).balance;
if (nativeBalance > 0) address(recipient).call{value: nativeBalance}("");
}

// ensures liquidity of the position is empty before burning the token.
function _validateBurn(uint256 tokenId) internal view {
bytes32 positionId = getPositionIdFromTokenId(tokenId);
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IPositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ interface IPositionManager {
/// @param payload is an encoding of actions, and parameters for those actions
/// @param deadline is the deadline for the batched actions to be executed
/// @return returnData is the endocing of each actions return information
function modifyLiquidities(bytes calldata payload, uint256 deadline) external returns (bytes[] memory);
function modifyLiquidities(bytes calldata payload, uint256 deadline) external payable returns (bytes[] memory);

function nextTokenId() external view returns (uint256);
}
71 changes: 71 additions & 0 deletions test/position-managers/Gas.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ contract GasTest is Test, PosmTestSetup, GasSnapshot {
uint256 FEE_WAD;

LiquidityRange range;
LiquidityRange nativeRange;

function setUp() public {
(alice, alicePK) = makeAddrAndKey("ALICE");
Expand All @@ -48,6 +49,7 @@ contract GasTest is Test, PosmTestSetup, GasSnapshot {
deployMintAndApprove2Currencies();

(key, poolId) = initPool(currency0, currency1, IHooks(address(0)), 3000, SQRT_PRICE_1_1, ZERO_BYTES);
(nativeKey,) = initPool(CurrencyLibrary.NATIVE, currency1, IHooks(address(0)), 3000, SQRT_PRICE_1_1, ZERO_BYTES);
FEE_WAD = uint256(key.fee).mulDivDown(FixedPointMathLib.WAD, 1_000_000);

// Requires currency0 and currency1 to be set in base Deployers contract.
Expand All @@ -63,6 +65,7 @@ contract GasTest is Test, PosmTestSetup, GasSnapshot {

// define a reusable range
range = LiquidityRange({poolKey: key, tickLower: -300, tickUpper: 300});
nativeRange = LiquidityRange({poolKey: nativeKey, tickLower: -300, tickUpper: 300});
}

function test_gas_mint() public {
Expand Down Expand Up @@ -317,4 +320,72 @@ contract GasTest is Test, PosmTestSetup, GasSnapshot {

function test_gas_burn() public {}
function test_gas_burnEmpty() public {}

// Native Token Gas Tests
function test_gas_mint_native() public {
uint256 liquidityToAdd = 10_000 ether;
bytes memory calls = getMintEncoded(nativeRange, liquidityToAdd, address(this), ZERO_BYTES);

(uint256 amount0,) = LiquidityAmounts.getAmountsForLiquidity(
SQRT_PRICE_1_1,
TickMath.getSqrtPriceAtTick(nativeRange.tickLower),
TickMath.getSqrtPriceAtTick(nativeRange.tickUpper),
uint128(liquidityToAdd)
);
lpm.modifyLiquidities{value: amount0 + 1}(calls, _deadline);
snapLastCall("PositionManager_mint_native");
}

function test_gas_mint_native_excess() public {
uint256 liquidityToAdd = 10_000 ether;
bytes memory calls = getMintEncoded(nativeRange, liquidityToAdd, address(this), ZERO_BYTES);

(uint256 amount0,) = LiquidityAmounts.getAmountsForLiquidity(
SQRT_PRICE_1_1,
TickMath.getSqrtPriceAtTick(nativeRange.tickLower),
TickMath.getSqrtPriceAtTick(nativeRange.tickUpper),
uint128(liquidityToAdd)
);
// overpay on the native token
lpm.modifyLiquidities{value: amount0 * 2}(calls, _deadline);
snapLastCall("PositionManager_mint_nativeWithSweep");
}

function test_gas_increase_native() public {
uint256 tokenId = lpm.nextTokenId();
mintWithNative(SQRT_PRICE_1_1, nativeRange, 10_000 ether, address(this), ZERO_BYTES);

uint256 liquidityToAdd = 10_000 ether;
bytes memory calls = getIncreaseEncoded(tokenId, liquidityToAdd, ZERO_BYTES);
(uint256 amount0,) = LiquidityAmounts.getAmountsForLiquidity(
SQRT_PRICE_1_1,
TickMath.getSqrtPriceAtTick(nativeRange.tickLower),
TickMath.getSqrtPriceAtTick(nativeRange.tickUpper),
uint128(liquidityToAdd)
);
lpm.modifyLiquidities{value: amount0 + 1}(calls, _deadline);
snapLastCall("PositionManager_increaseLiquidity_native");
}

function test_gas_decrease_native() public {
uint256 tokenId = lpm.nextTokenId();
mintWithNative(SQRT_PRICE_1_1, nativeRange, 10_000 ether, address(this), ZERO_BYTES);

uint256 liquidityToRemove = 10_000 ether;
bytes memory calls = getDecreaseEncoded(tokenId, liquidityToRemove, ZERO_BYTES);
lpm.modifyLiquidities(calls, _deadline);
snapLastCall("PositionManager_decreaseLiquidity_native");
}

function test_gas_collect_native() public {
uint256 tokenId = lpm.nextTokenId();
mintWithNative(SQRT_PRICE_1_1, nativeRange, 10_000 ether, address(this), ZERO_BYTES);

// donate to create fee revenue
donateRouter.donate{value: 0.2e18}(nativeRange.poolKey, 0.2e18, 0.2e18, ZERO_BYTES);

bytes memory calls = getCollectEncoded(tokenId, ZERO_BYTES);
lpm.modifyLiquidities(calls, _deadline);
snapLastCall("PositionManager_collect_native");
}
}
Loading

0 comments on commit a266de8

Please sign in to comment.