Skip to Content
ReferenceContract types

Contract types

Claros publishes data through two small Odra contracts on Casper testnet (casper-test). Both are keyed by the same feed_id string, and both follow the Pyth model: on-chain values are integers, and the human value is amount / 10^decimals.

  • AttestationRegistry stores the feed VALUES. Its read methods return an Attestation.
  • FeedRegistry stores self-describing METADATA. Its read methods return a Feed.

This page is the type reference: the exact struct fields with their Rust types, the read entry points, the Option returns, and the feed-enumeration methods. For the full integration walkthrough (declaring external_contract traits and wiring a ContractRef), see cross-contract reads. For off-chain access, see the TypeScript SDK and the REST API.

These types come straight from contracts/src/attestation_registry.rs and contracts/src/feed_registry.rs. If you read Claros from your own contract, re-declare them verbatim under #[odra::odra_type], because Odra derives the on-chain serialization from the field order and types. Any drift will misparse the bytes.

The two registries

These are the live package addresses on casper-test. Cross-contract callers pass them as Address arguments (Casper contract package addresses); the SDK and REST service have them baked in.

RegistryStoresRead returnsPackage address (casper-test)
AttestationRegistryfeed values, by feed_idAttestationhash-236b510436c60b6a797d175c72c6014de367d43f1de1ca45f580d112f98116cc
FeedRegistryfeed metadata, by feed_idFeedhash-741cc223c14c2c00c9f06d7bb5c4be2f824fbf0c8b09a147bf1835570bddf5b6

AttestationRegistry names its read parameter asset_id while FeedRegistry names it feed_id, but both take the identical Claros feed_id string (for example OP-1 or EIA.PET.PRICE.WTI.DAILY). They are the same key into two different stores.

Attestation

A single recorded value plus its provenance. Returned by get_latest and get_at on AttestationRegistry.

use odra::casper_types::U512; use odra::prelude::*; #[odra::odra_type] pub struct Attestation { pub period: u64, pub amount: U512, pub source_hash: String, pub attester: Address, pub timestamp: u64, }
FieldRust typeMeaning
periodu64Provider period key the value covers, encoded as an integer (for example 20260623 for 2026-06-23, or epoch seconds for hourly feeds). See attestation and freshness for the per-cadence encoding.
amountU512The integer value. The human value is amount / 10^decimals, where decimals comes from the matching Feed.
source_hashStringA deterministic provenance digest of the canonical source row. Recompute it from the upstream data to verify the on-chain number was not altered.
attesterAddressThe account that wrote the record: the feed’s claimant. For first-party feeds this is the Claros agent (account-hash 43d7dd06d5538e504e54a3f235f1596f7d2e803e9065bf3c0d040f5cd31a21d4); operator-claimed feeds carry their operator’s key.
timestampu64The on-chain commit time in epoch milliseconds, stamped by the chain via get_block_time() at inclusion, not supplied by the caller.

period and timestamp answer different questions. period is what the value describes (the real-world interval); timestamp is when it was written. A robust freshness check uses both, as covered on the attestation page.

Feed

Self-describing metadata for a feed, so a consumer reading a value can interpret it (decimals, unit, source) entirely on-chain. Returned by get_feed on FeedRegistry.

#[odra::odra_type] pub struct Feed { pub decimals: u8, pub unit: String, pub title: String, pub source: String, pub route: String, pub frequency: String, pub description: String, }
FieldRust typeMeaning
decimalsu8The scale exponent. Real value = amount / 10^decimals. Per-feed, so always read it rather than assuming a fixed scale.
unitStringDisplay unit, for example $/bbl, MWh, or percent.
titleStringHuman title, for example WTI crude spot.
sourceStringUpstream source, for example EIA or City of San Diego.
routeStringProvider route, for example petroleum/pri/spt.
frequencyStringOne of daily, weekly, monthly, annual, hourly, quarterly.
descriptionStringA longer human description of the feed.

Values (AttestationRegistry) and metadata (FeedRegistry) are separate stores, registered and attested independently. A feed can exist in FeedRegistry (so get_feed returns Some) before it has any value (so get_latest returns None). Read both, with the same id, before you scale.

Read entry points

These are the methods a consumer calls. All are &self views: free to call off-chain (SDK, REST) and gas-only on-chain. The write methods (attest, register_feed) are restricted, see write entry points.

Entry pointSignatureReturns
get_latestget_latest(asset_id: String)Option<Attestation>, the most recent value for the feed
get_atget_at(asset_id: String, index: u64)Option<Attestation>, the historical value at index (0-based)
get_countget_count(asset_id: String)u64, number of attestations recorded for the feed
total_attestationstotal_attestations()u64, total attestations across every feed
get_attesterget_attester()Address, the single authorized attester (the Claros agent)

