Skip to Content
ConceptsFeeds & feed_id

Feeds & feed_id

A feed is a single real-world data series that Claros tracks and attests on Casper: WTI crude spot price, Lower-48 electricity demand, San Diego parking revenue, and so on. Every feed carries two things on-chain, a self-describing metadata record (what it is, its unit, how many decimals) and a latest attested value.

A feed_id is the stable, unique string that names that series. It is the only identifier you ever need. Pass it to the SDK, the REST API, or a cross-contract call and the reading comes back. If you have used Pyth, a feed_id plays the same role as a Pyth price feed id.

One feed_id is the single key into both Claros registries: the FeedRegistry (metadata) and the AttestationRegistry (value). Read both with the same string, then compute value = amount / 10^decimals.

Anatomy of a feed

A feed is split across two contracts so that a value never travels without the context needed to interpret it. This is the Pyth model: a price ships with its exponent. Both records are keyed by the same feed_id.

Metadata lives in the FeedRegistry. get_feed(feed_id) -> Option<Feed>:

FieldTypeExampleMeaning
decimalsu86scale exponent, value = amount / 10^decimals
unitString"$/bbl"unit of the value
titleString"WTI crude spot"human label
sourceString"EIA"upstream provider ("EIA" or "City of San Diego")
routeString"petroleum/pri/spt"provider route the value came from
frequencyString"daily"reporting cadence
descriptionString"Cushing WTI spot price"longer description

Value lives in the AttestationRegistry. get_latest(feed_id) -> Option<Attestation>:

FieldTypeExampleMeaning
periodu6420260622the data’s reporting period (YYYYMMDD daily, YYYYMM monthly, YYYY annual, epoch seconds hourly)
amountU51278940000the raw integer value
source_hashString"a1b2…"deterministic provenance digest of the upstream row
attesterAddressaccount-hash-43d7…the account that signed the attestation
timestampu641750636800000attestation block time (epoch milliseconds)

Put the two together and you have a reading. For EIA.PET.PRICE.WTI.DAILY the value is 78940000 / 10^6 = $78.94 / bbl.

The SDK and REST API return both halves pre-joined and scaled as one reading object:

FieldTypeNotes
feed_idstringthe key
valuenumberamount / 10^decimals, the human number
amountstringraw integer, U512-safe (string over HTTP, bigint in the SDK)
decimalsnumberscale exponent
unitstringfor example "$/bbl"
titlestringhuman label
sourcestringupstream provider
routestringprovider route
frequencystringreporting cadence
descriptionstringlonger description
periodnumberthe data’s reporting period
source_hashstringprovenance digest
updated_atnumberattestation time, epoch milliseconds

The feed_id naming convention

To both the contracts and the SDK a feed_id is an opaque string key. Nothing parses it. The naming scheme below is a human convention that makes ids readable and predictable; the machine mapping from a feed_id to its upstream query is a lookup table, not derived from the string.

EIA energy feeds

Energy feeds from the U.S. Energy Information Administration follow a dotted, uppercase scheme:

EIA.<FAMILY>.<METRIC>[.<SUBMETRIC>].<SPECIFIER>.<FREQ>

The first segment is always EIA, and the last segment is always the frequency. Everything in between identifies the quantity. Taking EIA.PET.PRICE.WTI.DAILY:

SegmentValueMeaning
EIAEIAprovider prefix
<FAMILY>PETEIA dataset family (petroleum)
<METRIC>PRICEthe quantity measured
<SPECIFIER>WTIthe specific series, region, or product
<FREQ>DAILYreporting cadence (always last)

Some ids carry an extra segment when a family has sub-metrics, for example EIA.ELEC.RETAIL.PRICE.US_ALL.MONTHLY (family ELEC, metric path RETAIL.PRICE, specifier US_ALL).

The <FREQ> suffix is one of DAILY, WEEKLY, MONTHLY, QUARTERLY, ANNUAL, or HOURLY. The <FAMILY> token maps to an EIA route family:

FamilyEIA route prefixDomain
NGnatural-gasNatural gas
PETpetroleumCrude oil and products
ELECelectricityElectricity
COALcoalCoal
DBFdensified-biomassWood pellets and biomass
NUCnuclear-outagesNuclear outages
STEOsteoShort-Term Energy Outlook
TOTALtotal-energyTotal energy
SEDSsedsState Energy Data System
CO2co2-emissionsCO2 emissions
INTLinternationalInternational energy

