Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

v1 source pure

Package gnsmath provides core mathematical operations for GnoSwap's concentrated liquidity AMM.

Readme View source

gnsmath

Core mathematical operations for GnoSwap's concentrated liquidity AMM.

Overview

This package provides the fundamental calculations for concentrated liquidity, including tick conversion, liquidity math calculations, sqrt price math, swap calculations, and bit manipulation utilities. Operations use Q64.96, Q128.128, and Q160 fixed-point representations where appropriate.

The implementation follows Uniswap V3's mathematical model, ensuring compatibility and correctness for cross-chain liquidity operations.

Features

  • Bit Math: MSB/LSB calculations for tick bitmap operations
  • Tick Math: Tick and Q64.96 sqrt-price conversions
  • Liquidity Math: Liquidity and token amount conversions for price ranges
  • Sqrt Price Math: Token amount conversions using Q64.96 format
  • Swap Math: Single-step swap calculations with fee handling
  • Overflow Protection: Built-in int256 overflow detection
  • Rounding Control: Configurable rounding for AMM safety

Core Concepts

Q96 Fixed-Point Format

Square root prices use Q64.96 representation:

  • sqrtPriceX96 = sqrt(token1/token0) * 2^96
  • Enables precise integer arithmetic without floating-point

Rounding Directions

  • Round UP: Amounts owed TO pool (deposits, exact input)
  • Round DOWN: Amounts owed FROM pool (withdrawals, exact output)

Usage

 1package main
 2
 3import (
 4    "gno.land/p/gnoswap/gnsmath/v1"
 5    i256 "gno.land/p/gnoswap/int256/v1"
 6    u256 "gno.land/p/gnoswap/uint256/v1"
 7)
 8
 9func main() {
10    // Calculate token amounts for a signed liquidity change.
11    sqrtPriceA := u256.MustFromDecimal("79228162514264337593543950336")
12    sqrtPriceB := u256.MustFromDecimal("79625275426524748796330556128")
13    liquidityDelta := i256.MustFromDecimal("1000000000000000000")
14    amount0 := gnsmath.GetAmount0Delta(sqrtPriceA, sqrtPriceB, liquidityDelta)
15    amount1 := gnsmath.GetAmount1Delta(sqrtPriceA, sqrtPriceB, liquidityDelta)
16    println(amount0.ToString(), amount1.ToString())
17
18    // Q64.96 prices and a positive amount remaining select exact input.
19    feePips := uint64(3000) // 0.3%
20    currentPrice := u256.MustFromDecimal("79228162514264337593543950336")
21    targetPrice := u256.MustFromDecimal("158456325028528675187087900672")
22    liquidity := u256.MustFromDecimal("1000000000000000000")
23    amountRemaining := i256.MustFromDecimal("1000000")
24    sqrtPriceNext, amountIn, amountOut, feeAmount := gnsmath.SwapMathComputeSwapStep(
25        currentPrice, targetPrice, liquidity, amountRemaining, feePips,
26    )
27    println(sqrtPriceNext.ToString(), amountIn.ToString(), amountOut.ToString(), feeAmount.ToString())
28
29    tickBitmap := u256.NewUint(0xFF00)
30    println(gnsmath.BitMathMostSignificantBit(tickBitmap)) // 15
31    println(gnsmath.BitMathLeastSignificantBit(tickBitmap)) // 8
32}

API

Bit Math

  • BitMathMostSignificantBit(x *u256.Uint) uint8 - Find MSB position (0-255)
  • BitMathLeastSignificantBit(x *u256.Uint) uint8 - Find LSB position (0-255)

Tick Math

  • TickMathGetSqrtRatioAtTick(tick int32) *u256.Uint - Convert tick to Q64.96 sqrt price
  • TickMathGetTickAtSqrtRatio(sqrtPriceX96 *u256.Uint) int32 - Convert a Q64.96 sqrt price to tick; accepts [MinSqrtRatio, MaxSqrtRatio)

