By: Austin Adams, Sara Reynolds, and Rachel Eichenberger
Uniswap Protocol simplifies decentralized trading, but its underlying mathematics can be complex. This guide addresses common technical questions for developers, analysts, and researchers:
- Q Notation: Fixed-point arithmetic in Uniswap v3
- Exchange Rate Calculation: Deriving prices from
sqrtPriceX96 - Ticks vs. Tick-Spacing: Relationship to liquidity and price ranges
Q Notation: Fixed-Point Arithmetic
Uniswap v3 uses Q notation (e.g., X96, X128) to represent fractional numbers in fixed-point arithmetic, ensuring precision without floating-point operations.
Key Points:
- Convert Q notation to actual values by dividing by (2^n) (e.g.,
sqrtPriceX96→ divide by (2^{96})). - Maintains integer efficiency while mimicking decimal precision.
Code Example:
import { BigNumber } from 'ethers';
import bn from 'bignumber.js';
bn.config({ EXPONENTIAL_AT: 999999, DECIMAL_PLACES: 40 });
function encodePriceSqrt(reserve1, reserve0) {
return BigNumber.from(
new bn(reserve1.toString()).div(reserve0.toString()).sqrt()
.multipliedBy(new bn(2).pow(96))
.integerValue(3)
.toString()
);
}
encodePriceSqrt(1000000000000000000, 1539296453); // WETH/USDC reserves Calculating the Current Exchange Rate
The pool’s price is stored in slot0 as sqrtPriceX96. Use this over tick for higher precision.
Formula:
[ \text{Price} = \left(\frac{\text{sqrtPriceX96}}{2^{96}}\right)^2 \times \frac{10^{\text{Decimal0}}}{10^{\text{Decimal1}}} ]
Example (USDC-WETH Pool):
- Input:
sqrtPriceX96 = 2018382873588440326581633304624437 Output:
- Price of 1 USDC in WETH: 0.000649647439939888
- Price of 1 WETH in USDC: 1539.296453
Code Snippet:
function getPrice(PoolInfo) {
const sqrtPriceX96 = PoolInfo.SqrtX96;
const price0 = ((sqrtPriceX96 / 2**96)**2) / (10**PoolInfo.Decimal1 / 10**PoolInfo.Decimal0);
const price1 = 1 / price0;
console.log("Price of token0 in token1:", price0);
console.log("Price of token1 in token0:", price1);
} Ticks vs. Tick-Spacing
Definitions:
- Tick: Unit defining a specific price (e.g., tick 202919 → price range).
- Tick-Spacing: Minimum interval between initialized ticks, set by the pool’s fee tier (e.g., 5 bps pools have a spacing of 10).
Relationship to Price:
- Current Tick-Range: ([ \text{floor(tick / tick-spacing)} \times \text{tick-spacing}, \text{floor(tick / tick-spacing) + 1} \times \text{tick-spacing} ))
- Example: For tick 202919 in a 5-bps pool (spacing=10), the range is [202910, 202920).
FAQ
1. Why use sqrtPriceX96 instead of tick?
- Answer:
sqrtPriceX96retains sub-integer precision, whiletickfloors values, losing granularity.
2. How does tick-spacing affect liquidity?
- Answer: Liquidity can only change at initialized ticks (multiples of tick-spacing). Trades between ticks use constant liquidity like Uniswap v2.
3. What’s the maximum tick value in Uniswap v3?
- Answer: Ticks range from -887272 to 887272, covering prices from near-zero to near-infinity.
👉 Explore Uniswap v3’s technical documentation
Conclusion
Understanding Uniswap v3’s math—Q notation, price derivation, and tick mechanics—empowers better development and analysis. For advanced topics like virtual liquidity and fee calculations, refer to Part 2.
Further Reading:
👉 Dive deeper into decentralized finance
Sign up for updates from the Uniswap Labs team to stay informed on protocol advancements.