Uniswap v3 liquidity units describe a position's contribution to its price curve. They are not token balances, dollars or TVL. To calculate the principal in token0 and token1, combine the position's liquidity with its actual range and the current pool price, using consistent token units. The same liquidity amount can represent two tokens inside the range or one token outside it.
A position record can show a large liquidity integer beside a modest token balance. That difference can come from the unit scale. Offering more of one token can also leave liquidity unchanged because the other token limits the deposit.
The practical test is to convert both ways. Reconstruct the principal from liquidity, then recover liquidity from those token amounts. Once those checks agree, add the decimal conversion and any separately accounted fees.
Start with the position, its range and its units
This article uses Uniswap v3. A vault or wrapper can add its own share accounting; its share balance needs a separate conversion into the underlying position.
For one plain position, collect its liquidity, lower tick, upper tick and current pool price. Uniswap Labs identifies these as the inputs for reconstructing holdings in its v3 position-math primer. Also record which contract is token0 and which is token1, and each token's decimals.
Use A for token0 and B for token1. Define P as whole B per whole A. A price of 4 therefore means one A exchanges for four B at the pool's marginal price. Keep this orientation through the calculation, even if an interface displays the reciprocal.
The position's L and the pool's current active liquidity are different records. The pool interface's liquidity() reports the liquidity currently in range across participating positions. Substituting that pool-wide value for a particular position's L assigns other providers' liquidity to your calculation. The distinction is explicit in the v3-core v1.0.0 pool-state interface.
For the trading-depth implications, see DEX TVL versus active liquidity. Here the task is narrower: identify the token quantities represented by one position record.
Convert L into principal amounts
Let a be the square root of the lower price and b the square root of the upper price. Take the square root of current P and clamp it between a and b; call that result s. Clamping means using a below the range, the current square root inside it and b above it.
The principal quantities are:
- A amount = L × (1/s − 1/b).
- B amount = L × (s − a).
These compact formulas reproduce the three branches in Uniswap's LiquidityAmounts library, pinned to the v1.3.0 commit. The contract operates on integer token amounts and encoded square-root prices; the following teaching example first uses whole-token units.
Consider a hypothetical position with lower price 1 B/A, upper price 9 B/A and normalized L = 1,200. Thus a = 1 and b = 3. Assume ordinary tokens, unchanged bounds and no addition or removal of liquidity. Exclude fees, rewards, gas and rounding from this principal calculation.
| Pool price, B/A | Position location | Clamped s | Principal A | Principal B |
|---|---|---|---|---|
| 0.25 | Below range | 1 | 800 | 0 |
| 4 | Inside range | 2 | 200 | 1,200 |
| 16 | Above range | 3 | 0 | 2,400 |
At P = 4, A is 1,200 × (1/2 − 1/3) = 200. B is 1,200 × (2 − 1) = 1,200. Below the range, replacing s with 1 yields 800 A and zero B. Above it, replacing s with 3 yields zero A and 2,400 B.
All three rows represent the same L. The changing composition follows the swaps that move price, rather than an automatic change in the position's liquidity units. The out-of-range position guide covers the separate questions of fee eligibility and subsequent inventory decisions.
These round bounds make the arithmetic transparent; they are not a claim that a deployed pool accepts arbitrary endpoints. Actual bounds must match initializable ticks, as the Uniswap v3 SDK guide specifies. Use the selected pool's actual bounds when reproducing a live position. The tick-spacing versus price-range explanation addresses that conversion.
Why the square root of your token balances is different
At the middle observation, multiplying the actual balances gives 200 × 1,200 = 240,000. Its square root is approximately 489.898, which does not recover L = 1,200.
The missing distinction is between real principal and virtual reserves. Uniswap v3's virtual-reserve model includes offsets that let a bounded position behave like a larger constant-product curve within its interval. The v3 whitepaper, section 2, defines that translated curve.
For this example, add L/b = 400 to A and L × a = 1,200 to B. The resulting virtual reserves are 600 A and 2,400 B. Their product is 1,440,000, whose square root is exactly 1,200. In whole-token units, L has units of √(A × B), which explains why it is not a standalone amount of either token.
The extra 400 A and 1,200 B are mathematical offsets. They are not additional tokens available to withdraw. This is why applying the full-range reserve shortcut directly to a bounded position's real balances produces the wrong L.
Valuation needs another calculation. At P = 4, the principal is worth 200 × 4 + 1,200 = 2,000 B using the pool ratio as the mark. That value is neither the numeric L of 1,200 nor the virtual-reserve total. A dollar value additionally needs a dollar price for B and a stated valuation source.
Reverse the calculation to find the limiting deposit
Suppose a hypothetical deposit budget contains 250 A and 1,200 B, with the same 1–9 range and current P = 4. These are available amounts, not a promise that the position will consume both in full.
Strictly inside the range, solve each principal equation for L independently:
- A budget supports L = 250 ÷ (1/2 − 1/3) = 1,500.
- B budget supports L = 1,200 ÷ (2 − 1) = 1,200.
The smaller result controls: L = 1,200. It uses 200 A and 1,200 B, leaving 50 A outside the position. This minimum-of-two approach is the in-range rule in the pinned LiquidityAmounts implementation.
At an exact boundary, one principal amount and its inverse formula's denominator are both zero. Use the library's one-token boundary branch; the zero balance cannot provide a second independent estimate of L.
| Deposit record | A | B |
|---|---|---|
| Available budget | 250 | 1,200 |
| Principal supporting L = 1,200 | 200 | 1,200 |
| Unused budget | 50 | 0 |
Choosing L = 1,500 because the A balance permits it would require 1,500 B. The B budget is short by 300. A sufficient aggregate marked value cannot repair a missing required token without an additional operation such as a swap.
That operation belongs in the transaction plan. The arithmetic above does not automatically swap the residual A, refund a particular contract or select a route. Reconcile the actual amounts used by the confirmed operation against the intended budget.
Reconcile whole tokens with the contract's integer scale
Now give A 18 decimals and B 6 decimals. These are hypothetical tokens; their labels do not identify an existing pair. The middle row still means 200 whole A and 1,200 whole B.
| Quantity | Whole-token convention | Raw contract convention |
|---|---|---|
| A principal | 200 A | 200 × 10¹⁸ |
| B principal | 1,200 B | 1,200 × 10⁶ |
| Price, B/A | 4 | 4 × 10⁻¹² |
| Square-root price | 2 | 2 × 10⁻⁶ |
| Liquidity parameter | 1,200 | 1,200 × 10¹² |
The raw ratio changes because it compares base units of B with base units of A. Recover whole B/A by multiplying the raw price by 10¹². Uniswap Labs explains the Q96 encoding and decimal adjustment in its first v3 math primer.
For these decimal counts, raw L is scaled by the square root of 10¹⁸ × 10⁶, which is 10¹². The ideal raw L is therefore 1.2 × 10¹⁵. Dividing raw L by 10¹⁸ or 10⁶ alone gives the wrong normalized parameter.
Price has a separate binary encoding: sqrtPriceX96 represents the raw square-root ratio multiplied by 2⁹⁶. At this ideal price its magnitude is about 1.58456325 × 10²³. The liquidity integer itself does not carry that X96 price encoding.
Keep the calculation entirely in raw integer units with the protocol's math libraries, or explicitly convert every input into one consistent whole-token convention. Mixing a whole-token square-root price with raw L is enough to make an otherwise correct formula meaningless.
The table describes ideal continuous relationships. Permitted tick boundaries and integer rounding mean it is not an exact on-chain mint or withdrawal quote. For actual bounds, v3-core's TickMath v1.0.0 converts ticks into encoded square-root ratios.
Keep principal, fees and value in separate columns
Reconstructing principal does not calculate accrued fees. Uniswap v3 keeps fee earnings separately rather than automatically reinvesting them into L; see whitepaper section 3.2.1. A fee balance can grow while a position's L stays unchanged.
Use the fee-growth versus fees-owed reconciliation when adding that claim. The v1.3.0 position manager also credits released principal to tokens owed when liquidity decreases; see its pinned decreaseLiquidity implementation. Preserve the quantities and identify what each claim includes before valuing it.
Likewise, a change in a dashboard's marked value does not identify a deposit, withdrawal or newly earned fee. At the three teaching prices above, fee-free principal values are 200 B, 2,000 B and 2,400 B, despite unchanged L. Those marks are accounting observations, not returns measured against a holding benchmark.
A worksheet for investigating a mismatch
Use one block reference for all state reads. A price read after another swap can disagree with a position snapshot from an earlier block. Retain the following evidence:
| Record | Check before accepting the result |
|---|---|
| Chain, pool and position identifier | Position and pool belong to the intended deployment |
| Token0, token1 and decimals | Price direction and amount scaling agree |
| Position L and actual tick bounds | The input is position liquidity, with permitted endpoints |
| Pool square-root price | Price belongs to the same pool and block |
| Computed principal in each token | Both amounts use the correct range branch and rounding |
| Reverse-sized L | Strictly inside the range, each principal amount recovers the same L within rounding |
| Fee and previously released principal claims | Add each claim once, separately from current principal |
| Valuation marks | State the reporting currency, price source and time |
If reverse sizing disagrees materially, check the token order, raw-versus-whole units and bounds before changing the formula. If the math agrees but a withdrawal estimate differs, compare its state reference, rounding and included claims. Each discrepancy then has a specific place to investigate.
Write the final result as token quantities: “L represents 200 A and 1,200 B at this block, before separate claims.” That statement can be checked against the inputs. An unexplained liquidity integer cannot tell you what the position holds.