A few real feed ids live on-chain today:

feed_idWhat it isUnit
EIA.PET.PRICE.WTI.DAILYWTI crude spot price$/bbl
EIA.NG.PRICE.HENRYHUB.DAILYHenry Hub spot price$/MMBtu
EIA.ELEC.DEMAND.US48.HOURLYLower-48 electricity demandMWh
EIA.CO2.AGG.US_TOTAL.ANNUALU.S. total CO2 emissionsMMT CO2

Civic feeds

Non-EIA sources use short, provider-specific ids rather than the dotted scheme. The flagship civic feed is OP-1, San Diego parking revenue (source "City of San Diego"), which returns for example $2,734.20 for period 20260623. A feed_id can be any string the registrar chooses; the EIA convention is simply the scheme Claros uses for EIA series.

Because the id is opaque, the on-chain query never round-trips through string parsing. For EIA feeds the resolution table (feed_id to route / frequency / data_col / facets) is agent/src/eia-feeds.ts. See the EIA data model for how those routes and facets work.

One feed_id, both registries

The same feed_id is the dictionary key in both contracts: FeedRegistry.feeds (metadata) and AttestationRegistry.latest (value). Read one for context, the other for the number, and divide.

import { ClarosOracle } from 'claros-oracle'; const claros = new ClarosOracle(); // Casper testnet defaults baked in // one call: metadata + value + human number const r = await claros.getReading('EIA.PET.PRICE.WTI.DAILY'); // r.value === 78.94 // r.amount === 78940000n // r.decimals === 6 // r.unit === '$/bbl' // r.updated_at === 1750636800000 (epoch ms) // or the two halves separately, same id into each registry const meta = await claros.getFeed('EIA.PET.PRICE.WTI.DAILY'); // FeedRegistry const value = await claros.getValue('EIA.PET.PRICE.WTI.DAILY'); // AttestationRegistry

The AttestationRegistry read method is get_latest(asset_id: String), keeping the original asset_id parameter name from the project’s revenue-feed origins. It is the same string as the FeedRegistry’s feed_id. One id, both registries.

Enumerating feeds

The set of feeds is discoverable on-chain, no indexer required. The FeedRegistry maintains an index so the whole catalog can be walked.

import { ClarosOracle } from 'claros-oracle'; const claros = new ClarosOracle(); const ids = await claros.listFeedIds(); // ['EIA.PET.PRICE.WTI.DAILY', 'OP-1', …] const n = await claros.feedCount(); // 37 const id0 = await claros.feedIdAt(0); // first registered feed_id

The SDK’s listFeedIds() is just the loop below, which is also exactly what the REST API does to build GET /v1/feeds:

Read the count

feedCount() reads FeedRegistry.count, the number of distinct feeds ever registered.

Walk the index

For i in 0..count, feedIdAt(i) reads FeedRegistry.ids[i], the feed_id registered at that index.

Read each feed

Pass each id to getReading(id) (or both registries directly) to get metadata and value.

Casper (Odra) Mappings cannot be iterated. The FeedRegistry therefore keeps an explicit ids: Mapping<u64, String> alongside a count, appending each new feed_id at the next index when it is first registered. Re-registering an existing id updates its metadata in place and does not advance the count, so ids stay stable and gap-free.

Today 39 feeds are registered: the EIA energy mappings in agent/src/eia-feeds.ts, the civic feed OP-1, and the operator-claimed solar feed EIA.ELEC.GEN_SUN.US48.HOURLY. 38 serve live values; electricity interchange is registered but intentionally unattested (it is a signed metric).

From feed_id to upstream data

Each EIA feed_id resolves to one concrete EIA API v2 query, a tuple of { route, frequency, data_col, facets }. For example EIA.PET.PRICE.WTI.DAILY resolves to:

{ route: 'petroleum/pri/spt', frequency: 'daily', data_col: 'value', facets: { series: 'RWTC' }, // unit $/bbl, decimals 6 }

The facets object holds the filters that pin a single series within a dataset (here series: 'RWTC' selects the Cushing WTI spot price). To browse the whole EIA universe (232 datasets) and the routes, columns, and facets you can filter on, use discovery:

curl "https://<claros-api>/v1/datasets?family=petroleum&q=price"

See the EIA data model for a full treatment of routes, frequencies, columns, and facets.

Next

Last updated on