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
| thing | rule |
|---|---|
| USDC | u64 micro USDC, 6 decimals. $5 is 5000000 |
| price | u64 in units of 1e-9 USD. $83,000 is 83000000000000 |
| $DUST | u128 ledger units, 1e-6 $DUST |
| side | 0 long (MOON), 1 short (DUST) |
| trigger kind | 0 take profit, 1 stop loss |
| time | milliseconds. the Solana clock is whole seconds times 1000 |
update | the Pyth Pro Solana format signed message as bytes, at most 512 bytes |
| authority | the 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_roundis 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 thelazergroup: 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
| instruction | args | signer | what it does |
|---|---|---|---|
request_open | instrument_id u8, side u8, leverage u32, margin u64, tp_price u64, sl_price u64 | authority and payer | locks 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_open | update | any | fills 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_close | none | authority and payer | moves an Open position to pending close and writes a Request. an agent key can close only positions it opened |
settle | update | any | closes at the tick at ts_close, or at the frozen price on a retired instrument. a busted price dusts the bet |
liquidate | update | any with a UserAccount | closes a bet with a tick past its bust price. pays the bounty |
cancel_request | none | owner only | cancels 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
| instruction | args | signer | what it does |
|---|---|---|---|
place_limit | instrument_id u8, side u8, leverage u32, margin u64, limit_price u64, ttl_ms u64, tp_price u64, sl_price u64 | authority and payer | locks 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_order | update | any | opens the order at exactly the limit price when a tick crosses it inside the order's window. no bounty |
cancel_order | none | authority | writes cancel_ts, a future tick. fills on earlier ticks stay valid |
expire_order | none | any | refunds margin once no tick can still fill the order. closes the position account |
set_trigger | kind u8, price u64 | authority | sets, changes or cancels (price 0) a take profit or stop loss on an Open or Resting position. takes effect on a future tick |
execute_trigger | update | any, with a UserAccount unless it is the owner | closes 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
| instruction | args | signer | what it does |
|---|---|---|---|
request_dust_round | instrument_id u8, side u8, leverage u32, horizon_ms u32, margin u64 | authority and payer | locks 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_round | update_entry, update_exit | any | settles the round on the entry and exit ticks in one transaction. an entry tick that fails the quality gate voids the round |
cancel_request | none | owner only | cancels 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
| instruction | args | signer | what it does |
|---|---|---|---|
deposit | amount u64 | owner and payer | moves 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 |
withdraw | amount u64 | owner only | moves USDC from the vault to any token account of the USDC mint. at least 10 USDC, or the whole balance. never pausable |
register_session_key | key pubkey, expiry i64 | owner | at most 30 days. the zero key revokes. the key must differ from the owner and the agent key |
register_agent_key | key pubkey, expiry i64, max_margin u64, max_leverage u32, budget u64 | owner | see AI agents |
claim_dev | none | any | pays the accrued dev fee to a token account owned by the dev recipient |
staking
| instruction | args | signer | what it does |
|---|---|---|---|
stake | amount u128 | owner | moves $DUST from the free balance to the stake. instant |
unstake | amount u128 | owner | moves it back. instant |
claim | none | owner | moves pending staker USDC to the balance. Nothing if there is none |
push_fees | none | any | moves the staker fees and the house overflow above the reward cap to the stakers |
harvest | max_entries u32 | any | pays the head of the line. 1 to 32 entries. the UserAccounts of the head entries travel as remaining accounts in order |
referral
| instruction | args | signer | what it does |
|---|---|---|---|
set_referrer | referrer pubkey | owner and payer | names the referrer once, before the first deposit. never yourself. creates both accounts if missing. send it as [set_referrer, deposit] |
claim_referral | none | owner | moves the claimable referral USDC to the balance |
admin_set_ref_tier | target pubkey, tier u8 | owner | tier 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
| instruction | args | signer | what it does |
|---|---|---|---|
touch_oracle | id u8, update | any | verifies a tick and advances the high water mark. settles nothing. emits OracleTouched. fails on a retired instrument |
admin
owner:
| instruction | args |
|---|---|
genesis_finalize | start_delay_slots u64 |
admin_set_fees | win_fee_bps u16, loss_fee_bps u16. each at most 500 |
admin_set_reward_cap | cap u64. $250,000 to $100,000,000 |
admin_set_dm_basis | bps_5000 u16, bps_10000 u16. each 0 to 10000 |
admin_set_roles | owner, pause_guardian, risk_keeper, house_keeper, dev_recipient. each optional |
admin_list_instrument | feed pin, timing, impact, oracle gate and DUST MODE cells |
admin_set_impact | id u8, impact values |
admin_set_timing | id u8, timing values |
admin_set_dm_params | id u8, the 2 by 3 grid of deadband, haircut and curve values |
admin_set_oracle_cfg | id u8, min_publishers u16, conf_k_x10 u8, max_conf_cbp u16 |
admin_set_ref_tier | as above |
owner or risk keeper:
| instruction | args |
|---|---|
admin_set_min_margin | min_margin u64 |
admin_set_limits | max_notional u64, max_dm_margin u64 |
admin_set_oi_caps | id u8, cap_long u64, cap_short u64 |
admin_set_dm_caps | id u8, cap_long u64, cap_short u64 |
admin_set_delay | id u8, delay_ms u32 |
admin_set_dust_mode | enabled bool. enabling is owner only |
admin_set_dm_cells | id u8, mask u8. setting a bit is owner only |
admin_set_wallet_limits | max_dm_live_margin_wallet u64, max_dm_rounds_window u16, max_wallet_notional u64. the risk keeper may only lower |
pause guardian:
| instruction | args |
|---|---|
retire_instrument | id u8, update. permanent |
set_opens_gate | closed 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, lazerreferrer_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
| account | important fields |
|---|---|
Exchange | phase, 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.ledger | balances_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 |
Instrument | id, 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 |
UserAccount | book (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 |
Position | owner, 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 |
Request | owner, 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
| event | fields |
|---|---|
OpenRequested | pos, user, inst, side, margin, leverage, ts_star, by_agent, clock_ms, t_req_ms |
Opened | pos, entry, bust, ts_open, fallback, via, tp, sl, tick_price, executor. via is 0 market, 1 limit |
CloseRequested | pos, ts_close, clock_ms, t_req_ms |
Closed | pos, 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 |
Dusted | pos, user, inst, side, leverage, margin_lost, cause, minted_dust, ts_ms, by_agent, exit_px. cause 0 bust, 1 DUST MODE loss |
Cancelled | request, user, kind |
DustRequested | request, user, inst, side, margin, leverage, horizon_ms, ts_open, ts_exit, by_agent, clock_ms, t_req_ms |
DustSettled | request, user, outcome, entry, exit, net, by_agent. outcome 0 won, 1 dusted, 2 push |
TriggerSet | pos, user, kind, price, active_ts, by_agent, clock_ms |
TriggerExecuted | pos, user, kind, trigger_price, tick_ts, tick_price, bounty, executor, by_agent |
LimitPlaced | pos, user, inst, side, margin, leverage, limit, active_ts, expiry_ts, tp, sl, by_agent, clock_ms |
LimitCancelRequested | pos, cancel_ts, by_agent, clock_ms |
LimitExpired | pos, user, reason, by_agent. reason 0 expired, 1 cancelled, 2 retired |
OracleTouched | inst, ts_ms, price, clock_ms, slot, by |
the house, the line and staking
| event | fields |
|---|---|
Queued | seq, user, amount |
Harvested | entries, q_after |
Distributed | pool, sweep, acc |
Staked, Unstaked | user, amount, dust_after, staked_after, pending_after, total_staked_after |
StakeClaimed | user, amount, balance_after, staker_reserve_after, total_staked_after |
DevClaimed | amount |
referral and agents
| event | fields |
|---|---|
ReferralSet | user, referrer |
RefTierSet | user, tier, by |
ReferralEarned | referrer, referee, usdc, dust, source, by_agent. source 0 activation, 1 win fee, 2 loss fee, 3 bust fee, 4 DUST MODE |
ReferralClaimed | user, amount |
AgentKeyChanged | user, key, expiry, max_margin, max_leverage, budget, gen |
launch and admin record
| event | fields |
|---|---|
GenesisFinalized | d0, f, c, s, r0, start_slot |
InstrumentRetired | inst, frozen_price |
GateChanged | closed, by |
ParamsChanged | who, 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 |
DmBasisChanged | who, bps_5000, bps_10000 |
RiskParamChanged | who, 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 |
DmCellsChanged | inst, old_mask, new_mask, by |
WalletLimitsChanged | by, 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.
| code | name | meaning |
|---|---|---|
| 6000 | Math | math overflow or invalid arithmetic |
| 6001 | Unauthorized | unauthorized signer for this role |
| 6002 | OutOfBounds | parameter outside compiled bounds |
| 6003 | TradingNotLive | trading has not started (phase or start slot) |
| 6004 | AlreadyFinalized | genesis already finalized |
| 6005 | PredepositOpen | pre deposit window still open |
| 6006 | OpensGated | opens are gated (global gate, retired instrument, or stale oracle) |
| 6007 | InstrumentInactive | instrument inactive or retired |
| 6008 | AlreadyRetired | instrument already retired |
| 6009 | BadLeverage | leverage out of range for this product |
| 6010 | MarginTooSmall | margin below minimum |
| 6011 | NotionalTooLarge | notional above maximum |
| 6012 | OiCap | open interest cap exceeded |
| 6013 | InsufficientBalance | insufficient balance |
| 6014 | BelowMinimum | amount below minimum deposit or withdraw |
| 6015 | DustModeDisabled | DUST MODE is disabled |
| 6016 | BadHorizon | invalid DUST MODE horizon |
| 6017 | BadState | wrong request or position state |
| 6018 | TsMismatch | price update timestamp does not match deterministic ts* |
| 6019 | TsFuture | price update timestamp is in the future |
| 6020 | TsTooOld | liquidation update too old (backdating bound) |
| 6021 | NotBusted | position is not past its bust price |
| 6022 | CancelTooEarly | cancel only allowed 30 s after the request |
| 6023 | QueueFull | payout queue is full |
| 6024 | OracleDisabled | oracle kind disabled in this build |
| 6025 | OracleNotWired | oracle adapter not wired |
| 6026 | OracleInvalid | oracle account or update invalid |
| 6027 | BadQueueAccount | remaining account does not match the queue head user |
| 6028 | BadAccount | account does not belong to this exchange |
| 6029 | BadSession | session key invalid or expired |
| 6030 | TransfersDisabled | token transfers are disabled |
| 6031 | Nothing | nothing to do |
| 6032 | OracleFeedMismatch | oracle update is for a different feed id than the instrument's |
| 6033 | OracleChannelMismatch | oracle update is on a different channel than the instrument's |
| 6034 | OracleOffGrid | oracle update timestamp is not on the instrument's cadence grid |
| 6035 | OraclePayload | oracle payload malformed or missing a required property |
| 6036 | OracleStale | oracle high water mark is stale: bundle a touch_oracle update in the same transaction |
| 6037 | BadAgent | agent key invalid, expired, or not allowed to do this |
| 6038 | AgentMarginCap | agent per trade margin cap exceeded |
| 6039 | AgentLeverageCap | agent leverage cap exceeded |
| 6040 | AgentBudget | agent cumulative budget exhausted |
| 6041 | AgentScope | agent may only close positions it opened |
| 6042 | ReferrerAlreadySet | a referrer is already set (or the account is activated): referrals are immutable |
| 6043 | SelfReferral | an account cannot refer itself |
| 6044 | ReferrerMissing | the referrer account is required for this instruction and missing or mismatched |
| 6045 | BadTier | referral tier must be 0 or 1 |
| 6046 | TriggerPending | a trigger change is still pending: wait until the oracle passes its switch time |
| 6047 | BadTrigger | trigger price is on the wrong side of entry, bust or limit |
| 6048 | NotTriggered | the presented tick does not cross a trigger or the limit price |
| 6049 | UseLiquidate | the tick crosses the bust price: call liquidate |
| 6050 | TickNotRecent | the presented tick is older than the execution lookback |
| 6051 | OrderWindow | the presented tick is outside the order's active window |
| 6052 | OrderNotExpired | the order can still be filled: not expirable yet |
| 6053 | AlreadyCancelled | a cancel is already pending for this order |
| 6054 | BadTtl | order time to live outside the allowed bounds |
| 6055 | DmCellDisabled | this DUST MODE leverage by horizon cell is not enabled |
| 6056 | DmRateLimit | too many DUST MODE rounds requested by this wallet in the current window |
| 6057 | DmWalletMargin | this wallet's live DUST MODE margin would exceed the per wallet limit |
| 6058 | WalletNotional | this 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.
