Oracle v1. This is the new Pragma oracle on Miden, which replaces the current one. Its address is published here when it is deployed; the current oracle stays live during a migration window. The price keys change: see Pairs and keys.
Ready-to-run example
An account that calls
get_median on the oracle through a foreign call, with the checks below. cargo run --release -p consume-price -- --oracle ORACLE_ID --pair BTC/USDget_median never tells you whether the price is good enough for your use. It returns what the
oracle knows; the checks below are the consumer’s job. Skip one and a late, unknown or thin price
can look like a valid one.
The read
KEYispair_key("BTC/USD"): the Poseidon2 hash (MidenHasher::hash) of the pair name in upper case,BTC/USD, as UTF-8 bytes. Push it as a hex literal. The keys of the supported pairs are listed in Pairs and keys and never change.- Only publisher entries no older than
max_ageseconds (against the reference block timestamp) enter the median.get_medianuses 120 s. num_publishers: publishers whose entry is in the median.median_price,decimals: the median (average of the two middle prices for an even count) and the decimals of the median entry.0, 0whennum_publishersis 0.last_updated: timestamp of the newest entry ever published for the pair, fresh or not.0if nobody ever published it.
What to check before using a price
num_publishersis at least what you need.0means there is no price:median_priceanddecimalsare0and must never be used. The call does not fail in that case. Pick a minimum that matches the value at risk: with a single publisher the “median” is that publisher alone.- Distinguish late from unknown.
num_publishers = 0withlast_updated > 0is a late pair; both at0is a key nobody ever published, which is also what a typo in the pair name produces and what a paused oracle answers. Derive the key withpair_key, never by hand, and test it against a pair you know is live. - Freshness against your own tolerance. The default 120 s is a general-purpose window,
not a safety margin for your protocol. If you liquidate, settle or mint on this price, call
get_median_with_max_agewith the bound your protocol can live with, and comparelast_updatedwith the reference block timestamp yourself. decimalsis what you expect. It follows the pair (8 for most, 6 for USDT/USD, USDC/USD and XAUT/USD) and is read from the data, so assert it against the value your arithmetic assumes instead of hard-coding a scale.- The price is not zero and is plausible for your application. Add the bounds or the deviation cap your protocol needs (a circuit breaker against the last accepted price, for instance).
- Arithmetic stays in range. A price is a felt below 2^64 - 2^32 + 1. Multiplying it by an amount overflows quickly; scale in u64/u128 helpers, not raw felt arithmetic.
Things that can change under you
- Pause. We can pause the oracle in an incident (one signer). While it is paused,
get_medianandget_median_with_max_agedo not fail: they answer[0, 0, 0, 0], “no price”, which is the answer for a pair nobody published. A foreign call that fails would abort your whole transaction and cannot be caught on Miden, so a paused oracle is handled on the same path as a missing price: the check onnum_publishersbelow is what protects you, and you decide whether to block, fall back or wait.pm-oracle-cli medianreads the pause flag and says so;pm-multisig statusshows it. - Procedure root. A foreign procedure invocation is bound to the MAST root of
get_median. The oracle is code-upgradable (multisig, full quorum), and an upgrade that touchesget_medianor what it calls changes the root, after which a call using the old root fails (UnknownAccountProcedure, seen on the testnet after an upgrade of the dummy oracle). Pin the oracle account id and the root, and follow the upgrade announcements.examples/consume-priceis a complete consumer using this call. - Publisher set. Publishers are added and removed by the multisig (default quorum). The median
moves accordingly, which is why
num_publishersis part of the answer.
num_sources (how many exchanges fed a publisher’s own price) is stored in each publisher’s entry
and is not part of the median read. To inspect it, read the publishers’ entries off-chain.