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

program ​

one Anchor 0.31.1 program. program id q8WWu9FefmPYtLrz4mKRBYVEX5apB4UmRW1F3TxmaW3. the production IDL has 49 instructions, 6 account types, 34 events and 59 errors. two mock feed instructions exist only in a test build and are not in the production program.

addresses has the accounts. SDK has typed builders for every instruction.

conventions ​

thingrule
USDCu64 micro USDC, 6 decimals. $5 is 5000000
priceu64 in units of 1e-9 USD. $83,000 is 83000000000000
$DUSTu128 ledger units, 1e-6 $DUST
side0 long (MOON), 1 short (DUST)
trigger kind0 take profit, 1 stop loss
timemilliseconds. the Solana clock is whole seconds times 1000
updatethe Pyth Pro Solana format signed message as bytes, at most 512 bytes
authoritythe owner, a registered session key, or a registered agent key

transactions that read a price ​

  • the ed25519 precompile instruction must be at index 0. it carries one signature entry per signed update.
  • one price reading instruction per transaction. settle_dust_round is the exception: it has two updates and two signature entries, 0 and 1.
  • the instruction signer that is marked writable pays the Pyth fee, 1 lamport per update.
  • a request is made self refreshing by sending [ed25519, touch_oracle, request_*].
  • the price reading accounts are oracle (the Pyth storage account) and the lazer group: the Pyth program, the Pyth treasury, the system program and the instructions sysvar.

sizes and cost, measured on devnet: a bundled request is 851 bytes. the whole first trade is one transaction of 1,141 bytes against a limit of 1,232, and 117k compute units. simulated compute units: touch_oracle 28,049, settle_open 43,690, fill_order 39,065, liquidate 47,657, harvest 10,459.

trading ​

instructionargssignerwhat it does
request_openinstrument_id u8, side u8, leverage u32, margin u64, tp_price u64, sl_price u64authority and payerlocks margin, reserves open interest and the wallet's notional, writes a Position (pending open) and a Request with ts_star. tp and sl are held and armed at the fill. an invalid one is dropped and the bet still opens
settle_openupdateanyfills at the tick at ts_star. runs the quality gate, which can void the open. refunds and closes the position if the instrument is retired
request_closenoneauthority and payermoves an Open position to pending close and writes a Request. an agent key can close only positions it opened
settleupdateanycloses at the tick at ts_close, or at the frozen price on a retired instrument. a busted price dusts the bet
liquidateupdateany with a UserAccountcloses a bet with a tick past its bust price. pays the bounty
cancel_requestnoneowner onlycancels a stuck request under the soft, hard or retire rule

main failures: request_open fails with TradingNotLive, OpensGated, InstrumentInactive, OracleStale, BadLeverage, MarginTooSmall, NotionalTooLarge, WalletNotional, OiCap, InsufficientBalance, BadSession, BadAgent, AgentMarginCap, AgentLeverageCap, AgentBudget. settle fails with TsMismatch, UseLiquidate, BadAccount, ReferrerMissing. liquidate fails with TsTooOld, NotBusted, BadAccount.

orders ​

instructionargssignerwhat it does
place_limitinstrument_id u8, side u8, leverage u32, margin u64, limit_price u64, ttl_ms u64, tp_price u64, sl_price u64authority and payerlocks margin and writes a resting Position. no open interest until the fill. active_ts is the earliest allowed tick. ttl_ms is 5,000 to 86,400,000
fill_orderupdateanyopens the order at exactly the limit price when a tick crosses it inside the order's window. no bounty
cancel_ordernoneauthoritywrites cancel_ts, a future tick. fills on earlier ticks stay valid
expire_ordernoneanyrefunds margin once no tick can still fill the order. closes the position account
set_triggerkind u8, price u64authoritysets, changes or cancels (price 0) a take profit or stop loss on an Open or Resting position. takes effect on a future tick
execute_triggerupdateany, with a UserAccount unless it is the ownercloses the position when a tick crosses an armed trigger. a take profit at exactly its price, a stop loss at the worse of its price and the tick. pays the bounty

