Concentrated Liquidity

Swap & AMM

Uniswap V3–style AMM where liquidity providers deposit assets within a specific tick (price) range, enabling capital-efficient swaps on Subtensor.

Click items to navigate to their reference pages.

The Big Picture

Concentrated liquidity is the next-generation AMM powering TAO ↔ Alpha swaps on Subtensor. Unlike the basic constant-product AMM (used in Alpha Conversion), concentrated liquidity lets providers focus their capital in specific price ranges — earning higher fees on less capital. Price is discretized into "ticks" (each tick = 0.01% price change), and positions span ranges of ticks. When the price moves outside your range, your liquidity goes dormant. This design, pioneered by Uniswap V3, provides better execution for traders and higher capital efficiency for liquidity providers.

Why This Matters

The Swap pallet handles all TAO ↔ Alpha conversions for staking operations. Understanding ticks, positions, and fee accrual helps you provide liquidity efficiently, estimate swap slippage, and understand the parameters you see on add_liquidity and remove_liquidity calls.

Example Scenario

Alice provides liquidity on subnet 1 with tick_low = -100 and tick_high = 100 (price range ~0.99 to ~1.01). She deposits 10 TAO and the equivalent Alpha. While the price stays in her range, she earns fees on every swap. If the Alpha price rises above tick 100 (price > 1.01), her position becomes all-TAO and earns no fees until the price returns to her range.

Common Questions

What are ticks?
Ticks are integer indices that discretize price. Each tick represents a 0.01% price change: price = 1.0001^tick. Tick 0 = price 1.0, tick 100 ≈ price 1.01, tick -100 ≈ price 0.99. LPs choose a tick_low and tick_high to define their active range.
How is this different from Alpha Conversion?
Alpha Conversion uses a basic constant-product (x*y=k) AMM where liquidity covers the entire price range. Concentrated liquidity lets LPs focus capital in narrow ranges for much higher capital efficiency — but at the cost of complexity and the risk of going out-of-range.
What happens when price moves out of my range?
Your position becomes inactive — it holds only one asset (all TAO or all Alpha) and earns no fees. When price re-enters your range, the position reactivates. No action needed, it is automatic.
What is √price (AlphaSqrtPrice)?
The AMM stores the square root of price as a Q64.64 fixed-point number for efficient swap math. The core invariant L = Δy / Δ(√P) means swap amounts can be computed with a single multiplication rather than a division.

Use Cases

  • Providing liquidity for TAO ↔ Alpha swaps
  • Estimating swap slippage on concentrated liquidity pools
  • Understanding tick_low and tick_high parameters on LP calls
  • Building LP position management tools
  • Monitoring fee accrual on active positions

The Swap pallet implements a concentrated liquidity AMM modeled after Uniswap V3. Instead of spreading liquidity across the entire price curve (0 to ∞), LPs choose a tick range — a lower tick and upper tick — defining the price interval where their liquidity is active. This lets the same capital provide deeper liquidity around the current price.

Price is discretized into **ticks**: integer indices where `price = 1.0001^tick`. Each tick boundary is a price point, and positions span contiguous tick ranges. When the current price moves outside a position's range, that position's liquidity becomes inactive (it earns no fees until price re-enters the range).

Swap execution walks through active tick ranges, consuming liquidity at each step. Fee accrual is tracked globally per unit of liquidity (`FeeGlobalAlpha`, `FeeGlobalTao`), and individual positions earn fees proportional to their share of active liquidity during swaps.

The pallet also tracks a **sqrt price** representation (`AlphaSqrtPrice`) for efficient swap math — the core invariant is `L = Δy / Δ(√P)` where L is liquidity, y is the quote asset, and √P is the square root of price.

Triggers

  • LP calls add_liquidity to open or extend a position in a tick range
  • LP calls remove_liquidity to withdraw from a position
  • LP calls modify_position to adjust tick range or liquidity
  • Staking swap routes through the concentrated liquidity pool

Inputs (10)

ItemTypeRole
add_liquidity SwapcallOpen or add to a liquidity position in a tick range
remove_liquidity SwapcallWithdraw liquidity and collect accrued fees
modify_position SwapcallAdjust an existing position (change tick range or amount)
set_fee_rate SwapcallGovernance sets the swap fee rate
toggle_user_liquidity SwapcallEnable or disable user LP participation per subnet
disable_lp SwapcallDisable LP entirely for a subnet (governance)
AlphaSqrtPrice SwapstorageCurrent √price for each subnet pool (Q64.64 fixed-point)
CurrentTick SwapstorageCurrent tick index derived from √price
CurrentLiquidity SwapstorageActive liquidity at the current tick
FeeRate SwapstorageSwap fee as a fraction of trade value

Outputs (13)

ItemTypeRole
LiquidityAdded SwapeventEmitted when liquidity is deposited into a tick range
LiquidityRemoved SwapeventEmitted when liquidity is withdrawn from a position
LiquidityModified SwapeventEmitted when an existing position is modified
FeeRateSet SwapeventEmitted when the fee rate changes
UserLiquidityToggled SwapeventEmitted when user LP participation is toggled
Positions SwapstorageLP positions: owner, tick range, liquidity amount, fee snapshots
Ticks SwapstoragePer-tick state: net liquidity delta, fee accumulators outside the tick
TickIndexBitmapWords SwapstorageBitmap tracking which ticks have been initialized (for efficient traversal)
FeeGlobalAlpha SwapstorageCumulative fees per unit of liquidity (Alpha side)
FeeGlobalTao SwapstorageCumulative fees per unit of liquidity (TAO side)
LastPositionId SwapstorageAuto-incrementing position ID counter
ScrapReservoirAlpha SwapstorageDust amounts accumulated from rounding (Alpha side)
ScrapReservoirTao SwapstorageDust amounts accumulated from rounding (TAO side)

Source Files

pallets/swap/src/pallet/impls.rs
do_add_liquidity()do_remove_liquidity()do_modify_position()
pallets/swap/src/tick.rs
try_to_sqrt_price()try_from_sqrt_price()get_sqrt_ratio_at_tick()get_tick_at_sqrt_ratio()
pallets/swap/src/pallet/swap_step.rs
process_swap()

Formulas

Tick ↔ Price Conversion

Ticks are integer indices that map to prices exponentially. Each tick represents a 0.01% price change from the adjacent tick.

price = 1.0001^tick
tick = log(price) / log(1.0001)

Concentrated Liquidity Invariant

Within an active tick range, the relationship between liquidity (L), price change (√P), and asset amounts follows the V3 invariant.

L = Δy / Δ(√P)          // quote asset
L = Δx / Δ(1/√P)        // base asset
// Where √P = sqrt(1.0001^tick)

Fee Accrual per Unit Liquidity

Fees accumulate globally per unit of active liquidity. Each position earns fees proportional to its share.

fees_earned = (feeGlobal_current - feeGlobal_at_deposit) * position_liquidity
// Tracked separately for Alpha and TAO sides

Version History

v374 Swap pallet introduced with V3 concentrated liquidity AMM