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 // Convert to big.Float for precision sqrtPrice := new(big.Float).SetInt(sqrtPriceX96) // Calculate sqrtPrice^2 price := new(big.Float).Mul(sqrtPrice, sqrtPrice) // Divide by 2^192 (which is (2^96)^2) q192 := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(2), big.NewInt(192), nil)) price.Quo(price, q192) return price } // PriceToSqrtPriceX96 converts a price to sqrtPriceX96 func PriceToSqrtPriceX96(price *big.Float) *big.Int { // sqrtPriceX96 = sqrt(price) * 2^96 // Calculate sqrt(price) sqrtPrice := new(big.Float).Sqrt(price) // Multiply by 2^96 q96 := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(2), big.NewInt(96), nil)) sqrtPrice.Mul(sqrtPrice, q96) // 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 // Calculate 1.0001^(tick/2) base := 1.0001 power := float64(tick) / 2.0 result := math.Pow(base, power) // Convert to big.Float price := new(big.Float).SetFloat64(result) // Multiply by 2^96 q96Int := new(big.Int) q96Int.SetString(Q96, 10) q96 := new(big.Float).SetInt(q96Int) price.Mul(price, q96) // 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 } // Convert to big.Float sqrtPrice := new(big.Float).SetInt(sqrtPriceX96) q96Int := new(big.Int) q96Int.SetString(Q96, 10) q96 := new(big.Float).SetInt(q96Int) // Calculate sqrtPriceX96 / 2^96 ratio := new(big.Float).Quo(sqrtPrice, q96) // Calculate (sqrtPriceX96 / 2^96)^2 to get price price := new(big.Float).Mul(ratio, ratio) // Calculate log_1.0001(price) // log_1.0001(x) = ln(x) / ln(1.0001) priceFloat, _ := price.Float64() lnPrice := math.Log(priceFloat) lnBase := math.Log(1.0001) logRatio := lnPrice / lnBase // Convert to int tick := int(logRatio) 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 }