mirror of
https://github.com/lux-amm/v4-periphery.git
synced 2026-10-30 20:54:39 +00:00
210 lines
7.7 KiB
Solidity
210 lines
7.7 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.24;
|
|
|
|
import {CommonBase} from "forge-std/Base.sol";
|
|
import {Currency} from "@luxamm/v4-core/src/types/Currency.sol";
|
|
import {BalanceDelta, toBalanceDelta} from "@luxamm/v4-core/src/types/BalanceDelta.sol";
|
|
import {PoolKey} from "@luxamm/v4-core/src/types/PoolKey.sol";
|
|
import {TickMath} from "@luxamm/v4-core/src/libraries/TickMath.sol";
|
|
import {LiquidityAmounts} from "@luxamm/v4-core/test/utils/LiquidityAmounts.sol";
|
|
import {SafeCast} from "@luxamm/v4-core/src/libraries/SafeCast.sol";
|
|
|
|
import {IPositionManager} from "../../src/interfaces/IPositionManager.sol";
|
|
import {Actions} from "../../src/libraries/Actions.sol";
|
|
import {PositionConfig} from "./PositionConfig.sol";
|
|
import {Planner, Plan} from "../shared/Planner.sol";
|
|
import {HookSavesDelta} from "./HookSavesDelta.sol";
|
|
|
|
abstract contract LiquidityOperations is CommonBase {
|
|
using SafeCast for *;
|
|
|
|
IPositionManager lpm;
|
|
|
|
uint256 _deadline = block.timestamp + 1;
|
|
|
|
uint128 constant MAX_SLIPPAGE_INCREASE = type(uint128).max;
|
|
uint128 constant MIN_SLIPPAGE_DECREASE = 0 wei;
|
|
|
|
function mint(PositionConfig memory config, uint256 liquidity, address recipient, bytes memory hookData) internal {
|
|
bytes memory calls = getMintEncoded(config, liquidity, recipient, hookData);
|
|
lpm.modifyLiquidities(calls, _deadline);
|
|
}
|
|
|
|
function mintWithNative(
|
|
uint160 sqrtPriceX96,
|
|
PositionConfig memory config,
|
|
uint256 liquidity,
|
|
address recipient,
|
|
bytes memory hookData
|
|
) internal {
|
|
// determine the amount of ETH to send on-mint
|
|
(uint256 amount0,) = LiquidityAmounts.getAmountsForLiquidity(
|
|
sqrtPriceX96,
|
|
TickMath.getSqrtPriceAtTick(config.tickLower),
|
|
TickMath.getSqrtPriceAtTick(config.tickUpper),
|
|
liquidity.toUint128()
|
|
);
|
|
bytes memory calls = getMintEncoded(config, liquidity, recipient, hookData);
|
|
// add extra wei because modifyLiquidities may be rounding up, LiquidityAmounts is imprecise?
|
|
lpm.modifyLiquidities{value: amount0 + 1}(calls, _deadline);
|
|
}
|
|
|
|
function increaseLiquidity(
|
|
uint256 tokenId,
|
|
PositionConfig memory config,
|
|
uint256 liquidityToAdd,
|
|
bytes memory hookData
|
|
) internal {
|
|
bytes memory calls = getIncreaseEncoded(tokenId, config, liquidityToAdd, hookData);
|
|
lpm.modifyLiquidities(calls, _deadline);
|
|
}
|
|
|
|
// do not make external call before unlockAndExecute, allows us to test reverts
|
|
function decreaseLiquidity(
|
|
uint256 tokenId,
|
|
PositionConfig memory config,
|
|
uint256 liquidityToRemove,
|
|
bytes memory hookData
|
|
) internal {
|
|
bytes memory calls = getDecreaseEncoded(tokenId, config, liquidityToRemove, hookData);
|
|
lpm.modifyLiquidities(calls, _deadline);
|
|
}
|
|
|
|
function collect(uint256 tokenId, PositionConfig memory config, bytes memory hookData) internal {
|
|
bytes memory calls = getCollectEncoded(tokenId, config, hookData);
|
|
lpm.modifyLiquidities(calls, _deadline);
|
|
}
|
|
|
|
// This is encoded with close calls. Not all burns need to be encoded with closes if there is no liquidity in the position.
|
|
function burn(uint256 tokenId, PositionConfig memory config, bytes memory hookData) internal {
|
|
bytes memory calls = getBurnEncoded(tokenId, config, hookData);
|
|
lpm.modifyLiquidities(calls, _deadline);
|
|
}
|
|
|
|
// Helper functions for getting encoded calldata for .modifyLiquidities() or .modifyLiquiditiesWithoutUnlock()
|
|
function getMintEncoded(PositionConfig memory config, uint256 liquidity, address recipient, bytes memory hookData)
|
|
internal
|
|
pure
|
|
returns (bytes memory)
|
|
{
|
|
return getMintEncoded(config, liquidity, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, recipient, hookData);
|
|
}
|
|
|
|
function getMintEncoded(
|
|
PositionConfig memory config,
|
|
uint256 liquidity,
|
|
uint128 amount0Max,
|
|
uint128 amount1Max,
|
|
address recipient,
|
|
bytes memory hookData
|
|
) internal pure returns (bytes memory) {
|
|
Plan memory planner = Planner.init();
|
|
planner.add(
|
|
Actions.MINT_POSITION,
|
|
abi.encode(
|
|
config.poolKey,
|
|
config.tickLower,
|
|
config.tickUpper,
|
|
liquidity,
|
|
amount0Max,
|
|
amount1Max,
|
|
recipient,
|
|
hookData
|
|
)
|
|
);
|
|
|
|
return planner.finalizeModifyLiquidityWithClose(config.poolKey);
|
|
}
|
|
|
|
function getIncreaseEncoded(
|
|
uint256 tokenId,
|
|
PositionConfig memory config,
|
|
uint256 liquidityToAdd,
|
|
bytes memory hookData
|
|
) internal pure returns (bytes memory) {
|
|
// max slippage
|
|
return
|
|
getIncreaseEncoded(tokenId, config, liquidityToAdd, MAX_SLIPPAGE_INCREASE, MAX_SLIPPAGE_INCREASE, hookData);
|
|
}
|
|
|
|
function getIncreaseEncoded(
|
|
uint256 tokenId,
|
|
PositionConfig memory config,
|
|
uint256 liquidityToAdd,
|
|
uint128 amount0Max,
|
|
uint128 amount1Max,
|
|
bytes memory hookData
|
|
) internal pure returns (bytes memory) {
|
|
Plan memory planner = Planner.init();
|
|
planner.add(Actions.INCREASE_LIQUIDITY, abi.encode(tokenId, liquidityToAdd, amount0Max, amount1Max, hookData));
|
|
return planner.finalizeModifyLiquidityWithClose(config.poolKey);
|
|
}
|
|
|
|
function getDecreaseEncoded(
|
|
uint256 tokenId,
|
|
PositionConfig memory config,
|
|
uint256 liquidityToRemove,
|
|
bytes memory hookData
|
|
) internal pure returns (bytes memory) {
|
|
return getDecreaseEncoded(
|
|
tokenId, config, liquidityToRemove, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, hookData
|
|
);
|
|
}
|
|
|
|
function getDecreaseEncoded(
|
|
uint256 tokenId,
|
|
PositionConfig memory config,
|
|
uint256 liquidityToRemove,
|
|
uint128 amount0Min,
|
|
uint128 amount1Min,
|
|
bytes memory hookData
|
|
) internal pure returns (bytes memory) {
|
|
Plan memory planner = Planner.init();
|
|
planner.add(
|
|
Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, liquidityToRemove, amount0Min, amount1Min, hookData)
|
|
);
|
|
return planner.finalizeModifyLiquidityWithClose(config.poolKey);
|
|
}
|
|
|
|
function getCollectEncoded(uint256 tokenId, PositionConfig memory config, bytes memory hookData)
|
|
internal
|
|
pure
|
|
returns (bytes memory)
|
|
{
|
|
return getCollectEncoded(tokenId, config, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, hookData);
|
|
}
|
|
|
|
function getCollectEncoded(
|
|
uint256 tokenId,
|
|
PositionConfig memory config,
|
|
uint128 amount0Min,
|
|
uint128 amount1Min,
|
|
bytes memory hookData
|
|
) internal pure returns (bytes memory) {
|
|
Plan memory planner = Planner.init();
|
|
planner.add(Actions.DECREASE_LIQUIDITY, abi.encode(tokenId, 0, amount0Min, amount1Min, hookData));
|
|
return planner.finalizeModifyLiquidityWithClose(config.poolKey);
|
|
}
|
|
|
|
function getBurnEncoded(uint256 tokenId, PositionConfig memory config, bytes memory hookData)
|
|
internal
|
|
pure
|
|
returns (bytes memory)
|
|
{
|
|
return getBurnEncoded(tokenId, config, MIN_SLIPPAGE_DECREASE, MIN_SLIPPAGE_DECREASE, hookData);
|
|
}
|
|
|
|
function getBurnEncoded(
|
|
uint256 tokenId,
|
|
PositionConfig memory config,
|
|
uint128 amount0Min,
|
|
uint128 amount1Min,
|
|
bytes memory hookData
|
|
) internal pure returns (bytes memory) {
|
|
Plan memory planner = Planner.init();
|
|
planner.add(Actions.BURN_POSITION, abi.encode(tokenId, amount0Min, amount1Min, hookData));
|
|
// Close needed on burn in case there is liquidity left in the position.
|
|
return planner.finalizeModifyLiquidityWithClose(config.poolKey);
|
|
}
|
|
}
|