mainnet is not live yetdevnet beta is live at play.dust.bid
skip to content

SDK ​

@dust/sdk is the TypeScript client for the dust program. version 0.3.0. ESM, Node 22 or newer.

install: published at launch. the package is not on npm yet. the source builds with npm install and npm run build.

the SDK depends on two packages, pinned: @coral-xyz/anchor 0.31.1 and @solana/web3.js 1.98.4. use one copy of web3.js in your tree. two copies break instanceof PublicKey.

it is offline first and key free. the one exception is PythProClient. it is server side only and takes its API key from the caller. nothing in the package logs, embeds in a URL or stores a secret.

what is in the box ​

modulewhat it gives you
constantsprogram ids, seeds, enums, scales, the compiled limits a client needs, Pyth constants
pdaexchangePda, vaultPda, dustMintPda, instrumentPda, userPda, positionPda, requestPda, programDataPda, pythStoragePda, associatedTokenAddress
accountsdecoders for the six account types and the queue ring, plus views for staking, referrals and wallet limits, and vaultIdentityHolds
eventsparseDustEvents and eventsOf. all 34 events typed
errorsDustErrors with all 59 codes, parseDustErrorFromLogs, isDustError
timingtReq, tsStar, planTs, planTsBundled, cancelAllowed, the soundness helpers
mathbit exact ports of the program's bustPrice, coreWin, coreLoss, winFee, splitFee, refSplit
orders, limitsports of the order rules and the per wallet limit rules
validateerror prediction in the program's own check order: checkRequestOpen, checkPlaceLimit, checkRequestDustRound, checkFillOrder, checkSetTrigger and more
pythmessage parsing, the ed25519 precompile builder, and PythProClient for the exact tick at a time
ixDustIx: a synchronous builder for every one of the 49 instructions
txDustTx: bundled transactions with the ed25519 instruction at index 0, size checks, signing
agentagent key and session key registration, checkMarginRequest, checkCloseRequest
clientDustClient: typed reads, bundled request helpers and settle planners

the IDL is also exported at @dust/sdk/idl. the SDK build checks that its copy matches the program's IDL byte for byte.

read the exchange ​

DustClient.connect takes any object with getAccountInfo. a web3.js Connection works as is. it reads the Pyth treasury from the storage account, so it is correct on devnet and on mainnet.

ts
import { Connection } from '@solana/web3.js';
import { DustClient } from '@dust/sdk';

const conn = new Connection(process.env.SOLANA_RPC_URL!);   // a devnet RPC
const dust = await DustClient.connect(conn);

const ex = await dust.getExchange();
console.log('house cash', ex.ledger.lpCash);               // bigint, micro USDC
console.log('the line', ex.ledger.queueTotal);
console.log('tracked LP', ex.ledger.trackedLp);

const btc = await dust.getInstrument(0);                    // 0 BTC, 1 ETH, 2 SOL
console.log('hwm', btc.oracleTsHwm, 'delay', btc.delayMs, 'guard', btc.guardMs);

const check = await dust.checkVaultIdentity();              // vault tokens equal the ledger buckets
console.log(check.ok);

the decoded accounts use camelCase names and bigint for numbers. other reads: getUser(owner), getPosition(owner, id), getRequest(owner, id), getQueue(), getClock(), nextId(owner).

open a bet ​

a request is one transaction: [ed25519, compute budget, touch_oracle(latest tick), request_open]. the touch makes the request self refreshing. requestOpenBundled builds it and tells you the ts* the program will assign.

ts
import { Keypair } from '@solana/web3.js';
import { DustClient, PythProClient, Side, USDC, sendBuiltTransaction } from '@dust/sdk';

const pyth = new PythProClient({ apiKey: process.env.PYTH_PRO_API_KEY! });  // server side only

const { built, plan } = await dust.requestOpenBundled(pyth, {
  authority: user.publicKey,        // the owner, a session key or an agent key
  payer: relayer.publicKey,         // pays fees and rent. can be the user
  owner: user.publicKey,
  instrumentId: 0,
  side: Side.Long,                  // 0 long (MOON), 1 short (DUST)
  leverage: 100,
  margin: 50n * USDC,
  tpPrice: 0n,                      // optional take profit and stop loss, 0 for none
  slPrice: 0n,
});
const sig = await sendBuiltTransaction(conn, built, [relayer, user]);
console.log('price locks at', plan.tsStarMs);
  • a take profit or stop loss in the request is armed at the fill. an invalid one is dropped and the bet still opens.
  • if the latest tick is older than the chain's hwm, the helper sends the request plain when the hwm is still fresh. otherwise it throws StaleHwmError, which says how long to wait.
  • limit orders, closes, triggers and cancels have the same bundled helpers: placeLimitBundled, requestCloseBundled, setTriggerBundled, cancelOrderBundled, requestDustRoundBundled.

settle it ​

anyone can settle. once the exact tick at ts* exists, settleRequest picks settle_open, settle or settle_dust_round from the request kind, fetches the tick or ticks, and fills in the referrer account for you.

ts
const exchange = await dust.getExchange();
const request  = (await dust.getRequest(user.publicKey, 0n))!;
const position = (await dust.getPosition(user.publicKey, 0n))!;

const settle = await dust.settleRequest(pyth, {
  settler: keeper.publicKey,        // pays the Pyth fee and the transaction fee
  request,
  position,
  queue: exchange.queue,
});
await sendBuiltTransaction(conn, settle, [keeper]);

the position and request of a Core bet share one id. a DUST MODE round has a request and no position, so pass the request only. the settler needs a UserAccount only if the close lands on a busted price.

keeper helpers: findLiquidatingTick, findTriggeringTick, findFillingTick, fillOrderTx, executeTriggerTx. settlement and keepers says what each pays.

read what happened ​

ts
import { eventsOf, parseDustErrorFromLogs } from '@dust/sdk';

const tx = await conn.getTransaction(sig, { maxSupportedTransactionVersion: 0 });
const logs = tx?.meta?.logMessages ?? [];

for (const e of eventsOf(logs, 'Closed')) {
  console.log(e.data.pnl, e.data.via, e.data.exitPx);     // via: 0 settle, 1 take profit, 2 stop loss, 3 frozen
}
console.log(parseDustErrorFromLogs(logs));                 // { name, code, ... } for a failed transaction

classify a stop loss by Closed.via, never by comparing the exit price with the trigger. a stop closes at the tick, not at its trigger.

predict an error before you send ​

validate mirrors the program's checks in the program's order. it gives the exact error name and code, so a refusal costs nothing.

ts
import { checkFillOrder } from '@dust/sdk';

const verdict = checkFillOrder({ ex, inst, user, pos, ctx: { nowS, slot } }, { tsMs: tick.tsMs, price });
if (!verdict.ok) console.log(verdict.error.name, verdict.error.code);   // for example OrderWindow 6051

no wallet users ​

deposit and set_referrer take a separate payer. a wallet with no SOL can sign once while the relayer pays the rent. payer defaults to owner.

ts
dx.ix.setReferrer({ owner, payer: relayer, referrer });
dx.ix.deposit({ owner, payer: relayer, ownerAta, usdcMint, amount: 100n * USDC, referrer });

AI agent keys ​

registerAgentKeyIx builds the owner's registration of an agent key with its caps. see AI agents.

tests ​

the SDK has unit tests and end to end tests that run the production program in LiteSVM, with vectors computed by the Rust code. npm run test:live runs one test against the real Pyth Pro API and needs a key in the environment.