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

# Swaps

> Traversal, exact-in vs exact-out, and the sparse bin-array design

## Exact-in and exact-out

Both are single-hop, single-pool swaps, exposed as two separate on-chain instructions rather
than one instruction with a mode flag — the per-bin math genuinely branches on which side is
fixed:

* **`swap_exact_in`**: fixes the gross input, drains bins until it's fully consumed or the
  pool runs out. `min_amount_out` is the slippage floor.
* **`swap_exact_out`**: fixes the exact net output the trader wants to receive, drains bins
  until that target is reached. `max_amount_in` is the slippage ceiling.

`x_to_y = true`: caller pays X, receives Y, price moves **down**, traversal walks bins in
descending id order from the active bin. `x_to_y = false`: the mirror — caller pays Y, receives
X, price moves **up**, traversal ascends.

## Traversal

Bins below the active bin are Y-only, bins above are X-only (see
[Positions and liquidity](/concepts/positions-and-liquidity)) — a swap simply walks that
partition in the appropriate direction, consuming the output side of each bin it passes
through, until the trade is filled.

Traversal uses **sparse bin arrays with no bitmap**. The client supplies the relevant
`BinArray` accounts directly (including verified gaps for any uninitialized stretch); the
program has no on-chain index of which price regions are populated. This is the load-bearing
design trade-off behind the protocol's scale limit:

<Warning>
  **`MAX_BIN_ARRAYS_PER_TRAVERSAL = 6`**, 100 bins per array, so a single swap can cross **at
  most 600 bins**. At the tightest supported bin step (1 bp), that's roughly a 6.2% worst-case
  price drift ceiling per swap — the floor, since every coarser bin step covers proportionally
  more price range per bin. A trade that would need to walk further must be split across
  multiple transactions. This is a deliberate trade-off for the no-bitmap design, not an
  oversight — it keeps a swap's compute and account-loading cost bounded regardless of how much
  of the price curve is populated.
</Warning>

The fold terminates the instant the trade is filled — it does not necessarily touch every
account the client provided. If the supplied bin arrays run out before the trade fills, the
instruction fails with `InsufficientLiquidity`: a correct signal that the trade needs more
account coverage or must be split, not a bug.

## Fees are per-bin, and rise mid-swap

Each bin crossed pays a fee: `gross_amount_in − net_amount_in` for that bin, split between LPs
and the protocol (see [Fees](/concepts/fees)). Critically, the fee rate itself isn't constant
across one swap — advancing the active bin also accumulates the [dynamic fee](/concepts/fees)'s
volatility state, so **later bins in the same large swap are priced at a higher rate than
earlier ones**. A quote that doesn't simulate this bin-by-bin will systematically underprice a
swap that crosses more than one bin.

## Every rounding decision favors the pool

Whichever side of a fill is the "cost" (the input on a drain, the input on an exact-out fill)
rounds up; whichever side is the free variable rounds down. This holds symmetrically for both
directions and both modes — a trader can never extract more value than the bin's true reserves
represent, and the accumulated rounding always lands with LPs, never against them.

## Token-2022 transfer fees

Bins never see a transfer-fee-extension mint directly — the gross/net conversion happens
outside the bin fold entirely:

* **Exact-in**: nets the input down before folding, then nets the fold's raw output down before
  checking it against `min_amount_out`.
* **Exact-out**: grosses the output target up before folding, then grosses the fold's raw input
  up before checking it against `max_amount_in`.

So slippage protection always applies to what the trader actually receives or actually pays,
not the pre-transfer-fee intermediate value — a fee-bearing mint can't be used to bypass it.

## Transfer hooks

If either mint carries an active Token-2022 `TransferHook` extension, the swap instruction
resolves and appends its extra accounts automatically as part of the settlement CPI — nothing
extra to configure from the SDK's perspective. Building the instruction by hand (without the
SDK) requires supplying those extra accounts yourself via the `TransferHookX`/`TransferHookY`
remaining-accounts group.
