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

# Quoting without the SDK

> picon-dlmm-quote: the standalone quote engine underneath the TypeScript SDK

If you only need accurate swap-quote math — no RPC fetching, no transaction building, no
TypeScript — `picon-dlmm-quote` is the crate to reach for directly. It's what actually powers
`Pool.quoteExactIn`/`quoteExactOut` inside `@picon-finance/dlmm-sdk`, compiled to WebAssembly
for that use case, but it's a **standalone, dependency-free Rust crate with zero coupling to
the on-chain program** — it takes already-decoded plain values in and returns a quote result
out. Account decoding is entirely out of scope for it.

<Info>
  This is a deliberate duplication of the on-chain swap math, not an oversight — the on-chain
  program and an off-chain quote library have different pressures (compute-unit budget,
  zero-copy account layout, deployed-binary immutability vs. WASM portability), so coupling
  them with a dependency edge would make a change on either side a potential blast-radius change
  on the other.
</Info>

## When to use this instead of the full SDK

* You're integrating from **native Rust** (a router, an aggregator backend, an on-chain program
  that wants to simulate a quote) and don't want a WASM boundary at all.
* You're integrating from a non-TypeScript, non-Rust environment where WASM is your only
  practical option, and you don't need the SDK's RPC/state-management/instruction-building
  layer — just the math.
* You already manage your own account fetching/caching (e.g. inside a router that batches
  account fetches across many venues) and want to plug quoting directly into that, without the
  SDK's own `Pool`/`Position` entity classes imposing their own caching model on top.

## The core function

```rust theme={null}
pub fn quote_exact_in(input: QuoteExactInInput) -> Result<QuoteExactInOutput, QuoteError>
```

```rust theme={null}
pub struct QuoteExactInInput {
    pub amount_in: u64,               // gross, before input transfer fee
    pub x_to_y: bool,
    pub slippage_tolerance_bps: u16,
    pub now: i64,                     // unix timestamp, feeds dynamic-fee decay
    pub pool_state: PoolState,
    pub bin_arrays: Vec<Option<BinArray>>,
    pub input_transfer_fee: Option<TransferFee>,
    pub output_transfer_fee: Option<TransferFee>,
}

pub struct QuoteExactInOutput {
    pub pre_active_bin_id: i32,
    pub post_active_bin_id: i32,
    pub amount_in: u64,
    pub amount_out: u64,       // net of DLMM fee AND output transfer fee
    pub min_amount_out: u64,   // amount_out after slippage_tolerance_bps
    pub fee: u64,              // total DLMM fee (lp + protocol) across every bin crossed
    pub fee_rate_min: u128,    // lowest per-bin fee_rate seen during the fold
    pub fee_rate_max: u128,    // highest per-bin fee_rate seen — rises as bins are crossed
}
```

`quote_exact_out` is a fully separate, sibling function with the equivalent input/output shape
mirrored for the opposite fixed side — not a flag on `quote_exact_in`, since the per-bin math
genuinely branches on which side is fixed.

Internally, both replicate the exact same bin-by-bin dynamic-fee decay-then-accumulate state
machine the on-chain program runs (see [Fees](/concepts/fees)) — `fee_rate_min`/`fee_rate_max`
exist specifically because a swap crossing many bins prices later bins at a higher dynamic-fee
rate than earlier ones, so a single scalar fee rate would understate what a large trade actually
pays on its later portion.

`pre_active_bin_id`/`post_active_bin_id` let a caller compute price impact against the *exact*
active bin a specific quote was taken against, even if the caller's own live state has since
moved — the quote carries its own bin-id snapshot rather than assuming the caller froze state
at quote time.

## Bounded reach, same as the real swap

`bin_arrays` is effectively capped by how many accounts you supply — the same
`MAX_BIN_ARRAYS_PER_TRAVERSAL` (6) the real `swap_exact_in` instruction enforces. A quote that
can't fill within the supplied arrays returns `QuoteError::InsufficientLiquidity`, signaling the
trade needs to be split across on-chain transactions — this is a correct, expected signal, not
a bug to special-case away.

## Feature flags: native vs. WASM

```toml theme={null}
[features]
default = ["dep:bytemuck"]
wasm = ["dep:wasm-bindgen", "dep:serde", "dep:serde-big-array", "dep:tsify"]
```

* **`default` (native)**: `Bin`/`BinArray` derive `bytemuck::Pod`/`Zeroable` and are
  `#[repr(C)]` — laid out for zero-copy reinterpretation of raw account bytes, if you're
  decoding directly from a fetched `AccountInfo` in Rust.
* **`wasm`**: swaps those derives for `serde`/`tsify`, so the same structs cross a WASM
  boundary as plain JS objects — this is the mode the TypeScript SDK's build uses.

```bash theme={null}
# Native Rust consumer
cargo add picon-dlmm-quote

# WASM build, if you need it from a non-Rust, non-TS environment
cargo build -p picon-dlmm-quote --target wasm32-unknown-unknown \
    --no-default-features --features wasm --release
```

## Errors

```rust theme={null}
pub enum QuoteError { Overflow, InsufficientLiquidity, ZeroSwapAmount }
```

Implements `std::error::Error` natively. Under the `wasm` feature, it converts to a real thrown
JS `Error` rather than surfacing as a silent `undefined` or a panic.

<Warning>
  There is currently no automated cross-check against the on-chain program's own test vectors —
  the manual-drift risk between the two implementations (on-chain vs. this crate) is accepted,
  not eliminated. If your integration is high-value, budget for your own verification against
  real on-chain execution — see [Aggregators and
  routers](/integrations/aggregators-and-routers#test-against-real-execution-not-just-review)
  for why this matters in practice.
</Warning>
