A Uniswap v3 tick is an integer coordinate for a raw ratio of token1 units per token0 unit. Convert it with 1.0001 raised to the tick, adjust for both tokens’ decimals, then take the reciprocal if you want the opposite quote direction. A tick gives a price boundary; sqrtPriceX96 gives the pool’s more precise current ratio. Converting a price back to a tick requires downward rounding and a boundary check.
A number such as 200,000 can look unrelated to a displayed token price. The connection becomes clear once you keep three things separate: the pool’s fixed token order, its smallest token units, and the direction of the human-readable quote. Most large discrepancies arise before any complicated liquidity calculation begins.
Start with the pool’s own labels
This guide uses Uniswap v3, with implementation details checked against the v3-core v1.0.0 release. It does not assume every automated market maker uses these representations.
In the v3 factory, the token with the lower numeric contract address becomes token0; the other becomes token1. A ticker, a website’s pair name or your preferred base asset does not choose that order. Read the actual pool’s token addresses before interpreting its price. Uniswap v3 factory source.
Suppose a hypothetical pool has token A as token0 with 6 decimals, and token B as token1 with 18 decimals. One whole A contains one million raw units; one whole B contains one quintillion. These are invented assets and prices, not observations from a live market.
Write the intended unit beside every result. “B per A” means how many whole B correspond to one whole A. “A per B” is its reciprocal. Neither is automatically a dollar price.
One tick has three price representations
Let i be the tick and R the raw ratio, measured in smallest units of token1 per smallest unit of token0. The conceptual boundary is R = 1.0001^i. Equal tick increments therefore multiply the ratio by the same factor; they do not add the same absolute amount. This logarithmic arrangement is specified in section 6.1 of the Uniswap v3 whitepaper.
Let d0 and d1 be token0’s and token1’s decimal counts. The conversions are:
| Representation | Calculation | Units |
|---|---|---|
| Raw boundary ratio | R = 1.0001^i | Raw token1 per raw token0 |
| Human forward price | H = R × 10^(d0−d1) | Whole token1 per whole token0 |
| Human reverse price | 1/H | Whole token0 per whole token1 |
The decimal factor follows from converting one whole token0 into raw units, applying R, then converting the resulting raw token1 into whole tokens. For A and B, the factor is 10^(6−18), or one trillionth. Uniswap Labs’ v3 math primer explains this adjustment and the reciprocal display convention.
The sign of a tick alone does not tell you whether one whole token is worth more than the other. Tick zero means a raw ratio of one. In this hypothetical pool, that becomes only 10^-12 B per A because the decimal counts differ.
Convert tick 200,000, keeping the units attached
For the hypothetical A/B pool, calculate the raw boundary first:
1.0001^200000 ≈ 484,680,305.025734.
That large value is in raw B units per raw A unit. Applying the decimal factor gives approximately 0.000484680305 B per A. Taking its reciprocal gives approximately 2,063.215669 A per B.
These are three descriptions of the same boundary. Showing 484,680,305 as a whole-token price would leave out the decimal conversion. Showing 0.000484680305 with an “A per B” label would reverse the meaning without reversing the number.
A move to the next higher tick increases B per A by a factor of 1.0001. The reciprocal A-per-B price decreases by that factor. A dashboard can therefore show a falling price while the pool tick rises, simply because the dashboard displays the other direction.
Displaying the reciprocal does not change the pool’s stored tick or swap its token addresses. Keep the pool coordinate fixed and convert the output units.
Walk back from a human price to a tick
Now choose a second hypothetical input: 2,000 A per B. The goal is to find the tick boundary immediately below the corresponding raw B/A ratio.
First reverse the human quote: 1/2000 = 0.0005 B per A. Undo the decimal adjustment by multiplying by 10^(18−6). The raw ratio is 500,000,000.
The continuous coordinate is ln(R) / ln(1.0001), which gives approximately 200,311.201456. Rounding toward negative infinity gives 200,311. A logarithm can use any consistent base; the ratio of logarithms is what matters.
Do not stop at the integer. Convert that tick and the next tick back into prices:
| Boundary or target | Raw B/A ratio | Human B per A | Human A per B |
|---|---|---|---|
| Tick 200,311 | 499,989,927.790881 | 0.000499989928 | 2,000.040290 |
| Chosen target | 500,000,000 | 0.000500000000 | 2,000.000000 |
| Tick 200,312 | 500,039,926.783660 | 0.000500039927 | 1,999.840306 |
The raw target lies between the two boundaries. The reciprocal column runs downward, so its inequality reverses: 1,999.840306 is below 2,000, which is below 2,000.040290. Checking both columns catches a direction error that a plausible-looking tick might conceal.
“Downward” refers to the tick coordinate and raw forward ratio. It does not mean the smaller number in every human quote direction.
Negative coordinates need the same rule. For a separate arithmetic check, take the reciprocal of the raw ratio, 500,000,000. Its continuous coordinate is −200,311.201456, whose floor is −200,312. Truncating toward zero gives −200,311 and selects the wrong interval. This reversed coordinate is a mathematical validation fixture; merely changing the displayed quote of the actual pool leaves its tick unchanged.
Use exact boundaries when precision matters
The logarithm is useful for understanding the conversion. Contract-compatible software also needs the implementation’s integer rules. In v3-core v1.0.0, TickMath.getSqrtRatioAtTick returns a Q64.96 square-root boundary, with an upward rounding step. Its inverse selects the greatest tick whose encoded boundary does not exceed the supplied square-root ratio. Uniswap v3 TickMath source.
For the 500,000,000 example, an integer boundary search agrees with the logarithm: tick 200,311. Its encoded lower boundary and the next boundary bracket the target’s encoded square-root price. Near an exact boundary, use these integer comparisons rather than trusting the last digits of a floating-point logarithm.
Price-to-tick rounding is also separate from selecting a permitted liquidity-position boundary. A pool’s tick spacing limits which ticks can be initialized. The tick spacing versus price range guide explains that additional step. Finding the interval containing a price does not establish that its lower tick is an allowed position endpoint.
Why the stored tick can disagree with precise spot
The pool exposes both tick and sqrtPriceX96 through slot0. The latter represents the square root of the raw token1/token0 ratio multiplied by 2^96. Decode the raw current ratio as (sqrtPriceX96 / 2^96)^2, then apply the same decimal adjustment and optional reciprocal. Uniswap Labs recommends this route for greater spot precision because an integer tick loses the location within an interval. Uniswap Labs’ price calculation.
There is a boundary exception to a simple floor comparison. The released v3 pool-state interface explicitly allows the stored tick to differ from the tick inferred from the square-root price when price is on a boundary.
Suppose this hypothetical pool uses tick spacing 10 and tick 200,000 is initialized. If a decreasing-price swap ends exactly there after the boundary transition, the loop stores tick 199,999 while the square-root price remains at boundary 200,000. In the increasing direction, the transition stores 200,000. These assignments apply when the swap reaches its next tick boundary; otherwise, a price change makes the loop recompute the tick from the square-root price. Uniswap v3 pool transition source.
A reconciliation tool should preserve both returned fields and recognize this case. Replacing the stored tick with a freshly calculated value can discard state that matters to liquidity accounting.
Diagnose the discrepancy before changing the formula
| Symptom | First check |
|---|---|
| Price differs by a power of ten | Decimal counts and the sign of d0−d1 |
| One display rises while the tick falls | Reciprocal quote direction |
| Negative price-to-tick result misses by one | Floor versus truncation toward zero |
| Stored tick differs at an exact boundary | Last transition direction and encoded boundary |
| Spot matches but swap output differs | Trade size, route, liquidity and fees |
That last row requires a different calculation. The concentrated-liquidity execution guide follows finite swaps across ranges. A converted marginal price alone cannot specify the tokens received for a chosen input amount.
Likewise, a spot conversion does not reproduce an averaged oracle reference; use the TWAP versus spot guide to reconcile the time window. And a v2 reserve-product field belongs to another implementation: kLast versus current k explains that accounting distinction.
Keep a conversion worksheet with enough detail to reproduce the result:
| Save | Validate |
|---|---|
| Chain, pool address and block identity | Every value belongs to the intended pool state |
| Token addresses and decimal counts | Token order and units are verified |
| Raw tick and square-root price | A displayed value has not replaced either input |
| Desired quote direction and decimal factor | The conversion ends in the intended whole-token units |
| Neighboring encoded boundaries | Lower boundary ≤ target < upper boundary |
| Reciprocal result | The quote reverses and its inequality reverses too |
Preserve the unrounded inputs, then round only the displayed result. For the hypothetical 2,000 A-per-B input, the decisive check is the bracket: tick 200,311 below the raw target, tick 200,312 above it, and the reciprocal inequality reversed.
