> ## Documentation Index
> Fetch the complete documentation index at: https://docs.picon.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# Bins and pricing

> The coordinate system every other concept builds on

## 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:

```
price(bin_id) = base^bin_id        (Q64.64 fixed point)
base = 1 + bin_step / 10_000
```

`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](/concepts/swaps) for the traversal consequences).

```
bin_array_index = bin_id.div_euclid(100)     // which array a bin lives in
bin_index       = bin_id.rem_euclid(100)     // local slot, 0..99
bin_id          = bin_array_index * 100 + bin_index
```

`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](/concepts/positions-and-liquidity) and how [swaps drain
bins](/concepts/swaps#traversal): 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.

<Tip>
  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.
</Tip>

## 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](/concepts/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:

```ts theme={null}
import { getBinPriceFromStep } from "@picon-finance/dlmm-sdk";

const price = getBinPriceFromStep(pool.data.binStep, someBinId); // Q64.64, or undefined
```

`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.
