Contracts & addresses
Claros runs on Casper testnet (network id casper-test). Four Odra (Rust) contracts
make up the on-chain system, and two external testnet packages (WCSPR and WiseLending)
complete the agent’s payment and yield path. Every contract is upgradable, so its
package hash is the stable address you integrate against: an upgrade publishes a new
contract version under the same package, and your calls keep working unchanged.
This page is the canonical address and entry-point reference. For how a value is produced and read, see how it works; for the on-chain record shape, see the attestation.
Contract addresses
| Contract | Package hash (cspr.live) | Role |
|---|---|---|
| AttestationRegistry | 236b51…16cc | Stores feed values by feed_id: the latest reading plus the full append-only history. |
| FeedRegistry | 741cc2…f5b6 | Stores self-describing feed metadata by feed_id (decimals, unit, source, route, frequency). |
| TreasuryVault | a90b08…fd7b | Append-only ledger of the agent’s reinvestment decisions and treasury holdings. |
| EligibilityGate | 7be33b…5227 | Zero-knowledge (Groth16) access gate the agent clears before it is allowed to attest. |
The AttestationRegistry and the FeedRegistry share one key: a feed_id string. Read the
value from one and the metadata from the other, then compute value = amount / 10^decimals.
See the two registries.
Writes are gated per feed, not per deployment. New feeds are registered by any caller
holding a ZK eligibility credential, and the registry records that caller as the feed’s
claimant; only the claimant can update or attest the feed afterwards. Feeds that predate
the network upgrade remain with the first-party Claros agent, account-hash
43d7dd…21d4,
which also owns the EligibilityGate and TreasuryVault. Any other caller of a gated entry
point reverts with Unauthorized, so every attested value carries a verifiable attester
you can check on-chain. See Run an agent and earn to claim
a feed of your own.
Network
| Setting | Value |
|---|---|
| Network name (chain id) | casper-test |
| JSON-RPC endpoint | https://node.testnet.casper.network/rpc |
| Block explorer | https://testnet.cspr.live |
| Gas and fee token | CSPR (fees are denominated in motes, where 1 CSPR = 1,000,000,000 motes) |
| Contract addressing | by package hash (upgrades preserve the address) |
| Transaction format | Casper 2.0 (Condor) TransactionV1, signed with casper-js-sdk v5 |
Casper testnet charges the gas limit you set, not the gas you consume, and it does not refund the difference. A call that sets a 20 CSPR limit but uses 6 CSPR of compute still costs 20 CSPR. Size every transaction’s payment deliberately: too low and it runs out of gas (and you still pay the limit), too high and you overpay. The agent sizes each attestation and treasury call accordingly, and cross-contract reads should set a tight limit.
Deployment manifest
The full machine-readable manifest lives at shared/deployments.json. The package hashes
below are the same ones the SDK bakes in as defaults, so most integrators
never paste an address by hand.
{
"network": "casper-test",
"rpc": "https://node.testnet.casper.network/rpc",
"explorer": "https://testnet.cspr.live",
"contracts": {
"attestation_registry": "236b510436c60b6a797d175c72c6014de367d43f1de1ca45f580d112f98116cc",
"feed_registry": "741cc223c14c2c00c9f06d7bb5c4be2f824fbf0c8b09a147bf1835570bddf5b6",
"treasury_vault": "a90b082d863c5977c6e54654fec10e523a38760529e664a87e9e8a8e887ffd7b",
"eligibility_gate": "7be33b056c8804e0886cd6f20a75109a0fe92deab505754b97a49fde15aa5227"
},
"external": {
"wcspr": "3d80df21ba4ee4d66a2a1f60c32570dd5685e4b279f6538162a5fd1314847c1e",
"wiselending_scspr": "baa50d1500aa5361c497c06b40f2822ebb0b5fce5b1c3a037ea628cb68d920f3"
},
"agent_account_hash": "43d7dd06d5538e504e54a3f235f1596f7d2e803e9065bf3c0d040f5cd31a21d4"
}These values are package hashes (no hash- or contract- prefix). When you query global
state directly, prefix a package hash with hash- (for example hash-236b51…16cc), which is
exactly what the SDK does under the hood.
Contract reference
Each contract is an Odra module. Below are the key entry points (parameter types as declared
in contracts/src), the events they emit, and the revert errors they define. Every getter is
a free read; the privileged write entry points revert for any caller other than the role noted.
AttestationRegistry
The value store. The agent calls attest once per genuinely new period; everyone else reads.
Source: contracts/src/attestation_registry.rs. Package
236b51…16cc,
contract 48bba0…e75b (v2), installed in transaction ed0820…90fd and upgraded in 954e7b…2244.
| Entry point | Signature | Access | Returns |
|---|---|---|---|
attest | attest(asset_id: String, period: u64, amount: U512, source_hash: String) | Feed claimant only (legacy attester for pre-network feeds) | none, emits RevenueAttested |
get_latest | get_latest(asset_id: String) | Public view | Option<Attestation> |
get_at | get_at(asset_id: String, index: u64) | Public view | Option<Attestation> |
get_count | get_count(asset_id: String) | Public view | u64 (history length for a feed) |
total_attestations | total_attestations() | Public view | u64 (global count across feeds) |
get_attester | get_attester() | Public view | Address |
get_latest returns an Attestation { period: u64, amount: U512, source_hash: String, attester: Address, timestamp: u64 },
where timestamp is the chain commit time in epoch milliseconds. See
the attestation record for the field meanings and freshness rules.
- Event:
RevenueAttested { asset_id, period, amount, source_hash, index, timestamp } - Error:
Unauthorized(for a claimed feed, a caller other than its claimant invokedattest; for pre-network feeds, a caller other than the legacy attester)
FeedRegistry
The metadata store. Holds one self-describing Feed record per feed_id, so a consumer can
interpret a value entirely on-chain (the Pyth model: the value ships with its exponent and unit).
Source: contracts/src/feed_registry.rs. Package
741cc2…f5b6,
deployed in transaction 83487c…8917.
| Entry point | Signature | Access | Returns |
|---|---|---|---|
register_feed | register_feed(feed_id: String, decimals: u8, unit: String, title: String, source: String, route: String, frequency: String, description: String) | ZK-eligible callers (new feeds; caller becomes claimant), claimant only (updates) | none, emits FeedRegistered |
get_feed | get_feed(feed_id: String) | Public view | Option<Feed> |
get_feed_at | get_feed_at(index: u64) | Public view | Option<String> (the feed_id at that index) |
feed_count | feed_count() | Public view | u64 |
get_feed returns a Feed { decimals: u8, unit: String, title: String, source: String, route: String, frequency: String, description: String }.
Re-registering an existing feed_id updates it in place and does not double-count. Use
feed_count with get_feed_at to enumerate every live feed (the SDK
wraps this as listFeedIds).
- Event:
FeedRegistered { feed_id, decimals, unit, source } - Error:
Unauthorized(a new-feed caller without a ZK eligibility credential, or an update by anyone other than the feed’s claimant)
TreasuryVault
The agent’s on-chain reinvestment ledger. The agent records each treasury decision here after it attests, so the capital trail is auditable.
Source: contracts/src/treasury_vault.rs. Package
a90b08…fd7b,
contract 61a3b6…d0bf, deployed in transaction bcbe32…f2a0.
| Entry point | Signature | Access | Returns |
|---|---|---|---|
record_reinvest | record_reinvest(venue: String, amount_in: U512, amount_out: U512, reasoning: String) | Agent only | none, emits Reinvested |
update_holdings | update_holdings(wcspr_liquid: U512, scspr_held: U512) | Agent only | none, emits TreasuryUpdated |
get_reinvestment | get_reinvestment(index: u64) | Public view | Option<Reinvestment> |
reinvestment_count | reinvestment_count() | Public view | u64 |
total_reinvested | total_reinvested() | Public view | U512 |
venue_total | venue_total(venue: String) | Public view | U512 (total routed to one venue) |
wcspr_liquid | wcspr_liquid() | Public view | U512 |
scspr_held | scspr_held() | Public view | U512 |
get_agent | get_agent() | Public view | Address |
A Reinvestment is { venue: String, amount_in: U512, amount_out: U512, reasoning: String, timestamp: u64 },
where reasoning is the agent’s recorded rationale for the decision.
- Events:
Reinvested { index, venue, amount_in, amount_out, reasoning, timestamp },TreasuryUpdated { wcspr_liquid, scspr_held, timestamp } - Error:
Unauthorized(a caller other than the agent invoked a write)
EligibilityGate
A zero-knowledge access gate. A caller proves (off-chain) that it knows a secret whose leaf is in a depth-20 MiMC7 Merkle allowlist, without revealing which leaf. The contract verifies the Groth16 proof on-chain, binds the caller’s account, and burns a one-time nullifier so the proof cannot be replayed. The Claros agent clears this gate before it attests.
Source: contracts/src/eligibility_gate.rs. Package
7be33b…5227,
deployed in transaction bb3595…be88.
| Entry point | Signature | Access | Returns |
|---|---|---|---|
verify_eligibility | verify_eligibility(proof: Vec<u8>, root: U256, nullifier_hash: U256) | Public, proof-gated | none, emits EligibilityGranted |
set_root | set_root(new_root: U256) | Owner only | none, emits RootUpdated |
is_eligible | is_eligible(who: Address) | Public view | bool |
get_root | get_root() | Public view | U256 |
granted_count | granted_count() | Public view | u64 |
The public inputs to verify_eligibility are (root, nullifier_hash, caller). The on-chain
allowlist root is currently 5652912302653102267326913836753961554938404630179929975228700590098587111483
(it moves as operators enroll), and the gate has granted eligibility twice so far
(granted_count = 2: the first-party agent in transaction b3048a…a433 and operator #2 in
f5b96c…9de7a). Internals are covered on the ZK eligibility gate page.
- Events:
EligibilityGranted { account, nullifier_hash, timestamp },RootUpdated { root } - Errors:
NotOwner(1),UnknownRoot(2),InvalidProof(3),AlreadyClaimed(4)
External packages
Two existing Casper testnet packages complete the agent’s economic loop. Claros does not own them; it integrates against their deployed package hashes.
| Package | Package hash (cspr.live) | Role in Claros |
|---|---|---|
| WCSPR (Wrapped CSPR) | 3d80df…7c1e | Settlement asset for the paid x402 feed. It supports transfer_with_authorization, which the x402 exact scheme uses so a client can authorize a transfer that the facilitator settles on-chain. |
| WiseLending sCSPR | baa50d…20f3 | Liquid-staking yield venue. The agent stakes treasury CSPR through its stake entry point, receives sCSPR, and records the move in the TreasuryVault. |
See the autonomous agent for how the agent decides between staking into WiseLending, native delegation, and holding.
Referencing these contracts
How you point at these package hashes depends on where your code runs. The free off-chain reads use only the two registries; the contract path can call any of them.
SDK
The claros-oracle SDK has the testnet RPC and the two registry package
hashes baked in as defaults, so there is nothing to paste:
import { ClarosOracle } from 'claros-oracle'
const oracle = new ClarosOracle() // casper-test RPC + registries baked in
// Override only to point at a different deployment:
const custom = new ClarosOracle({
rpc: 'https://node.testnet.casper.network/rpc',
attestationRegistry: '236b510436c60b6a797d175c72c6014de367d43f1de1ca45f580d112f98116cc',
feedRegistry: '741cc223c14c2c00c9f06d7bb5c4be2f824fbf0c8b09a147bf1835570bddf5b6',
})The SDK reads the AttestationRegistry and FeedRegistry only. The TreasuryVault and EligibilityGate are read on-chain or over JSON-RPC.
Verify it on-chain
Nothing here requires trusting this page. Every claim is checkable on testnet right now.
- Open the explorer. Each package hash above links to its cspr.live contract-package page, where you can see its versions, entry points, and recent activity.
- Count the feeds.
FeedRegistry.feed_countreturns 39 registered feeds (38 serving live values). The SDK helperfeedCount()andGET /v1/feedsboth reflect this. - Count the attestations.
AttestationRegistry.total_attestationsreturns 78+ total attestations across all feeds. - Check the gate.
EligibilityGate.granted_countreturns 2.
import { ClarosOracle } from 'claros-oracle'
const oracle = new ClarosOracle()
await oracle.feedCount() // 37
await oracle.getReading('OP-1') // San Diego parking, value 2734.2 USD
await oracle.getReading('EIA.PET.PRICE.WTI.DAILY')// WTI crude, value 78.94 $/bblVerify the deployed code matches the repo
Casper has no Etherscan-style “verified source” badge, so Claros ships the proof in the repository instead. contracts/wasm/ holds the exact deployed artifacts, and one script compares them byte-for-byte against the chain:
node scripts/verify-onchain.mjsFor each contract it fetches the install transaction from the public RPC, extracts the session module_bytes (the wasm the network stored at deploy time), and compares its sha256 against the artifact in contracts/wasm/. All four contracts match:
| Contract | Deployed wasm sha256 |
|---|---|
| AttestationRegistry | 7c207fe977cd904a9ebd45f227373897e46235b3efcb2f4790c6bcef31b7a987 |
| FeedRegistry | f690dc619de65063cbec86fc1e891a58834cefe7a9a6c4d9064e1824eee85146 |
| TreasuryVault | 5c0ff440ae985f94d68687a15de25100486714b331eb3f4a2f78f964a8076aee |
| EligibilityGate | 9b497c53c466d1f8d0eb3d2c203e06b892e8517123397820cc126695dfdd6db0 |
The script needs no keys and no dependencies, so anyone can rerun it against the public network at any time. The contracts build from contracts/src with Odra 2.8 on the repo’s pinned Rust toolchain.