Early rate$2,400 of senior audit time for $500. Early members keep the rate as it climbs.$2,400 of senior audit time for $500See how
Back to Blog
Why Uniswap V3 Threw Away the LP Token
UniswapDeFiSolidityWeb3 Security

Why Uniswap V3 Threw Away the LP Token

13 min

TL;DR — Quick Summary

  • Concentrated liquidity lets a stablecoin LP with $10,000 offer the same trading depth as $40 million in Uniswap V2 — roughly 4,000x capital efficiency, from one design decision.
  • The catch is symmetric: outside your range you hold one token and earn nothing. Concentration is leverage on your fees and on your risk.
  • Because every LP's range is different, positions are no longer interchangeable — so V3 has no LP token. A position is an owner, a lower tick, and an upper tick.
  • With no shared reserves, fees cannot compound. V3 tracks fee growth per position and you withdraw separately.
  • V3 does not store the price. It stores the square root of the price, because in those coordinates swaps become linear and the quadratic disappears.

The full walkthrough, with the code written on screen. The article below covers the same ground if you would rather read it.

Introduction

Here is a number that sounds made up.
In Uniswap V3, a stablecoin liquidity provider with $10,000 can offer the same trading depth as someone with $40 million in Uniswap V2.
That is not a trick and it is not marketing. It is about 4,000x the capital efficiency, and it comes from a single design decision. Everything else in V3 — every piece of math you have heard is difficult, ticks, square root prices, fee growth accumulators — exists to support that one idea.
So it is worth understanding the decision before the machinery. This article is section 1 of Building Uniswap V3, an 18-section module where you do not read about concentrated liquidity. You write it.

What was actually wrong with V2

In Uniswap V2, your liquidity sits on the whole curve. x * y = k, from a price of zero all the way to infinity. That is elegant, and for a long time it was the right trade.
Now think about what it means if you are providing liquidity to a stablecoin pair.
USDC and DAI trade in a band of maybe ±0.05%. Your capital is sitting there, ready to quote a price of $0.10. Ready to quote $10.00. Prices that pair will never see, in any market condition, ever.
So how much of your money is actually working?
Under 1%. The rest is dead weight — and it is not merely idle, it is actively diluting the fees earned by every other liquidity provider in the pool. A V2 stablecoin pool is a room where almost everyone is being paid to stand somewhere nobody will ever walk.

The answer: pick a range

Instead of covering every price, you choose one. A lower bound and an upper bound, and your capital only backs trades inside it.
Inside that range, your position behaves exactly like a V2 pool. Same constant product curve, just over a shorter segment. Outside it, your position converts entirely into one token and sits idle until the price comes back.
That is the whole mechanism. Everything downstream is bookkeeping for it.

The stablecoin case, with real numbers

Take USDC/DAI at a price of 1.0. You place a range of 0.9995 to 1.0005 — about ±5 ticks.
Run the efficiency math on that and you get roughly 4,000x.
So $10,000 in that range provides the same in-range depth as $40 million spread across a V2 pool. It also earns fees like $40 million, for as long as the price stays inside.

Where the catch is

You should be suspicious of a 4,000x improvement with no cost, so here it is.
If the price leaves your range, your position is 100% one token and earns nothing. Zero. You are out of the market until price returns — if it returns.
That is the whole trade, and it is worth stating plainly because the marketing around V3 rarely does: concentration is leverage on your fees and leverage on your risk, in the same proportion. There is no free lunch here. There is a free choice.
Two consequences follow that are easy to miss.
The first is that "out of range" is a normal operating state, not an error. A position placed entirely on one side of the price is a range order — it converts from one token to the other as price passes through, which approximates a limit order and is genuinely useful. Any contract that manages positions has to treat single-sided positions as ordinary.
The second is that tight ranges invite competition on speed rather than on patience. If liquidity can be placed precisely and withdrawn instantly, a searcher can mint a very concentrated position immediately before a large swap, capture most of its fee, and burn immediately after — bearing none of the inventory risk that passive providers accept. That is just-in-time liquidity, and it is not a bug in V3. It is the direct consequence of making provision granular.
Building something that holds user funds? The Founder Security Sprint is a focused engagement for teams shipping their first protocol — architecture review, threat model, and the findings that matter before you are live. And if you want to read protocol code like this every week alongside other security engineers, that is Zealynx Insiders.