Liquidity Math

  • GetLiquidityForAmounts(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, amount0, amount1 *u256.Uint) *u256.Uint
    • Calculate max liquidity from token amounts and price range
  • GetAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, liquidity *u256.Uint) (*u256.Uint, *u256.Uint)
    • Calculate token amounts represented by liquidity and price range
  • LiquidityMathAddDelta(x *u256.Uint, y *i256.Int) *u256.Uint
    • Apply signed liquidity delta; the result is bounded by MaxUint128 and panics if it exceeds that bound

Sqrt Price Math

  • GetAmount0Delta(sqrtRatioAX96, sqrtRatioBX96 *u256.Uint, liquidity *i256.Int) *i256.Int
    • Calculate token0 amount as liquidity * (1/√Pa - 1/√Pb) after ordering the ratios; Q64.96 scaling and rounding are applied
  • GetAmount1Delta(sqrtRatioAX96, sqrtRatioBX96 *u256.Uint, liquidity *i256.Int) *i256.Int
    • Calculate token1 amount as liquidity * (√Pb - √Pa) / 2^96 after ordering the ratios; rounding depends on the sign of liquidity

Swap Math

  • SwapMathComputeSwapStep(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity *u256.Uint, amountRemaining *i256.Int, feePips uint64) (*u256.Uint, *u256.Uint, *u256.Uint, *u256.Uint)
    • Returns: (nextSqrtPrice, amountIn, amountOut, feeAmount)
    • Handles both exact input and exact output swaps

Overview

Package gnsmath provides core mathematical operations for GnoSwap's concentrated liquidity AMM.

## Overview

This package provides the fundamental calculations for concentrated liquidity, including tick conversion, liquidity math calculations, sqrt price math, swap calculations, and bit manipulation utilities. All operations use Q64.96, Q128.128, and Q160 fixed-point arithmetic where appropriate.

The implementation follows Uniswap V3's mathematical model.

## Features

  • Bit Math: MSB/LSB calculations for tick bitmap operations
  • Tick Math: tick and Q64.96 sqrt-price conversions
  • Liquidity Math: liquidity and token amount math for price ranges
  • Sqrt Price Math: token amount conversions using Q64.96 format
  • Swap Math: single-step swap calculations with fee handling
  • Overflow Protection: explicit int256/uint256 overflow checks
  • Rounding Control: configurable rounding for AMM safety

## Core Concepts

### Q64.96 Fixed-Point Format

Square root prices use Q64.96 representation:

  • sqrtPriceX96 = sqrt(token1/token0) * 2^96
  • Enables precise integer arithmetic without floating-point

### Rounding Directions

  • Round UP: amounts owed TO pool (deposits, exact input)
  • Round DOWN: amounts owed FROM pool (withdrawals, exact output)

## API

### Bit Math

  • BitMathMostSignificantBit(x *u256.Uint) uint8
  • BitMathLeastSignificantBit(x *u256.Uint) uint8

### Tick Math

  • TickMathGetSqrtRatioAtTick(tick int32) *u256.Uint
  • TickMathGetTickAtSqrtRatio(sqrtPriceX96 *u256.Uint) int32; accepts `[MinSqrtRatio, MaxSqrtRatio)` (the upper bound is exclusive)

### Liquidity Math

  • GetLiquidityForAmounts(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, amount0, amount1 *u256.Uint) *u256.Uint
  • GetAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, liquidity *u256.Uint) (*u256.Uint, *u256.Uint)
  • LiquidityMathAddDelta(x *u256.Uint, y *i256.Int) *u256.Uint

### Sqrt Price Math

  • GetAmount0Delta(sqrtRatioAX96, sqrtRatioBX96 *u256.Uint, liquidity *i256.Int) *i256.Int
  • GetAmount1Delta(sqrtRatioAX96, sqrtRatioBX96 *u256.Uint, liquidity *i256.Int) *i256.Int