main failures: place_limit fails with BadTtl, BadTrigger, OutOfBounds for a limit price of 0 or above 10^15. fill_order fails with OrderWindow, TickNotRecent, NotTriggered, UseLiquidate, OiCap. set_trigger fails with BadTrigger, TriggerPending. execute_trigger fails with TickNotRecent, NotTriggered, UseLiquidate.

DUST MODE ​

instructionargssignerwhat it does
request_dust_roundinstrument_id u8, side u8, leverage u32, horizon_ms u32, margin u64authority and payerlocks margin and writes a Request with the entry tick ts_open and the exit tick ts_open + horizon. leverage 5000 or 10000. horizon 5000, 15000 or 60000
settle_dust_roundupdate_entry, update_exitanysettles the round on the entry and exit ticks in one transaction. an entry tick that fails the quality gate voids the round
cancel_requestnoneowner onlycancels a stuck round. margin back

main failures: DustModeDisabled, BadLeverage, BadHorizon, DmCellDisabled, MarginTooSmall, NotionalTooLarge (above the DUST MODE margin cap), DmRateLimit, DmWalletMargin, OiCap.

funds and keys ​

instructionargssignerwhat it does
depositamount u64owner and payermoves USDC from the owner's token account to the vault and credits the ledger. the first deposit pays the 1 USDC activation fee. the amount after the fee must be at least 10 USDC. payer funds the UserAccount rent
withdrawamount u64owner onlymoves USDC from the vault to any token account of the USDC mint. at least 10 USDC, or the whole balance. never pausable
register_session_keykey pubkey, expiry i64ownerat most 30 days. the zero key revokes. the key must differ from the owner and the agent key
register_agent_keykey pubkey, expiry i64, max_margin u64, max_leverage u32, budget u64ownersee AI agents
claim_devnoneanypays the accrued dev fee to a token account owned by the dev recipient

staking ​

instructionargssignerwhat it does
stakeamount u128ownermoves $DUST from the free balance to the stake. instant
unstakeamount u128ownermoves it back. instant
claimnoneownermoves pending staker USDC to the balance. Nothing if there is none
push_feesnoneanymoves the staker fees and the house overflow above the reward cap to the stakers
harvestmax_entries u32anypays the head of the line. 1 to 32 entries. the UserAccounts of the head entries travel as remaining accounts in order

referral ​

instructionargssignerwhat it does
set_referrerreferrer pubkeyowner and payernames the referrer once, before the first deposit. never yourself. creates both accounts if missing. send it as [set_referrer, deposit]
claim_referralnoneownermoves the claimable referral USDC to the balance
admin_set_ref_tiertarget pubkey, tier u8ownertier 0 or 1, which is 20% or 40% of the dev cut

main failures: SelfReferral, ReferrerAlreadySet, ReferrerMissing on the first deposit of a referred wallet, BadTier.

oracle ​

instructionargssignerwhat it does
touch_oracleid u8, updateanyverifies a tick and advances the high water mark. settles nothing. emits OracleTouched. fails on a retired instrument

admin ​

owner:

instructionargs
genesis_finalizestart_delay_slots u64
admin_set_feeswin_fee_bps u16, loss_fee_bps u16. each at most 500
admin_set_reward_capcap u64. $250,000 to $100,000,000
admin_set_dm_basisbps_5000 u16, bps_10000 u16. each 0 to 10000
admin_set_rolesowner, pause_guardian, risk_keeper, house_keeper, dev_recipient. each optional
admin_list_instrumentfeed pin, timing, impact, oracle gate and DUST MODE cells
admin_set_impactid u8, impact values
admin_set_timingid u8, timing values
admin_set_dm_paramsid u8, the 2 by 3 grid of deadband, haircut and curve values
admin_set_oracle_cfgid u8, min_publishers u16, conf_k_x10 u8, max_conf_cbp u16
admin_set_ref_tieras above

owner or risk keeper:

instructionargs
admin_set_min_marginmin_margin u64
admin_set_limitsmax_notional u64, max_dm_margin u64
admin_set_oi_capsid u8, cap_long u64, cap_short u64
admin_set_dm_capsid u8, cap_long u64, cap_short u64
admin_set_delayid u8, delay_ms u32
admin_set_dust_modeenabled bool. enabling is owner only
admin_set_dm_cellsid u8, mask u8. setting a bit is owner only
admin_set_wallet_limitsmax_dm_live_margin_wallet u64, max_dm_rounds_window u16, max_wallet_notional u64. the risk keeper may only lower

