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>
67 lines
3.2 KiB
Solidity
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;
|
|
}
|
|
}
|