### Swap Math

  • SwapMathComputeSwapStep(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity *u256.Uint, amountRemaining *i256.Int, feePips uint64) (*u256.Uint, *u256.Uint, *u256.Uint, *u256.Uint)

Constants 1

const minTick, maxTick, Q96_RESOLUTION, Q160_RESOLUTION, MAX_UINT128, Q96

 1const (
 2	// minTick is the minimum valid tick index in the concentrated liquidity model.
 3	// Represents the lowest possible price: 1.0001^(-887272) ≈ 0
 4	minTick = -887272
 5
 6	// maxTick is the maximum valid tick index in the concentrated liquidity model.
 7	// Represents the highest possible price: 1.0001^887272 ≈ infinity
 8	maxTick = 887272
 9
10	Q96_RESOLUTION  uint = 96
11	Q160_RESOLUTION uint = 160
12
13	MAX_UINT128 = "340282366920938463463374607431768211455" // 2^128 - 1
14	Q96         = "79228162514264337593543950336"           // 2^96
15)
source

Functions 24

func BitMathLeastSignificantBit

1func BitMathLeastSignificantBit(x *u256.Uint) uint8
source

BitMathLeastSignificantBit returns the 0-based position of the least significant bit in x. This function is used in AMM calculations for efficient bit manipulation and range queries.

Parameters:

  • x: the non-zero value for which to compute the least significant set bit

Returns:

  • bitIndex: the zero-based position of the least significant set bit (0-255)

Panics if x is zero.

func BitMathMostSignificantBit

1func BitMathMostSignificantBit(x *u256.Uint) uint8
source

BitMathMostSignificantBit returns the 0-based position of the most significant bit in x. This function is essential for AMM calculations involving price ranges and tick boundaries.

Parameters:

  • x: the non-zero value for which to compute the most significant set bit

Returns:

  • bitIndex: the zero-based position of the most significant set bit (0-255)

Panics if x is zero.

func GetAmount0Delta

1func GetAmount0Delta(
2	sqrtRatioAX96, sqrtRatioBX96 *u256.Uint,
3	liquidity *i256.Int,
4) *i256.Int
source

GetAmount0Delta computes the signed token0 amount represented between two prices. Positive liquidity rounds the amount up; negative liquidity returns a negative amount rounded down after applying the magnitude.

Parameters:

  • sqrtRatioAX96: First price endpoint in Q64.96 square-root format.
  • sqrtRatioBX96: Second price endpoint in Q64.96 square-root format.
  • liquidity: Signed liquidity value; its sign determines the result sign and rounding.

Returns:

  • amount0Delta: Signed int256 token0 amount represented by the range.

Panics if an input is nil or the computed magnitude cannot be represented by int256.

func GetAmount1Delta

1func GetAmount1Delta(
2	sqrtRatioAX96, sqrtRatioBX96 *u256.Uint,
3	liquidity *i256.Int,
4) *i256.Int
source

GetAmount1Delta computes the signed token1 amount represented between two prices. Positive liquidity rounds the amount up; negative liquidity returns a negative amount rounded down after applying the magnitude.

Parameters:

  • sqrtRatioAX96: First price endpoint in Q64.96 square-root format.
  • sqrtRatioBX96: Second price endpoint in Q64.96 square-root format.
  • liquidity: Signed liquidity value; its sign determines the result sign and rounding.

Returns:

  • amount1Delta: Signed int256 token1 amount represented by the range.

Panics if an input is nil or the computed magnitude cannot be represented by int256.

func GetAmountsForLiquidity

1func GetAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, liquidity *u256.Uint) (*u256.Uint, *u256.Uint)
source

GetAmountsForLiquidity calculates the amounts of token0 and token1 represented by a specified liquidity within a price range.

If the current price is below the lower bound, only token0 is required. If the current price is above the upper bound, only token1 is required. When the price is within the range, both token0 and token1 are calculated.

Parameters:

  • sqrtRatioX96: Current square-root price in Q64.96 format.
  • sqrtRatioAX96: First price-range endpoint in Q64.96 format.
  • sqrtRatioBX96: Second price-range endpoint in Q64.96 format; the endpoints are ordered internally.
  • liquidity: Non-negative liquidity amount to value.