pause guardian:

instructionargs
retire_instrumentid u8, update. permanent
set_opens_gateclosed bool. the guardian or the owner closes. only the owner opens

upgrade authority only: initialize with InitializeArgs { owner, pause_guardian, risk_keeper, dev_recipient }.

the bounds of each admin value are in parameters. what each key can do is in security.

the accounts of each instruction ​

w writable, s signer, ? optional. the order is the IDL order.

request_open        authority[s], payer[ws], exchange[w], instrument[w], user[w], position[w], request[w], system_program
settle_open         settler[ws], exchange[w], instrument[w], user[w], position[w], request[w], req_rent_receiver[w], pos_rent_receiver[w], oracle, lazer
request_close       authority[s], payer[ws], instrument, user, position[w], request[w], system_program
settle              settler[ws], exchange[w], instrument[w], user[w], settler_user[w?], referrer_user[w?], position[w], request[w], queue[w], req_rent_receiver[w], pos_rent_receiver[w], oracle, lazer
liquidate           liquidator[ws], exchange[w], instrument[w], user[w], liquidator_user[w], referrer_user[w?], position[w], close_request[w?], queue[w], pos_rent_receiver[w], req_rent_receiver[w], oracle, lazer
cancel_request      authority[s], exchange[w], instrument[w], user[w], request[w], position[w?], req_rent_receiver[w], pos_rent_receiver[w]
place_limit         authority[s], payer[ws], exchange[w], instrument, user[w], position[w], system_program
fill_order          executor[ws], exchange, instrument[w], user[w], position[w], oracle, lazer
cancel_order        authority[s], instrument, user, position[w]
expire_order        signer[s], exchange[w], instrument, user[w], position[w], pos_rent_receiver[w]
set_trigger         authority[s], instrument, user, position[w]
execute_trigger     executor[ws], executor_user[w?], exchange[w], instrument[w], user[w], referrer_user[w?], position[w], close_request[w?], queue[w], pos_rent_receiver[w], req_rent_receiver[w], oracle, lazer
request_dust_round  authority[s], payer[ws], exchange[w], instrument[w], user[w], request[w], system_program
settle_dust_round   settler[ws], exchange[w], instrument[w], user[w], referrer_user[w?], request[w], queue[w], req_rent_receiver[w], oracle, lazer
deposit             owner[s], payer[ws], exchange[w], user[w], vault[w], usdc_mint, owner_ata[w], token_program, system_program, referrer_user[w?]
withdraw            owner[s], exchange[w], user[w], vault[w], usdc_mint, destination[w], token_program
set_referrer        owner[s], payer[ws], user[w], referrer_user[w], system_program
claim_referral      owner[s], exchange[w], user[w]
register_*_key      owner[s], user[w]
stake / unstake     owner[s], exchange[w], user[w]
claim               owner[s], exchange[w], user[w]
push_fees           signer[s], exchange[w], queue
harvest             signer[s], exchange[w], queue[w]        + the head entries' UserAccounts as remaining accounts
claim_dev           signer[s], exchange[w], vault[w], usdc_mint, dev_token[w], token_program
touch_oracle        signer[ws], instrument[w], oracle, lazer
retire_instrument   guardian[ws], exchange, instrument[w], oracle, lazer

referrer_user is required when the bet's owner has a referrer and the referrer is not the settler, liquidator or executor. if it is the same wallet, send the one account and omit referrer_user.

the six accounts ​

