settlement and keepers
a bet is two steps. the request locks margin and names a target tick. the settle presents that tick and fills. this page covers the second step: who does it, what it pays, and what happens when it does not happen.
request, then settle
| what | request | settle | the settle needs |
|---|---|---|---|
| open | request_open | settle_open | the tick at ts_star |
| close | request_close | settle | the tick at ts_close |
| DUST MODE round | request_dust_round | settle_dust_round | the entry tick and the exit tick |
| bust | none | liquidate | any tick past the bust price in the bet's window |
| limit order | place_limit | fill_order | a tick that crosses the limit, inside the order's window |
| take profit, stop loss | set_trigger | execute_trigger | a tick that crosses the trigger |
the settle is only ever the exact tick the timing rule named. the settler cannot choose a tick. see pricing oracle.
who can settle
anyone. there is no settler role. the signer pays the Pyth fee of 1 lamport per update and the transaction fee.
a few accounts matter:
- the bounty account. if a close lands on a busted price, the bounty is credited to the settler's own UserAccount. a settler without one fails with
BadAccount. the position owner needs none, because a self settle pays the owner. - the referrer account. if the owner has a referrer, the referrer's UserAccount must travel with the settle, unless the referrer is the settler. without it the transaction fails with
ReferrerMissing. - rent. the Position and Request accounts refund their rent to whoever paid it at request time, not to the settler.
liquidations
liquidate is open to anyone. it takes a signed tick and a UserAccount for the liquidator.
the tick is valid when:
- its timestamp is after the entry tick
- if a close is pending, its timestamp is not after
ts_close - its price is at or past the bust price
the tick does not have to be recent. the history decides, not the clock. a pending close has priority from ts_close onward, and any crossing before it still dusts the bet.
the liquidator cannot be the position owner.
the program also refuses to close a bet over a bust it has already recorded. settle and execute_trigger fail with UseLiquidate when the chain's last recorded price is already past the bust, inside the bet's window. the way out is liquidate.
what bots get paid
| action | who is paid | how much | paid from |
|---|---|---|---|
liquidate | the liquidator's UserAccount | the larger of 0.1% of margin and $0.01 | the forfeited margin. nothing is added to the trader's loss |
settle of a busted close | the settler's UserAccount | the same | the forfeited margin |
execute_trigger | the executor's UserAccount. nothing if the owner executes | the same size, capped at what the trader was credited | the margin that comes back to the trader. an internal move |
fill_order | nobody | 0 | the house runs fill keepers |
settle_open, settle, settle_dust_round | nobody | 0 | the settler pays its own fees |
harvest, push_fees, expire_order, touch_oracle | nobody | 0 |
claim_dev also has no caller reward. any wallet can send it, and the money goes only to the dev recipient's token account.
cancel rules
an owner can cancel a request that is stuck. the rules give liveness without giving a free option.
let T be the target tick of the request: ts_star for an open or a close, and the exit tick for a DUST MODE round. cancel_request is allowed when any one of these holds:
| rule | condition | shipped value |
|---|---|---|
| soft | the clock is more than 30 s past T and the program has never recorded a tick at or after T | 30,000 ms |
| hard | the clock is more than cancel_hard_ms past T | 600,000 ms. bounds 60,000 to 3,600,000 |
| retired | the instrument is retired and T is after the frozen tick | immediate |
the soft rule exists because a plain 30 s cancel would be a free option. a trader who sees a losing price while the keeper is slow could back out. once the oracle has moved past T, someone could have settled, so the soft rule stops applying.
what a cancel does:
| request | result |
|---|---|
| open | margin back, open interest and wallet notional released, agent budget credited back, the position account closed |
| close | the position goes back to Open |
| DUST MODE round | margin back, round exposure released |
only the owner can cancel. a session key or an agent key cannot.
orders
a limit order cancel is not a refund. it writes cancel_ts, the next tick time under the same timing rule. fills on ticks before cancel_ts stay valid, so nobody cancels against a tick they already saw.
the margin comes back through expire_order, which anyone can send once no tick can still fill the order. that is true when the instrument is retired, or the hwm is more than the lookback past the order's last tick, or the clock is more than 30 s past it. a fill and an expiry are never both valid.
a change to a take profit or stop loss writes a pending slot that takes effect on a future tick. a second change waits until the chain has seen that tick and the lookback has passed. otherwise a trader could retract a change after seeing which side the tick fell on.
retire and freeze
the pause guardian can retire an instrument. it needs a verified tick with a time at or after the hwm, so it cannot freeze at an old price. the call is permanent. a retired id never reopens, and a relisting gets a fresh id.
after a retire:
| thing | what happens |
|---|---|
| new open, DUST MODE request, limit order | refused with InstrumentInactive |
| pending open | settle_open voids it: margin, open interest and wallet notional released, Cancelled emitted. the owner can also cancel at once if its ts_star is after the frozen tick |
| open position | request_close skips the freshness gate. settle closes at the frozen price, with Closed.via = 3. if the frozen price is past the bust, it is dusted |
| liquidation | liquidate reads the frozen price. NotBusted if it is not past the bust |
| take profit, stop loss, limit fill | execute_trigger, set_trigger, fill_order and cancel_order are refused. a position exits through request_close and settle |
| resting limit order | expire_order works at once. margin back, LimitExpired with reason 2 |
touch_oracle | refused. the hwm never moves again |
| withdraw, harvest, claims | never affected |
decided and undecided DUST MODE rounds
a DUST MODE round can be decided before the retire or not. the exit tick is the line.
| round | rule |
|---|---|
| decided: the exit tick is at or before the frozen tick | it settles on its real entry and exit ticks. the retire cannot cancel it. a decided loss cannot be refunded by retiring a market |
| undecided: the exit tick is after the frozen tick | it resolves as a push at the frozen price. margin back, no fee, no $DUST. the owner can also cancel at once |
the same line decides cancel_request: a retire unlocks only requests whose target is after the frozen tick.
what a pause never blocks
withdraw, harvest, closes and settles, liquidations and claims cannot be paused. the opens gate is the only switch for new risk. closing it blocks request_open, request_dust_round, place_limit and fill_order. the guardian or the owner can close it. only the owner reopens it.
running a keeper
everything above can be done by any wallet:
- a wallet with SOL. a transaction with one Pyth update costs 10,000 lamports in fees plus 1 lamport for the update.
- a Pyth Pro access token, kept on the server, to fetch the exact tick for
ts_star. - for bounties, a UserAccount. the first deposit creates it. it must be at least 10 USDC after the 1 USDC activation fee.
- the SDK has the planners:
settleRequest,findLiquidatingTick,findTriggeringTick,findFillingTick,fillOrderTxandexecuteTriggerTx. see SDK.
a duplicate send is harmless. the instruction reverts on the wrong state and costs only the fee.
the reference keepers start in dry run. they build and simulate every transaction, and send only when DRY_RUN is switched off.
what the keepers promise
these are operating duties, not program rules:
- K1 archives every tick and settles each request. before a close it checks for a bust or a trigger crossing in the bet's window
- K2 scans the archive and liquidates as soon as a tick crosses, including a catch up scan after a restart
- K3 keeps the hwm fresh while anything is live
- K4 pays the line when cash exists, pushes fees, and claims the dev fee
- K6 checks the vault identity every minute, the hwm age, the line depth and keeper balances, and pages a human
- K7 and K7b fill and execute within the lookback of the first crossing tick
on devnet, K1 settled 72 of 72 DUST MODE rounds on the first attempt. the landing time was 0.73 to 1.07 s after the exit tick.