Returns:

  • amount0: Token0 amount represented by liquidity; zero when the current price is at or above the upper endpoint.
  • amount1: Token1 amount represented by liquidity; zero when the current price is at or below the lower endpoint. Call ToString() on either value for decimal display.

Notes:

  • If liquidity is zero, the function returns zero values for both tokens.
  • At a boundary, the corresponding out-of-range token amount is zero.

Example: ``` amount0, amount1 := GetAmountsForLiquidity(

Example
1u256.MustFromDecimal("79228162514264337593543950336"),  // sqrtRatioX96 (1.0 in Q64.96)
2u256.MustFromDecimal("39614081257132168796771975168"),  // sqrtRatioAX96 (0.5 in Q64.96)
3u256.MustFromDecimal("158456325028528675187087900672"), // sqrtRatioBX96 (2.0 in Q64.96)
4u256.MustFromDecimal("1000000"),                        // Liquidity

)

println("Token0:", amount0.ToString(), "Token1:", amount1.ToString())

// Output: Token0: 500000, Token1: 500000 ```

func GetLiquidityForAmounts

1func GetLiquidityForAmounts(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, amount0, amount1 *u256.Uint) (liquidity *u256.Uint)
source

GetLiquidityForAmounts calculates the maximum liquidity supported by two token amounts.

The current square-root price determines which token amounts are active. Below the range only token0 is used, above it only token1 is used, and inside it the smaller of the token0- and token1-derived liquidities is returned.

Parameters:

  • sqrtRatioX96: Current square-root price, encoded as a Q64.96 ratio.
  • sqrtRatioAX96: First price-range endpoint, encoded as a Q64.96 ratio.
  • sqrtRatioBX96: Second price-range endpoint, encoded as a Q64.96 ratio; the endpoints are sorted.
  • amount0: Available token0 amount.
  • amount1: Available token1 amount.

Returns:

  • liquidity: Maximum liquidity supported by the amounts, constrained to uint128.

Panics if a selected amount/range calculation has identical bounds or exceeds uint128.

func LiquidityMathAddDelta

1func LiquidityMathAddDelta(x *u256.Uint, y *i256.Int) *u256.Uint
source

LiquidityMathAddDelta calculates the new liquidity after applying a signed delta. A negative delta subtracts its magnitude; a non-negative delta adds its magnitude.

Parameters:

  • x: Current non-negative liquidity value.
  • y: Signed liquidity delta; positive values add and negative values subtract.

Returns:

  • liquidity: Updated liquidity, constrained to the uint128 range.

Panics if x or y is nil, subtraction underflows, addition overflows uint256, or the resulting liquidity exceeds MaxUint128.

func MAX_SQRT_RATIO

1func MAX_SQRT_RATIO() *u256.Uint
source

MAX_SQRT_RATIO returns the upper boundary used by Q64.96 square-root price math.

Returns:

  • maxSqrtRatio: A fresh *u256.Uint containing 1461446703485210103287273052203988822378723970342. Inverse tick conversion treats this boundary as exclusive.

func MIN_SQRT_RATIO

1func MIN_SQRT_RATIO() *u256.Uint
source

MIN_SQRT_RATIO returns the minimum valid Q64.96 square-root price ratio.

Returns:

  • minSqrtRatio: A fresh *u256.Uint containing 4,295,128,739, the lower boundary accepted by the pool's square-root price math.

func MaxInt128

1func MaxInt128() *i256.Int
source

MaxInt128 returns the largest positive value representable by a signed 128-bit integer.

Returns:

  • maxInt128: A fresh *i256.Int containing 2^127 - 1, used as the upper bound for conversions that must fit in the signed int128 range.

func SafeAbsInt64

1func SafeAbsInt64(a int64) int64
source

SafeAbsInt64 returns the non-negative absolute value of a.

