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

# Aggregators and routers

> Integrating Picon DLMM as a routable venue in an existing multi-venue router

Plugging Picon DLMM into a router or aggregator as one routable pool type typically splits into
two pieces: an off-chain quoting/route-building library linked into the router's own backend,
and — if the router's design calls for it — a small on-chain CPI adapter that invokes the real
`swap_exact_in`/`swap_exact_out` instruction on the router's behalf. This page covers the
integration-specific pitfalls that aren't obvious from the instruction reference alone.

## Off-chain: decode via the IDL, not hand-mirrored structs

Don't hand-mirror the `Pool`/`BinArray`/instruction-argument structs as local Borsh types — a
future field reorder on-chain would silently break decoding instead of failing to compile. Use
`anchor_lang::declare_program!` against the published IDL instead: this generates real typed
structs with correct discriminators and field layouts straight from the same IDL published on
mainnet, so a schema mismatch becomes a compile error.

`BinArray`/`Bin` specifically are `#[repr(C)]` zero-copy accounts on-chain, so a direct
`bytemuck::try_pod_read_unaligned` reinterpret of the raw account bytes is sound and fast for
those two types. `Pool` is not laid out identically to
[`picon-dlmm-quote`](/integrations/quoting-without-the-sdk)'s `PoolState` (different field
sets, not `#[repr(C)]` on either side), so converting between them needs an explicit
field-by-field copy, not a cast.

## Fetch the right bin-array window, not a symmetric one

This is the single easiest mistake to make when integrating a router venue.
**`MAX_BIN_ARRAYS_PER_TRAVERSAL` (6) is the cap per swap *direction*, not "arrays fetched on
each side of the active bin."** Fetching `active ± 6` (13 arrays total) and handing that whole
window to a single-direction swap instruction overshoots the on-chain cap by one and fails with
`InvalidBinArray`.

The correct window, matching the SDK's own `generateBinArrayIndicesForSwap`: each direction's
window is the active array itself, plus up to 4 arrays behind it on the deep-traversal side,
plus exactly 1 ahead of it as a drift margin — asymmetric per direction. Fetch the union of
both directions once per refresh (9 arrays total), then slice down to the exact
`MAX_BIN_ARRAYS_PER_TRAVERSAL`-sized directional window before quoting or building an
instruction for a specific direction.

## Account ordering

The swap instruction's token-account and token-program pairs are **always in X/Y order
on-chain, never input/output order.** A router that places "input mint's account" at the X slot
happens to work for `x_to_y` swaps (where input coincidentally is X) and fails outright for
`y_to_x` swaps.

## Transfer hooks: resolve them yourself outside the SDK

Picon DLMM's on-chain program fully supports Token-2022 transfer-hook mints in the swap
instruction — but if you're hand-building the instruction outside the SDK (which resolves this
automatically), you need to resolve the hook's `ExtraAccountMetaList` yourself and append the
resulting accounts to the `TransferHookX`/`TransferHookY` remaining-accounts group. The
`spl-transfer-hook-interface`/`spl-tlv-account-resolution` crates do this natively in Rust; the
TypeScript SDK's own hand-rolled resolver (`resolveTransferHookAccountsX`/
`resolveTransferHookAccountsY`, written because `@solana/kit` doesn't ship one either) is a
working reference if you're porting the logic elsewhere.

## Test against real execution, not just review

Quoting correctly and building a *valid, executable* instruction are different claims, and only
one of them is checkable without actually running the transaction. Bugs in bin-array windowing
and account ordering — the two pitfalls above — are exactly the kind of thing that passes code
review and unit tests that only check quote math in isolation, but fails the moment a real
instruction executes. Budget for an execution-level test (e.g. against a local validator
simulator like LiteSVM, with a real dumped program binary) before treating a router integration
as done.
