OracleConfig account per market. It records the latest Pyth observation, a short time-weighted average that feeds a volatility estimate, and an Active/Passive health flag. Prices and confidence values here are scaled to 1e9: $562.33 is stored as 562_330_000_000.
How mint-redeem uses it
Mint-redeem readsOracleConfig only for a market whose oracle_type is Continuum, and only in the Normal risk state. It takes last_price / 1_000 as the 1e6 price. It reverts with OraclePriceUnavailable if the config is paused or Passive, if last_price is 0, or if last_update_time is more than max_staleness seconds old. In every other risk state it prices from Market.user_twap_price. All 13 live devnet markets are Continuum markets running in ProxyMode, so mint-redeem reads this account for none of them.
Accounts
OracleConfig PDA
Continuum market, Market.oracle_address holds this PDA. Fields in account order:
TwapState holds cumulative_price (u128), cumulative_time (u64), last_twap (u64), last_update (i64) and window_seconds (u32). PriceObservation holds price (u64, 1e9), confidence (u64, 1e9) and timestamp (i64). OracleHealthConfig holds max_staleness_seconds (i64, default 60), max_confidence_bps (u16, default 200) and _reserved ([u8; 16]); the first 8 bytes of _reserved store the Pyth publish time of the latest observation.
OracleFeed PDA
update_price_observation accepts only a registered, active feed.
On any cluster other than devnet, the keeper picks a feed on each update. It tries
Primary first, then the kind for the current US session (Overnight when the market is closed), then Fallback, PreMarket, PostMarket, Overnight and Custom. It uses the first feed that is trading, no older than max_staleness and within health_config.max_confidence_bps. If that feed isn’t Primary, or the config is Passive, the keeper puts the market in ProxyMode rather than Normal. On devnet the keeper skips feed selection entirely.
Instructions
Setup (admin)
initialize_oracle_config(max_confidence_interval: u64, max_staleness: i64, keeper_authority: Pubkey)
Creates the OracleConfig for a mint-redeem Market. The signer must be the Market’s authority (else UnauthorizedAdmin) and becomes admin_authority. The config starts Active, with a 60 s keeper window, a 300 s user window and the default health thresholds. Accounts: oracle_config, market, authority, system_program.
upsert_oracle_feed(priority: u16, kind: OracleFeedKind, is_active: bool)
Creates or updates the OracleFeed for one Pyth price account. The handler first parses the account as a Pyth price feed (InvalidPythAccount if it isn’t one). admin_authority signs and pays. Accounts: oracle_config, oracle_feed, pyth_oracle, admin_authority, system_program.
realloc_oracle_config()
Grows a config to 1,781 bytes with zero fill and changes no data; admin_authority signs and pays. Configs created before the 9 Oct 2026 upgrade were 16 bytes short of a full observation ring. All 14 devnet configs have been grown.
Updates (keeper)
update_price_observation()
Takes no arguments. Accounts: oracle_config, oracle_feed, pyth_oracle, and keeper, which must equal keeper_authority (else UnauthorizedKeeper). The instruction:
- Fails with
OraclePausedwhen paused. A second call in the same second returns without writing. - Reads the Pyth account with
get_price_uncheckedand rescales price and confidence to 1e9. It applies no staleness, confidence or move-size limit. - Stores the Pyth account in
pyth_oracleand counts a move of 50 bps or more inrecent_move_count. - Sets
market_statetoActiveif the Pyth print is at mosthealth_config.max_staleness_secondsold and its confidence, in bps of the previouslast_price, is at mostmax_confidence_bps. Otherwise it setsPassive. - Appends the observation to the ring, recomputes
keeper_twapandrecent_volatility_bps, and copies the spot price intouser_twap.last_twap. - Writes
last_price,last_confidenceandlast_update_time, and stores the Pyth publish time inhealth_config._reserved.
reset_volatility_counters()
Keeper-signed, with the same accounts as update_price_observation, so it also needs a registered, active feed. It zeroes recent_move_count only and leaves recent_volatility_bps as is.
Configuration (admin)
Each of these takesoracle_config and an authority signer that must equal admin_authority.
configure_twap_windows(keeper_window_seconds: Option<u32>, user_window_seconds: Option<u32>)sets the keeper window (30 to 120 s) and the user window (300 to 600 s). A value outside its range fails withInvalidTwapWindow. The user window no longer affects anything.configure_health_thresholds(max_staleness_seconds: Option<i64>, max_confidence_bps: Option<u16>)sets theActive/Passivethresholds.update_oracle_config(max_confidence_interval: Option<u64>, max_staleness: Option<i64>, is_paused: Option<bool>, keeper_authority: Option<Pubkey>)changes any subset;Noneleaves a field unchanged. It is the only way to unpause.emergency_pause()setsis_paused = true.
update_price_observation, get_price, get_user_twap, get_user_price and get_keeper_price fail with OraclePaused. Mint-redeem honours the pause only in the Normal risk state, so it has no effect on a market in ProxyMode.
Read-only helpers
These return their value as Anchor return data, so another program can call them by CPI. All take onlyoracle_config, except get_price.
The deployed devnet program has no
force_price instruction.
Errors
→ Full error catalog
TypeScript
This compares QQQ’sOracleConfig with the live price on its Market account. provider is set up as in SDK setup, and the IDLs are published in continuum-docs/idl.
See also
Oracle concept
How the keeper prices markets and checks each print.
Risk states
How ProxyMode, Stress and Recovery change fees and quotes.