One decision, and the LP token has to go

Here is where it gets interesting, and where most explanations of V3 stop short.
That single choice — pick a range — cascades through the entire architecture. The first casualty is the LP token.
In V2, every liquidity provider owns the same thing: a slice of one curve. Everyone's claim is denominated in the same units, so a fungible share token works perfectly. Your LP tokens and mine are interchangeable because our positions are identical in kind.
In V3, my range and your range are different products. A position from 0.9995 to 1.0005 and a position from 1,800 to 2,200 are not the same asset, cannot be pooled, and cannot be exchanged one for the other.
So there is no LP token. A position is just three things: an owner, a lower tick, and an upper tick. The core identifies it by the hash of those three values. The periphery wraps that as an NFT, which is a convenience layer, not the protocol.

And it keeps cascading

Once shared reserves are gone, fees cannot auto-compound. In V2, fees stayed in the reserves and quietly increased the value of every share. In V3 there is no shared pot for them to sit in, so the protocol tracks fee growth per position, and you withdraw fees separately from principal. Your fees earn nothing while they wait.
And price now has to move through discrete boundaries. Because liquidity turns on and off at range edges, the swap loop cannot just solve for a new price — it has to notice every boundary crossing and adjust how much liquidity is actually active. That is what a tick is for, what liquidity net records, and why finding the next boundary cheaply requires a tick bitmap.
None of that machinery exists because someone wanted the code to be clever. It exists because ranges made the simple version impossible.

Working auditors in your corner, all year

Zealynx Insiders: weekly live sessions, 1:1 advisory, pair-auditing, and Krait runs on your code, from the firm behind 42 audits. Founders get a two-day audit session on the $500/year plan.

No spam. Unsubscribe anytime.

The strangest thing in the codebase

V2 thinks in reserves. V3 thinks in liquidity and price.
Liquidity is L = sqrt(x * y). And the pool does not store the price. It stores the square root of the price, in fixed point, as sqrtPriceX96.
Why go to that trouble?
Because in those coordinates, swaps become linear. Add some amount of a token to a pool with liquidity L, and the square root of the price moves by exactly that amount divided by L. No quadratic formula, no square roots computed on-chain during a swap.
Every amount in V3 collapses to L times a difference of square root prices. That is it. That single property is the entire reason section 3 of this module is a library called SqrtPriceMath, and it is why the protocol's arithmetic looks alien until you know what it is optimising for.
There is a second reason worth naming: working in integers means every division truncates, and in a protocol handling adversarial input, which way it truncates is a security boundary. V3 chooses the direction deliberately at every division — amounts owed round up, amounts paid round down — so that truncation always favours the pool. A $48 million exploit has lived in exactly this class of detail. Math that was almost right.

Three fees instead of one

One last piece of the shape.
V2 hardcoded a 0.30% fee for every pair in existence. V3 offers fee tiers, and each tier is a separate pool: 0.05% for stables, 0.30% for majors, 1.00% for the exotic stuff. A 0.01% tier was added by governance later.
A single global fee could never be right. A USDC/DAI provider takes almost no divergence risk and competes on volume, so 0.30% prices them out of the market. A provider on a freshly launched token faces severe adverse selection, and 0.30% may not cover it. One number cannot serve both.
Each tier also maps to a tick spacing, which controls how finely you are permitted to place a position at all. That pairing is not decoration — it is how the protocol bounds the gas cost of a swap, since every initialized tick a swap crosses is a storage read it has to pay for.

The shape of it

