Integrate
On-chain prices
Read the oracle's median price on the Internet Computer (ICP), from the registry canister or as a certified document over HTTPS.
The nodes push their signed prices to the registry canister on the Internet Computer (ICP). The canister checks each signature against the registry and serves the median of the nodes for each priced feed. Use it when your code runs on the Internet Computer, or when one certified document is enough and you do not want a connection to every node.
| On-chain | Rust SDK | |
|---|---|---|
| Who takes the median | The canister | Your service |
| Update rate | Each feed every 30 s, and at once on a 10 bp move | Each node publish, about every second |
| Client key | None | Listed by governance |
| Feeds | 14 | All the network's feeds |
From a canister
Add the types you need. Candid ignores the fields you leave out.
rustuse candid::{CandidType, Deserialize, Principal}; use ic_cdk::call::Call; use serde_bytes::ByteBuf; #[derive(CandidType, Deserialize, PartialEq)] enum PriceStatus { Ok, Degraded, Halted } #[derive(CandidType, Deserialize)] struct PriceRecord { status: PriceStatus, price: Option<i64>, conf: Option<u64>, expo: i32, publish_time_ns: u64, }Call
get_pricewith the feed's 32-byte id. The id is keccak256 of the symbol; the feeds reference lists them.rust// CRYPTO.BTC/USD let feed_id = hex::decode("0523e79c7ed211911a19afaa4def68c896bc72d674bf33c5c64d4d7089e320e2")?; let oracle = Principal::from_text("xb5gv-diaaa-aaaaf-qg5qa-cai")?; let reply = Call::bounded_wait(oracle, "get_price") .with_arg(ByteBuf::from(feed_id)) .await?; let btc: Option<PriceRecord> = reply.candid()?;Use the price only when the status is
OkorDegraded. The price isprice × 10^expo, and the band isconf × 10^expo.rustif let Some(record) = btc.filter(|record| record.status != PriceStatus::Halted) { let price = record.price.unwrap_or_default() as f64 * 10f64.powi(record.expo); }
The canister resolves the price when you call it. A feed whose nodes stop pushing goes Halted after the canister's price window, so a price is never older than that window. get_price_window returns it in seconds: 75 by default, and about three push intervals once governance sets it. get_prices returns every feed, and get_feeds lists the feeds and their ids.
| Field | Meaning |
|---|---|
status |
Ok, Degraded or Halted. |
price, conf |
The median of the nodes and its band, as integers at expo. |
publish_time_ns |
The newest publish time of the nodes that count. |
nodes |
The nodes that count. At least the registry's quorum, or the feed halts. |
concerns |
Why the price is not Ok, such as node_divergence. |
resolved_at_ns |
When the canister resolved it. |
Over HTTPS
curl https://xb5gv-diaaa-aaaaf-qg5qa-cai.icp0.io/prices.jsonThe gateway checks the canister's certificate before it serves the document. Each entry has the fields of the Rust SDK's resolved price, with decimals as strings.
The canister writes the document when a node's frame arrives, so the document can be older than the certificate. Check resolved_at_ns, and treat a document older than the price window as halted.
To check the certificate yourself, fetch from the raw domain, https://xb5gv-diaaa-aaaaf-qg5qa-cai.raw.icp0.io/prices.json, and verify it with ic-response-verification, as the Rust SDK does for the registry.
/frames.json holds the signed batches and frames that carry each node's latest update, for a client that checks the nodes' signatures itself. Each entry names its node, its kind (batch or price) and its feeds. A batch is a list of price updates under one signature; the signing reference gives its layout. The entries can be as old as the price window.
What you trust
The canister checks the frames as the Rust SDK does and takes the same median. You trust the canister's code and its controllers, as you already trust them for the registry. See the trust model.