Skip to main content

Firm Quotes

Every quote Vulcx serves carries a quoteId — a short-lived commitment to the exact route and price you were shown. Hand it back when you build the swap and the engine replays that route instead of silently re-quoting, with your minimum-out anchored to the quoted price. Within a short window you can go further and redeem the quote firm: slippage collapses to a few basis points around the quoted price, and if the market moved past that, the API refuses before you pay a transaction fee.
Firm redemption is price-or-fail, not a fill guarantee. If the pool genuinely moved, your request fails cleanly with a 409 and you re-quote — you never execute at a worse price than you committed to.

The three ways to build a swap

Where quoteIds come from

GET /api/v1/quote responses and every WebSocket quote push include:
string
The commitment ID, e.g. q_b15372a8d82a93d1e328ae24e5d3669a. Omitted when the quote can’t be pinned (e.g. split routes).
integer
How long the quoteId stays redeemable (default 3000).
integer
How long it stays redeemable with firm: true (default 400).
A quoteId minted over REST is bound to your API key — only requests authenticated with the same key can redeem it. Pair, amount, and swapMode must match the original quote exactly.

Route pinning (pass quoteId)

The returned transaction’s minAmountOut is computed from the quoted amountOut, not from a fresh re-quote — what the user approved is what the chain enforces. Your slippageBps still applies on top, so a route-pinned swap behaves exactly like today’s flow when the price hasn’t moved, and refuses to quietly degrade when it has.

Firm redemption ("firm": true)

Within firmForMs of the quote, add "firm": true to the same request. The engine re-evaluates the pinned route against live pool state:
  • Price held (or improved) → the swap builds with slippage collapsed to the server’s firm margin (default 10 bps) around the quoted price. Your slippageBps is ignored.
  • Price drifted past the margin409 Conflict, no transaction is built, and you can re-quote and retry in one round-trip. Favorable drift always passes.
The firm window is sub-second (~400 ms ≈ ten Fogo blocks). It’s designed for machine flows — session-key signing, bots, protocol integrations — where quote → sign → submit completes in tens of milliseconds. A human confirming a wallet popup can’t beat it; use route pinning (seconds-long) for popup flows.
Both POST /api/v1/swap and POST /api/v1/instructions accept quoteId and firm identically.

Error contract

The SDK surfaces these as QuoteExpiredError (410) and QuoteStaleError (409) — both non-retryable as-is; catch them, re-quote, and retry with the new quoteId.

Signed quotes

Every quote that carries a quoteId also carries an Ed25519 signature over its price (when the deployment has signing enabled — production does):
string
Base58 Ed25519 signature over the canonical quote message.
integer
The absolute expiry (unix milliseconds) embedded in the signed message.
The signature covers the UTF-8 bytes of the pipe-joined canonical message:
with mints in base58, amounts in base10, exactIn as 0/1, and expiry in unix milliseconds. The verification key is public and unauthenticated:
Pin the key and verify offline — a signed quote is provable: a partner, auditor, or dispute process can check exactly what price was committed and until when, without trusting the transport or the SDK. Verification in TypeScript:

Live invalidation on the stream

If you subscribe to the WebSocket stream, the server pushes
when the last quote it broadcast for a pair drifts past the firm margin — so your UI can stop offering the firm price the instant it dies instead of discovering it at redemption. It’s best-effort sugar: the redemption-time check is the real gate.

Why this works on Fogo

Fogo’s ~40 ms blocks mean a 400 ms firm window spans ~10 blocks of runway, and quote → commit round-trips fit inside it with session-key signing. The quoted price is enforced on-chain by the aggregator program’s balance-delta check — if the pool moved past your min-out between build and execution, the transaction fails rather than filling worse. Worst case is a failed transaction, never a worse price.
Last modified on August 31, 2026