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

architecture ​

dust is one Anchor program, one USDC vault and one $DUST mint. Around them sit roles that anyone can run: keepers that settle, liquidate and fill. A relayer pays gas, an indexer reads the events and a price feed draws the chart.

the program has 49 instructions, 6 account types, 34 events and 59 errors. it is built with Anchor 0.31.1.

the pieces ​

  player wallet
      |   one signature, then a session key signs the bets
      v
  app ==> relayer (fee payer, builds the bundle) ==> Solana RPC
                                                         |
  keepers K1 K2 K3 K4 K7 K7b (anyone can run) ========> |
                                                         v
     +================================================================+
     |  dust program                      49 instructions             |
     |  Exchange   Instrument x N   UserAccount   Position   Request  |
     |  QueueRing (the line)   USDC vault   $DUST mint (Token 2022)   |
     +================================================================+
          ^                                       |
          | signed price tick, verified by CPI    | events
          |                                       v
   Pyth Pro verifier program                   indexer ==> feed, boards, cards

  price feed (server side Pyth stream) ==> the chart in the app. it never settles a bet.
  K6 watchdog reads the chain and the keepers, and signs nothing.
piecewhat it iswhat it can touch
dust programone Anchor program, program id q8WWu9FefmPYtLrz4mKRBYVEX5apB4UmRW1F3TxmaW3all state below
USDC vaulta token account at the PDA ["vault"], token authority is the Exchange PDAdebited only by withdraw and claim_dev
$DUST minta Token-2022 mint at the PDA ["dust_mint"], 6 decimals, authority is the Exchange PDA, supply 0at launch $DUST lives in the ledger. the program has no instruction that mints it to a wallet
Pyth Pro verifierthe official verifier program pytd2yyk641x7ak7mkaasSJVXh6YYZnC7wTmtgAyxPt and its storage accountchecks signatures on price ticks
keepersPM2 processes that hold only SOLcall permissionless instructions
relayera service that signs as fee payerpays fees and rent. never an authority over funds
indexerreads finalized transactions, decodes events, serves an APIread only
price feeda server that streams Pyth Pro prices for displaynothing on chain

accounts ​

accountseedssizeholds
Exchange["exchange"]656 Broles, the ledger buckets, fee parameters, genesis values, global limits and switches. sizes include the 8 byte discriminator
Instrument["instrument", id], id is one byte395 Bfeed pin, timing, the confidence ring, impact and DUST MODE cells, open interest, caps, retire state
UserAccount["user", owner]313 BUSDC balance, $DUST, stake, referrer, session and agent keys, wallet limit counters
Position["pos", owner, id], id is a u64 in little endian237 Ba Core position or a resting limit order, with its take profit and stop loss slots
Request["req", owner, id]161 Ba pending open, close or DUST MODE round, with its target tick ts_star
QueueRinga plain program owned account, created before initialize196,632 Bthe FIFO payout line, 4096 entries

instrument ids are PDA seeds, not Pyth feed ids. id 0 is BTC, 1 is ETH, 2 is SOL. the Pyth feed ids are 1, 2 and 6.

a DUST MODE round is one Request account and no Position. a limit order is a Position with status Resting.

where the money sits ​

every dollar in the vault is in exactly one bucket:

vault tokens = A + Mg + LP + SB + SF + SP + DF + RF
bucketfieldmeaning
Abalances_totalsum of every UserAccount balance
Mgmargin_totalmargin locked in pending and open bets, DUST MODE rounds and resting orders
LPlp_cashfree cash of the house
SBside_buckethouse cash earmarked for the line
SFstaker_pendingstaker fees collected, not yet pushed
SPstaker_reservepushed staker rewards, not yet claimed
DFdev_accdev fees, not yet claimed
RFref_pendingreferral USDC owed, not yet claimed

the line Q (queue_total) is a debt, not a bucket. it is never part of the vault. K6 checks the identity once a minute.

all user USDC is a ledger entry. the vault token account moves only in deposit, withdraw and claim_dev.

how a bet moves ​

every price event is two steps: a request, then a settle.

  1. request. the program locks margin, reserves open interest and writes a target tick time ts_star. it reads no price.
  2. settle. anyone presents the signed tick whose timestamp is exactly ts_star. the program verifies it and fills at that price.
actionrequestsettle
openrequest_opensettle_open
closerequest_closesettle
DUST MODE roundrequest_dust_roundsettle_dust_round, with the entry tick and the exit tick in one transaction
bustnoneliquidate, with any tick past the bust price
limit orderplace_limitfill_order, at exactly the limit price
take profit, stop lossset_triggerexecute_trigger

the timing rule behind ts_star is in pricing oracle. the rules for who settles and what it pays are in settlement and keepers.

the keepers ​

every instruction a keeper sends has no role check. any wallet can send it. keepers hold only SOL.

roleprocessinstructionsneeds
K1 settledust-k1settle_open, settle, settle_dust_roundSOL, a Pyth Pro key, a tick archive
K2 liquidatedust-k2liquidateSOL and a UserAccount, because the bounty is credited there
K3 heartbeatdust-k3touch_oracleSOL. a touch with one Pyth update costs 10,000 lamports
K4 distributedust-k4harvest, push_fees, claim_devSOL
K6 watchdogdust-k6noneno key. it only reads
K7 and K7b order executordust-k7, dust-k7bfill_order, execute_trigger, expire_orderSOL and a UserAccount for the trigger bounty. two instances with two different keys

K1 archives every tick it sees, so any ts_star can be served. before it settles a close it checks the archive for a bust or a trigger crossing in the bet's window. K3 spaces its touches at least 200 ms apart while anything is live, and 1 to 8 s apart when idle.

the numbering keeps K5 for the risk role. that role is a key that tunes caps, not a process.

the relayer ​

the relayer is a fee payer. it builds the transaction, signs as payer, sends it and reports the status.

  • first trade in one bundle. a new player with no SOL signs once. the transaction is [ed25519, set_referrer, deposit, register_session_key, touch_oracle, request_open]. it is 1,141 bytes, the limit is 1,232.
  • what it pays. 2,240,280 lamports of rent for each new UserAccount, which is never refunded. 15,001 lamports for each user signed bundled request.
  • what it cannot do. the payer is never an authority, a seed or a destination, and no instruction credits it. the owner's signature authorises every movement of the owner's funds.
  • it is not a gate. requests are permissionless. the relayer pays fees, nothing more. you can pay your own.

the indexer ​

the indexer reads finalized transactions, decodes the events with the SDK, stores them in SQLite and serves an HTTP and WebSocket API: feed, boards, house numbers and trade cards.

  • finalized transactions are the only writer. a confirmed preview is pushed to WebSocket clients and never stored.
  • one database per program id. a database stamped with another program is refused.
  • it is a view. the chain is the record. everything it holds can be rebuilt from the raw log.

the price feed ​

the price feed streams Pyth Pro prices for the chart and serves history bars. the browser never talks to Pyth. the Pyth Pro key stays on the server.

a bet never fills on the feed. a fill uses only the signed tick that the program verifies on chain.

what is not in the program ​

no raw action passthrough, no relayer gated trading, no manual credit, no sweeper, and no admin signature that can move a user balance. the full list of powers is in security.