Section 1 of 18

Learn
+5 Lynx

Introduction to Uniswap V3

Key takeaway: Uniswap V3's core innovation is concentrated liquidity: instead of spreading capital across the entire price curve from 0 to infinity like V2, each liquidity provider picks a price range [Pa, Pb] and their capital only backs trades inside it. The pool stops tracking token reserves and instead tracks liquidity L = sqrt(x * y) and the square root of price, sqrtPriceX96. Real reserves become a derived quantity; positions become ranges; and a stablecoin LP who picks a tight range around 1.00 achieves roughly 4,000x the capital efficiency of a V2 LP providing the same depth.

What You Are Building

You are going to build Uniswap V3. The real one. The concentrated-liquidity engine behind the most-forked DEX architecture of the last five years: PancakeSwap V3, Aerodrome Slipstream, and Ramses are this codebase with modifications, while KyberSwap Elastic and Algebra rebuilt the same design from scratch — reading any of them starts with understanding this code. By the end of this module you will have written the tick system, the fee accounting machine, the swap loop, the TWAP oracle, the factory, and a single-hop router, modernized to Solidity 0.8.x.

The Uniswap V2 module is the recommended prerequisite. V3 is the sequel: same two-token pool, radically different engineering. If AMM invariants or LP shares are fuzzy, build V2 first.

The Problem With V2, and the Leap

In V2, liquidity lives on the entire curve x * y = k, from a price of zero to a price of infinity. LP into a USDC/DAI pool and your capital stands ready to quote prices like 0.10 or 10.00 — prices that pair will never trade at. For a stablecoin pair trading in a band of ±0.05%, well under 1% of the pooled capital ever touches a trade; the rest is dead weight diluting every LP's fee yield.

V3's answer is concentrated liquidity: each LP chooses a price range [Pa, Pb] and deploys capital only there. Inside the range, the position behaves exactly like a V2 pool — same constant-product curve — but only over that segment. Outside it, the position converts entirely into one token and idles until price returns.

This one decision forces every other change in the protocol:

  • Positions are ranges, not shares. Different ranges are not fungible, so V3 has no LP token. A position is identified by (owner, tickLower, tickUpper) — the periphery wraps it as an NFT.
  • Fees can no longer auto-compound into reserves. They are tracked per position via "fee growth" accumulators and withdrawn separately (sections 4 and 6).
  • Price must move through discrete boundaries. Ranges start and end at ticks, and the swap loop must detect crossings and adjust active liquidity (sections 2, 4, 5, and Part 2).

The L and sqrt(P) Worldview

V2 thinks in reserves (x, y). V3 thinks in liquidity and price:

L = sqrt(x * y)        P = y / x

Invert those definitions and you get the virtual reserves back:

x = L / sqrt(P)        y = L * sqrt(P)

Why bother? Because in these coordinates, swaps become linear: adding Δy of token1 to a pool with liquidity L moves the square root of price by exactly Δ(sqrt(P)) = Δy / L, and symmetrically Δ(1/sqrt(P)) = Δx / L. No quadratic formulas. Every amount calculation in V3 reduces to L times a difference of square-root prices — which is why the pool stores sqrtPriceX96 (the square root of price in Q64.96 fixed point) as its primary state variable, and why section 3 is a library called SqrtPriceMath.

Virtual vs real reserves. A concentrated position on [Pa, Pb] behaves like a V2 pool with virtual reserves x = L/sqrt(P), y = L*sqrt(P) — but the LP only deposits the real reserves, the slice of the curve between Pa and Pb:

real x = L * (1/sqrt(P) - 1/sqrt(Pb))
real y = L * (sqrt(P) - sqrt(Pa))

The narrower the range, the less real capital fakes the same virtual depth. That gap is the capital efficiency.

Capital Efficiency, Worked Example

Take a USDC/DAI pool at price 1.0000, with an LP in the range [0.9995, 1.0005] — about ±5 ticks. For a symmetric range [P/r, P*r], the real capital required is a fraction (1 - 1/sqrt(r)) of the V2 full-range capital, so:

efficiency = 1 / (1 - 1/sqrt(r))
           = 1 / (1 - 1/1.00025)     for r = 1.0005
           ≈ 1 / 0.00025 ≈ 4001x

Roughly 4,000x. A stablecoin LP with $10,000 in a ±0.05% range provides the same in-range depth as a V2 LP with $40 million — and earns fees accordingly. The flip side: if price exits the range, the position is 100% one token and earns nothing. Concentration is leverage on fee income and on inventory risk. There is no free lunch; there is a free choice.

Fee Tiers

V2 hardcoded 0.3%. V3 launched with three tiers, each a separate pool per token pair: 0.05% (fee 500) for stable pairs, 0.30% (fee 3000) for majors, 1.00% (fee 10000) for exotics. Each tier maps to a tick spacing (10, 60, 200) controlling how finely positions can be placed — you implement that mapping in the factory. Governance can enable new tiers but never change existing ones.

The Road Ahead

  • Part 1 (sections 2–6): the math. The tick/price coordinate system, SqrtPriceMath, the Tick library and its fee-growth trick, the tick bitmap, Position accounting.
  • Part 2 (sections 7–12): the pool. State, mint, burn/collect, SwapMath, and the crown jewel — the swap loop that walks price space tick by tick.
  • Part 3 (sections 13–18): oracle, factory, periphery. The observation ring buffer, flash loans, CREATE2 deployment, a single-hop router.

Everything you write is the real code, adapted to Solidity 0.8.x, with every unchecked block justified in prose. Write it once and every V3 fork you ever audit becomes familiar territory.

Knowledge Check

Answer correctly to earn lynx

What is the core innovation that lets Uniswap V3 liquidity providers allocate capital only within a chosen price range instead of across the entire price curve?

Two words. It is the phrase V3 is famous for.