Reading price sources
audience: ai
The oracle’s inputs are exchange and chain feeds. None of them are mosaik-native; all of them live on the other side of the enclave boundary. This chapter specifies what the oracle subscribes to and what the admission criterion for each source is. The next chapter (wrapping) walks how the subscriptions terminate inside the enclave.
The source catalog
The oracle ships a declared source catalog —
the set of upstream feeds it reads, broken down per
token pair. The catalog’s blake3 digest is folded
into OracleParameters.source_catalog_digest
(setup), so any change to the catalog
is a new oracle Config.content.
pub struct SourceCatalog {
pub entries: &'static [(TokenPair, &'static [Source])],
}
pub enum Source {
CexSpot {
exchange: CexExchange, // Binance, Coinbase, Kraken, …
symbol: &'static str, // "ETHUSDC", "ETH-USD", …
endpoint: &'static str, // wss://…
tls_spki: &'static [u8], // pinned SPKI digest
},
OnChainAmm {
chain_id: u64,
pool_address: Address,
protocol: AmmProtocol, // UniswapV3, Curve, …
read_mode: OnChainReadMode, // ArchiveNode or LightClient
quorum: u8, // minimum node agreement
},
OnChainOracle {
// References another mosaik oracle organism as a
// source. Used for long-tail pairs where this
// oracle aggregates peers.
organism: OrganismRef<'static>,
pair: TokenPair,
},
}
Three classes of source, each with its own admission criterion:
- CEX spot feeds — pinned TLS SPKI. The
enclave’s HTTPS client refuses any cert chain
whose leaf SPKI does not match
tls_spki. A forced MITM would show as a handshake failure and a source-unavailable state (see below), not as a silently wrong price. - On-chain AMM reads — either an archive node
the oracle operator runs (trust folded into the
Measurements through
chain_idand the client binary) or a light client verifying against Ethereum consensus (trustless in the light-client sense).quorumcontrols how many independent archive or light-client reads must agree before the source is considered fresh. - On-chain oracle (peer) — another mosaik oracle organism. The enclave subscribes to that organism’s stream exactly the way any consumer does (binding). This is how long-tail pairs get coverage: aggregate two or three other oracles whose Measurements the enclave admits.
Per-pair source selection
A pair’s entry in the catalog is the ordered list of sources the oracle will consult each tick. The aggregation policy (aggregation) decides what to do with the collected quotes; the source list only decides what to ask.
pub static SOURCE_CATALOG: SourceCatalog = SourceCatalog {
entries: &[
(TokenPair::new("USDC", "ETH"), &[
Source::CexSpot {
exchange: CexExchange::Binance,
symbol: "ETHUSDC",
endpoint: "wss://stream.binance.com:9443/ws/ethusdc@bookTicker",
tls_spki: BINANCE_WSS_SPKI_2026Q2,
},
Source::CexSpot {
exchange: CexExchange::Coinbase,
symbol: "ETH-USD",
endpoint: "wss://ws-feed.exchange.coinbase.com",
tls_spki: COINBASE_WSS_SPKI_2026Q2,
},
Source::OnChainAmm {
chain_id: 1,
pool_address: uniswap_v3::ETH_USDC_POOL,
protocol: AmmProtocol::UniswapV3,
read_mode: OnChainReadMode::LightClient,
quorum: 1,
},
]),
// further pairs …
],
};
Reasonable mixed catalogs combine CEX and on-chain sources; a pure-CEX catalog is cheapest to run but vulnerable to exchange-side outages and pair-delisting events. A pure-on-chain catalog is settlement-honest for the chain in question but lags the CEX book during volatile periods.
Staleness accounting
Each source carries a per-source timestamp. The
oracle’s
OracleParameters.max_source_age_ms
(setup) is the threshold past which a
source is considered stale. The aggregation
policy decides what to do with a stale source;
commonly: drop it from the quorum and flag the
resulting tick if the drop pushes the source count
below a declared minimum.
The in-enclave source state machine per source:
handshake OK, first msg within window
┌─────── ──────────── ──────────── ──────┐
▼ │
fresh ──── msg older than max_age_ms ────► stale
▲ │
│ ▼
└─── reconnect + first msg within ── reconnecting
A source in reconnecting drops out of the
aggregation quorum. The oracle publishes a tick
anyway if the remaining sources meet the minimum;
otherwise it publishes no tick and the consumer
sees a gap in the stream cadence.
What the catalog is not
- Not a price index. The catalog names sources, not weightings. Weightings and outlier rejection are the aggregation policy’s job (aggregation).
- Not a failover list. Sources are consulted in parallel each tick; there is no primary/secondary distinction. Aggregation and quorum decide which sources contribute to the published tick.
- Not operator-confidential. The catalog digest
is part of
OracleParameters, which is part of the oracle’sConfig.content, which is reproducible from the operator’s release page. Consumers pin it transitively through the content hash.
Forward
The sources listed above live outside mosaik. The next chapter walks how they are terminated inside the enclave so that nothing outside the Measurements set can observe or tamper with their reads.