harb/onchain/src/libraries/UniswapMath.sol
openhands 85350caf52 feat: OptimizerV3 with direct 2D staking-to-LP parameter mapping
Core protocol changes for launch readiness:

- OptimizerV3: binary bear/bull mapping from (staking%, avgTax) — avoids
  exploitable AW 30-90 kill zone. Bear: AS=30%, AW=100, CI=0, DD=0.3e18.
  Bull: AS=100%, AW=20, CI=0, DD=1e18. UUPS upgradeable with __gap[48].
- Directional VWAP: only records prices on ETH inflow (buys), preventing
  sell-side dilution of price memory
- Floor formula: unified max(scarcity, mirror, clamp) — VWAP mirror uses
  distance from adjusted VWAP as floor distance, no branching
- PriceOracle (M-1 fix): correct fallback TWAP divisor (60000s, not 300s)
- Access control (M-2 fix): deployer-only guard on one-time setters
- Recenter rate limit (M-3 fix): 60-second cooldown for open recenters
- Safe fallback params: recenter() optimizer-failure defaults changed from
  exploitable CI=50%/AW=50 to safe bear-mode CI=0/AW=100
- Recentered event for monitoring and indexing
- VERSION bump to 2, kraiken-lib COMPATIBLE_CONTRACT_VERSIONS updated

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:21:18 +00:00

67 lines
3.2 KiB
Solidity

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;
import { ABDKMath64x64 } from "@abdk/ABDKMath64x64.sol";
import "@aperture/uni-v3-lib/TickMath.sol";
import { Math } from "@openzeppelin/utils/math/Math.sol";
/**
* @title UniswapMath
* @notice Abstract contract providing mathematical utilities for Uniswap V3 price and tick calculations
* @dev Contains pure mathematical functions for price/tick conversions and validations
*/
abstract contract UniswapMath {
using Math for uint256;
/// @notice Calculates the Uniswap V3 tick corresponding to a given price ratio between Kraiken and ETH
/// @param t0isWeth Boolean flag indicating if token0 is WETH
/// @param tokenAmount Amount of the Kraiken token
/// @param ethAmount Amount of Ethereum
/// @return tick_ The calculated tick for the given price ratio
function _tickAtPrice(bool t0isWeth, uint256 tokenAmount, uint256 ethAmount) internal pure returns (int24 tick_) {
require(ethAmount > 0, "ETH amount cannot be zero");
if (tokenAmount == 0) {
// KRAIKEN/ETH
tick_ = TickMath.MAX_TICK;
} else {
// Use ABDKMath64x64 for precise division and square root calculation
int128 priceRatioX64 = ABDKMath64x64.div(int128(int256(tokenAmount)), int128(int256(ethAmount)));
// KRAIKEN/ETH
tick_ = _tickAtPriceRatio(priceRatioX64);
}
// convert to tick in a pool
tick_ = t0isWeth ? tick_ : -tick_;
}
/// @notice Converts a price ratio to a Uniswap V3 tick
/// @param priceRatioX64 The price ratio in ABDKMath64x64 format
/// @return tick_ The corresponding tick
function _tickAtPriceRatio(int128 priceRatioX64) internal pure returns (int24 tick_) {
// Convert the price ratio into a sqrt price in the format expected by Uniswap's TickMath
uint160 sqrtPriceX96 = uint160(int160(ABDKMath64x64.sqrt(priceRatioX64) << 32));
tick_ = TickMath.getTickAtSqrtRatio(sqrtPriceX96);
}
/// @notice Calculates the price ratio from a given Uniswap V3 tick
/// @dev Returns price (token1/token0) in Q96 format: price * 2^96
/// Computed as sqrtRatioX96² / 2^96 = (sqrt(price) * 2^96)² / 2^96 = price * 2^96
/// @param tick The tick for which to calculate the price ratio
/// @return priceRatioX96 The price ratio in Q96 format
function _priceAtTick(int24 tick) internal pure returns (uint256 priceRatioX96) {
uint256 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick);
priceRatioX96 = sqrtRatioX96.mulDiv(sqrtRatioX96, (1 << 96));
}
/// @notice Clamps tick to valid range and aligns to tick spacing
/// @param tick The tick to clamp
/// @param tickSpacing The tick spacing to align to
/// @return clampedTick The clamped and aligned tick
function _clampToTickSpacing(int24 tick, int24 tickSpacing) internal pure returns (int24 clampedTick) {
// Align to tick spacing first
clampedTick = tick / tickSpacing * tickSpacing;
// Ensure tick is within valid bounds
if (clampedTick < TickMath.MIN_TICK) clampedTick = TickMath.MIN_TICK;
if (clampedTick > TickMath.MAX_TICK) clampedTick = TickMath.MAX_TICK;
}
}