accountimportant fields
Exchangephase, opens_enabled, dust_mode_enabled, transfers_enabled, the roles, usdc_mint, vault, dust_mint, queue, d0, r0, fees (win and loss bps, bounty, flat_threshold, reward_cap, tail_scale, r0u), ledger, min_margin, max_notional, max_dm_margin, the three per wallet limits, default_oi_cap, dm_default_cap, dm_basis_bps
Exchange.ledgerbalances_total, margin_total, lp_cash, side_bucket, staker_pending, staker_reserve, dev_acc, queue_total, tracked_lp, tail_hwm, dust_supply, total_staked, acc, ref_pending
Instrumentid, symbol, retired, frozen_price, frozen_ts, cadence_ms, delay_ms, guard_ms, oracle_ts_hwm, hwm_slot, last_price, feed_id, channel_id, min_publishers, the confidence ring, impact values, DUST MODE cells, oi_long, oi_short, caps, exec_lookback_ms, dm_cell_mask
UserAccountbook (balance, dust, staked, snap, pending, ref_usdc, ref_earned_usdc, ref_earned_dust), session_key, agent_key and its caps, referrer, ref_tier, next_id, dm_live_margin, open_notional
Positionowner, id, instrument, side, status, leverage, margin, notional, entry_price, bust_price, entry_ts, ts_close, by_agent, the limit order fields, the take profit and stop loss slots
Requestowner, id, kind, instrument, side, leverage, margin, horizon_ms, t_req_ms, ts_star, position, rent_payer, by_agent

position status: 0 pending open, 1 open, 2 pending close, 3 resting. request kind: 0 open, 1 close, 2 DUST MODE. the QueueRing is a zero copy ring of head, len and 4096 entries of user, amount, seq.

events ​

use the SDK's parseDustEvents to decode them. an event is emitted by emit! and read from the transaction logs.

trading ​

eventfields
OpenRequestedpos, user, inst, side, margin, leverage, ts_star, by_agent, clock_ms, t_req_ms
Openedpos, entry, bust, ts_open, fallback, via, tp, sl, tick_price, executor. via is 0 market, 1 limit
CloseRequestedpos, ts_close, clock_ms, t_req_ms
Closedpos, user, pnl, kind, fee, queued, ts_ms, fallback, by_agent, via, minted_dust, exit_px. kind 0 win, 1 loss, 2 flat. via 0 settle, 1 take profit, 2 stop loss, 3 frozen
Dustedpos, user, inst, side, leverage, margin_lost, cause, minted_dust, ts_ms, by_agent, exit_px. cause 0 bust, 1 DUST MODE loss
Cancelledrequest, user, kind
DustRequestedrequest, user, inst, side, margin, leverage, horizon_ms, ts_open, ts_exit, by_agent, clock_ms, t_req_ms
DustSettledrequest, user, outcome, entry, exit, net, by_agent. outcome 0 won, 1 dusted, 2 push
TriggerSetpos, user, kind, price, active_ts, by_agent, clock_ms
TriggerExecutedpos, user, kind, trigger_price, tick_ts, tick_price, bounty, executor, by_agent
LimitPlacedpos, user, inst, side, margin, leverage, limit, active_ts, expiry_ts, tp, sl, by_agent, clock_ms
LimitCancelRequestedpos, cancel_ts, by_agent, clock_ms
LimitExpiredpos, user, reason, by_agent. reason 0 expired, 1 cancelled, 2 retired
OracleTouchedinst, ts_ms, price, clock_ms, slot, by

the house, the line and staking ​

eventfields
Queuedseq, user, amount
Harvestedentries, q_after
Distributedpool, sweep, acc
Staked, Unstakeduser, amount, dust_after, staked_after, pending_after, total_staked_after
StakeClaimeduser, amount, balance_after, staker_reserve_after, total_staked_after
DevClaimedamount

referral and agents ​

eventfields
ReferralSetuser, referrer
RefTierSetuser, tier, by
ReferralEarnedreferrer, referee, usdc, dust, source, by_agent. source 0 activation, 1 win fee, 2 loss fee, 3 bust fee, 4 DUST MODE
ReferralClaimeduser, amount
AgentKeyChangeduser, key, expiry, max_margin, max_leverage, budget, gen

launch and admin record ​

eventfields
GenesisFinalizedd0, f, c, s, r0, start_slot
InstrumentRetiredinst, frozen_price
GateChangedclosed, by
ParamsChangedwho, key. key 1 fees, 2 reward cap, 3 roles, 4 impact, 5 timing, 6 DUST MODE basis, 7 DUST MODE parameter grid, 8 oracle gate
DmBasisChangedwho, bps_5000, bps_10000
RiskParamChangedwho, inst, key, a, b. inst 255 means exchange level. key 1 min margin, 2 DUST MODE switch, 3 limits, 4 open interest caps, 5 DUST MODE caps, 6 delay
DmCellsChangedinst, old_mask, new_mask, by
WalletLimitsChangedby, max_dm_live_margin_wallet, max_dm_rounds_window, max_wallet_notional

