Uniswap v3 tick spacing restricts which tick indices can be used as a position’s lower and upper boundaries. A price range spans two permitted boundaries, so spacing is not the range’s total width. Convert your intended prices into the pool’s token order and units, choose a rounding policy, then translate the submitted ticks back into prices. Outward, inward and nearest rounding can produce different valid ranges.
The useful comparison has three records: the prices you wanted, the ticks you submitted and the prices those ticks actually represent. When a form snaps an input to permitted boundaries, check whether those boundaries preserve your intended coverage.
The mechanics here use Uniswap v3. Other concentrated-liquidity implementations need their own pool rules.
The grid constrains endpoints
In v3, a pool with tick spacing 60 permits position boundaries at … −180, −120, −60, 0, 60, 120, 180 … . A position can span one of those intervals or many. Its two bounds need not be adjacent. These rules follow section 6.1 of the Uniswap v3 whitepaper.
“Usable” and “initialized” answer different questions. A usable tick is permitted as a boundary. An initialized tick currently has liquidity referencing it as a position boundary. You can select a permitted boundary that nobody else is currently using. The pool’s current tick can also lie between initialized boundaries; spacing does not require the current price to jump from one permitted endpoint to the next. The v3 active-liquidity guide describes these distinctions.
The original v3 factory configuration paired three fee tiers with the following spacings. This table refers to the constructor in v3-core v1.0.0, rather than an exhaustive list of every tier enabled on every deployment.
| Swap fee tier | Tick spacing | Price increase between adjacent usable ticks |
|---|---|---|
| 0.05% | 10 | About 0.100045% |
| 0.30% | 60 | About 0.601773% |
| 1.00% | 200 | About 2.020032% |
Those price increments are calculated as 1.0001^spacing − 1. They are percentages of the starting price, not fixed token amounts. The 0.30% fee and approximately 0.60% endpoint step describe different things; neither tells you how wide a particular LP position is.
Additional fee tiers can be enabled. For an actual position, read the intended pool’s tickSpacing() alongside its tokens and fee. The v3 pool interface exposes those values. A remembered fee table is a useful cross-check, but the pool identity belongs in the calculation.
Choose the condition rounding must preserve
For the worked example, let A be token0 and B be token1. Both fictional tokens have 18 decimals. Quote every price as whole B per whole A, so higher prices correspond to higher ticks.
With equal decimals in that orientation, a tick i represents P = 1.0001^i. The real-valued tick coordinate for a target price is ln(P) / ln(1.0001). This coordinate can be fractional; it is an intermediate calculation, not a valid submitted tick. The Uniswap Labs math primer explains the tick-price relationship and decimal adjustment.
Let s be the pool’s tick spacing. Decide which condition matters before applying a rounding function:
- Outward rounding preserves coverage. Round the lower coordinate down to a multiple of s and the upper coordinate up. The resulting price interval contains the requested interval, subject to protocol limits.
- Inward rounding stays inside the request. Round the lower coordinate up and the upper coordinate down. Some requested coverage is removed, and a sufficiently narrow interval can disappear.
- Nearest rounding minimizes each endpoint’s tick-coordinate distance. Round each coordinate to its nearest multiple of s. For this method, exact half-step ties round away from zero. One endpoint may move inward while the other moves outward.
These are different objectives. “Nearest” does not mean “cover everything I typed,” and outward rounding does not mean “keep every resulting price inside my limits.” If those limits express a firm constraint, inspect the translated result before funding it.
For outward rounding, calculate the lower index as floor(lower coordinate / s) × s. Calculate the upper index as ceil(upper coordinate / s) × s. Inward rounding swaps those two functions. Retain sufficient precision until both integer indices are chosen; rounding the desired prices or their tick coordinates first adds another decision.
One desired range produces three different results
Consider an original hypothetical request for 0.985–1.015 B per A in a v3 pool with spacing 60. This is a range-conversion example, not a live pool observation or a suggested deposit.
The target coordinates are approximately −151.143935 and 148.893569. Divide them by 60, apply the selected policy and multiply back by 60. Neither endpoint is a nearest-rounding tie. Then calculate each resulting price from its integer tick.
| Policy | Lower tick | Upper tick | Actual lower price, B/A | Actual upper price, B/A |
|---|---|---|---|---|
| Outward | −180 | 180 | 0.982161916 | 1.018162060 |
| Inward | −120 | 120 | 0.988072306 | 1.012071682 |
| Nearest | −180 | 120 | 0.982161916 | 1.012071682 |
All three pairs have distinct, ordered endpoints divisible by 60. They still implement different coverage. The nearest policy expands below the requested lower price while stopping below the requested upper price. A valid transaction cannot settle whether that change matches the provider’s intention.
Measure width consistently. Using upper price divided by lower price, minus one, the requested interval is approximately 3.045685% wide. Outward rounding produces 3.665398%; inward produces 2.428909%; nearest produces 3.045299%. The nearest result has almost the same width as the request while being shifted toward lower prices. Comparing width alone would miss the change.
There is a particular trap on the negative side. Dividing the lower coordinate by 60 gives about −2.519066. Its mathematical floor is −3, producing −180. Truncating toward zero gives −2, producing −120. Both outputs are permitted ticks, but the latter moves the lower bound inward. A divisibility check alone cannot detect that error.
The calculations use high-precision real arithmetic, with table prices rounded for display. Production integrations should use the implementation’s fixed-point price and tick libraries for contract-compatible values. The pinned v3 TickMath library specifies its Q64.96 conversion and supported tick limits.
A narrow interval can collapse
Keep spacing 60 and request 0.9995–1.0005 B/A. The target coordinates are approximately −5.001500 and 4.999000. Nearest rounding sends both to tick 0. Inward rounding does too.
That leaves no interval: lower and upper would be equal. It is not a smaller funded range. The v3 pool’s bound checks require the lower tick to be strictly less than the upper tick.
Outward rounding instead gives −60 and 60. Its width is approximately 1.207168% on the same upper/lower convention, compared with about 0.100050% requested. This example straddles tick 0, so preserving both requested sides requires two usable steps. The smallest possible valid range elsewhere can span one step; that does not mean every desired interval fits into one.
Adding a step to repair equal endpoints creates a new range with a changed placement or width. Show the candidate prices and make that change explicit.
Attach units before reversing the quotation
The example’s equal decimals remove one source of confusion. With different decimals, the whole-token B/A price includes a decimal scaling factor. An interface may also show A/B, reversing the direction of increasing prices and exchanging which displayed endpoint corresponds to the lower tick. The tick versus price guide works through that conversion.
For any positive interval from low to high in B/A, its reciprocal interval runs from 1/high to 1/low in A/B. Inverting each endpoint without reversing their order creates misleading labels. Perform the rounding in a clearly defined increasing tick-price orientation, then transform the resulting endpoints into the user’s display quotation.
An exact requested price that already lies on a usable tick should remain there. Floating-point noise close to a boundary can incorrectly add an extra step. Validate boundary cases with the chosen library, including negative ticks, exact multiples and values near the protocol’s permitted limits. If rounding takes an endpoint beyond the usable limits, revise or reject the request. Clamping it creates a different coverage decision that needs its own review.
Review the submitted range as a separate decision
Before minting, save a worksheet that someone else could reproduce:
| Record | Check |
|---|---|
| Chain, protocol version and pool | The calculation references the intended deployment and token pair |
| token0, token1 and decimals | Whole-token prices use the correct orientation and scaling |
| Desired prices and rounding objective | Coverage, containment or nearest placement is stated |
| Submitted lower and upper ticks | Both are permitted multiples, ordered and within protocol limits |
| Prices translated from those ticks | The actual endpoints satisfy the selected objective |
| Funding preview and current state | Token amounts and activity status refer to the resulting range |
Endpoint checks are only part of a funding review. Changing the bounds changes the position used to calculate token requirements. The liquidity units versus token amounts guide separates the liquidity parameter from the assets required at a given price. Recheck that preview after rounding rather than carrying over quantities from an earlier proposed range.
Likewise, a position’s ownership identifier does not prove that it has the intended bounds or remains funded. The LP token versus position NFT guide explains which state belongs behind that identifier. After confirmation, read the actual stored ticks and reconcile them with the reviewed request.
A finer grid gives more precise placement choices. Fees earned, time in range and net results also depend on the position and subsequent activity. The out-of-range liquidity guide follows the inventory and fee-eligibility consequences once price leaves the selected bounds.
Keep the original desired prices beside the final ticks and translated prices. If the translation changes a constraint you care about, revise the proposed range before funding it. The arithmetic should make that choice visible.
