Skip to main content

Bins

Every bin has a unique signed integer identifier, bin_id: i32. Bin 0 is the par bin (price 1.0); positive ids are above par, negative below. A bin’s price is fixed and deterministic — no bin’s price can ever be anything other than what its id implies:
bin_step, in basis points, is fixed per pool at creation and sets how far apart adjacent bins are priced. A finer bin_step (e.g. 1 bp) covers a huge price range at high density; a coarser one (e.g. 400 bp) covers a wider range per bin, more coarsely. 22 bin steps are supported, from 1 bp to 400 bp. Prices are pre-computed once when a bin array is created and stored per bin, so a swap never has to exponentiate on the hot path.

Bin arrays

Bins are grouped into fixed-size bin arrays of 100 bins each — the unit of account allocation. Bin arrays are created permissionlessly, on demand (create_bin_array), and a swap or deposit only ever loads the arrays it actually touches. There is no bitmap scanning the full range up front — this is a deliberate sparse design (see Swaps for the traversal consequences).
div_euclid, not plain integer division, is required — truncating division misclassifies negative ids (bin_id = -1 lands in array -1 under div_euclid, array 0 under /).

The active bin

At any moment, exactly one bin is the pool’s active bin — the bin currently holding both tokens, at the pool’s current market price. Every bin below the active bin holds only Y (it’s a bid); every bin above holds only X (it’s an ask). This partition isn’t a separate invariant the program maintains — it falls directly out of how deposits are distributed and how swaps drain bins: a swap can leave a fully-drained bin single-sided and still active, and the next deposit or swap prices correctly against whatever that bin’s actual post-drain composition is.
There’s no oracle and no curve formula to invert. The active bin’s price is the market price, full stop — reading it is one field access (Pool.activeBinId), not a computation.

Fee tiers

Independent of bin_step, a pool also fixes a base fee rate at creation, from a table of 23 supported tiers, 0.01% to 10.00%. Fee rate is stored in native units where FEE_PRECISION = 1_000_000_000 represents 100% — one native unit is 0.00001 basis points, far finer than the 23 supported tiers themselves need, leaving headroom below basis-point resolution for tiers finer than the current table. See Fees for how the static rate combines with the dynamic surcharge.

Computing a bin’s price off-chain

The SDK exposes the same Q64.64 math the program uses, so a client can price a bin — e.g. to pick lowerBinId/upperBinId for a new position — without an RPC round trip:
undefined means someBinId is outside the representable range for that bin_step — every supported bin_step has a fixed, compile-time-verified min_bin_id/max_bin_id range, sized so base^bin_id stays representable.