Ranges instead of shares. Square root prices instead of reserves. Fee growth instead of compounding.
Each of those is a direct consequence of the one decision at the top, and holding that chain in your head is what makes the rest of the codebase readable rather than arbitrary.
Next in this series: what a tick actually is, why prices are stored as powers of 1.0001, and how the pool finds the next initialized tick without scanning the whole number line.

FAQ

1. What is concentrated liquidity in Uniswap V3?
Concentrated liquidity lets a liquidity provider allocate capital to a chosen price range instead of the entire curve from zero to infinity. Inside that range the position behaves like a Uniswap V2 pool over a shorter segment; outside it, the position holds a single token and earns no fees. For a stablecoin pair in a tight range this yields roughly 4,000x the capital efficiency of V2.
2. Why does Uniswap V3 have no LP token?
Because positions are no longer fungible. In V2 every provider owns a slice of the same curve, so a share token works. In V3 each provider chooses their own range, and two positions with different ranges are different products that cannot be pooled or exchanged. A V3 position is identified by the hash of its owner, lower tick, and upper tick. The NFT you see is the periphery's NonfungiblePositionManager wrapping that, not the core protocol.
3. Why does Uniswap V3 store the square root of the price?
Because swaps become linear in square-root space. Adding an amount of a token to a pool with liquidity L moves the square root of the price by exactly that amount divided by L, so every swap amount reduces to L multiplied by a difference of square root prices. This eliminates the quadratic formula and any on-chain square root during a swap. The value is stored as sqrtPriceX96, a Q64.96 fixed-point number.
4. What is the downside of a narrow liquidity range?
If the price moves outside your range, the position converts entirely into one token and stops earning fees until the price returns. The narrower the range, the higher the fee multiplier and the more often you fall out of it. Narrow ranges also demand active rebalancing, and each rebalance realises the divergence loss and costs gas. Concentration multiplies fee income and inventory risk by the same factor.
5. Why does Uniswap V3 have multiple fee tiers?
Because the fee compensates a provider for risk, and risk varies enormously by pair. A stablecoin provider faces almost no divergence risk, so 0.30% is a tax that drives volume elsewhere; a provider on a long-tail token faces severe adverse selection, and 0.30% may not cover it. V3 offers 0.01%, 0.05%, 0.30% and 1.00% tiers, each a separate pool with its own tick spacing, and lets the market choose. The cost is that liquidity for one pair is split across pools, which is why routing moved into the periphery.

Glossary

TermDefinition
Concentrated LiquidityAllocating liquidity to a chosen price range rather than the entire curve, raising capital efficiency at the cost of range risk.
TickA discrete price point where each step corresponds to a 0.01% price change, used as the boundary of every V3 position.
sqrtPriceX96The square root of the pool price multiplied by 2^96, the fixed-point format V3 stores instead of the price itself.
Tick SpacingThe fixed interval constraining which ticks a position may use as boundaries, set per fee tier and immutable.
Fee TierThe fixed swap fee attached to a pool, chosen at creation, where the same pair may have one pool per tier.
Fee GrowthA global accumulator of fees per unit of liquidity, letting the pool settle any position by differencing two snapshots.
Range OrderA position placed entirely on one side of the price, converting between tokens as price passes through it.
Just-In-Time LiquidityMinting a concentrated position immediately before a large swap and burning it after, capturing fees without inventory risk.

Write it yourself

This article is section 1 of Building Uniswap V3, a free 18-section module on the Zealynx Academy. You write the protocol — the sqrt price math, the tick library, the bitmap, the swap loop, the oracle, the factory, the router — and a test suite checks every line as you go.
If you would rather learn it alongside other engineers, with a live build session every week and office hours where you bring your own code, that is Zealynx Insiders.

Working auditors in your corner, all year

Zealynx Insiders: weekly live sessions, 1:1 advisory, pair-auditing, and Krait runs on your code, from the firm behind 42 audits. Founders get a two-day audit session on the $500/year plan.

No spam. Unsubscribe anytime.