package uniswap import ( "math" "math/big" "github.com/holiman/uint256" ) const ( // Q96 represents 2^96 used in Uniswap V3 sqrtPriceX96 calculations Q96 = "79228162514264337593543950336" // 2^96 as string to avoid overflow // Tick spacing for different fee tiers LowTickSpacing = 10 MediumTickSpacing = 60 HighTickSpacing = 200 ) // SqrtPriceX96ToPrice converts sqrtPriceX96 to a price // Price is represented as token1/token0 func SqrtPriceX96ToPrice(sqrtPriceX96 *big.Int) *big.Float { // price = (sqrtPriceX96 / 2^96)^2 // price = sqrtPriceX96^2 / 2^192 // Initialize global cached constants initConstants() // Validate input if sqrtPriceX96 == nil || sqrtPriceX96.Sign() <= 0 { return new(big.Float).SetFloat64(0.0) } // Convert to big.Float for precision sqrtPrice := new(big.Float).SetPrec(256).SetInt(sqrtPriceX96) // Calculate sqrtPrice^2 price := new(big.Float).SetPrec(256) price.Mul(sqrtPrice, sqrtPrice) // Divide by 2^192 using global cached constant denominator := new(big.Float).SetPrec(256).SetInt(GetQ192()) price.Quo(price, denominator) return price } // PriceToSqrtPriceX96 converts a price to sqrtPriceX96 func PriceToSqrtPriceX96(price *big.Float) *big.Int { // sqrtPriceX96 = sqrt(price) * 2^96 // Initialize global cached constants initConstants() // Calculate sqrt(price) input := new(big.Float).SetPrec(256).Copy(price) sqrtPrice := new(big.Float).SetPrec(256).Sqrt(input) // Multiply by 2^96 using global cached constant multiplier := new(big.Float).SetPrec(256).SetInt(GetQ96()) sqrtPrice.Mul(sqrtPrice, multiplier) // Convert to big.Int sqrtPriceX96 := new(big.Int) sqrtPrice.Int(sqrtPriceX96) return sqrtPriceX96 } // TickToSqrtPriceX96 converts a tick to sqrtPriceX96 func TickToSqrtPriceX96(tick int) *big.Int { // sqrtPriceX96 = 1.0001^(tick/2) * 2^96 // Initialize global cached constants initConstants() // Calculate 1.0001^(tick/2) // For better precision, especially for large tick values, we use logarithms // 1.0001^(tick/2) = e^(ln(1.0001) * tick/2) lnBase := GetLnBase() // ln(1.0001) ≈ 9.999500016666e-05 logResult := lnBase * float64(tick) / 2.0 result := math.Exp(logResult) // Convert to big.Float price := new(big.Float).SetFloat64(result) // Multiply by 2^96 using global cached constant price.Mul(price, GetQ96Float()) // Convert to big.Int sqrtPriceX96 := new(big.Int) price.Int(sqrtPriceX96) return sqrtPriceX96 } // SqrtPriceX96ToTick converts sqrtPriceX96 to a tick func SqrtPriceX96ToTick(sqrtPriceX96 *big.Int) int { // tick = log_1.0001(sqrtPriceX96 / 2^96)^2 // tick = log_1.0001(price) // tick = 2 * log_1.0001(sqrtPriceX96 / 2^96) if sqrtPriceX96.Cmp(big.NewInt(0)) <= 0 { return 0 // Invalid input } // Initialize cached constants initConstants() // Convert to big.Float sqrtPrice := new(big.Float).SetInt(sqrtPriceX96) q96Float := GetQ96Float() // Calculate sqrtPriceX96 / 2^96 ratio := new(big.Float).Quo(sqrtPrice, q96Float) // Calculate ln(sqrtPriceX96 / 2^96) to avoid potential overflow // tick = 2 * ln(sqrtPriceX96 / 2^96) / ln(1.0001) lnRatio, _ := ratio.Float64() lnValue := math.Log(lnRatio) // Calculate tick tick := int(2.0 * lnValue * GetInvLnBase()) return tick } // GetTickAtSqrtPrice calculates the tick for a given sqrtPriceX96 using uint256 func GetTickAtSqrtPrice(sqrtPriceX96 *uint256.Int) int { // This is a simplified implementation // In practice, you would use a more precise logarithmic calculation // Convert to big.Int for calculation sqrtPriceBig := sqrtPriceX96.ToBig() return SqrtPriceX96ToTick(sqrtPriceBig) } // GetNextTick calculates the next initialized tick func GetNextTick(currentTick int, tickSpacing int) int { // Round down to nearest tick spacing tick := ((currentTick / tickSpacing) + 1) * tickSpacing return tick } // GetPreviousTick calculates the previous initialized tick func GetPreviousTick(currentTick int, tickSpacing int) int { // Round down to nearest tick spacing tick := (currentTick / tickSpacing) * tickSpacing return tick }