As of the latest on-chain verification, feed_count() returns 39 and total_attestations() returns 78. Both grow as agents register feeds and attest new periods, so read them live rather than hardcoding.

The Option returns

get_latest, get_at, get_feed, and get_feed_at all return Option<...>. None is a normal, expected outcome, not an error condition:

CallReturns None when
get_feed(feed_id)the feed_id is not registered in FeedRegistry
get_latest(asset_id)the feed has never been attested
get_at(asset_id, index)no attestation exists at that index
get_feed_at(index)index >= feed_count()

On-chain, unwrap deliberately with a meaningful revert (unwrap_or_revert or a match) rather than assuming a value exists. Off-chain, the SDK mirrors this: getReading, getValue, getFeed, and feedIdAt each return T | null.

// Cross-contract: handle the None case explicitly. let att = match AttestationRegistryContractRef::new(self.env(), areg).get_latest(feed_id) { Some(a) => a, None => self.env().revert(Error::NoData), // feed unknown or not yet attested };

Feed enumeration

FeedRegistry keeps an index so you can list every feed without an off-chain catalog. Read feed_count() for the total, then walk get_feed_at(i) from 0 to count - 1 to get each feed_id, and get_feed(id) for its metadata.

The on-chain method is get_feed_at(index). The TypeScript SDK surfaces the same call as feedIdAt(i), and adds listFeedIds() as a convenience that loops it for you. Same underlying read, two names.

let freg = FeedRegistryContractRef::new(self.env(), feed_registry); let n = freg.feed_count(); // u64, e.g. 39 for i in 0..n { if let Some(id) = freg.get_feed_at(i) { let feed = freg.get_feed(id).unwrap_or_revert(&self.env()); // feed.decimals, feed.unit, feed.frequency, ... } }

Scaling: amount / 10^decimals

Every value is an integer. To get the human number, read decimals from the Feed and divide. decimals is per-feed, so never assume a fixed scale. On-chain, keep the math in integers (wasm contracts are no_std, so there are no floats):

// EIA.PET.PRICE.WTI.DAILY: decimals = 6, amount = 78_940_000 ($78.94/bbl) let scale = U512::from(10u64).pow(U512::from(feed.decimals as u32)); // 1_000_000 let whole = att.amount / scale; // 78

The worked example, including how to recover the fractional part without floats, is on cross-contract reads. Off-chain, the SDK and REST compute value for you.

Method names across surfaces

The same on-chain entry point has a different name in each reading surface. This table maps them:

On-chain (Odra)SDK (claros-oracle)REST (claros-api)
get_latest(asset_id)getValue(id)GET /v1/feeds/:id
get_feed(feed_id)getFeed(id)(folded into GET /v1/feeds/:id)
get_latest + get_feedgetReading(id)GET /v1/feeds/:id
feed_count()feedCount()length of GET /v1/feeds
get_feed_at(index)feedIdAt(i)(enumerated by GET /v1/feeds)
get_at(asset_id, index)(not exposed)(not exposed)

The SDK’s Attestation equivalent is its FeedValue interface (period, amount as a bigint, source_hash, attester as a string, timestamp), and getReading returns a Reading that bundles Feed + FeedValue + the scaled value. Field meanings are identical; see the SDK for the TypeScript types.

Events

If you index attestations instead of polling get_latest, both contracts emit a typed event on write:

ContractEventFields
AttestationRegistryRevenueAttestedasset_id: String, period: u64, amount: U512, source_hash: String, index: u64, timestamp: u64
FeedRegistryFeedRegisteredfeed_id: String, decimals: u8, unit: String, source: String

RevenueAttested.index is the 0-based position the new value took in that feed’s history, so you can fetch it later with get_at(asset_id, index).

Write entry points

These exist for completeness. They are restricted to the Claros agent and owner, so a third-party contract cannot call them (both revert Unauthorized = 1 for any other caller).

Entry pointContractCallerEffect
attest(asset_id, period, amount, source_hash)AttestationRegistryfeed claimant onlyWrites the latest value, appends to history, bumps the counts
register_feed(feed_id, decimals, unit, title, source, route, frequency, description)FeedRegistryZK-eligible callers (new feeds), claimant only (updates)Registers or updates a feed; re-registering an existing id updates it without double-counting

New feeds are claimed by whichever ZK-eligible account registers them; feeds that predate the network upgrade remain with the first-party Claros agent, account-hash 43d7dd06d5538e504e54a3f235f1596f7d2e803e9065bf3c0d040f5cd31a21d4.

Last updated on