Parameters:

  • a: Signed int64 value whose magnitude is requested.

Returns:

  • magnitude: |a| as int64.

Panics when a is math.MinInt64 because its positive magnitude cannot be represented by int64.

func SafeAddInt64

1func SafeAddInt64(a, b int64) int64
source

SafeAddInt64 returns the exact sum of two signed 64-bit integers.

Parameters:

  • a: First signed int64 operand.
  • b: Second signed int64 operand.

Returns:

  • sum: a + b when the mathematical result is within [math.MinInt64, math.MaxInt64].

Panics if the signed int64 sum overflows or underflows.

func SafeAddUint64

1func SafeAddUint64(a, b uint64) uint64
source

SafeAddUint64 returns the exact sum of two unsigned 64-bit integers.

Parameters:

  • a: First uint64 operand.
  • b: Second uint64 operand.

Returns:

  • sum: a + b when the mathematical result is at most math.MaxUint64.

Panics if the uint64 sum overflows.

func SafeConvertToInt128

1func SafeConvertToInt128(value *u256.Uint) *i256.Int
source

SafeConvertToInt128 converts a non-negative 256-bit integer to signed int128.

Parameters:

  • value: Unsigned 256-bit value to convert; nil is invalid.

Returns:

  • converted: A new *i256.Int representing value when it is at most 2^127 - 1.

Panics when value is nil or exceeds the largest positive signed int128 value.

func SafeConvertToInt64

1func SafeConvertToInt64(value *u256.Uint) int64
source

SafeConvertToInt64 converts a non-negative 256-bit integer to int64.

Parameters:

  • value: Unsigned 256-bit value to convert; nil is invalid.

Returns:

  • converted: value represented as int64 when it is at most math.MaxInt64.

Panics when value is nil or outside the int64 range.

func SafeConvertToUint128

1func SafeConvertToUint128(value *u256.Uint) *u256.Uint
source

SafeConvertToUint128 verifies that value fits in the uint128 range.

No representation conversion is performed: the original pointer is returned when its value is at most 2^128 - 1.

Parameters:

  • value: Non-nil unsigned 256-bit value to validate.

Returns:

  • converted: The same *u256.Uint pointer when value fits in uint128.

Panics if value is nil or exceeds the maximum uint128 value.

func SafeMulDivInt64

1func SafeMulDivInt64(a, b, c int64) int64
source

SafeMulDivInt64 returns the truncated quotient (a * b) / c.

The product is formed in signed 256-bit arithmetic before division, so an intermediate product may exceed int64 while the final quotient must still fit.

Parameters:

  • a: First signed int64 factor.
  • b: Second signed int64 factor.
  • c: Non-zero signed int64 divisor.

Returns:

  • quotient: The signed integer quotient after dividing a * b by c.

Panics if the 256-bit product overflows, c is zero, or the quotient is outside the representable int64 range.

func SafeMulInt64

1func SafeMulInt64(a, b int64) int64
source

SafeMulInt64 returns the exact product of two signed 64-bit integers.

Parameters:

  • a: First signed int64 factor.
  • b: Second signed int64 factor.

Returns:

  • product: a * b when the mathematical result is within [math.MinInt64, math.MaxInt64].

Panics if the signed int64 product overflows or underflows.

func SafeSubInt64

1func SafeSubInt64(a, b int64) int64
source

SafeSubInt64 returns the exact difference of two signed 64-bit integers.

Parameters:

  • a: Signed int64 minuend.
  • b: Signed int64 subtrahend.

Returns:

  • difference: a - b when the mathematical result is within [math.MinInt64, math.MaxInt64].

Panics if the signed int64 difference overflows or underflows.

func SafeSubUint64

1func SafeSubUint64(a, b uint64) uint64
source

SafeSubUint64 returns the exact difference of two unsigned 64-bit integers.

Parameters:

  • a: Unsigned uint64 minuend.
  • b: Unsigned uint64 subtrahend; it must not exceed a.

Returns:

  • difference: a - b.

