Skip to content
Solana

Docs · Integrations

SDK: read, quote and build

The reel TypeScript client: decode accounts, quote a swap exactly as the program would, and build instructions your wallet signs. In the repo today; the npm package is coming.

Where it lives

The SDK is the folder sdk/src in reel's repository, imported in the app as @/sdk/src. It targets @solana/web3.js 1.x and has no @solana/spl-token dependency: its own token helpers create ATAs, wrap and unwrap SOL, and decode token accounts.

The npm package is coming. Nothing is published to npm yet, and any package claiming to be reel's SDK is not ours.

Program ids come from NEXT_PUBLIC_REEL_PROGRAM_ID and NEXT_PUBLIC_REEL_HOOK_PROGRAM_ID. This deployment's values: reel Ajvhesd3q6yJFgnBL6vppAJDF7gP6nmksA8WkrTM4vcJ, reel-hook oF7b3JDvBhT34SjHPsC1xiZrkgNKwFzPncTxk2TYGdx. Until the programs are deployed there is nothing on mainnet to call; use LiteSVM, solana-test-validator or simulation.

What's in it

ModuleExportsUse
idsREEL_PROGRAM_ID, REEL_HOOK_PROGRAM_ID, VAULT_AUTH, token program ids, NATIVE_MINTConstants and the vault authority PDA
pdapoolPda, rootPda, launchPda, positionPda, ledgerPda, vaultPda and the restEvery seed from PROGRAM.md §2
codecdecodePool, decodePosition, decodeLaunch, decodeLedger, accountFilters, accountKind, encodeParamsAccount layouts, generated from the Rust structs
quotequoteSwap, minOut, maxInThe swap pipeline, mirrored exactly
ixix.swap, ix.liquidityAdd, ix.feesCollect, ix.launchCreate and every other instructionInstruction builders, one per tag
tokencreateAtaIdempotentIx, wrapSolIxs, unwrapSolIx, decodeTokenAccountToken accounts without spl-token
errorsparseCustomError, errorName, REEL_ERRORS, HOOK_ERRORSProgram error codes by name
params, validatedefaultParams, BIT, BUCKET, validateParamsBuild a PoolParams and check it against the PoolInit rules before you send

Quotes are checked against test vectors the program's own tests produce (sdk/test-vectors/swaps.json), so a quote and a swap agree to the atom.

Read every pool

Every reel account starts with an 8-byte tag, so one getProgramAccounts call with the tag and size finds them all. accountFilters builds that filter.

ts
import { Connection } from "@solana/web3.js"
import { REEL_PROGRAM_ID, accountFilters, decodePool } from "@/sdk/src"

const connection = new Connection(rpcUrl, "confirmed")
const accounts = await connection.getProgramAccounts(REEL_PROGRAM_ID, {
  filters: accountFilters("Pool"),
})
const pools = accounts.map((a) => ({ address: a.pubkey, pool: decodePool(a.account.data) }))

A pool's address is also derivable: poolPda(root, baseMint, quoteMint, index), where reel's shared root is rootPda(0).

Quote a swap

quoteSwap clones the pool, rolls its time-based state forward to now and slot (the LP stream, the dynamic-fee reference, the Storm sample, the managed fee), then prices the swap.

ts
import { decodePool, minOut, quoteSwap } from "@/sdk/src"

const info = await connection.getAccountInfo(poolAddress)
if (!info) throw new Error("No pool at that address")
const pool = decodePool(info.data)
const slot = BigInt(await connection.getSlot("confirmed"))
const time = await connection.getBlockTime(Number(slot))
if (time === null) throw new Error("No block time for that slot")

// Buy with 1 SOL (9 decimals), exact-in.
const q = quoteSwap(pool, 1_000_000_000n, { now: BigInt(time), slot, buy: true })
const limit = minOut(q.output, 50) // 0.5% slippage

q.out breaks the swap into every charge: LP fees, the protocol cut, burn, recapture and buckets. Pass the extra inputs a pool's blocks need: oracle (Q64.64) for Market Guard, recapRef for recapture, hours for the market-hours fee, exitAge for Exit Clock. For exact-out, pass exactOut: true; q.input is what you pay, and maxIn(q.input, bps) is the limit.

Build the swap

ts
import { createAtaIdempotentIx, ix, poolRef, wrapSolIxs } from "@/sdk/src"

const ref = poolRef(poolAddress, pool)
const wrap = wrapSolIxs(wallet, wallet, 1_000_000_000n) // wSOL in the same transaction
const base = createAtaIdempotentIx(wallet, wallet, pool.baseMint, pool.baseProgram)
const swapIx = ix.swap({
  user: wallet,
  pool: ref,
  userBase: base.ata,
  userQuote: wrap.ata,
  side: 0, // 0 buy (quote → base), 1 sell
  mode: 0, // 0 exact-in, 1 exact-out
  amount: 1_000_000_000n,
  limit,
})
const instructions = [...wrap.ixs, base.ix, swapIx]

When the base mint carries reel's transfer hook, ix.swap appends the seven hook accounts itself (hookExtras). Optional slots go in extra: the launch account (writable during a guard), the Wallet Cap counter, the seller's holder account, the oracle, a sibling pool, the advisory accounts and ix.referralAccounts(trader, referrer, quoteMint). Unused slots are filled with the reel program id.

Simulate and read errors

Simulate every transaction before you ask a wallet to sign it. A refused swap fails with a custom error code; parseCustomError turns the logs into its name.

ts
import { parseCustomError } from "@/sdk/src"

const sim = await connection.simulateTransaction(tx)
if (sim.value.err) {
  const name = parseCustomError((sim.value.logs ?? []).join("\n")) // "SlippageExceeded", "BuysPaused"…
}

Codes 6000 and up are reel's, 7000 and up are reel-hook's. Read Errors and events.