ParamsChanged key 7 comes from admin_set_dm_params. a change to the cell mask emits DmCellsChanged instead.

errors ​

Anchor numbers custom errors from 6000.

codenamemeaning
6000Mathmath overflow or invalid arithmetic
6001Unauthorizedunauthorized signer for this role
6002OutOfBoundsparameter outside compiled bounds
6003TradingNotLivetrading has not started (phase or start slot)
6004AlreadyFinalizedgenesis already finalized
6005PredepositOpenpre deposit window still open
6006OpensGatedopens are gated (global gate, retired instrument, or stale oracle)
6007InstrumentInactiveinstrument inactive or retired
6008AlreadyRetiredinstrument already retired
6009BadLeverageleverage out of range for this product
6010MarginTooSmallmargin below minimum
6011NotionalTooLargenotional above maximum
6012OiCapopen interest cap exceeded
6013InsufficientBalanceinsufficient balance
6014BelowMinimumamount below minimum deposit or withdraw
6015DustModeDisabledDUST MODE is disabled
6016BadHorizoninvalid DUST MODE horizon
6017BadStatewrong request or position state
6018TsMismatchprice update timestamp does not match deterministic ts*
6019TsFutureprice update timestamp is in the future
6020TsTooOldliquidation update too old (backdating bound)
6021NotBustedposition is not past its bust price
6022CancelTooEarlycancel only allowed 30 s after the request
6023QueueFullpayout queue is full
6024OracleDisabledoracle kind disabled in this build
6025OracleNotWiredoracle adapter not wired
6026OracleInvalidoracle account or update invalid
6027BadQueueAccountremaining account does not match the queue head user
6028BadAccountaccount does not belong to this exchange
6029BadSessionsession key invalid or expired
6030TransfersDisabledtoken transfers are disabled
6031Nothingnothing to do
6032OracleFeedMismatchoracle update is for a different feed id than the instrument's
6033OracleChannelMismatchoracle update is on a different channel than the instrument's
6034OracleOffGridoracle update timestamp is not on the instrument's cadence grid
6035OraclePayloadoracle payload malformed or missing a required property
6036OracleStaleoracle high water mark is stale: bundle a touch_oracle update in the same transaction
6037BadAgentagent key invalid, expired, or not allowed to do this
6038AgentMarginCapagent per trade margin cap exceeded
6039AgentLeverageCapagent leverage cap exceeded
6040AgentBudgetagent cumulative budget exhausted
6041AgentScopeagent may only close positions it opened
6042ReferrerAlreadySeta referrer is already set (or the account is activated): referrals are immutable
6043SelfReferralan account cannot refer itself
6044ReferrerMissingthe referrer account is required for this instruction and missing or mismatched
6045BadTierreferral tier must be 0 or 1
6046TriggerPendinga trigger change is still pending: wait until the oracle passes its switch time
6047BadTriggertrigger price is on the wrong side of entry, bust or limit
6048NotTriggeredthe presented tick does not cross a trigger or the limit price
6049UseLiquidatethe tick crosses the bust price: call liquidate
6050TickNotRecentthe presented tick is older than the execution lookback
6051OrderWindowthe presented tick is outside the order's active window
6052OrderNotExpiredthe order can still be filled: not expirable yet
6053AlreadyCancelleda cancel is already pending for this order
6054BadTtlorder time to live outside the allowed bounds
6055DmCellDisabledthis DUST MODE leverage by horizon cell is not enabled
6056DmRateLimittoo many DUST MODE rounds requested by this wallet in the current window
6057DmWalletMarginthis wallet's live DUST MODE margin would exceed the per wallet limit
6058WalletNotionalthis wallet's concurrent Core notional would exceed the per wallet limit

the table is generated from the IDL's error list and errors.rs. TransfersDisabled and OracleNotWired are defined and no instruction raises them in this version. Exchange.transfers_enabled is false and has no setter. OracleDisabled is raised only by a build without the Pyth feature or a mock feed in a production build.