Section 5 of 9
Concentrated Liquidity: Ticks, Ranges, and NFT Positions
Concentrated Liquidity: Ticks, Ranges, and NFT Positions
The Capital Efficiency Problem
The constant product AMM you built in the previous sections distributes liquidity uniformly across all prices from zero to infinity. Think about what that means for a stablecoin pair like USDC/USDT. The price trades within a tiny band around 1.00, yet your liquidity sits ready to handle prices like 0.001 or 1,000,000. Over 99% of deposited capital earns zero fees at any given moment.
Uniswap V2 processes roughly $1 billion in daily volume, but an LP providing $1 million gets a minuscule share of fees because their capital competes against the entire theoretical price range. Concentrated liquidity fixes this. By letting LPs choose a specific price range, a $1 million position covering only the active trading range can earn the same fees as a $100 million full range position. That is 100x better capital efficiency.
How Ticks Discretize the Price Space
Concentrated liquidity divides the continuous price line into discrete points called ticks. Each tick i maps to a specific price using the formula:
price(i) = 1.0001^i
The base of 1.0001 means each tick is exactly one basis point (0.01%) away from its neighbors. Tick 0 represents a price of 1.0. Tick 1 represents 1.0001. Tick negative 1 represents 0.9999. This gives LPs extremely fine control over their range boundaries.
The contract does not store prices directly. Instead, it stores the square root of the price as a fixed point number called sqrtPriceX96. This encoding (sqrt of the price, multiplied by 2^96) enables efficient computation of token amounts without expensive division or square root operations during swaps.
Tick Spacing and Fee Tiers
Not every tick is usable. Tick spacing determines which ticks LPs can actually select as range boundaries. Uniswap V3 ties tick spacing to fee tiers:
| Fee Tier | Fee Rate | Tick Spacing | Use Case | |----------|----------|-------------|----------| | 0.01% | 100 | 1 | Stablecoin pairs | | 0.05% | 500 | 10 | Correlated pairs | | 0.30% | 3000 | 60 | Most pairs | | 1.00% | 10000 | 200 | Exotic pairs |
Tighter spacing means LPs can set narrower ranges (more capital efficient), but swaps cross more ticks and cost more gas. Wider spacing reduces gas but limits precision. This is a deliberate engineering tradeoff.
Why Positions Become NFTs
In Uniswap V2, every LP has the same position: full range. Their shares are interchangeable, so LP tokens are fungible ERC-20s. You can add them, transfer them, use them as collateral. Simple.
In concentrated liquidity, every position has a unique range. One LP covers ticks 100 to 200. Another covers ticks 150 to 300. These positions are fundamentally different in their risk profile, fee earning potential, and token composition. They cannot be represented by fungible tokens.
Uniswap V3 solves this by representing each position as an ERC-721 NFT. The NFT encodes the owner, the pool, the lower tick, the upper tick, and the liquidity amount. This has major implications for composability. Protocols building on top of concentrated liquidity (vaults like Beefy CLM, yield aggregators, lending protocols) must handle NFT positions instead of simple ERC-20 balances. It is more complex but also more expressive.
How Fee Accumulation Changes
In a constant product AMM, fees are simple. Every swap pays a flat 0.3%, and fees are distributed proportionally to all LPs based on their share of total liquidity. In concentrated liquidity, only LPs whose range includes the current price earn fees from a given swap.
The protocol tracks this with a concept called fee growth. The pool maintains a global counter (feeGrowthGlobal0X128 and feeGrowthGlobal1X128) that increases with every swap. Each tick stores how much fee growth occurred on the "outside" of that tick. By combining these values, the protocol can compute exactly how much fee growth happened inside any position's range, without iterating through historical data.
This is clever but subtle. The "outside" convention means that when the price crosses a tick, the tick's feeGrowthOutside value flips. The full formula for fee growth inside a position's range requires checking whether the current tick is above, below, or within the position's boundaries.
What You Will Build
Over the next four sections, you will implement a simplified version of the Uniswap V3 core:
- Tick and Position libraries for tracking liquidity at each price point and managing individual LP positions
- The swap loop that moves the price across tick boundaries, deducting fees at each step
- Position Manager for the mint/burn/collect lifecycle, plus a TWAP oracle for price feeds
- Final integration tying everything together into a working protocol
The code is simplified for teaching. Production V3 uses heavy fixed point math optimization (Q64.96, Q128.128) and bit manipulation for tick lookups. You will use the same architecture and data structures, with clearer math.
Verify Your Understanding
What is the discrete unit that Uniswap V3 uses to divide the price space into specific points where liquidity can be added or removed?
Each one represents a price of 1.0001 raised to that index.
Knowledge Check
Answer correctly to earn lynx
Verify your understanding