threshold
submissions must remain.
If the cluster has fewer than 2 * (signers - threshold) + 1 submissions, it
must also satisfy highest * 10000 <= lowest * (10000 + max_cluster_spread_bps).
A failed count or spread check is a quorum miss. A qualifying cluster produces
the lower median.
Signatures omit the Soroban-injected Env argument. Construction uses
__constructor(admin: Address, signers: Vec<Address>, threshold: u32, resolution: u32) -> Result<(), Error>. The signer set must have no duplicates,
and threshold must be between 1 and the signer count.
The price aggregator applies additional staleness and sanity checks when using
this oracle as a source. See Oracles.
Submitting prices
signer must authorize. submit_prices applies one package_timestamp to
every entry and fails with #10 LengthMismatch if the two vectors differ in
length.
package_timestamp is in milliseconds. Every submission is checked against
five rules before it is stored:
The timestamp ordering rule applies per signer and feed. The aggregate uses
the oldest qualifying package timestamp, which may move backwards when the set
of qualifying submissions changes.
A stored submission immediately recomputes that feed’s aggregate. Submitted
prices must be in
1..=1000000000000000000000000. Prices use 8 decimals.
History retains at most 12 aggregates. A write within one resolution period
replaces the last history entry; a later write appends and evicts the oldest
entry when needed.
Reading prices
The contract exposes Reflector-compatible reads and RedStone-shaped bulk reads. All reads are permissionless. History reads can renew storage TTL.
The RedStone-shaped reads return
#7 NoDataForFeed when no aggregate exists. A
submission quorum miss retains a previous aggregate; reads can keep serving it
until it expires with #8 StaleData. Owner-triggered recomputation and signer
removal clear the aggregate if quorum is no longer met. Reflector-shaped reads
return None for unavailable or stale data.
Freshness bounds
Submission eligibility and served-price freshness use separate bounds.
The consuming source’s
max_stale_seconds must cover
max_submission_age_seconds and fit within its oracle’s outer staleness bound.
The price aggregator checks the earlier of package and write timestamps, so its
price can expire before this contract’s write-age check rejects the aggregate.
Administration
The configuration methods below are owner-gated (#[only_owner]). This oracle
has its own owner; the lending governance contract has no typed operation that
forwards these calls. Read get_owner() to identify the deployed oracle’s authority.
The contract also exposes the OpenZeppelin ownership surface:
Recompute feeds in batches. Each feed costs roughly one ledger entry per signer
plus three, so a full sweep can exceed the transaction footprint limit. Use
feeds() to list ids. Simulate each batch to check its resource use.
Error codes
Decode these codes only when the XOXNO oracle is the emitter associated with the final error diagnostic. A top-level controller call can fail in a dependency. See error attribution. Ownership checks also use OpenZeppelin errors, outside this table.Next
Oracles
How the price aggregator composes this into a validated USD price.
Oracle disruption runbook
What to do when a feed stops producing an aggregate.
Addresses
Oracle adapter registry addresses and verification steps.

