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.
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
| Module | Exports | Use |
|---|---|---|
| ids | REEL_PROGRAM_ID, REEL_HOOK_PROGRAM_ID, VAULT_AUTH, token program ids, NATIVE_MINT | Constants and the vault authority PDA |
| pda | poolPda, rootPda, launchPda, positionPda, ledgerPda, vaultPda and the rest | Every seed from PROGRAM.md §2 |
| codec | decodePool, decodePosition, decodeLaunch, decodeLedger, accountFilters, accountKind, encodeParams | Account layouts, generated from the Rust structs |
| quote | quoteSwap, minOut, maxIn | The swap pipeline, mirrored exactly |
| ix | ix.swap, ix.liquidityAdd, ix.feesCollect, ix.launchCreate and every other instruction | Instruction builders, one per tag |
| token | createAtaIdempotentIx, wrapSolIxs, unwrapSolIx, decodeTokenAccount | Token accounts without spl-token |
| errors | parseCustomError, errorName, REEL_ERRORS, HOOK_ERRORS | Program error codes by name |
| params, validate | defaultParams, BIT, BUCKET, validateParams | Build 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.
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.
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% slippageq.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
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.
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.