118 lines
No EOL
5.4 KiB
Solidity
118 lines
No EOL
5.4 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import {IUniswapV3Pool} from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
|
|
import {TickMath} from "@aperture/uni-v3-lib/TickMath.sol";
|
|
import {LiquidityAmounts} from "@aperture/uni-v3-lib/LiquidityAmounts.sol";
|
|
import {ThreePositionStrategy} from "../../src/abstracts/ThreePositionStrategy.sol";
|
|
|
|
/**
|
|
* @title LiquidityBoundaryHelper
|
|
* @notice Helper library for calculating safe trade sizes within liquidity boundaries
|
|
* @dev Prevents trades that would exceed available liquidity and cause SPL errors
|
|
*/
|
|
library LiquidityBoundaryHelper {
|
|
/**
|
|
* @notice Calculates the maximum ETH amount that can be traded (buy HARB) without exceeding position liquidity limits
|
|
* @param pool The Uniswap V3 pool
|
|
* @param liquidityManager The liquidity manager contract
|
|
* @param token0isWeth Whether token0 is WETH
|
|
* @return maxEthAmount Maximum ETH that can be safely traded
|
|
*/
|
|
function calculateBuyLimit(
|
|
IUniswapV3Pool pool,
|
|
ThreePositionStrategy liquidityManager,
|
|
bool token0isWeth
|
|
) internal view returns (uint256 maxEthAmount) {
|
|
(, int24 currentTick,,,,,) = pool.slot0();
|
|
|
|
// Get position data
|
|
(uint128 anchorLiquidity, int24 anchorLower, int24 anchorUpper) = liquidityManager.positions(ThreePositionStrategy.Stage.ANCHOR);
|
|
(uint128 discoveryLiquidity, int24 discoveryLower, int24 discoveryUpper) = liquidityManager.positions(ThreePositionStrategy.Stage.DISCOVERY);
|
|
|
|
// If no positions exist, return 0 (no safe limit)
|
|
if (anchorLiquidity == 0 && discoveryLiquidity == 0) {
|
|
return 0;
|
|
}
|
|
|
|
uint256 maxEth = 0;
|
|
|
|
// Check anchor position
|
|
if (currentTick >= anchorLower && currentTick < anchorUpper && anchorLiquidity > 0) {
|
|
uint160 currentSqrtPrice = TickMath.getSqrtRatioAtTick(currentTick);
|
|
uint160 upperSqrtPrice = TickMath.getSqrtRatioAtTick(anchorUpper);
|
|
|
|
if (token0isWeth) {
|
|
maxEth = LiquidityAmounts.getAmount0ForLiquidity(currentSqrtPrice, upperSqrtPrice, anchorLiquidity);
|
|
} else {
|
|
maxEth = LiquidityAmounts.getAmount1ForLiquidity(currentSqrtPrice, upperSqrtPrice, anchorLiquidity);
|
|
}
|
|
}
|
|
// Check discovery position
|
|
else if (currentTick >= discoveryLower && currentTick < discoveryUpper && discoveryLiquidity > 0) {
|
|
uint160 currentSqrtPrice = TickMath.getSqrtRatioAtTick(currentTick);
|
|
uint160 upperSqrtPrice = TickMath.getSqrtRatioAtTick(discoveryUpper);
|
|
|
|
if (token0isWeth) {
|
|
maxEth = LiquidityAmounts.getAmount0ForLiquidity(currentSqrtPrice, upperSqrtPrice, discoveryLiquidity);
|
|
} else {
|
|
maxEth = LiquidityAmounts.getAmount1ForLiquidity(currentSqrtPrice, upperSqrtPrice, discoveryLiquidity);
|
|
}
|
|
}
|
|
|
|
// Apply safety margin (90% of calculated max)
|
|
return (maxEth * 9) / 10;
|
|
}
|
|
|
|
/**
|
|
* @notice Calculates the maximum HARB amount that can be traded (sell HARB) without exceeding position liquidity limits
|
|
* @param pool The Uniswap V3 pool
|
|
* @param liquidityManager The liquidity manager contract
|
|
* @param token0isWeth Whether token0 is WETH
|
|
* @return maxHarbAmount Maximum HARB that can be safely traded
|
|
*/
|
|
function calculateSellLimit(
|
|
IUniswapV3Pool pool,
|
|
ThreePositionStrategy liquidityManager,
|
|
bool token0isWeth
|
|
) internal view returns (uint256 maxHarbAmount) {
|
|
(, int24 currentTick,,,,,) = pool.slot0();
|
|
|
|
// Get position data
|
|
(uint128 anchorLiquidity, int24 anchorLower, int24 anchorUpper) = liquidityManager.positions(ThreePositionStrategy.Stage.ANCHOR);
|
|
(uint128 floorLiquidity, int24 floorLower, int24 floorUpper) = liquidityManager.positions(ThreePositionStrategy.Stage.FLOOR);
|
|
|
|
// If no positions exist, return 0 (no safe limit)
|
|
if (anchorLiquidity == 0 && floorLiquidity == 0) {
|
|
return 0;
|
|
}
|
|
|
|
uint256 maxHarb = 0;
|
|
|
|
// Check anchor position
|
|
if (currentTick >= anchorLower && currentTick < anchorUpper && anchorLiquidity > 0) {
|
|
uint160 currentSqrtPrice = TickMath.getSqrtRatioAtTick(currentTick);
|
|
uint160 lowerSqrtPrice = TickMath.getSqrtRatioAtTick(anchorLower);
|
|
|
|
if (token0isWeth) {
|
|
maxHarb = LiquidityAmounts.getAmount1ForLiquidity(lowerSqrtPrice, currentSqrtPrice, anchorLiquidity);
|
|
} else {
|
|
maxHarb = LiquidityAmounts.getAmount0ForLiquidity(lowerSqrtPrice, currentSqrtPrice, anchorLiquidity);
|
|
}
|
|
}
|
|
// Check floor position
|
|
else if (currentTick >= floorLower && currentTick < floorUpper && floorLiquidity > 0) {
|
|
uint160 currentSqrtPrice = TickMath.getSqrtRatioAtTick(currentTick);
|
|
uint160 lowerSqrtPrice = TickMath.getSqrtRatioAtTick(floorLower);
|
|
|
|
if (token0isWeth) {
|
|
maxHarb = LiquidityAmounts.getAmount1ForLiquidity(lowerSqrtPrice, currentSqrtPrice, floorLiquidity);
|
|
} else {
|
|
maxHarb = LiquidityAmounts.getAmount0ForLiquidity(lowerSqrtPrice, currentSqrtPrice, floorLiquidity);
|
|
}
|
|
}
|
|
|
|
// Apply safety margin (90% of calculated max)
|
|
return (maxHarb * 9) / 10;
|
|
}
|
|
} |