Skip to main content
The price aggregator resolves external observations into USD prices for account valuation. The controller consumes these prices; the pool does not value assets in USD. A failed price check prevents valuation-dependent transactions from proceeding.

Configuration keys

An oracle is keyed by token or intermediate reference, rather than by hub or spoke:
Each key maps to an AssetOracle:

Price sources

An LP source must be the only source on its key. Nested Scaled.quote and LP leg references are bounded by MAX_RESOLUTION_DEPTH = 3; exceeding the bound returns #229. A reference cycle returns #225 OracleCycleDetected.

Feed providers

FeedSource contains provider, decimals, and max_stale_seconds: Reflector asset references accept Stellar(Address) or Symbol(Symbol); a String reference returns #204. A direct Reflector feed must be USD-quoted. A Reflector factor in Scaled must instead quote the Stellar token named by Scaled.quote = Token(address). A USD-quoted factor or Ref quote fails with #220. FeedNature distinguishes market observations from fundamental valuations. It affects staleness handling and the leg-age comparison described below.
At least one complete non-LP source composition must have no unsmoothed market dependency, or configuration fails with #38 SpotOnlyNotProductionSafe. A direct Reflector TWAP feed or direct fundamental feed meets this requirement. A fundamental factor does not make an unsmoothed market quote dependency safe.
The XOXNO oracle aggregates authenticated signer submissions into a median under a configured quorum. The quorum applies across stored submissions; each submit call authenticates one registered signer. Submission age and relative skew control eligibility. See XOXNO oracle ABI for submission, aggregation persistence, and administration. A configured feed id does not prove that an observation is available or establish its upstream pricing method. Match the provider address and feed id to the oracle configuration and read a usable quote before integrating an asset.

Composition and tolerance

A two-source oracle requires both legs to pass resolution and freshness checks. It serves their midpoint, (a + b) / 2, only when they agree within tolerance. One readable leg is incomplete and fails with #205 UnsafePriceNotAllowed; there is no single-leg fallback. Agreement compares the larger price to the smaller, expressed as a BPS ratio, against upper_ratio_bps. The configured upper bound must be between 10_150 and 12_500. Governance expresses this as excess tolerance above BPS = 10_000, from 150 through 2,500 bps. Invalid tolerance returns #208. At configuration, lower_ratio_bps must equal half_up(BPS * BPS / upper_ratio_bps). The price-time comparison uses the upper ratio; comparing larger to smaller covers either direction of deviation. A single-source oracle serves its accepted leg directly. It still checks freshness, positivity, and sanity, and requires a tighter sanity band: MAX_SINGLE_SOURCE_SANITY_BAND_BPS = 1000. Two sources with identical provider trust sets also receive this cap. LP oracles use MAX_LP_SANITY_BAND_BPS = 8182. An overly wide single-source band returns #226. For two-source independence, RequireDisjoint forbids shared provider contracts. AllowShared requires the declared shared set to match the actual set exactly, or configuration fails with #232 IndependenceNotDeclared.

Freshness and sanity

If future-dated observations leave no readable leg, resolution returns #210 NoLastPrice. In a dual-source oracle, one remaining fresh leg gives #205, while a stale remaining leg gives #206. The sanity band must be at least MIN_SANITY_BAND_BPS = 50 wide and stay within MAX_REASONABLE_PRICE_WAD, equal to $1 billion per whole token. Sanity is a pass/fail check on the resolved price; it never clamps an out-of-range value.

Failure behavior

Every valuation-dependent mutation needs a complete price snapshot. Provider failure, stale observations, disagreement, or invalid sanity checks revert the operation. This also applies to risk-decreasing actions when they need valuation. There is no per-flow permissive mode or TWAP-to-spot fallback. Choose the read method by its failure behavior: price_spread reports source divergence, not a confidence interval around the midpoint. Dashboards can use quotes to report an individual market failure. Transaction logic must require a valid price before using it. PriceFeedRaw contains price_wad, asset_decimals, and timestamp. PriceStatus contains final_wad, primary_wad, secondary_wad, price_timestamp, stale, deviation, valid, and error_code: Option<u32>. The price timestamp uses the older accepted leg. The controller’s get_market_indexes_detailed includes equivalent diagnostics under price_wad, primary_price_wad, anchor_price_wad, stale, deviation, valid, and error_code.
A resolution failure can zero other diagnostic fields, including stale and deviation. Their being false does not prove those checks passed. Require valid: true; use error_code to explain a failure. None supplies no error reason and does not make an invalid quote usable.

Price-aggregator interface

Read signatures are:
Governance routes the configuration and upgrade calls: A non-LP configuration probe rejects configuration failures but may accept a stale, deviating, or out-of-band observation. LP admission requires a usable price. Dependent keys receive structural validation, so a successful configuration change does not establish that dependent prices are usable. The three configuration methods emit UpdateAssetOracleEvent on ["config", "asset_oracle"] with the full resulting configuration. Use the Oracle disruption runbook for diagnosis and repair, and Governance for scheduling changes.