Market Lifecycle
A SportsPerp market exists in one of several states. This page documents each state, the transitions between them, and the permissionless and admin operations that drive them.
The state diagram
initialize_market
β
βΌ
ββββββββββββββββ pause_market ββββββββββββββββ
β ACTIVE β βββββββββββββββββββββββββΊ β PAUSED β
β β βββββββββββββββββββββββββ β β
ββββββββ¬ββββββββ unpause_market ββββββββββββββββ
β
β (oracle stale > 72 hours)
β sunset_market (permissionless)
β
β β OR β
β
β force_settle_market (admin)
βΌ
ββββββββββββββββ
β SUNSETTING β positions can still close normally
ββββββββ¬ββββββββ
β (all positions closed, or admin calls settle)
βΌ
ββββββββββββββββ
β SETTLED β settle_position unwinds any remaining positions
β β at the settlement price
ββββββββββββββββState 1: Active
A market that is open for normal trading. paused = false, oracle is fresh (< 2h staleness), sunset_requested = false. This is the steady-state for the 68 launch markets.
All 38 instructions (open, close, place trigger, add/remove collateral, crank funding, run liquidations) are valid.
State 2: Paused
Admin-triggered emergency halt. paused = true.
- Allowed: add collateral, cancel trigger orders, and the bad-debt machinery (
backstop_liquidate,auto_deleverage,unwind_backstopβ these are gated on settlement state, not onpaused) - Blocked: open new positions, close existing positions, partial-close, withdraw collateral, place or execute trigger orders (stop-loss, take-profit, limit-open), apply funding (funding does not accrue while paused),
partial_liquidate, legacyliquidate, andupdate_oracleitself
Triggered by:
- Pause (
pause_marketinstruction). Gated on the oracle authority (the dedicated oracle key) β the admin keypair may also call it. Used for anything requiring caution: suspected oracle manipulation, discovery of a new risk scenario, coordinated response to a data-feed incident.
Unpaused by unpause_market (admin only). The market resumes from the same state β positions, triggers, insurance accounting all preserved.
Emergency pause is market-specific. Pausing market 3 has no effect on market 4. This lets admin narrowly target the affected surface without halting unrelated trading.
Pause is a hard freeze, not a soft halt. Because it blocks both closes and withdrawals, it traps every open position on that market until an admin unpauses it. It is a minutes-to-hours incident tool β for a suspected oracle compromise or a corrupted data feed β and it must never be used as a response to a player injury or an expected absence: a multi-week pause over an injury would lock every holder out of their position and collateral for the duration. See Markets for what actually happens to a playerβs market when theyβre injured (nothing β the market stays open).
State 3: Sunsetting
A market heading toward formal settlement. Triggered by one of:
- Stale oracle sunset (permissionless). If the oracle hasnβt received an update in > 72 hours, anyone can call
sunset_market. This is the mechanism for retiring a player who has been dropped from the roster β the crank stops pushing updates, 72 hours pass, sunset is called. - Admin force-sunset.
force_settle_marketimmediately puts a market into sunsetting state without waiting for staleness. Used for planned market retirement (end-of-season player rotation, team relegation handling).
During sunsetting:
- Allowed: close existing positions, withdraw collateral, cancel triggers
- Blocked: open new positions, place triggers, add collateral
The market continues to accrue funding and remains liquidatable β existing traders can still be liquidated if they let their positions go underwater during sunset.
A settlement price is fixed at sunset time. Specifically, the current mark_price_ema at the moment sunset_market or force_settle_market was called becomes the canonical price for any residual positions.
State 4: Settled
All remaining positions have closed, or admin has force-settled the market. The market is effectively terminated.
- Allowed:
settle_positionβ permissionless instruction that closes any position still open in a settled market, at the fixed settlement price. This is a trader-protection mechanism: even if you forget about a position in a settled market, anyone can close it for you (at the settlement price, to your wallet). - Not allowed: everything else. The market is read-only after settlement except for
settle_position.
The 72-hour sunset rule
The 72-hour oracle-staleness trigger is specifically designed to handle roster rotations:
- A player is dropped from the weekly roster regeneration (
generate-roster.mjs). - The crank reads
roster.json, notices the player is no longer listed, stops pushing updates for that market. - 72 hours pass.
- A permissionless keeper (or any account) calls
sunset_market. The sunset price is locked. - Traders with open positions have a generous window to close voluntarily at any price during sunset.
- Any stragglers eventually get
settle_positionβd at the sunset price.
The 72-hour window is long enough that accidental stale pushes (e.g., a 2-hour network outage) never trigger sunset, but short enough that stale markets clear promptly.
How settlement price is chosen
sunset_market locks the settlement price to the last good oracle_price stored on the market β the price that was current immediately before staleness began. This is deliberate: staleness is detected by elapsed time, not by oracle content, so the most recent oracle value is still the freshest signal the protocol has of the marketβs true level.
| Signal | Used by sunset_market? | Notes |
|---|---|---|
Last oracle_price on the market | β yes | Frozen at sunset; no new oracle pushes are accepted afterwards |
| Raw composite mark | β no | Mark price blends in vAMM impact β too easily gamed in a thin market right before sunset |
| 150 s mark EMA | β no | Reasonable alternative, but the canonical sunset price is the last oracle_price for auditability and simplicity |
force_settle_market is different: the admin passes an explicit settlement_price parameter, which is written directly to the market. That instruction exists for emergencies (e.g., a market that needs to be retired ahead of the 72h window) and is the only path by which an admin can choose the settlement price.
Insurance fund during sunset
Sunsetting does not release insurance exposure associated with absorbed positions on that market. BackstopPosition accounts remain and are unwound against the sunset price during settlement. This ensures the fundβs balance is correctly adjusted regardless of market lifecycle state.
Admin capabilities and their guardrails
The admin keypair has several lifecycle instructions available, each with specific protections:
| Instruction | What it does | Guardrail |
|---|---|---|
pause_market / unpause_market | Toggle paused state (pause_market gates on the oracle authority and also accepts admin; unpause_market is admin-only) | Cannot skip settlement or invalidate positions |
force_settle_market | Skip the 72 h wait and settle immediately at an admin-supplied settlement_price | Settlement is irreversible; the market is moved to Settled, no further oracle pushes are accepted, and remaining positions are closed at the supplied price by settle_position. Use only for genuine emergencies (e.g., retiring a market ahead of a roster rotation when waiting 72 h is unsafe). |
update_market_params | Adjust oracle blend weights (live/between-match), vAMM impact factor, ADL enable | Cannot change market_id or settlement state; fees, max leverage, and funding interval are immutable after initialize_market |
configure_insurance | Adjust max_backstop_exposure and target_balance | Cannot pull insurance below the configured balance via this instruction |
There is no βcancel all positionsβ instruction. Admin can accelerate a market toward settlement and, in force_settle_market, supply the settlement price; in the normal sunset_market path the price is fixed to the last oracle_price and is not admin-chosen.
Further reading
- Markets β how the 68 initial markets are structured.
- Mark Price β the EMA that becomes the settlement price.
- Insurance Fund β how backstop exposures are handled through settlement.
- Oracle Design β the 2h staleness limit (vs the 72h sunset threshold).