zenith-dlmm — Liquidity Book (bins)
zenith-dlmm is a discrete liquidity-book DEX. Instead of a continuous curve, liquidity lives in fixed price bins. Within a bin the price is constant, so a swap fills at a single price until the bin is exhausted, then moves to the next bin. This gives zero-slippage trades inside a bin and precise control over where liquidity sits.
Program ID (devnet): 7pxn8tEm44gXjfPH9YXsLywuYpAbgbxq9nPwG1XQczsz
Core ideas
Bins and bin step
Each bin id maps to a price via the bin step (in basis points): adjacent bins differ by a fixed ratio 1 + bin_step/10_000. A bin holds reserves of both tokens; the pair tracks an active bin — the one the current price sits in. Bins below the active bin hold the quote token, bins above hold the base token, and the active bin holds a blend. Bins are stored in fixed-size bin array accounts, allocated as needed and passed to swaps as remaining accounts so a trade can cross several bins in one transaction.
Swaps fill bin by bin
A swap consumes the active bin's opposing reserve at that bin's price, then crosses to the next initialized bin and repeats, accumulating output and fees per bin. Crossing shifts the active bin id. Because each bin has a constant price, the per-bin fill is exact.
Dynamic fee + oracle
The fee has a constant base component plus a volatility-driven component: a volatility accumulator grows with price moves and decays over idle slots, so fees rise during turbulence and relax when quiet. A TWAP oracle account records a rolling price history for downstream consumers. Fees split into an LP share (tracked per bin as fee growth) and a protocol share.
State
| Account | Holds |
|---|---|
LbPair | Active bin, bin step, base fee, volatility accumulator, protocol fee, references to the oracle. |
BinArray | A contiguous run of bins (reserves + per-bin fee growth). |
Position | Owner, the bin range it covers, and per-bin liquidity + fee checkpoints. |
Oracle | Rolling price observations for the TWAP. |
Instructions
| Instruction | Purpose |
|---|---|
initialize_lb_pair | Open a pair at a starting active bin, bin step, and fees. |
initialize_bin_array | Allocate a bin array covering a range of bin ids. |
initialize_oracle | Allocate the pair's TWAP oracle. |
initialize_position | Open a position over a bin range. |
add_liquidity | Deposit across bins using a strategy — Spot, Curve, or BidAsk — that shapes how liquidity is distributed. |
remove_liquidity | Withdraw from a position's bins. |
swap | Trade across bins, crossing arrays as needed. |
claim_fee | Claim a position's accrued per-bin LP fees. |
claim_protocol_fee | Withdraw the protocol fee share. |
close_position | Close an empty position. |
Liquidity strategies
add_liquidity accepts a shape for how the deposit spreads across the chosen bin range:
- Spot — even across the range (uniform depth).
- Curve — concentrated toward the middle (deeper near the current price).
- BidAsk — weighted toward the edges (for range orders / directional views).
What proves it works
- Host unit + vector tests in the program and math crate.
- On-chain lifecycle tests, and devnet e2e checks in
sdk/scriptsthat assert the realized on-chain output equals the SDK quote exactly, that an LP round-trip conserves value, and that a seeded position's fee claim matches the replayed fee growth.