Panics if b is greater than a and the subtraction would underflow uint64.

func SafeUint64ToInt64

1func SafeUint64ToInt64(value uint64) int64
source

SafeUint64ToInt64 converts a uint64 to a signed int64 without changing its value.

Parameters:

  • value: Unsigned value to convert; it must be no greater than 2^63 - 1.

Returns:

  • converted: value represented as int64.

Panics when value exceeds math.MaxInt64.

func SwapMathComputeSwapStep

1func SwapMathComputeSwapStep(
2	sqrtRatioCurrentX96 *u256.Uint,
3	sqrtRatioTargetX96 *u256.Uint,
4	liquidity *u256.Uint,
5	amountRemaining *i256.Int,
6	feePips uint64,
7) (*u256.Uint, *u256.Uint, *u256.Uint, *u256.Uint)
source

SwapMathComputeSwapStep computes one swap step within a single tick range. It determines the next square-root price, input amount, output amount, and fee, using exact-input or exact-output semantics from amountRemaining.

Parameters:

  • sqrtRatioCurrentX96: Current pool square-root price in Q96 fixed-point format.
  • sqrtRatioTargetX96: Tick-boundary square-root price that this step may reach.
  • liquidity: Non-negative liquidity active between the current and target prices.
  • amountRemaining: Signed amount remaining; non-negative selects exact input, negative selects exact output.
  • feePips: Fee rate in millionths of the input amount (3,000 represents 0.3%).

Returns:

  • sqrtRatioNextX96: Square-root price after applying this step, within the valid pool price bounds.
  • amountIn: Amount of the input token consumed by this step, excluding the fee.
  • amountOut: Amount of the output token produced by this step.
  • feeAmount: Input-token fee charged for this step.

Panics if an input pointer is nil, feePips is at least 1,000,000, arithmetic overflows, or the resulting square-root price is outside the valid bounds.

func TickMathGetSqrtRatioAtTick

1func TickMathGetSqrtRatioAtTick(tick int32) *u256.Uint
source

TickMathGetSqrtRatioAtTick calculates sqrt price ratio for given tick.

Converts tick index to square root price in Q64.96 fixed-point format. Based on Uniswap V3's mathematical formula: price = 1.0001^tick. Uses bit manipulation for gas-efficient calculation.

Parameters:

  • tick: tick index in range [-887272, 887272]

Returns:

  • sqrtPriceX96: the Q64.96 square root of the token1/token0 price, rounded up

Mathematical formula:

Example
1sqrtPriceX96 = sqrt(1.0001^tick) * 2^96

Panics if tick outside valid range. Critical for all price calculations in concentrated liquidity.

func TickMathGetTickAtSqrtRatio

1func TickMathGetTickAtSqrtRatio(sqrtPriceX96 *u256.Uint) int32
source

TickMathGetTickAtSqrtRatio calculates the tick index for a given square root price ratio.

Converts a square root price ratio in Q64.96 format back to its tick index, returning the greatest tick where TickMathGetSqrtRatioAtTick(tick) <= sqrtPriceX96. For this inverse API, sqrtPriceX96 must be in `[MinSqrtRatio, MaxSqrtRatio)`; the upper bound equals the max-tick output but is itself excluded.

Parameters:

  • sqrtPriceX96: square root price ratio in Q64.96 format within [MinSqrtRatio, MaxSqrtRatio)

Returns:

  • tick: the greatest tick whose calculated ratio is at most sqrtPriceX96

Algorithm:

  1. Scales ratio from Q64.96 to Q96.128 by left-shifting 32 bits
  2. Finds MSB (most significant bit) to determine magnitude
  3. Calculates log_2 using fixed-point arithmetic
  4. Converts log_2 to log_sqrt(1.0001) to get tick
  5. Returns appropriate tick based on bounds checking

Panics if sqrtPriceX96 is nil or outside valid range [minSqrtRatio, maxSqrtRatio). Critical for converting prices to ticks for position management.

Imports 7

Source Files 11