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

position descriptor proxy tests #427

Merged
merged 14 commits into from
Jan 17, 2025
128 changes: 57 additions & 71 deletions test/position-managers/PositionManager.modifyLiquidities.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -982,59 +982,18 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF
}
}

function test_fuzz_mintFromDeltas_burn_maxFot_cannotRedeemBothTokens(
uint256 amount0,
uint256 amount1,
int24 tickLower,
int24 tickUpper
) public {
MockFOT(address(fotToken)).setFee(10_000);
tickLower = int24(
bound(
tickLower,
fotKey.tickSpacing * (TickMath.MIN_TICK / fotKey.tickSpacing),
fotKey.tickSpacing * (TickMath.MAX_TICK / fotKey.tickSpacing)
)
);
tickUpper = int24(
bound(
tickUpper,
fotKey.tickSpacing * (TickMath.MIN_TICK / fotKey.tickSpacing),
fotKey.tickSpacing * (TickMath.MAX_TICK / fotKey.tickSpacing)
)
);

tickLower = fotKey.tickSpacing * (tickLower / fotKey.tickSpacing);
tickUpper = fotKey.tickSpacing * (tickUpper / fotKey.tickSpacing);
vm.assume(tickUpper > tickLower);

function test_mintFromDeltas_burn_maxFot_cannotRedeemBothTokens() public {
int24 tickUpper = 60;
(uint160 sqrtPriceX96,,,) = manager.getSlot0(fotKey.toId());
{
uint128 maxLiquidityPerTick = Pool.tickSpacingToMaxLiquidityPerTick(fotKey.tickSpacing);
(uint256 maxAmount0, uint256 maxAmount1) = LiquidityAmounts.getAmountsForLiquidity(
sqrtPriceX96,
TickMath.getSqrtPriceAtTick(tickLower),
TickMath.getSqrtPriceAtTick(tickUpper),
maxLiquidityPerTick
);

maxAmount0 = maxAmount0 == 0 ? 1 : maxAmount0 > STARTING_USER_BALANCE ? STARTING_USER_BALANCE : maxAmount0;
maxAmount1 = maxAmount1 == 0 ? 1 : maxAmount1 > STARTING_USER_BALANCE ? STARTING_USER_BALANCE : maxAmount1;
amount0 = bound(amount0, 1, maxAmount0);
amount1 = bound(amount1, 1, maxAmount1);
}
// current tick is 0, tickLower is -60
int24 tickLower = TickMath.getTickAtSqrtPrice(sqrtPriceX96) - fotKey.tickSpacing;

uint256 tokenId = lpm.nextTokenId();

BalanceDiff memory balance0 = BalanceDiff(fotKey.currency0.balanceOf(address(this)), 0);
BalanceDiff memory balance1 = BalanceDiff(fotKey.currency1.balanceOf(address(this)), 0);
BalanceDiff memory balance0PM = BalanceDiff(fotKey.currency0.balanceOf(address(manager)), 0);
BalanceDiff memory balance1PM = BalanceDiff(fotKey.currency1.balanceOf(address(manager)), 0);

Plan memory planner = Planner.init();

planner.add(Actions.SETTLE, abi.encode(fotKey.currency0, amount0, true));
planner.add(Actions.SETTLE, abi.encode(fotKey.currency1, amount1, true));
planner.add(Actions.SETTLE, abi.encode(fotKey.currency0, 1 ether, true));
planner.add(Actions.SETTLE, abi.encode(fotKey.currency1, 1 ether, true));
planner.add(
Actions.MINT_POSITION_FROM_DELTAS,
abi.encode(
Expand All @@ -1050,19 +1009,51 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF
// take the excess of each currency
planner.add(Actions.TAKE_PAIR, abi.encode(fotKey.currency0, fotKey.currency1, ActionConstants.MSG_SENDER));

// needed to remove variables because of stack too deep
// Read below as:
// bool currency0IsFOT = fotKey.currency0 == Currency.wrap(address(fotToken));
// bool positionIsEntirelyInOtherToken = currency0IsFOT
// ? TickMath.getSqrtPriceAtTick(tickUpper) <= sqrtPriceX96
// : TickMath.getSqrtPriceAtTick(tickLower) >= sqrtPriceX96;
// if (positionIsEntirelyInOtherToken) {
if (
fotKey.currency0 == Currency.wrap(address(fotToken))
? TickMath.getSqrtPriceAtTick(tickUpper) <= sqrtPriceX96
: TickMath.getSqrtPriceAtTick(tickLower) >= sqrtPriceX96
) {
// alice adds the liquidity in range [-60, 60]
vm.prank(alice);
seedToken(fotToken, alice);
IERC20(Currency.unwrap(fotKey.currency1)).approve(address(permit2), type(uint256).max);
permit2.approve(Currency.unwrap(fotKey.currency1), alice, type(uint160).max, type(uint48).max);
lpm.modifyLiquidities(planner.encode(), _deadline);
vm.stopPrank();

// set tick lower to current tick, making the new position that will be added entirely in other token
// tickLower = 0 = current tick
tickLower = TickMath.getTickAtSqrtPrice(sqrtPriceX96);

BalanceDiff memory balance0 = BalanceDiff(fotKey.currency0.balanceOf(address(this)), 0);
BalanceDiff memory balance1 = BalanceDiff(fotKey.currency1.balanceOf(address(this)), 0);
BalanceDiff memory balance0PM = BalanceDiff(fotKey.currency0.balanceOf(address(manager)), 0);
BalanceDiff memory balance1PM = BalanceDiff(fotKey.currency1.balanceOf(address(manager)), 0);

// set fee of fotToken to 100%
MockFOT(address(fotToken)).setFee(10_000);

// assume currency1 is fotToken
if (!(fotKey.currency0 == Currency.wrap(address(fotToken)))) {
// MINT FROM DELTAS.
tokenId = lpm.nextTokenId();

planner = Planner.init();

planner.add(Actions.SETTLE, abi.encode(fotKey.currency0, 1 ether, true));
planner.add(Actions.SETTLE, abi.encode(fotKey.currency1, 1 ether, true));
planner.add(
Actions.MINT_POSITION_FROM_DELTAS,
abi.encode(
fotKey,
tickLower,
tickUpper,
MAX_SLIPPAGE_INCREASE,
MAX_SLIPPAGE_INCREASE,
ActionConstants.MSG_SENDER,
ZERO_BYTES
)
);
// take the excess of each currency
planner.add(Actions.TAKE_PAIR, abi.encode(fotKey.currency0, fotKey.currency1, ActionConstants.MSG_SENDER));

// add liquidity
lpm.modifyLiquidities(planner.encode(), _deadline);

balance0._after = fotKey.currency0.balanceOf(address(this));
Expand All @@ -1073,13 +1064,13 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF
// Calculate the expected resulting balances used to create liquidity after the fee is applied.
Balance memory expected;
{
bool currency0IsFOT = fotKey.currency0 == Currency.wrap(address(fotToken));
uint256 expectedFee = (currency0IsFOT ? amount0 : amount1).calculatePortion(10_000);
(expected._0, expected._1) = currency0IsFOT
? (balance0._before - balance0._after - expectedFee, balance1._before - balance1._after)
: (balance0._before - balance0._after, balance1._before - balance1._after - expectedFee);
// expected fee is the same amount that was sent, since fee is now 100%
uint256 expectedFee = 1 ether;
(expected._0, expected._1) =
(balance0._before - balance0._after, balance1._before - balance1._after - expectedFee);
}
assertEq(expected._0, balance0PM._after - balance0PM._before);
// expected._1 is 0 because no fotToken was sent to PM because of 100% fee
assertEq(expected._1, balance1PM._after - balance1PM._before);
{
// the liquidity that was created is a diff of the balance change
Expand All @@ -1097,12 +1088,7 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF
// BURN.
planner = Planner.init();
// Note that the slippage does not include the fee from the transfer.
planner.add(
Actions.BURN_POSITION,
abi.encode(
tokenId, expected._0 == 0 ? 0 : expected._0 - 1, expected._1 == 0 ? 0 : expected._1 - 1, ZERO_BYTES
)
);
planner.add(Actions.BURN_POSITION, abi.encode(tokenId, expected._0 - 1, 0, ZERO_BYTES));

planner.add(Actions.TAKE_PAIR, abi.encode(fotKey.currency0, fotKey.currency1, ActionConstants.MSG_SENDER));

Expand All @@ -1113,7 +1099,7 @@ contract PositionManagerModifyLiquiditiesTest is Test, PosmTestSetup, LiquidityF

// After redeeming the position, the liquidity provider should not have received any of the fot token since the position was entirely in the other token.
assertEq(fotKey.currency1.balanceOf(address(this)), balance1._after);
assertGe(fotKey.currency0.balanceOf(address(this)), balance0._after);
assertGt(fotKey.currency0.balanceOf(address(this)), balance0._after);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this assumes that the fot token is currency1?


assertEq(lpm.getPositionLiquidity(tokenId), 0);
}
Expand Down
Loading