Canonical reference for Pharos live-price selection, fallback enrichment, and source-specific normalization.
Supply fallback behavior is owned by Supply Snapshot: Supply Pipeline. Cross-pipeline cache and integrity guardrails are retained in Data Integrity Guardrails below.
Agent navigation — Grep the heading you need: Overview · Active Price Coverage Health · Versioning · Primary Consensus · Provider-Specific Normalization · Authoritative Overrides · Fallback Enrichment · Current Operator Limitations · Confidence Model · Update Rules · Data Integrity Guardrails · Gold & Silver Spot Prices · Treasury Benchmark Rates · Stale Data Monitoring.
Overview
Pharos separates critical publication from best-effort corroboration:
- 15-minute publication runs the full primary consensus: DefiLlama and CoinGecko, the curated CoinGecko ticker and CEX lanes, RedStone, Curve on-chain/oracle, reserve NAV telemetry, promoted DEX observations, and the post-consensus pool challenge. Registered authoritative overrides then run before publication.
- Hourly corroboration runs after the existing
:09status-check chain, ahead of the:15publication. This avoids the previous full publication-cycle wait after:00publication without putting provider requests on the primary path or extending source TTLs. It requires a valid published cache to build its cohort; unavailable or malformed cache reads leave the prior staging untouched. Fallback probes restore curated provider identifiers, contract overrides, and NAV hints omitted from public rows before enrichment. Original missing-price identity survives probe price clearing so bounded provider searches prioritize recovery over low-depth corroboration. It runsenrichMissingPrices()and the explicitly enabled exact-address provider against only the latest missing or low-depth rows. It stages actual fetched observations inprice:corroboration-observations:v1for a later publication to revalidate, separately from ordinaryprice_cachereplay provenance. Each hourly attempt also persists a boundedprice-corroborationcron event with the scheduled slot, Worker version, resolution counts and provider status/error classes. Admin status exposes it assync-stablecoins.latestEventwhen it is the newest event; newer critical slot events take precedence. Provider URLs, credentials, response bodies and raw error messages are excluded. DexScreener diagnostics distinguish failed requests from successful empty or price-ineligible responses; circuit and price-admission rules are unchanged. The same 15-minute price-observation refresh also re-observes the rows whose published price comes from the exact-address lane, because those quotes live for a single publication window and are never replayable fromprice_cache; a row that lane prices, or any row still missing a price, gets a fresh exact-address observation each slot instead of waiting for the next hourly collection.
Both publication paths record priceObservationEffectiveness in progress and final cron metadata, including the size-compacted main result. It reports staging status/slot/age, loaded and eligible observation counts, mutually exclusive discard reasons, and eligible outcomes (alreadyPriced, assetAbsent, policyRejected, selected, notNeededAfterSelection). Loaded count is null when the staging payload was not read or validated. Eligible outcomes sum to eligible observations; eligible plus discarded sum to loaded observations when known. selected means applied to the candidate payload, not independent proof of cache publication; use the final run's publication evidence and cache generation to confirm delivery. Minimum freshness headroom measures remaining source TTL among eligible observations. The shared completion path reads the existing staging cache even when all assets already have prices, with no additional provider requests or changes to admission, freshness or selection order.
The output is the cached price, priceSource, priceConfidence, priceObservedAt, priceObservedAtMode, priceSyncedAt, optional priceSourceConfidenceProfile, and compatibility priceUpdatedAt fields served through /api/stablecoins.
When an asset still has no usable current price after validation and fallback recovery, Pharos keeps price = null, priceConfidence = null, and serializes priceSource = "missing" so the cache payload stays structurally valid while still making the missing-price state explicit.
Active Price Coverage Health
Price coverage is evaluated independently from active-row publication. evaluateStablecoinActivePriceCoverage() compares the final payload to the current active registry and counts a row as observed/priced only when isObservedPrice() admits its finite positive price. A valid published nominalPriceReference without an observed price is a separate reviewed category, not a missing gap: nominalReferenceCount, nominalReferenceIds, and nominalReferenceMarketCapUsd expose it with nominalReferenceReason: "reviewed-nominal-reference". The nominal market-cap total is null if any constituent supply is unreadable. A trusted observed market quote takes precedence over its nominal sidecar for coverage. Rows with neither remain missing even though their supply and lifecycle data remain publishable. Nominal-only assets do not accumulate missing streaks, enter missing-price alerts or duration bands, or inflate observed-price counts. Status evidence records an informational active_price_coverage_nominal_reference cause.
Every published main and CoinGecko-supply-fallback sync-stablecoins run writes activePriceCoverage into cron metadata with the expected, present, and priced active counts; exact priced and missing IDs; the positive raw USD circulating value affected; and per-gap price, source, observation-time, confidence, market-cap, consecutive-generation count, rejection class, and last accepted provenance. Compact streak state survives the cron metadata size guard. The producer compacts below 60 KiB before scheduler enrichment, reserving 4 KiB for lease and slot metadata so coverage remains top-level evidence in the persisted row. Because the compacted shape still grows with the missing-asset count, the guard walks an explicit degradation ladder — it first drops the attempt ledger's duplicate of the coverage missing-ID list, then verbose per-gap details, then attempt records, keeping exact ID sets, full compact streak state, counts, and truncation markers at every rung — and, when no rung fits a catalog that has outgrown the budget, fails closed with a scalar-only envelope marked sizeGuardEvidenceDropped instead of letting the global 64 KiB persistence compaction destroy the evidence unmarked. A gap does not downgrade a successfully completed cron execution or block the otherwise valid stablecoins cache; its warning eligibility is governed by the persistence gate described below and by the price-gap review registry. A gap may be acknowledged by a dated, owned, expiring review (STABLECOIN_PRICE_GAP_REVIEWS in worker/src/lib/stablecoin-publication-coverage.ts): the gap still publishes as missing in every count and ID list, but it is never alert-eligible while the review is valid. Acknowledgement is recomputed from the registry on every evaluation and every read — never persisted as truth — so adding, renewing, removing, or expiring a review takes effect immediately, and an expiry re-arms the alert even before the next producer run. Publication omission waivers are a separate mechanism (STABLECOIN_PUBLICATION_WAIVERS); acknowledgement never waives an active row, and never applies to a depeg or to an asset that has a price.
/api/health reads this state from the latest sync-stablecoins cron row. It reports complete only when the recorded registry size, disjoint observed-priced plus reviewed-nominal ID sets, and zero-gap counts all match the current active registry. Missing metadata is unknown. Unknown coverage fails closed and degrades public health because the public surface cannot prove exact active-price coverage. An incomplete assessment is warning-only: once at least one missing asset is alert-eligible — that is, its consecutive-missing-generation streak has reached ACTIVE_PRICE_COVERAGE_ALERT_GENERATIONS (currently two), the same threshold that raises the /api/status cause to warning severity — /api/health emits the active-price-coverage-incomplete warning listing only the offending unacknowledged, alert-eligible IDs (an acknowledged gap never appears there), but the public banner remains healthy unless another public-impact gate is degraded. A transient single-cycle miss (typically a fallback-rotation asset not yet re-priced) leaves public health healthy and emits no warning, while the JSON activePriceCoverage payload still reports the exact present/priced/missing counts and IDs for observability, plus acknowledgedGapIds/acknowledgedGapCount, the per-asset acknowledgedGap owner/reason/sources/expiry, and expiredGapReviewIds/invalidGapReviewIds. The admin /api/status data-quality layer records an active_price_coverage_incomplete/unknown cause and its missing-price ratio bands at any gap, independent of the public-health severity gate; when no unacknowledged gap is alert-eligible the cause is informational and names each acknowledged ID with its review expiry, alongside informational price_gap_reviews_expired/price_gap_reviews_invalid causes so expired and malformed reviews get renewed or converted into a freeze/delist decision rather than silently lapsed. Both publication paths carry the per-asset ID, symbol, rejection class, streak, and last accepted source/time in the persisted activePriceCoverage cron metadata; there is no separate cron-event or webhook delivery today, so operator triage reads the /api/health warning, the /api/status cause, and that metadata. This is separate from activePublicationCoverage, which answers whether the active rows themselves are present.
Continuity reads distinguish an absent prior generation from a failed read or malformed prior metadata. Only successful absence starts a new missing streak at one. Read failures persist consecutiveMissingGenerations: null with streakUnavailableReason (previous-coverage-read-failed or previous-coverage-malformed), including in compact state; subsequent missing generations retain that uncertainty until a priced generation establishes a new boundary. Current measured counts remain available. Unknown continuity conservatively makes unacknowledged missing assets alert-eligible and projects escalation priority into the provider queues, without treating that priority as a measured streak. Valid acknowledgements still suppress escalation. Material gaps with unknown duration retain a degraded warning but never claim a week-long critical duration. The shared ActivePriceCoverageHealthSchema owns the public contract; missing, failed, or malformed current coverage reads publish null counts, affected market cap and maximum streak with unavailableReason, never synthetic zero.
Versioning
- Current methodology version: <!-- GENERATED-START: methodology-version-pricing-pipeline -->
v6.41<!-- GENERATED-END: methodology-version-pricing-pipeline --> - Canonical version module:
shared/lib/methodology-versions/registry.ts - Public changelog route:
/methodology/pricing-pipeline-changelog/ - Longform methodology section:
/methodology/#pricing-pipeline-methodology
Primary Consensus
fetchPrimaryPrices() keeps the established depeg-protective consensus in the critical 15-minute publication path. It combines DefiLlama intake observations with CoinGecko, curated exchange tickers, RedStone, Curve, reserve NAV telemetry, and promoted DEX observations, then applies the pool challenge. Registered authoritative protocol/NAV overrides run afterward and may replace the selected market price. Only the five missing-price fallback passes and exact-address providers are detached to hourly corroboration.
Source Weights
| Source | Weight | Module / Origin | Cadence and role |
|---|---|---|---|
CoinGecko /simple/price | 2 | worker/src/lib/coingecko-simple-price.ts | One live primary fetch surface per 15-minute publication; uses upstream last_updated_at when available and rejects stale rows. |
| CoinGecko ticker | 2 | worker/src/lib/cg-ticker.ts | Curated exchange-ticker corroboration for the tracked Kinesis assets. |
| DefiLlama stablecoins list | 1 | Typed quote from the intake response | Local primary input on every 15-minute publication; no second request. |
| Binance spot | 2 | worker/src/lib/cex-tickers.ts | Batch venue input retained in 15-minute consensus. |
| Kraken spot | 2 | worker/src/lib/cex-tickers.ts | Explicit-pair venue input with alias-safe symbol mapping. |
| Bitstamp spot | 1 | worker/src/lib/cex-tickers.ts | Lower-weight all-tickers corroboration venue. |
| Coinbase spot | 2 | worker/src/lib/cex-tickers.ts | Per-symbol venue input. |
| RedStone | 1 | worker/src/lib/redstone.ts | Fresh exact-case oracle symbols with venue-agreement gating and solo retry recovery. |
| Curve on-chain and crvUSD oracle | 3 | worker/src/lib/curve-onchain.ts | Configured pool routes plus the crvUSD PriceAggregator oracle. |
| Chainlink/Superstate/JPMorgan reserve NAV telemetry | 3 | reserve_composition | Matched fresh reserve snapshots, with fresh/static FX conversion for non-USD NAVs. |
| Promoted DEX protocol lanes | 2–3 | worker/src/lib/depeg-helpers.ts | Per-protocol observations from dex_prices; each lane must agree with a hard source or an independent promoted DEX lane, and the aggregate is withheld whenever any promoted protocol candidate exists, even when every lane is then rejected (registry, freshness, TVL, or corroboration). |
| Authoritative protocol/NAV overrides | authoritative replacement | worker/src/lib/authoritative-price-sources/ | Bounded route registry evaluated after primary consensus for assets with a registered known source. |
| CoinGecko Onchain exact-address | provenance weight 1 | worker/src/lib/address-price-providers/coingecko-onchain.ts | Hourly corroboration only, limited to the prior publication's missing or fewer-than-three-source rows; never blocks the 15-minute publication. |
Reserve NAV quotes use the shared decoder in worker/src/lib/reserve-nav-price.ts. It accepts only registered NAV adapters, independently checks the successful snapshot fetch age and upstream NAV evidence clock, and rejects invalid/nonpositive NAV, missing or malformed metadata, and stale or excessively future evidence. JPMorgan JLTXX uses the exact issuer Token Class transaction NAV with the shared five-day business-day NAV source-age policy; Chainlink and Superstate retain their existing source-specific policies. Supply admission can read the same matched successful snapshot before a new NAV asset has a previous stablecoin cache row: fiat-cg.ts values positive native on-chain supply at that observed NAV and applies the existing trusted FX conversion for non-USD classes. This valuation does not itself fabricate a published market quote; primary reserve-NAV consensus supplies the live price. A missing/stale NAV and absent alternative trusted price leave the candidate out, rather than assigning nominal $1.
Promoted DEX corroboration is candidate-scoped. A hard-source match admits only the agreeing protocol lane, while DEX-only corroboration requires an independent protocol lane within the existing divergence threshold. Divergent siblings are excluded with lacked_corroboration telemetry rather than inheriting another lane's evidence.
The primary CEX, ticker, oracle, promoted-DEX, reserve-telemetry, and pool-challenge lanes remain part of sync-stablecoins publication because they protect depeg detection. Inline exact-address transport does not. DexScreener-address, DexPaprika-address, Alchemy-address, Moralis-address, and Birdeye-address adapters were removed; CoinGecko Onchain is the only retained exact-address adapter and is disabled unless explicitly allowlisted.
Pyth Hermes was retired from live primary consensus on 2026-08-26 after Pyth's API-key mandate made the free tier unavailable for API access. New runs do not request the Pyth lane and stablecoin metadata no longer carries pythFeedId; the pricing registry retains the retired pyth key only so historical price provenance remains renderable.
Historical note (v2.0→v2.1): The DL coins API (
coins.llama.fi/prices/current/coingecko:{id}) was removed from primary consensus because it returned CoinGecko-sourced data, creating illusory two-source agreement. It is still used in fallback enrichment via contract-address queries.
Reused Solomon provider identity
CoinGecko reassigned solomon-usdv to the separate Chancery mint in September 2026. On 2026-09-24 DefiLlama re-pointed stablecoin asset 261 to the replacement mint as well (circulating jumped 1,514,736 → 5,581,590 and now equals the replacement supply), which made the legacy profile's listed supply feed attribute the replacement's supply to the legacy coin alongside usdv-solomon-v2. Since 2026-09-27 the legacy usdv-solomon therefore ingests supply through the supplemental single-contract on-chain lane (detailProvider: "coingecko", no llamaId, no geckoId): the worker reads getTokenSupply on the exact legacy mint Ex5DaKYMCN6QWFA4n67TmMwsH8MJV68RX6YXTmVM532C (≈1.51M USDV, 9 decimals), fails closed when the probe is unreadable, values the row at the USD peg reference, and leaves the re-pointed DefiLlama 261 list row untracked. Every remaining DefiLlama price lane for the legacy id — the 261 list row and the solana:Ex5Da… supplemental contract-price fallback — aliases the replacement, so applyTrackedAssetOverrides nulls the price at intake and worker/src/lib/solomon-usdv-identity.ts rejects those source families, unknown/cached provenance, and contaminated agreement lineage at publication and previous-price replay; exact-mint market quotes (the reviewed Jupiter Meteora pool) still follow the ordinary admission rules. Cached detail responses are sanitized before optional enrichment from the canonical publication.
The replacement usdv-solomon-v2 alone owns geckoId: solomon-usdv and uses the existing CoinGecko supplemental current-price/market-cap path. The recycled provider history is current-only: CoinGecko market-history, direct price backfill, depeg-history audit, hourly price history, caller-provided history seeds and their DefiLlama proxy history are withheld. New detail charts use local supply/cache history, and forward local collection continues. No historical rows are deleted and no guessed migration date divides the mixed provider series.
Consensus Rules
Before clustering, repeated quotes with the same source key are collapsed to one provider observation using their median,
maximum configured weight, and conservative observation time. Registered source keys that share a
depegSourceFamily are then reduced to the strongest representative for that family. This prevents multiple
deployments from one address provider, or correlated lanes such as CoinGecko list and CoinGecko ticker data, from
creating false multi-source confidence or gaining extra median weight.
computePriceConsensus() then behaves as follows:
- 0 sources -> no result
- 1 source ->
single-source - 2+ sources -> build fully pairwise agreement clusters within a peg-aware threshold
- best cluster with 2+ members -> initially
highconfidence, publish the cluster median, and keep the best trusted member as internal provenance. For even-sized clusters, the median is the midpoint average of the two middle sorted members. - no 2+ cluster:
- fixed pegs -> stay in fixed-peg mode even if the reference price is temporarily unavailable; choose the best trusted fallback source by trust tier first, then reference proximity, and mark
low - NAV tokens -> use a wider 500 bps cluster threshold first, otherwise choose the best trusted fallback source and mark
low
- fixed pegs -> stay in fixed-peg mode even if the reference price is temporarily unavailable; choose the best trusted fallback source by trust tier first, then reference proximity, and mark
When multiple clusters have the same size, the winner is chosen deterministically by:
- larger total cluster weight
- stronger trust tier (any hard-tier member > mixed > all soft) — prevents a tight soft cluster from beating an equal-weight hard cluster on proximity alone
- tighter internal spread
- proximity to peg reference (when available)
- stable alphabetical source label as the final tie-break
Source labels list all agreeing sources alphabetically:
- 1 source: source name directly
- 2+ sources:
sourceA+sourceB+sourceC(full list, no truncation)
High-confidence consensus now separates:
- the published price: the median of the agreeing winning cluster
- the selected source: the best cluster member kept internally for provenance and downstream trust policy
Inside the winning cluster, the selected source is chosen by:
- higher configured weight
- stronger trust tier
- closer distance to the reference price
- alphabetical source key
When severe fixed-peg downside publication is accepted because multiple candidate sources independently confirm the downside, that candidate-price evidence is carried through the later prevalidation and post-enrichment validation passes as long as the current asset price, source, and confidence still match the selected primary result. Post-enrichment validation also merges a same-run primary candidate set with a current fallback quote when fallback recovery replaced the selected result. This keeps a corroborated low-confidence depeg price from being cleared as if it were genuinely single-source, without loosening the guardrail for unrelated fallback, correlated list-only, or stale prices.
Severe fixed-peg downside corroboration counts independent source families, not raw source labels. CoinGecko-derived sources share one lineage, DefiLlama list/detail/contract sources share one lineage, each CEX/oracle source keeps its own lineage, and promoted DEX protocol lanes count by protocol. A CoinGecko plus DefiLlama-list downside pair is treated as correlated list-aggregator evidence and cannot publish a severe downside price unless a separate hard or non-list family also corroborates it.
Publication Pool Challenge
sync-stablecoins invokes the pool challenge after primary consensus on every 15-minute publication. It remains a critical depeg safeguard: current published challenger pools can downgrade a weak soft-source result or replace it when the independent-protocol rules below are satisfied. The hourly fallback/address corroboration phase does not replace this check.
After consensus, weak soft-source results where the selected/agreeing source cluster is pool-challenge eligible are challenged against current individual priced pools from the published challenger snapshot (dex_price_challenger_snapshots + dex_price_challengers) that meet the live $100K TVL minimum and are fresh within DEX_FRESHNESS_SEC. Eligible source families include CoinGecko, DefiLlama-list, dex-promoted, and promoted protocol-level DEX sources (fluid-dex, balancer-dex, curve-dex, uniswap-v3-dex, uniswap-v4-dex, raydium-dex, orca-dex, meteora-dex, pancakeswap-dex, aerodrome-dex, velodrome-dex) as long as the selected cluster does not include an exempt hard source. Non-selected hard candidates do not by themselves exempt the selected soft result, but they can corroborate the narrow high-TVL replacement exception below. NAV tokens are excluded from the pool challenge entirely: their fair value is their published NAV and the peg-aware divergence threshold does not map to a meaningful DEX-liquidity check, so diverging pools cannot downgrade or replace a NAV price. The standard divergence threshold is peg-type-aware: 500 bps for USD pegs, min(2× depeg threshold, 500) for non-USD pegs (e.g., 300 bps for JPY/EUR). High-TVL replacement paths use the peg depeg threshold as their result-vs-pool trigger when the soft result is still inside that same threshold. If ANY qualifying protocol median diverges from the weak result beyond the applicable threshold:
Challenger publication preserves protocol diversity before applying its 95% qualifying-TVL coverage target: it first retains the highest-TVL qualifying pool from each protocol, ordered by total qualifying protocol TVL, then fills remaining slots from the global pool-TVL order until the coverage target or 50-row hard cap is reached. If more than 50 protocols qualify, representatives from the 50 largest protocol groups are retained. This prevents a dominant venue from consuming the coverage budget before a smaller independent protocol can reach the multi-protocol replacement check; it does not change the per-pool TVL floor, freshness rules, validation, or replacement thresholds. Challenger pools come only from the retained set that already passed the discovery-time pool-price coherence admission (DEX Liquidity), so a provider row whose tracked-leg price is incoherent with its own pair ratio never becomes challenge or DEX-bridge evidence.
- Confidence downgrades to
lowonly while the divergence is unresolved: the downgrade is skipped when ≥2 independent protocol medians corroborate the selected price and strictly outnumber the diverging protocols (corroboratingProtocolGroupsOutvoteinworker/src/lib/constants.ts, the same authority as the replacement precedence test). A tie, a diverging majority, or a single corroborating protocol still downgrades, and any replacement that happens still downgrades because DEX evidence displaced the consensus. - The price is replaced when diverging protocol-level challenger prices span ≥2 independent protocols. A single protocol's pools may share data-quality issues (vault-token counterparties, misconfigured pairs), and one rogue pool inside an otherwise agreeing protocol does not make that protocol count as corroborating disagreement. A high-TVL multi-protocol path also replaces a near-peg soft result when at least two independent protocol medians each carry at least
$5MTVL, are depeg-sized in the same direction, diverge from the soft result by at least the peg depeg threshold, and agree with each other inside the existing pool-challenge bps band. If an additional high-TVL protocol median shows the same direction but breaks pairwise coherence, Pharos selects the largest coherent same-direction high-TVL subset instead of letting that outlier veto the otherwise corroborated replacement. A narrow single-protocol exception exists when that protocol median carries at least the$5Mhigh-TVL threshold, the protocol median itself is depeg-sized versus the peg reference, the DEX mark materially diverges from the published soft result, and a hard market/oracle/protocol primary candidate agrees with that protocol median within the normal consensus threshold. When replacement fires, Pharos first collapses each protocol to a TVL-weighted median price, then evaluates divergence and the final replacement from those protocol medians. When only one lower-TVL or uncorroborated protocol diverges, or no coherent high-TVL same-direction subset remains, the original price is preserved but confidence stayslow. The diverging protocols must also not be outnumbered by the challenger protocols whose medians corroborate the current price: replacement weight is provider-reported challenger TVL and a dormant pool keeps its last traded price behind a large nominal reserve, so a two-protocol diverging minority could otherwise outvote a corroborated consensus (the 2026-09-24vchf-vnxreplacement used the Celo Uniswap v3VCHF/USD₮pool, last traded 2026-03-15 behind a reported $4.7M reserve, against four protocols sitting on the ECB franc rate). A corroborating majority no longer downgrades confidence (see item 1); the high-TVL multi-protocol and single-protocol hard-corroborated exceptions above can still replace the price, and a replacement itself still downgrades.
Before any pool-challenge divergence or replacement decision, protocol-level challenger medians must pass the peg-aware dex_observation price validator. This keeps inverse or malformed commodity marks (for example 1 / XAUUSD instead of a USD-per-ounce gold token price) from downgrading or replacing a healthy primary price, while valid depeg-sized DEX medians remain eligible for the normal replacement paths.
When pool-challenge replacement fires, the selected primary result is rewritten in lockstep so downstream carry-through sees the new source: allPrices, observedAtBySource, and observedAtModeBySource are collapsed to a single pool-tvl-weighted entry, the replacement observedAt is the minimum of the contributing pools' observed-at timestamps (with mode local_fetch), and agreeSources / candidateSources / disagreeSources are updated to match. This keeps hasCorroboratedSevereDownsideCandidate and the primary-candidate carry-through lane from reading stale pre-replacement sources during later validation passes.
If the selected primary price is a severe fixed-peg downside and at least two live candidate sources independently corroborate that downside by source family, including at least one depeg-authoritative source such as RedStone or Curve on-chain, pool challenge can still downgrade confidence but cannot replace the selected price with a DEX pool median. The same candidate corroboration also satisfies the temporal-jump guard when the previous trusted price was near peg. This keeps near-peg or stale DEX liquidity from erasing a corroborated severe depeg while preserving the normal challenge behavior for weak, uncorroborated soft-source prices.
The DEX bridge and the pool challenge now deliberately read from different storage views:
dex_prices.price_sources_json: one aggregate per protocol, used for primary-price promotiondex_price_challenger_snapshots+dex_price_challengers: current individual challenger pools, selected from the full retained DEX pool set with protocol-first diversity and bounded TVL coverage for large-pool challenge / depeg confirmationdex_liquidity.top_pools_json: display-oriented top pools for UI detail, no longer the canonical challenger source
Dead or explicitly blocked DEX ids, including Bunni and its chain-scoped variants, are filtered upstream and cannot contribute challenger pools, promoted DEX bridge sources, or pool-challenge replacement marks.
This catches cases where multiple aggregators or DEX-derived bridge sources agree on a misleading price derived from small pools while ignoring large pools that show a depeg. When the challenge fires, on-chain pool liquidity provides a more honest price signal than aggregator consensus because large pools carry proportional weight. Hard sources (Binance, Kraken, Bitstamp, Coinbase, Curve on-chain, Curve oracle, RedStone with multi-venue agreement, protocol-redeem) are exempt because they provide independent market/oracle data.
Provider-Specific Normalization
Primary provider implementations are normalized before their prices can enter the active 15-minute sync-stablecoins consensus. The exact-address rule applies only to hourly corroboration.
- Source freshness gate: primary candidate admission runs every timestamped source through a registry-backed freshness check before the source can enter consensus. The gate enforces each source's
maxTrustedAgeSec, required observed-at metadata, default observed-at mode, and a 10-minute future-skew ceiling. This is the final shared admission check on top of provider-local stale filtering. - CoinGecko simple-price freshness:
/simple/pricerequestslast_updated_at; when CoinGecko supplies it, rows older than the source trust window are rejected before consensus instead of being stamped as fresh local fetches. If the field is absent despite the request, the row can still enter as local-fetch provenance for backwards compatibility with partial responses. - CoinGecko simple-price precision: every Worker
/simple/pricerequest usesprecision=full, including primary, supplemental, native-fiat, confirmation, status, and auxiliary pricing paths. Threshold decisions therefore receive CoinGecko's unrounded value instead of a display-rounded quote. - Kraken symbols: Kraken uses explicit request-pair and response-key maps in
worker/src/lib/cex-tickers.ts;USDT/USDreturnsUSDTZUSD, so the integration does not rely on naive string slicing.MXNB/USD(mxnb-juno, reviewed 2026-09-27) is the first non-USD-peg Kraken voice: Kraken's MXNB listing is the same Juno-issued token CoinGecko'smxnbticker set reports, so the hard-market bid/ask midpoint can corroborate the soft CoinGecko aggregate and the consensus stops being pool-challenge downgraded by the thin Avalanche Trader Joe book. - Bitstamp ticker surface: Bitstamp is fetched from the exchange-wide all-tickers endpoint and then filtered through an explicit tracked-pair allowlist so venue coverage stays deterministic.
- Coinbase symbols:
fetchPrimaryPrices()uppercases symbols before Coinbase lookup. Active pairs: USDT, PAXG, USDS, USD1, and AUDD (reviewed 2026-10-01). The formerHONEY-USDpair was removed: Coinbase'sHONEYasset is Solana Hivemapper (https://api.exchange.coinbase.com/currencies/HONEY), not Berachain HONEY (now BUSD), so it had been a wrong-asset price voice.AUDD-USDCis Coinbase's USDC-quotedfx_stablecoinbook: the USDC quote is treated at USD par (Coinbase converts it 1:1), Coinbase's AUDD asset resolves to the exact Novatti AUDD Ethereum contract tracked byaudd-novatti, and CoinGecko's AUDD ticker set lists this market. Its per-pairtimeis a last-trade timestamp, so on quiet books the quote is only admitted while that print is inside the shared hard-market 10-minute window; outside it the row falls back to the previous CoinGecko single-source behavior. - Binance market scope: The active roster currently uses the direct
USDTUSDandUSDCUSDmarkets. Stable-quoted markets remain supported by multiplying the raw quote by a same-run tracked quote/USD price, but delisted assets such as BFUSD are removed from the live provider roster. If a DEX bridge row contains Binance orderbook evidence for an actively configured asset, the overlapping aggregatedex-promotedlane is suppressed. - RedStone symbols:
worker/src/lib/redstone.tsonly queries the exact-case tracked subset inREDSTONE_TRACKED_SYMBOL_ALLOWLIST(20 symbols includingUSDe,crvUSD, andfxUSD). Unsupported symbols are filtered out before transport, and test coverage now guards the allowlist against stale untracked entries. Where metadata symbols differ from RedStone API symbols (e.g.,FRXUSD→frxUSD,EURC→EUROC,XAUT→XAUt), the module translates viaREDSTONE_SYMBOL_CONFIGentries. Each entry also declares the canonical stablecoin id that may consume the feed, and fetched quotes are keyed by that id before consensus so same-symbol assets cannot share a hard-oracle quote. - Unavailable-provider watchlist (reviewed September 15, 2026): Bitstamp
DAI/USDis absent from both its successful trading-pair metadata and all-ticker responses; RedStoneUSDHis absent from the configuredredstone-primary-prodbatched and single-symbol snapshots. Both were removed from the active provider configuration. Reinstate Bitstamp DAI only after an enabled exact pair and fresh ticker return; reinstate RedStone USDH only after fresh exact-case data with the reviewed venue breakdown returns, still binding it exclusively tousdh-native-marketsrather than the unrelated Hubble symbol peer. This records current unavailability, not a claim of permanent delisting. Other DAI/USDH price sources and all publication guards remain unchanged. - RedStone request shape: RedStone requests are sent in sequential batches of 10 symbols; any symbol missing from a batch response is retried once as a single-symbol request.
- RedStone freshness + transparency gate: RedStone entries are only admitted when they carry a timestamp newer than 5 minutes and a usable per-venue price breakdown. Timestamp-less or opaque aggregate-only responses are rejected.
- RedStone multi-venue gate: RedStone prices now need at least 2 venues and at least 60% venue agreement before they can enter primary consensus; a single venue is treated as insufficient corroboration, and the published RedStone price is derived from the venue median instead of the provider aggregate.
- CEX capability semantics: Binance, Kraken, Bitstamp, and Coinbase are all still treated as hard-market voices, but their registry metadata now makes their actual capabilities explicit. Binance is modeled as last-trade-only without bid/ask depth, while Kraken, Bitstamp, and Coinbase expose bid/ask-derived spot surfaces. Kraken retains local-fetch freshness (its
/0/public/Tickerhas no per-pair UNIX timestamp), while Bitstamp (via responsetimestamp, UNIX seconds) and Coinbase (via responsetime, ISO-8601) now publish per-pair upstream observation times and stampobservedAtMode = "upstream". Bitstamp and Coinbase rows are rejected before hard-market admission when their upstream timestamps are stale, missing, invalid, or future-skewed beyond the shared source limit. A non-empty Kraken APIerrorarray is an upstream failure rather than healthy empty coverage. - CEX orderbook bounds: direct Binance, Coinbase, and Kraken orderbook reads use endpoint-sized response caps. Binance requests 500 levels rather than 1,000 because only the ±2% depth band is scored; Coinbase retains its aggregated level-2 REST book.
- Jupiter Price API V3 freshness semantics: Jupiter fallback accepts documented sparse no-quote rows as healthy empty coverage. Rows that carry a quote still need
usdPrice,decimals,blockId, and optionalpriceChange24h/liquidity.blockIdis checked against a fresh SolanagetSlotreference fetched sequentially from a bounded three-endpoint RPC roster; a blocked primary falls through, while complete slot-reference failure rejects the quotes. OptionalcreatedAtis not used for freshness, and optionalliquidityis treated as an extra guard only when present. - FX cadence and bucket claim:
worker/src/cron/sync-fx-rates.tsis triggered in the 15-minute quarter-hourly slot, but scheduled time maps each invocation into a 30-minute cadence bucket. The first delivery claims the bucket with a generation-fenced compare-and-swap; a bucket is completed only after canonical publication, and a failed bucket remains retryable at the next quarter-hour slot. Frankfurter's maintained hosted API atapi.frankfurter.dev(ECB data) covers the primary fiat set, and the pegs Frankfurter/ECB does not publish are filled from the secondary daily currency API;PRIMARY_FX_CURRENCIESandSECONDARY_FX_CURRENCY_TO_PEGinworker/src/lib/fx-config.tsown that split. WhenOPENEXCHANGERATES_API_KEYis configured, OXR is an optional overlay, not part of the critical publication. CZK and PLN are first-class primary fiat pegs sourced from ECB data through Frankfurter, with business-daily cadence. AED is a first-class secondary fiat peg sourced from the dated fawazahmed0 currency API, with calendar-daily cadence; it is also included in the existing full-set secondary/ExchangeRate-API recovery and Open Exchange Rates overlay maps. All references are stored as USD per one currency unit. The dirham's 3.6725 AED-per-USD currency peg is not a hardcoded runtime reference: the Worker must admit a provider quote or eligible cached quote. - FX source TTL gates: The existing
fx-rates-metacache row persistssourceLastSuccessAtBySourcefor the three secondary mirrors, OXR, metals, and Chainlink. After a successful Frankfurter publication, each mirror is requested only when its own six-hour TTL is due; a Frankfurter failure deliberately attempts all three mirrors for immediate recovery. OXR is limited to its six-hour TTL, while metals and Chainlink retain the 30-minute TTL needed by non-USD peg bounds. Fresh overlay state is carried forward without changing the canonical FX payload shape. - Chainlink reference overlay:
worker/src/cron/sync-fx-rates.tsoverlays curated Chainlink EUR/USD, GBP/USD, JPY/USD, XAU/USD, and XAG/USD feeds onto the sharedfx-ratescache when their on-chain quotes are fresh and within 5% of the current reference stack. The worker now tries dedicated dRPCeth_calltransport for the supported Base / Ethereum / Arbitrum feeds before falling back to the shared chain RPC pool and then the existing Etherscan V2 proxy path, so the Chainlink overlay can still recover when earlier quarter-hour jobs have already saturated the shared Alchemy/public RPC budget. The overlay is fetched only when its 30-minute source TTL is due; Frankfurter / secondary FX APIs andgold-api.comremain fallback sources for uncovered or divergent feeds, and commodity pegs now also have a stablecoins-cache … - Chainlink round-quality classification:
worker/src/lib/chainlink-round-data.tsdecodeslatestRoundData()for every Chainlink consumer. A response that cannot be decoded at all (fewer than four words, or non-hex characters) remains a transport failure, but a decodable round whose answer is zero/negative or whoseupdatedAtis zero is returned with aninvalidReasoninstead of throwing: the reference-overlay snapshot counts it underinvalidAnswersrather thanfetchErrors, and the Midas mMEV NAV yield source treats it as unavailable evidence. Reserve adapters (chainlink-nav,chainlink-por,cap-vault) read the same round throughrequireChainlinkLatestRoundData()and still fail closed on it. - Secondary + tertiary FX fallbacks:
sync-fx-rates.tscompares only the secondary mirrors whose six-hour source TTL is due after a successful Frankfurter publication; on Frankfurter failure it compares the jsDelivr@fawazahmed0/currency-api@latestmirror, the directlatest.currency-api.pages.devendpoint, and the date-pinned jsDelivr package for the current UTC date regardless of their TTLs, then persists the fresher valid dated snapshot. The currencies keyed inSECONDARY_FX_CURRENCY_TO_PEG(worker/src/lib/fx-config.ts) always use this daily secondary path, and when Frankfurter is unavailable the same feed can temporarily backstop the wider fiat FX set. If both Frankfurter and the secondary mirrors are unavailable, the worker falls through to ExchangeRate-API's daily USD snapshot before dropping into cached-fallback mode. - FX fallback publication and provenance: a live full-set fallback publishes only when it supplies every expected fiat peg; partial coverage preserves the previous complete canonical cache, enters an eligible cached fallback, and degrades the cron result with
partial-live-fallback-coverage. Provider values without a valid upstream date or timestamp remain live-source values withnullobservation provenance, so cadence freshness classifies them as non-fresh rather than substituting the local sync clock. Commodity peer medians retain their cache observation time when available, while timestamp-less peer-median andgold-api.comvalues likewise retainnull; cached metal rates must pass the current bounds before carry-forward. - FX freshness semantics:
fx-rates-metatracks usable cache freshness (usableSyncAt) separately from per-peg source freshness metadata (sourceUpdatedAtByPeg,sourceCadenceByPeg,sourceDateByPeg) and per-source successful fetch timestamps. Intraday sources (gold-api.com, stablecoins-cache commodity peer medians, pure realtime recoveries) still age on wall-clock seconds, while daily sources (ECB/Frankfurter and the secondary currency-API feed) are evaluated against their expected publish cadence instead of a naive 6-hour clock. When OXR or Chainlink overlays refine an already-fresh daily fiat reference, the worker now preserves that daily cadence/date metadata instead of downgrading the peg to synthetic intraday-only provenance. When the live FX fetches fail, same-day live fiat references can therefore be carried forward against their daily publish cadence rather than aging immediately into false intraday staleness, and commodity references can recover from the freshstablecoinscache instead of inheriting stale metals timestamps. Cached fallback runs are reserved for cases where the job cannot refresh a live source and the carried-forward daily references are no longer cadence-valid, so non-USD and commodity validation cannot silently look fresh after a real upstream aging event. If a cached-fallback run later refreshes fresh full-set fiat coverage through OXR or Chainlink-backed overlays, the job now promotes itself back toliveimmediately instead of continuing to accumulate fallback streaks on already-recovered rates. - FX generation publication:
fx-ratesandfx-rates-metaare one generation.persistFxRateState()writes both rows in one atomic D1 batch under a pair-level clock fence: each row is written only while neither FX row is newer than the run'ssyncStartSec, so a newer row for either key blocks the whole pair and a failed statement rolls both back. The metadata carriesratesSha256, the SHA-256 of the exact stored rates bytes, because equal-second clocks are not unique run identities.loadFxRateState()reads both rows in one statement. A pair isverifiedonly when both rows share oneupdated_atand the digest matches. Metadata written beforeratesSha256existed is accepted aslegacy-timestamponly when both rows share oneupdated_at; the first publication by the current producer replaces it, after which that transitional branch is removable. A missing, malformed, or mismatched metadata row (legacy split writes, two same-second runs, a partial overwrite) keeps the rates' own publication clock as cache age but attaches no source provenance: per-peg source times arenull, never the cache write time, pricing treats every such rate asstale, and the producer cannot carry those rates forward as cadence-valid. - OXR observation time:
fetchRealtimeFxRates()returns a typed observation carrying OXR's own snapshottimestamp. A snapshot with a missing timestamp, one more than two hours old (one missed hourly publish), or one more than five minutes in the future is rejected with a machine-readable reason (openexchange-rates-observation-rejectedcron event), records a breaker failure, and leaves every incumbent rate and its provenance unchanged. An admitted overlay stamps each peg with that upstream time, never the fetch or sync clock, so a later Chainlink quote is compared against the real OXR observation age: a genuinely fresher Chainlink round replaces the OXR value, and only a Chainlink round older than the OXR snapshot is skipped. - FX per-peg admission and health:
assessFxPegAdmission()is the single per-peg assessment behind both pricing (getFxReferenceTypeFromState) and FX health (buildFxCacheStatus). A present non-USD rate has no admissible provenance when its metadata generation is unverifiable (metadata-missing,metadata-malformed,metadata-generation-mismatch), its source mode is absent (source-provenance-missing), a live or cached intraday source has no source time (source-time-missing, including timestamp-lessgold-api.comand peer-median metals), or its source time is more than five minutes ahead of the reader (source-time-future). Pricing keeps its established verdicts (stalefor unverifiable metadata,noneotherwise). Health never reports such a rate as healthy: the FX cache status is at leastdegraded,healthy: false,degraded: true, anddegradedReasonnames the cause —fx-metadata-<identity>for an unverifiable generation, orfx-source-provenance-unknown:<peg>=<reason>,…listing every affected peg — with the same pegs in the human-readable warning. Cache recency alone can never make an unknown source healthy. - Direct native-peg live-publication guard: supported non-USD fiat assets with reliable CoinGecko native pairs can derive a fresh
native quote × FX referenceUSD mark during post-enrichment. ARS, CLP, and NGN are included where CoinGecko exposes direct native pairs; COP, GHS, KES, KGS, PEN, and XOF remain on the secondary daily FX mirror and deterministic validation bounds because CoinGecko does not currently expose usable native simple-price quotes for those pegs. That native-implied mark can correct materially divergent weak or mixed-source live USD publications and can also fill a missing live price for supported assets when the derived mark passes the shared publication guards. - Native lane scope:
coingecko-native-impliedis a fresh fallback-validation lane, not a second replay-safe primary consensus source. Pharos can publish it for the current run when it is the best validated live mark, but it is not written intoprice_cachefor later replay continuity. - Historical native-peg replay: supported non-USD fiat backfills now prefer direct CoinGecko native-fiat history and compare that series to the native
1.0peg before falling back to USD-denominated market history. In that native-fiat mode, replay now uses daily points plus a two-point confirmation window across 36 hours so thin hourly native prints cannot manufacture long false depeg streaks during repair. - Peg-aware validation bounds: USD and fiat FX pegs with usable references share the same upside tolerance ratio, so non-USD fiat prints are capped at
1.19 × referencePriceinstead of the broader commodity band. Gold and silver references keep the existing2 × referencePriceupper band, withcommodityOuncesscaling for fractional tokens. Authoritative downside modes still keep their explicit lower-bound relaxation. The shared peg taxonomy classifies CZK, PLN, and AED asfiat_fxand normalizes them topeggedCZK,peggedPLN, andpeggedAED. No-reference USD price guardrails are CZK[0.02, 0.1], PLN[0.1, 0.5], and AED[0.2, 0.35]; live FX sanity bounds are CZK[0.02, 0.1], PLN[0.1, 0.5], and AED[0.25, 0.3]. These are validation bands, not substitute prices. Each currency also supports CoinGecko's corresponding lowercase native quote currency (czk,pln,aed). - GeckoTerminal probe removal: the optional GT pool-probe helper was deleted. It had been disabled in the production
sync-stablecoinspath since the Worker heap-boundary finding, and after the Safety Score V8 retirement it had no remaining execution caller.sync-stablecoinsstill persists a compactgtProbemetadata marker (inlineDisabled = true,isolationReason = "worker-memory-boundary", empty stats) for operational provenance, but/api/statusno longer projects that marker; thegeckoterminal-probecircuit key stays mapped for thegeckoterminalprice source. Reintroducing the probe requires a separately budgeted producer, not an inline pass. - Circuit-breaker accounting: for RedStone, a transport-successful request that returns zero usable prices is still recorded as an unsuccessful outcome for breaker state. This avoids treating empty responses as healthy data. DexScreener discovery records a successful aggregate outcome when any request in the run succeeds; a later HTTP 429 or WAF 1015 stops the remaining crawl but does not turn that partial-success run into a breaker failure, while a zero-success hard refusal still fails the breaker.
- CoinGecko ticker breaker semantics:
worker/src/lib/cg-ticker.tsstill rejects stale or otherwise unusable Kinesis ticker rows for price publication, but thecoingecko-tickercircuit breaker now tracks endpoint availability rather than row freshness. A successful/coins/{id}/tickersresponse with only stale/unusable USD rows no longer opens the source breaker; only transport failures or non-OK responses count as breaker failures. - Curve on-chain sanity bound: Implied prices from
get_dycalls are capped at< 10,000(to accommodate commodity tokens like PAXG/XAUT at ~$2,900). - Curve on-chain quote sizing:
get_dyquotes are sized per config so the floored integer output spans at least 10,000 quanta (worker/src/lib/curve-onchain.ts). Outputs with ≥ 6 decimals keep the historical 1-unit quote; a 2-decimal output such as the GUSD/3Crv metapool is quoted at 100 units, because a 1-unit quote floors 0.9996 GUSD to 0.99 and reports exactly 1/0.99 = 1.0101 (observed in production as the GUSDcurve-onchainprice at block 26064625; the same pool quotes 99.97 GUSD per 100 USDC). - Curve on-chain block-timestamp freshness: Curve on-chain reads now pin
get_dycalls to a single block number fetched up front and stamp each priced pair with that block's timestamp (observedAtMode = "upstream"). Runs older than 300 s of wall-clock time vs the block timestamp are rejected asupstream-errorunder the same 300-second ceiling shared withcurve-oracle. - Curve on-chain route resolution: Curve configs declare whether a route is
direct,one-hop,trusted-wrapper, or explicitchained-hop. Hop routes multiply by the resolved USD price of their via asset rather than assuming a$1reference. Chained hops must opt in withrouteType = "chained-hop"andmaxHopDepth > 1; missing dependencies, route-shape mismatches, and cycles fail closed. - Curve oracle staleness guard: The
curve-oraclevoice (crvUSDPriceAggregator.price()EMA) is fetched against a resolved block number and its block timestamp is used to stampobservedAt. Reads with a block timestamp older than 5 minutes (CURVE_ORACLE_MAX_STALENESS_SEC = 300) are rejected before entering primary consensus, so a stale-replica RPC cannot single-source publish an EMA read minutes behind chain head.curve-oraclenow uses its ownCIRCUIT_SOURCE.CURVE_ORACLEbreaker separately from the per-poolcurve-onchainbreaker, so an aggregator outage does not suppress per-pool Curve reads and vice versa. - Live-reserve NAV telemetry: Primary consensus can admit NAV prices already fetched by live-reserve adapters when the
reserve_compositionrow is matched toreserve_sync_state.last_success_at.chainlink-nav,superstate-liquidity, andjpmorgan-navrows must expose a positivemetadata.navPerTokenand verifiedsourceTimestamp/oracleUpdatedAt. USD NAVs publish directly; non-USD NAVs first multiply by fresh or static FX references from the shared validation cache. - Exact-address price corroboration: the hourly
:09phase queries only CoinGecko Onchain for assets whose latest publication is missing a price or has fewer than three consensus sources. Targets come only from canonicalasset.address,contracts, ortradedContractsmetadata and are matched by exact chain+address; symbol search remains retired. The reviewed VUSD override remains fail-closed against current metadata and CoinGecko network support. The 15-minute coverage refresh re-observes the narrower cohort that lane's one-generation quote lifetime requires — rows it prices and rows with no price — using persisted routing hints for the deployment that last produced a quote; both cadences share the same admission rules, and neither may renew a quote by re-reading its own cache. - Address-provider enablement:
ADDRESS_PRICE_PROVIDERS_ENABLEDis an explicit allowlist. An unset value enables nothing. The only accepted value iscoingecko-onchain-address, and that value still requiresCOINGECKO_API_KEY; production keeps that provider explicitly pinned. The retired DexScreener, DexPaprika, Alchemy Prices, Moralis, and Birdeye adapters can be recovered from git only if a future reviewed cohort needs one. - Address-provider trust semantics: exact-address observations add bounded provenance weight only when they agree with the published or fallback reference within the live divergence threshold. Unreviewed CoinGecko Onchain targets must also report parseable liquidity at or above the shared $50K floor; missing or malformed reserve data is rejected. The reviewed VUSD target keeps its explicit liquidity-field override. Exact-address observations do not replace a current published price and are non-depeg-authoritative on their own.
- Binance host cascade and environment availability: Binance ticker fetches no longer retry the same host on server-side failures. On HTTP 5xx, 429, or Worker-side 403/451, the fetcher short-circuits to the next host (
data-api.binance.vision->api.binance.com) instead of consuming the Retry-After budget against a host that is already failing. When every host returns403/451, durable runtime state suppresses the predictable environment block for six hours, then permits one probe host. One invocation-scoped promise is shared by primary consensus and pending-depeg confirmation, so the same stablecoin run cannot request Binance twice. A successful probe clears the block; network exceptions remain ordinary provider failures. Intra-host retries are reserved for transient network exceptions where the host itself has not answered. - Direct-API DEX bridge: per-protocol DEX prices are aggregated before primary-consensus admission. The currently registered promoted DEX sources are Fluid, Balancer, Curve, Uniswap V3, Uniswap V4, Raydium, Orca, Meteora, PancakeSwap, Aerodrome Slipstream, and Velodrome Slipstream, so each of those protocols can contribute at most one elevated source per asset.
- DEX bridge overlap guard: when at least one promoted per-protocol DEX bridge candidate exists for an asset, the overlapping
dex-promotedaggregate is withheld so the same bridge observation family cannot self-confirm through a rejected protocol lane plus its aggregate. A valid aggregatedex-promotedsource can enter as the soft DEX fallback only when no promoted protocol candidate exists for the asset and the Binance overlap guard is clear. Aggregate admission normally requires the global $1M depeg-trust TVL floor. The reviewedvusd-virtueroute may instead enter primary publication at the existing $250K UI floor while fresh; it remains a soft single-source mark, cannot become depeg-authoritative, and still needs corroboration for a severe downside publication. - DEX aggregate primary budget (v6.40): consensus stamps a cluster with its oldest agreeing leg (
observedAtis the conservative minimum), so beside any other primary leg thedex-promotedaggregate joins only while its row is at mostDEPEG_PRIMARY_PRICE_MAX_AGE_SEC(30 minutes) old. Its own 75-minuteDEX_FRESHNESS_SECwindow still decides row eligibility. As the only admissible leg it keeps that window and publishes as single-source with its own clock. Without this bound, the hourly aggregate pinned fresh CEX/oracle agreement for USDC, USDT and other majors to the previous DEX run. The next DEX liquidity stage then rejected them as quote legs under the 30-minute depeg-trust gate and dropped theirdex_pricesrows, and the cycle reversed an hour later. - Promoted DEX corroboration gate: a lone promoted DEX protocol is admitted only when a hard market/oracle/protocol source agrees within the live threshold. If no validated non-DEX source remains, the lone candidate is rejected with
lacked_corroboration; two or more promoted DEX protocols are admitted as candidate sources and consensus then determines agreement. - DEX bridge freshness preservation: primary pricing now keeps the per-source
updatedAtvalues already stored insidedex_prices.price_sources_jsonwhen rebuilding promoted DEX sources. It only falls back to the row write time when a source-specific timestamp is missing or invalid, so freshness is no longer flattened across the entire bridge row. Each promoted protocol lane is then freshness-checked independently before candidate admission, so a fresh parentdex_pricesrow cannot carry a stale or future-skewed protocol lane into consensus. - DEX dataset freshness warning: the status evaluator emits
dex_pricing_bridge_stalewhen the published DEX liquidity dataset (cache) age exceeds the lane's reviewed four-hour endpoint budget (CACHE_FRESHNESS_LANES.dexLiquidity.endpointMaxAgeSec, four hourly scoring runs), even while the cache remains inside its longer public-display availability budget. This makes the loss of promoted DEX sources and fresh pool-challenge observations visible without redefining public endpoint health. The per-rowDEX_FRESHNESS_SEC(75-minute) window remains a row-level admission gate fordex_pricesobservations in depeg and pool-challenge logic and is deliberately not reused as a dataset-level threshold. - DEX bridge source explainability: stale
dex_prices.price_sources_jsonrows, malformed source snapshots, missing pricing-source registry mappings, below-threshold protocol sources (< $50KTVL), and promoted DEX candidates rejected for lacking corroboration are logged with structured reasons. Published DEX-inclusive stablecoin rows can also carrypriceSourceConfidenceProfilewith active protocol-lane count, freshest DEX lane age, and whether the price relies only on the aggregatedex-promotedlane. - Retained-pool DEX bridge publication:
dex_pricesis rebuilt from the final retained pool surface after dedupe, caps, and scoring filters. An eligible exact direct-API pool carrying a reviewed quote dependency takes precedence over a unique derived-identity primary duplicate even when the direct API reports zero 24-hour volume; this keeps the exact identified row and its price evidence together. A retained pool without an embedded price may otherwise use direct evidence only when the evidence has the same canonical pool id, exact identity confidence, and independently clears the existing$50Kobservation floor; its weight is capped at the smaller of the retained and evidence TVLs. Ambiguous derived identities, mismatched ids, sub-threshold evidence, and raw observations without a retained pool cannot leak into promoted DEX bridge sources ordexPriceCheck. - Direct-API fetch hard stops: direct DEX API fetchers run serially (
1protocol fetch at a time), share a15 srequest timeout policy, and use deterministic pagination caps with resume markers. Orca cursor requests retain the same TVL sort and minimum-TVL filter as the refreshed head, preventing resumed scans from drifting into zero-TVL inventory. A provider that returns usable rows plus partial errors remains visible through fallback and source-warning diagnostics but is not counted as a failed source;failedSourcesis reserved for providers that return no usable response. Raydium and Orca no longer stop on a single below-threshold or empty-eligible page when upstream sort semantics drift; they record partial/degraded states instead of silently truncating. - Direct-API merge explainability: direct-API pools are unit-normalized before merge, invalid TVL/token-unit rows are dropped centrally, exact pool ids are canonicalized before dedupe, and merge metadata counts accepted protocol-chain lanes plus exclusions for invalid units, untracked tokens, TVL thresholds, sanity caps, and duplicate identity conflicts. Staged-pool merge also persists skip dimensions by protocol, chain, reason, threshold, and conflict for cron metadata/alerts.
- Direct-API tracked quote pricing: direct-API pair conversion now prefers only fresh authoritative tracked stablecoin prices from the cached stablecoins payload for quote legs before falling back to peg references. Weak or stale tracked prices no longer feed back into the DEX bridge, and unknown addressed
USDC/USDT-style tokens still do not get unconditional$1treatment. A navToken is never priced from a non-NAV print: its quote leg resolves only through a guarded NAV reference (a high-confidence protocol-redeem override observed within the depeg primary freshness window, admitted byisGuardedNavReferencePriceinworker/src/cron/dex-liquidity/orchestrator-phases/lookups.ts), and a missing or stale NAV leaves the leg unpriced rather than falling back to a peg or generic reference mark. - Pool-price coherence admission: the GeckoTerminal and CoinGecko Onchain pool rows that feed
dex_prices, promoted DEX bridge sources, and the challenger snapshot are admitted only after the shared pair-price coherence policy (POOL_PRICE_COHERENCE_POLICYinworker/src/cron/dex-liquidity/pool-price-coherence.ts): the tracked leg's USD price must agree with the pool's own pair ratio × the counter-leg's USD price withinmaxPairDivergenceBps = 500, and rows carrying the provider broken-price signature — leg USD prices published while every pair-ratio input is null or0.0— are rejected with reasonpool-pair-ratio-unavailable(a usable but conflicting ratio usespool-pair-price-incoherent). The whole pool is rejected, so it stages no TVL, price observation, or challenger row; zero volume and zero transactions are never rejection inputs. See DEX Liquidity for the owning policy detail. - Fail-closed DEX publication: a primary-anchored
dex_pricesrow cannot publish without a usable primary. When the preloaded trusted map carries no primary for a stablecoin, the row is withheld before staging with machine-readable reasonprimary-missinginstead of running the outlier filter, deviation band, and display-ratio band against a null comparator; the withholds are surfaced in the persistence diagnostics (withheldByStablecoin, plustruncatedWithheldStablecoinswhen the bounded sample overflows). - DEX token matching and dedupe: direct-API, staged, and fallback DEX pools resolve tracked assets by
chain + addressfirst. If an upstream token already carries an address and that address is unknown to the canonical registry, it is dropped instead of falling back to symbol; symbol fallback is reserved for addressless rows and must still be unique within the same chain. Repeated observations of the same physical pool are collapsed by exact pool id or a conservative derived identity before they enterdex_prices. - Direct-API pair conversion: non-USD tracked stablecoin pairs use peg-reference-aware conversion rather than treating every tracked stablecoin counterparty as
$1.
These normalization rules live in code because they are provider quirks, not business-level scoring decisions.
Authoritative Overrides
After market/oracle consensus, the provider registry under worker/src/lib/authoritative-price-sources/ can replace or recover the chosen live price for source-reviewed assets whose executable value is better represented by direct protocol redemption, an instantly redeemable tracked base asset, a protocol-native oracle, or an identity-bound exact market route.
Current Scope
| Asset | Source |
|---|---|
cusd-cap | Cap getBurnAmount(address,uint256) |
iusd-infinifi | infiniFi RedeemController.receiptToAsset(uint256) |
usdai-usd-ai | inherits tracked pyusd-paypal pricing as a redeemable PYUSD wrapper |
iusd-initia | inherits tracked ausd-agora pricing as an AUSD-backed Initia wrapper |
usdcx-movement | inherits tracked usdc-circle pricing as a Circle xReserve-backed USDC wrapper |
m-m0 | inherits tracked wm-m0 pricing as the underlying M0 unit |
usdk-kast | inherits fresh tracked wm-m0 pricing as a Solana M0 extension unit |
xo-exodus | inherits fresh tracked wm-m0 pricing as a Solana M0 extension unit |
usdn-noble | inherits fresh tracked m-m0 pricing as an M0-backed rebasing Noble unit |
usdnr-nerona | inherits tracked wm-m0 pricing as an M0 extension unit |
weusd-picwe | missing-price fallback to tracked usdc-circle with PicWe's 1% redemption-fee haircut |
sofid-sofi | nominal USD par reference (protocol-par) fallback behind the admitted CoinGecko consensus |
usbd-bima | nominal USD par reference (protocol-par) for observable DefiLlama supply |
usdq-quill | nominal USD par reference (protocol-par) for observable DefiLlama supply |
zarm-mento | nominal ZAR par reference (protocol-par) using fresh/static ZAR/USD FX |
xofm-mento | nominal XOF par reference (protocol-par) using fresh/static XOF/USD FX |
susdt-spark | ERC-4626 convertToAssets(1 share) × tracked usdt-tether price |
susdc-spark | ERC-4626 convertToAssets(1 share) × tracked usdc-circle price |
steakusdt-steakhouse | ERC-4626 convertToAssets(1 share) × tracked usdt-tether price |
steakusdc-steakhouse | ERC-4626 convertToAssets(1 share) × tracked usdc-circle price |
bbqusdc-steakhouse | ERC-4626 convertToAssets(1 share) × tracked usdc-circle price |
susds-sky | registry ERC-4626 convertToAssets(1 share) × tracked usds-sky price |
susde-ethena | registry ERC-4626 convertToAssets(1 share) × tracked usde-ethena price |
srusde-strata | ERC-4626 convertToAssets(1 share) × tracked usde-ethena price |
gtusdc-gauntlet | ERC-4626 convertToAssets(1 share) × tracked usdc-circle price |
gtusdcp-gauntlet | ERC-4626 convertToAssets(1 share) × tracked usdc-circle price |
yvusdc-yearn | ERC-4626 convertToAssets(1 share) × tracked usdc-circle price |
autousd-auto-finance | ERC-4626 convertToAssets(1 share) × tracked usdc-circle price |
eearn-ember | ERC-4626 convertToAssets(1 share) × tracked usdc-circle price |
savusd-avant | ERC-4626 convertToAssets(1 share) × tracked avusd-avant price |
susn-noon | ERC-4626 convertToAssets(1 share) × tracked usn-noon price |
syzusd-yuzu | ERC-4626 convertToAssets(1 share) × tracked yzusd-yuzu price |
stkgho-umbrella-aave | ERC-4626 convertToAssets(1 share) × tracked gho-aave price |
syusd-aegis | ERC-4626 convertToAssets(1 share) × tracked yusd-aegis price |
sbold-k3-capital | ERC-4626 convertToAssets(1 share) × tracked bold-liquity price |
ybold-yearn | ERC-4626 convertToAssets(1 share) × tracked bold-liquity price |
said-gaib | registry ERC-4626 convertToAssets(1 share) × tracked aid-gaib price |
usdx-kava | exact Kava usdx:usd aggregate plus authorized raw-oracle validation |
deuro-deuro | exact Ethereum EURC StablecoinBridge redemption × fresh tracked EURC price |
aznd-mu-digital | fresh exact Ethereum Curve AZND -> USDC quote with balance and impact checks |
sgho-aave | registry Aave savings previewRedeem(1 share) × tracked gho-aave price |
aa-falconx-mev-capital | Idle CDO virtualPrice(address tranche) × tracked usdc-circle price |
oned-gennius | reviewed 1:1 USDC issuer-conversion reference from tracked usdc-circle; fresh own market prices win |
pyusdx-moonpay | reviewed 1:1 PYUSD issuer-conversion reference from tracked pyusd-paypal; fresh own market prices win |
sirloinusdc-steakhouse | ERC-4626 convertToAssets(1 share) on base × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
susdf-falcon | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked usdf-falcon price (18-decimal shares / 18-decimal assets) |
sreusd-resupply | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked reusd-resupply price (18-decimal shares / 18-decimal assets) |
sfrax-frax | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked frax-frax price (18-decimal shares / 18-decimal assets) |
sparkusdtbc-spark | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked usdt-tether price (18-decimal shares / 6-decimal assets) |
pendleusdc-pendle | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
skymoneyusdsflagship-sky | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked usds-sky price (18-decimal shares / 18-decimal assets) |
senpathusd-sentora | ERC-4626 convertToAssets(1 share) on tempo × fresh tracked pathusd-bridge price (18-decimal shares / 6-decimal assets) |
skymoneyusdtsavings-sky | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked usdt-tether price (18-decimal shares / 6-decimal assets) |
krusdc-keyrock | ERC-4626 convertToAssets(1 share) on arc × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
senpyusdpst-sentora | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked pyusd-paypal price (18-decimal shares / 6-decimal assets) |
senpyusdmwin-sentora | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked pyusd-paypal price (18-decimal shares / 6-decimal assets) |
senrlusdv2-sentora | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked rlusd-ripple price (18-decimal shares / 18-decimal assets) |
steakeurcv-steakhouse | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked eurcv-societe-generale-forge price (18-decimal shares / 18-decimal assets) |
senpyusdmain-sentora | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked pyusd-paypal price (18-decimal shares / 6-decimal assets) |
sparkusdc-spark | ERC-4626 convertToAssets(1 share) on base × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
steakusdg-steakhouse | ERC-4626 convertToAssets(1 share) on robinhood × fresh tracked usdg-paxos price (18-decimal shares / 6-decimal assets) |
sxsrlusd-sentora | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked rlusd-ripple price (18-decimal shares / 18-decimal assets) |
senpyusdprimev2-sentora | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked pyusd-paypal price (18-decimal shares / 6-decimal assets) |
armusdcs-wintermute | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
arcusdc-galaxy | ERC-4626 convertToAssets(1 share) on arc × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
susdc-spark-v1 | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
cscbusdc-clearstar | ERC-4626 convertToAssets(1 share) on base × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
bbqusdc-steakhouse-v2 | ERC-4626 convertToAssets(1 share) on ethereum × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
hyperusdca-hyperithm | ERC-4626 convertToAssets(1 share) on monad × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
ethenausdc-steakhouse | ERC-4626 convertToAssets(1 share) on base × fresh tracked usdc-circle price (18-decimal shares / 6-decimal assets) |
When a live override validates successfully, direct protocol/NAV quotes and high-confidence tracked-base inheritance are written with:
priceSource = "protocol-redeem"priceConfidence = "high"
Nominal par routes are the exception: they never publish protocol-redeem provenance (see the nominal par references below).
Scoped tracked-base inheritance from a fresh replay-safe single-source parent keeps the parent priceSource and
priceConfidence = "single-source" so downstream publication guardrails continue to see the inherited quote's soft
upstream provenance instead of treating it as depeg-authoritative protocol redemption.
Vault NAV routes (ERC-4626 convertToAssets, Aave previewRedeem, Idle CDO virtualPrice) also persist their
last-good assets-per-share rate to the durable authoritative_vault_rates table on every successful live read. When the
live rate read fails and the asset has no publishable current price, the route publishes
cached rate × fresh trusted parent price as protocol-redeem-cached-rate with priceConfidence = "low". The cached
lane requires the same trusted parent as the live route, rejects rates older than 24 hours or outside the vault sanity
bounds, never replaces a publishable incumbent price, and is registered non-replay-safe and non-depeg-authoritative so a
stale rate cannot feed depeg state or replay continuity. Cached-rate resolutions count as provider successes for the
grouped protocol-redeem circuit — an open circuit would skip the provider entirely and disable the rescue — while a
failure with no trusted cached rate still records the pre-existing failure and circuit semantics. Cron metadata exposes
cachedRateFallbacks and the attempt ledger records the cached source per asset.
Registry-backed ERC-4626 routes bind to the reviewed canonical chain deployment and keep the vault rate denominated in
the tracked parent asset. In particular, sUSDS reads its canonical Ethereum vault against tracked USDS, while sUSDe reads
its canonical Ethereum staking vault against tracked USDe. The rate becomes a USD price only after multiplication by a
fresh trusted parent price; there is no synthetic $1 parent or wrapper fallback, so a missing or untrusted parent leaves
the wrapper unpriced.
Legacy Spark USDC (susdc-spark-v1, Ethereum 0xbc65ad17c5c0a2a4d159fa5a503f4992c7b545fe) is distinct from tracked Spark V2 (susdc-spark): Legacy has 18-decimal shares and 6-decimal USDC assets, while V2 has 6-decimal shares. All newly configured vaults use the same NAV allowlist for live prices and pre-intake supply valuation; underlying asset units are converted to USD only through a fresh trusted tracked-parent price, including EURCV.
Tracked-base inheritance reuses the tracked parent's trusted live price. ONED and PYUSDx use reviewed one-for-one issuer conversion references, not direct-wrapper classifications or guaranteed liquid market pegs: their fresh admitted own market prices take precedence, including discounts. Their historical replay stays with their own market-price sources; parent history is not synthesized as their own record. Other configured inherited routes retain their existing history behavior.
Live tracked-base and NAV-wrapper inheritance judges parent provenance on the composite's replay-safe core, as if
agreeing non-replay-safe corroborators (for example an exact-address augmentation lane that joined the winning
cluster) were absent: a high-confidence parent stays trusted while its core keeps at least two replay-safe members, and
an agreeing soft member can neither downgrade that trust nor upgrade a core the gate would otherwise reject. A single
replay-safe core member padded to high confidence by soft corroborators is admitted only through the scoped
single-source opt-in and is preserved as single-source provenance. For scoped M0 inheritance paths, a fresh replay-safe single-source
M0 parent is also admissible, but it is preserved as a single-source child price with the parent source instead of
being upgraded into protocol-redeem high-confidence provenance. Low-confidence, fallback, cached, stale-sync,
single-source stale, or provenance-less parent prices are skipped, and parent-trust rejections are recorded in the
price-source attempt ledger as untrusted-parent with the parent id and rejection reason. For high-confidence composite parents, a fresh same-run priceSyncedAt can satisfy the live inheritance
freshness check when the composite's single displayed observedAt is older than one short-window component source; this
preserves source-specific admission from the parent run without falsely rejecting mixed-cadence composites such as
USDC. When inheritance is accepted, the override carries the parent source, confidence, observed-at timestamp,
observed-at mode, and replay-safety status for diagnostics.
Current tracked-base inheritance paths are:
usdai-usd-ai -> pyusd-paypaliusd-initia -> ausd-agorausdcx-movement -> usdc-circlem-m0 -> wm-m0usdk-kast -> wm-m0xo-exodus -> wm-m0usdn-noble -> m-m0usdnr-nerona -> wm-m0weusd-picwe -> usdc-circlewith a 1% redemption-fee haircut, only when no usable live market price existsoned-gennius -> usdc-circleas a reviewed 1:1 issuer-conversion reference; fresh own market prices winpyusdx-moonpay -> pyusd-paypalas a reviewed 1:1 issuer-conversion reference; fresh own market prices win
WEUSD is market-price-wins: a usable live market quote is never replaced by the 0.99 redemption floor. "Usable" means
the incumbent is a current registry-admitted market observation — every composite component is a non-retired,
non-protocol, non-cached source and its priceObservedAt passes that source's maxTrustedAgeSec and future-skew
window; a restored or carry-forward row that only refreshed priceSyncedAt, or protocol-redeem/cached provenance,
does not suppress the fresh fallback. Historical replay likewise uses market history rather than synthesizing the
redemption floor; unavailable market history fails closed by preserving existing replay rows.
For the other inheritance paths, this prevents thin secondary-market child-token prints, or missing child-market coverage, from dragging PegScore away from the executable value of the tracked parent rail.
Scoped nominal par references (v6.38, DEC-02 / CR-43) cover active assets with observable runtime supply and a source-reviewed primary redemption route, but no dependable current market quote. In v6.39 chfau-allunity and cadd-cad-digital moved to their ordinary primary consensus (fresh CoinGecko quotes backed by live DEX pools of the tracked contracts), and jpym-mento to the Mento FPMM route below. With no admissible quote these three rows publish missing, never par. On 2026-09-29 sofid-sofi was reactivated from quarantine once CoinGecko began publishing a positive market cap (≈$328.75M with $12.3M daily volume); its fresh CoinGecko consensus quote is the ordinary published price, and the nominal par reference remains only the no-trusted-quote fallback. XOFm stays on nominal par: its only observed Mento Broker quote includes a 200 bps spread and publishes about −225 bps against the XOF reference, beyond the 150 bps non-USD depeg threshold, which would present a spread as an off-peg reading. Par is a reviewed constant, not a runtime redemption read: the protocol-par provider (worker/src/lib/authoritative-price-sources/protocol-par.ts) returns it with confidence: null, observedAt: null and observedAtMode: "nominal_reference", under the protocol-par registry source (trust tier nominal_reference: not replay-safe, not depeg-authoritative, no freshness budget). Fee and capacity risk remains modeled in the redemption-backstop methodology rather than being hidden inside the token price. Non-USD routes must have a fresh or static FX reference for the peg currency; the FX clock is reference provenance, never the token's observation time, and is published on the reference as fxReferenceType (the same fresh/static admission vocabulary price validation uses) and fxObservedAt (the reference's per-peg source time, null when it has none, e.g. static FX). USD par carries neither field. The provider is configured for the five assets below; all five are active registry rows since the 2026-09-29 sofid-sofi reactivation, and each publishes par only when its own market quote is not trusted:
sofid-sofiat USD parityusbd-bimaat USD parityusdq-quillat USD parityzarm-mentoat ZAR parity converted through the live ZAR/USD referencexofm-mentoat XOF parity converted through the live XOF/USD reference
Precedence. applyProtocolPriceOverrides() sets nominalPriceReference: { price, source: "protocol-par", mode: "nominal_reference" } (plus the FX fields for non-USD par) on each routed asset and publishes it as the price only when the incumbent is not a trusted market quote. isTrustedMarketQuote() admits the incumbent only when the depeg detector would act on it without confirmation — classifyPrimaryDepegTrust() returns authoritative — and it has no protocol-override provenance. That requires an observed price within DEPEG_PRIMARY_PRICE_MAX_AGE_SEC that is not cached, fallback or low confidence, plus registry source authority: at high confidence two depeg-authoritative sources or one depeg-authoritative source with upstream observation time, and at single-source a source registered as single-source depeg-authoritative with an upstream observation time. Soft aggregators (CoinGecko, DefiLlama, CoinMarketCap) are never depeg-authoritative, so a thin single mark or CoinGecko + DefiLlama list agreement at high confidence cannot displace par. The gate applies no liquidity or venue-depth threshold of its own: trust comes from the registry source class (hard CEX, oracle and on-chain protocol sources are depeg-authoritative; aggregator and DEX lanes are not), consensus confidence and freshness. A trusted market discount is therefore published as the observed price with par kept alongside, while soft-aggregator marks, stale quotes, cached/fallback fills and legacy protocol-redeem par rows never displace par. settleNominalPriceReferences() re-applies the rule after post-enrichment validation and cached fallback, so a market quote rejected late falls back to par rather than to a fallback mark. Precedence is re-decided every run without hysteresis.
Nominal publication. Without a trusted market quote the row publishes price = par, priceSource = "protocol-par", priceObservedAtMode = "nominal_reference", priceConfidence, priceObservedAt and priceUpdatedAt null, empty consensus/agree sources, and priceSyncedAt = the publishing sync. Post-enrichment observation validation skips nominal rows (par was validated when applied). References and nominal prices are derived every run and never carried forward: a restored or previous row loses them unless this run's route produced par, so a non-USD route without a usable FX reference publishes neither. Nominal evidence is excluded from depeg observations and confirmation, V9 price facts, NAV captures, observed-price coverage (nominal-only assets are counted in the separate nominalReference* coverage category, not as missing — see the coverage paragraph above), comparison/export prices, peer references and the replay price_cache. Readers label it “Nominal par reference (not an observed price)”, without observation freshness or observed-price confidence. Rows published before v6.38 keep their protocol-redeem provenance.
Legacy replay rows. Pre-6.38 protocol-redeem par rows in price_cache are never rewritten, because par is never staged; they remain in the table indefinitely and are not deleted. The cached-fallback lane admits a cached row only inside its source's registry budget measured from the row's write clock (protocol-redeem: 15 minutes), so such a row can be republished — labelled cached / fallback, and only for an asset that has no price and no nominal reference that run — for at most 15 minutes after its last pre-cutover write, and never afterwards. Other readers of price_cache apply their own age rules.
Replay. Every configured route resolves historical replay to protocol-par with no series, so depeg backfill preserves existing event rows instead of synthesizing USD 1.0 history or replaying unadmitted market history.
These authoritative overrides are pre-applied before fallback enrichment and then applied again during final price completion. The early pass keeps known redeemable wrappers and extension assets out of unnecessary fallback-source work, while the final pass preserves the rule that fallback enrichment cannot overwrite a validated redemption price. Before that final application, the pipeline reruns only local/cache-backed authoritative providers against the post-fallback asset state. This lets a child such as usdn-noble see a same-run m-m0 repair without repeating any RPC-backed vault or redemption calls.
Live parent-derived overrides normally require a replay-safe parent source. A narrow audited exception lets said-gaib, sbold-k3-capital, ybold-yearn, syusd-aegis, usdk-kast, and xo-exodus use a fresh high-confidence same-run parent consensus even when that parent composite includes address-derived providers; m-m0, usdk-kast, xo-exodus, and usdn-noble may also inherit a fresh replay-safe single-source M0 parent. The M base-unit path requires the wM parent itself to report single-source confidence, so a high-confidence composite padded by non-replay-safe address sources remains ineligible. syusd-aegis may additionally inherit a fresh replay-safe single-source YUSD parent, and said-gaib may inherit a fresh replay-safe single-source AID parent, preserving that parent's source and single-source confidence instead of upgrading it to hard protocol provenance. Cached, stale, low-confidence, and non-replay-safe single-source parents remain rejected, and historical replay still uses only replay-safe provider paths. For sAID, the worker still verifies convertToAssets(1 share) against the registry-derived vault and rejects a missing, zero, or out-of-range conversion before multiplying by the trusted AID parent.
The deterministic recovery routes are deliberately asset-specific and fail closed:
- Kava USDX: the worker reads the official Tendermint
/headerresponse rather than downloading transaction bodies, and requires a freshkava_2222-10head, exactly one activeusdx:usdmarket with authorized oracles, an aggregate price, and at least one authorized raw price whose expiry covers the 30-minute cache trust window. Excessive raw-oracle dispersion or aggregate-to-median disagreement rejects the route. - AUDm / CADm / COPm: missing prices can use reviewed Celo Broker sell routes into USDm, multiplied by a fresh trusted USDm price. Pinned canonical state must verify token and exchange identity, current oracle reports, bidirectional breaker state, mint/burn permissions, conservative trading limits, and bounded quote depth/impact. Broker, manager and token implementations are pinned to the reviewed deployed code; unreviewed upgrades fail closed.
worker/src/lib/authoritative-price-sources/mento-broker.tsowns the route. Within one serial override stage, subsequent Broker routes reuse only a previously validated opening block/header; each route still reads its complete state and rechecks canonical block hash, current parent trust, oracle freshness and execution limits. This removes two sequential RPC reads per later route without adding concurrent requests or extending freshness budgets. Themento-brokersource shares Mento's source family and the same five-minute, fallback-confidence, non-replay-safe, non-depeg-authoritative policy as CHFm; it has no soft-source guard exemption and never overwrites a usable price. Mento's FX-gated oracle system closes the Broker lane every weekend: Chainlink-backed sortedOracles reports stop at the Friday 21:00 UTC close, sogetAmountOutrevertsno valid medianuntil relayers resume at the Sunday 23:00 UTC reopen. AUDm/CADm fall back to other market lanes for that window; COPm's remaining lanes are all inadmissible, so its weekend gap carries a reviewed price-gap acknowledgement (STABLECOIN_PRICE_GAP_REVIEWS). - CHFm / JPYm: when the price is missing, the reviewed Celo Mento FPMM route for that token (CHFm pool
0xdc81135fd82f02cae736e261fb676b716663e8b8, JPYm pool0x9861f6d2fe392b934c86ec89d2886ceb772b2b41) quotes it into USDm and multiplies by a fresh trusted USDm price. It verifies exact pool/token identity, fresh pinned block state, synchronized reserves and token balances, bounded fees, a one-unit quote plus a route-scaled impact quote (100 CHFm; 20,000 JPYm), a route-scaled input inventory floor (1,000 CHFm; 200,000 JPYm) with a 1,000-USDm output floor, conservative trading-limit headroom, and the pool swap-value constraint. Oracle/breaker rejection, insufficient headroom, stale state or parent, malformed reads, and failed canonical-block confirmation fail closed.mento-fpmmis fallback-confidence, non-replay-safe, non-depeg-authoritative soft evidence with a five-minute lifetime, observed at the older of parent and block time. It has no soft-source guard exemption and never overwrites a usable current price. The pool and token implementations are pinned to the reviewed deployed code;worker/src/lib/authoritative-price-sources/mento-fpmm.tsowns the route. The route uses its own circuit and the existing bounded live-override budget. Failed state reads record the pinned block and distinguish transport-null, malformed-batch, and configured-subcall failures without retaining raw RPC payloads. The FPMM quote path additionally revertsFXMarketClosed(OracleAdaptergetFXRateIfValid, MarketHoursBreaker weekend rules) from Friday 21:00 UTC until Sunday 23:00 UTC, so CHFm and JPYm lose this lane every weekend; the lane recovers automatically at the reopen and each weekend gap carries a reviewed price-gap acknowledgement (STABLECOIN_PRICE_GAP_REVIEWS). - Legacy USDv: missing prices can use Jupiter exact-in direct quotes for the reviewed Solana USDv/USDC Meteora pool
DmXXwEcK2c7fuVoW6TBzF5UDByhuQBHZS1qHnwprvHFH. Independent confirmed Solana reads require exact program ownership, account discriminators and sizes, enabled pool with no pending activation, reviewed legacy token programs and decimals, initialized unfrozen vaults, exact mint/vault identity, and at least 10,000 units in both inventories and 10,000 USDC in the active bin. The source deliberately pins bin array25QrV3HbQCv7iVPskPpoUr2MadM2A1oKBsVjWx7fnc2i(index -198); an active bin outside this reviewed array fails closed. Jupiter 1- and 1,000-USDv fee-inclusive quotes must use exactly the pinned pool, agree within 5%, fit the active-bin inventory and remain within 2% of its on-chain price. The response and route-update slots must precede confirmed state and remain within 600 slots. A confirmed-state read that lags the quote context (RPC-32010, or an answered context slot below the quotes) is retried up to three times about 450 ms apart with endpoint rotation, strictly inside the candidate deadline; the catch-up relaxes no pool-state guard and persistent lag still fails closed. The oldest quote dependency slot and the confirmed account-state slot bracket these observations; both endpoints must independently resolve to block timestamps younger than five minutes. Price multiplies the quote by a fresh trusted USDC observation; provenance preserves the older quote or parent time.jupiter-exactis a Meteora-family fallback source, non-replay-safe and non-depeg-authoritative; no soft-source publication exemption applies. Jupiter API failures or missing slot evidence do not produce a price. - BD: missing prices can use the reviewed Base Aerodrome stable BD/USDC pool
0xffdf1e3160b60c2e499fa25e51b5c192b9b15e3b. A pinned canonical block verifies exact token/factory/pool registration, 18/6 decimals, unpaused factory and USDC with an unblocked pool, fee at most 200 bps, reserve/balance equality and floors of 10,000 BD and 10,000 USDC. The fee-inclusive 1-BD sell quote must agree within 5% with a 1,000-BD depth quote. Both block and trusted USDC parent must be newer than five minutes; provenance uses the older timestamp.aerodrome-exactremains fallback-confidence, non-replay-safe and non-depeg-authoritative, with all generic soft-source severe-downside and temporal-jump publication checks retained. It never overwrites a usable price. - USDaf: missing prices can use the reviewed Ethereum Uniswap v4 USDaf/USDT pool
0xcd799508ddaa319e608547d3291a1a512da9a9acdd40599d89019ec82e3cf1e8, independently of DexScreener.usdaf-uniswap-v4.tsrecomputes the pool ID from exact token addresses, 500 fee units, tick spacing 10 and no hooks, pins canonical PoolManager/Quoter/ReservesLens runtime hashes, and reads one fresh block with a closing hash check. ReservesLens integrates real tick-range principal (not virtual reserves or singleton-wide balances); trusted-USDT-normalized TVL must meet the unchanged $50,000 DEX observation floor. Fee-inclusive 1- and 1,000-USDaf sell quotes must agree within 5% and fit output reserves; token decimals and USDT pause/blacklist state are verified. Block and trusted USDT parent must both be newer than five minutes, and provenance uses the older time.uniswap-v4-exactstays fallback-confidence, non-replay-safe, non-depeg-authoritative, subject to generic severe-downside/temporal-jump guards, and never replaces a usable price. Its six-second candidate budget and single-assetusdaf-uniswap-v4circuit retain the exact-route scheduler semantics: guarded no-quotes are neutral, thrown failures count. - Universal USD (USDU): missing prices can use the reviewed Ethereum Uniswap v3 USDU/USDT 0.01% pool
0x30bc4854086128ebb69ee6e67e2a51a87a0b41b0.usdu-uniswap-v3.tspins the exact pool, canonical factory and QuoterV2, both tokens and USDU implementation runtime hashes, verifies the EIP-1967 implementation slot, factory registration, token order (USDT/USDU), fee 100 and 6/6 decimals, and rechecks the canonical block hash. Direct pinned QuoterV2 exact-input calls quote 1,000 USDU and 100,000 USDU into USDT; their per-unit proceeds must agree within 5%, move the pool price in the correct sell direction and fit actual output-token inventory. Real pool token balances, not active-liquidity virtual reserves, must meet 10,000-token inventory floors and the unchanged $50,000 normalized DEX observation TVL floor. Both tokens must be unpaused, the pool unblacklisted, USDU's blacklister configured and the QuoterV2 USDT recipient unblacklisted. USDU transfer guards do not impose an allowlist, but a quote is not a promise that every individual holder is eligible. The sample proceeds are multiplied by a fresh trusted tracked USDT price, never nominal dollar par. Block and parent must each be newer than five minutes; provenance uses their older timestamp.uniswap-v3-exactremains fallback-confidence, non-replay-safe and non-depeg-authoritative, retaining generic severe-downside and temporal-jump publication protection and never replacing a publishable incumbent. Serial body-consumed RPC batches preserve direct Quoter callback semantics and occupy only one connection per existing authoritative lane, without increasing the trigger-wide connection peak. - AZND: six direct pool reads share one JSON-RPC batch at the original pinned block, preserving direct-call sender semantics and the existing connection and execution budgets. The exact Ethereum Curve pool must match the configured AZND/USDC order at a block no older than five minutes, meet the 1,000 AZND and 100 USDC balance floors, and keep the 10-AZND quote within 5% of the 1-AZND quote. The result is
curve-thin-onchainwithfallbackconfidence. Because the route remains a thin-pool, single-venue signal, it stays subject to the generic soft-source publication corroboration guardrails for severe fixed-peg downside and large temporal jumps; it remains neither replay-safe nor independently depeg-authoritative. A guarded no-quote result, including a balance-floor rejection, keeps AZND explicitly missing without failing the route's circuit; thrown or timed-out provider work still records a circuit failure. - Solayer sUSD: the Token-2022 interest-bearing mint
susdabGDNbhrnCa6ncrYo81u4s9GM8ecK2UwMyZiq4Xaccrues USD value through its on-chaininterestBearingConfig, and the route pricessusd-solayerfrom that exchange rate after every market lane for the asset failed (DefiLlama's stablecoins list stopped returning a price, CoinGecko carries only a stale thin-market ticker, and the single $7.8K DEX pool sits below the $50K liquidity floors). Two serial body-consumed Solana reads — a confirmedgetAccountInfo(jsonParsed) and agetBlockTimefor its context slot — verify Token-2022 program ownership, mint type, 6 decimals, and a complete interest config, then compute the exact SPL exchange rate (including the on-chain whole-bps average-rate rounding) at the observed block time. The result publishes as high-confidenceprotocol-redeemobserved at the block time, bounded to the 0.5–10 NAV ratio band; wrong program/owner, malformed config, stale or future block identity, or a rate outside the band fails closed.susd-solayer.tsowns the route.
The missing-only dEURO recovery reads the reviewed immutable Ethereum EURC bridge 0xB4fF7412f08C22d7381885e8BdA9EE9825092fd1. Three bounded sequential RPC batches pin the deployed bridge and dEURO bytecode, token identities, bridge minter permission, EURC decimals, transfer pause/blacklist state, and reserve/liability coverage to one canonical block no older than five minutes, then recheck its hash. The route requires at least 1,000 EUR of native redemption capacity and EURC reserves covering all bridge-minted dEURO. The deployed bridge permits burns after its mint horizon expires; it still requires holder allowance and active minter permission. Its one-to-one native redemption amount is multiplied by a fresh trusted EURC market price, retaining parent provenance and the older observation time. This is bounded bridge exit capacity, not full dEURO supply coverage or nominal EUR/USD parity. It uses existing protocol-redeem admission, with no historical replay or cached-rate fallback. Any incomplete identity, permission, liquidity, freshness or canonical-state evidence leaves dEURO missing.
Authoritative live pricing carries the scheduled runtime RPC map through both primary and CoinGecko-fallback price completion. Vault conversion, Idle CDO and dEURO bridge readers use the configured authenticated chain routes before existing public fallbacks; an explicit per-vault RPC list retains its existing priority. The dEURO attempt ledger records fixed stage rejection codes (head, state, identity/redemption, capacity or canonical check) instead of an opaque missing quote, without including provider URLs or upstream error payloads. These diagnostics do not turn unavailable reads into successful circuit evidence or relax admission guards.
The grouped protocol-redeem circuit records one outcome per bounded override pass. Any successful live quote proves group availability; only a pass with recorded failures and no live success increments its failure streak. The admission decision is reused throughout that pass, so failing contracts cannot block later healthy vaults mid-run. Cached-rate fallbacks do not prove live provider availability. Per-asset failures, parent-trust checks, cooldowns and request/time budgets remain intact.
The live override stage has a 10-second wall-clock budget and builds candidates only from the exact active registry, so frozen and quarantined rows cannot consume recovery time ahead of active price gaps. For this scheduler, an incumbent price is usable only when it is positive and carries publishable source plus observation-time provenance; a bare numeric value, priceSource = "missing", priceSource = "unknown", or a missing observation timestamp stays in the missing cohort. It first schedules alert-eligible current missing-price candidates, then the rest of the current missing candidates, and only then already-priced refresh candidates. Circuit-backed recovery probes remain ahead of ordinary routes inside the non-alert missing cohort so half-open circuit recovery is not starved, while no already-priced circuit refresh can run before a missing active candidate. Provider families are interleaved inside each partition. Each candidate also receives a bounded fairness slice inside the shared budget (2.5 seconds by default; heavier audited Kava and AZND routes declare larger caps), so one stalled wrapper fails its own attempt instead of skipping the remaining active gaps. Candidates are pulled in that same priority order onto at most four parallel lanes — the sync-stablecoins scheduler connection declaration (maxConnections: 4 in shared/lib/cron-jobs.ts) — and each lane takes the next candidate as soon as it frees, so a slow chain route (Kava's four serial reads, a Mento broker or FPMM quote, the Jupiter legacy-USDv pool read) no longer serializes ahead of every cheap local or cache-backed repair. A candidate whose route prices from a tracked parent row (liveParentByAssetId on the provider, for example the vault NAV parents, the mento USDm parents, and the tracked-base inheritance chain) is dispatched only after that parent's own attempt in the same pass has settled, so a same-run rescue chain (wm-m0 -> m-m0 -> usdn-noble) still resolves under lanes; a blocked candidate never holds a lane while other ready candidates remain. Before the lanes landed, production runs on 2026-09-23 attempted only 23-56 of 64 candidates and budget-skipped 8-41 of them per slot, which blanked override-only active prices such as usdv-solomon, sbold-k3-capital, eearn-ember, deuro-deuro, savusd-avant, ybold-yearn and chfm-mento; the lane count never exceeds the job's declared outbound connection budget and does not widen the shared budget, the per-candidate deadlines, or circuit admission. The pass-level attempt ledger is flushed after every lane settles so the diagnostic order stays the dispatch order regardless of completion order. A started recovery probe finalizes success or failure when the shared budget itself aborts; candidates never started by the shared budget remain neutral. Candidate-local fairness timeouts are recorded as asset-attributable timeout attempts but do not by themselves exhaust the stage or poison the grouped recovery circuit. Providers may explicitly keep an optional already-priced refresh failure neutral so it cannot poison the breaker that protects a future missing-price recovery. Missing-only thin routes such as AZND are not enqueued over an existing usable price, but they can run when an incumbent numeric value lacks publishable provenance. Kava and AZND each use dedicated circuits; identity mismatch, stale state, unavailable trusted dependencies, insufficient capacity, excessive impact or divergence, transport failure, and budget exhaustion all return no override. The pipeline never substitutes nominal peg parity for a failed executable route.
The same registry also supports historical replay for backfills where a provider can replay the same source safely, so admin rebuilds do not silently downgrade back to weaker market sources.
crvusd-curve no longer lives in the authoritative-override registry. Its Curve PriceAggregator.price() quote is now injected into primary consensus as the curve-oracle source alongside the other live pricing voices.
Fallback Enrichment
The hourly observation handoff expires after one hour plus one 15-minute publication interval, providing a bounded handoff envelope across hourly replacement. It independently enforces each source's existing observation-age limit, including the reviewed seven-day CoinGecko low-volume window. Only freshly collected provider observations enter it; published references and cached replays never do. A missing-price consumer revalidates candidates with the normal fallback and severe-downside corroboration guards, preserves their source and observation time, and publishes only fallback confidence. The existing cachedFallbackCount includes this D1 handoff and ordinary replay recovery. Empty successful collections clear the handoff; older writers cannot replace newer snapshots. Ordinary replay lifetime and trust rules are unchanged.
Fallbacks are detached from the critical publication. After the existing :09 status-check chain, runPriceCorroboration() reads the latest valid published stablecoins cache, selects only rows that are missing a price or have fewer than three consensus sources, and runs enrichMissingPrices() against isolated probe copies. A fallback error cannot change the completed sync-stablecoins result or block its canonical cache write. Successful probes update price_cache; the next 15-minute publication revalidates those entries before using them as continuity. The hourly pass order is:
- Pass 1: DefiLlama
coins.llama.fiby canonical tracked contract identity, using the upstream row address when present and falling back to curated trackedcontractsmetadata when the upstream row is addressless. Accepted quotes must carry a fresh upstream timestamp, confidence, and matching symbol, then pass shared peg-aware bounds before they can resolve the asset. Among usable deployment quotes already fetched in each pass, the newest upstream observation wins; ties retain lookup order. Schema-invalid OK responses recorddl-coinsbreaker failures instead of being treated as healthy empty coverage. Contract identities are deduplicated and encoded URLs are split at 8,000 characters, below the public 9,000-character limit. Primary and alternate passes share eight sequential batch fetches under the existing abort/retry budget. Valid earlier batches remain usable if a later request fails; incomplete work reportsdl-contracts, while local URL or batch-budget limits do not count as provider circuit failures. - Pass 1b: alternate tracked deployment fallback via DefiLlama; only known tracked deployments are probed, never synthetic same-address cross-chain identities. The same timestamp, confidence, symbol, and peg-aware gates apply.
- Pass 2: CoinMarketCap first fetches the stablecoins category batch (
v1/cryptocurrency/category?id=604f2753ebccdd50cd175fc1&limit=300&convert=USD) and then, when configured unresolvedcmcSlugrows remain, makes one boundedv3/cryptocurrency/quotes/latestrequest for at most 25 exact slugs. It setsskip_invalid=true, but CMC may still reject a batch for an unrecognized slug. Only when an HTTP 400 names one exact requested slug does the pass exclude that slug and retry once within the original request deadline; unknown or malformed errors fail closed. The retry prioritizes originally missing-price candidates after excluding the named invalid slug; if none remain, it retries the other requested candidates. Optional corroboration candidates omitted from this retry receiveretry-priority-deferredrequest-cap diagnostics, distinct from absent provider rows (missing-quote). The excluded asset remains unpriced withunsupported-quotediagnostics, including when the retry fails. Rows from a complete returned category page remain eligible after freshness and peg-aware checks; whennum_tokensproves an unseen tail, category rows are ignored and unresolved assets must use the exact-slug targeted lane so truncated category data cannot bypass active, volume, or contract-identity validation. The targeted lane fills its cap from originally missing-price assets first, rotating hourly within each priority group, and requires exact slug and symbol, an active CMC record, a supplied configured-contract match for assets with known contracts, a quote no older than one hour, positive 24-hour volume, and shared peg-aware reasonableness. Inactive, stale, zero-volume, missing/wrong-contract, colliding, malformed, or peg-impossible rows fail closed. Accepted targeted quotes enter a narrow provider-local cache that revalidates current identity and peg bounds while preserving the original upstream timestamp; it remains fallback-confidence, non-depeg-authoritative evidence and expires at a source-age boundary of two fetch cadences plus grace (see the enrichment cache rules below), so one missed CMC roll or one rotation-skipped fetch hour cannot blank an otherwise priceable asset. An eligible pass can make at most three requests (category, targeted, and one explicit-invalid-slug isolation retry); a successful pass consumes one UTC execution-hour quota bucket so completion latency cannot skip the next hourly producer. The typed D1 marker retains its actual write timestamp. A429instead imposes the full rolling one-hour cooldown, including when the category request succeeded before a targeted429; legacy or unrecognized markers retain that conservative rolling behavior, and future marker timestamps block calls. Quote freshness and request caps are unchanged.429still honorsRetry-After(see Data Integrity Guardrails). - Pass 3: Jupiter Price API for tracked Solana mints — calls the official V3 gateway with
JUPITER_API_KEYwhen configured, accepts documented sparse no-quote rows as healthy empty coverage, accepts quoted payloads withoutliquidity, checksblockIdfreshness against a sequential three-endpoint Solana current-slot fallback when a quote exists, applies optional liquidity gating only when liquidity is present, and remains subject to peg-aware validation. In addition to missing-price recovery, the pass can appendjupiteras a bounded soft candidate for low-depth Solana assets when the Jupiter quote agrees with the current primary price; it does not replace the selected price or add Jupiter toagreeSources. - Pass 4: DexScreener exact token-address pool lookup when chain+address are available. Exact-address recoveries publish
dexscreener-exact. The older last-resort symbol-search path is retired, so addressless assets no longer call/latest/dex/searchand remain explicitly missing unless another fallback resolves them. Each pass makes at most one same-chain request containing up to 30 exact addresses. Chains containing originally missing-price assets rotate first across hourly cycles, with the bounded address window rotating within the missing group before filling spare capacity from low-depth priced probes. If no missing-price targets remain, all candidate chains rotate. Low-depth-only chains can wait while recovery consumes the one-request budget. HTTP 429 responses and provider WAF code 1015 are hard refusals and end the pass immediately. The legacydexscreener-searchbreaker can still appear in health payloads while stale production state ages out, but new sync runs recover it through the no-candidates path instead of probing the search endpoint. - Pass 5: CoinGecko low-volume allowlisted fallback for selected tracked assets with an audited current CoinGecko row but no accepted price. It currently targets
deuro-deuro,usdn-smardex,cadm-mento,tryb-bilira,btcusd-btcfi,dllr-sovryn,gbpm-mento,audm-mento,copm-mento,chfm-mento,hchf-hedera-swiss-franc,money-defi-money, andhbd-hive; it runs after DefiLlama contract, CMC, Jupiter, and DexScreener recovery fail and only fills still-missing price fields withpriceConfidence: "fallback".
The DefiLlama /coins contract-address fallback, supplemental CoinGecko-id mirror fetches, and the DexScreener lookups used outside primary consensus (the dex-liquidity and dex-discovery crawls) now gate on and record against their own circuit breakers. CIRCUIT_SOURCE.DL_COINS wraps the coins.llama.fi/prices/current/... path so a DL regional outage opens the breaker instead of hammering the host, CIRCUIT_SOURCE.DL_PROTOCOLS wraps supplemental gold protocol mcap/TVL fetches as well as DEX protocol reads, dexscreener-prices wraps only the batched /tokens/v1/{chainId}/{addresses} exact-address pricing lane, and dexscreener-liquidity wraps optional DEX liquidity/discovery pool lookups through /token-pairs/v1/{chainId}/{tokenAddress}. Discovery records one aggregate DexScreener outcome for each discovery invocation, while scoring fallback records one outcome for its invocation. Ordinary partial target failures remain successful when another request reaches the source; a hard refusal still stops later requests, but the discovery aggregate records success when any earlier request succeeded and records failure only when the refusal leaves the run with zero successful requests.
Tracked DefiLlama rows that collapse to zero supply are repaired before pricing when the row has no usable chart-history repair or its chart-history value is below the tracked repair floor. The repair remains scoped to source-reviewed deployments for CADD and the Mento JPY/ZAR/XOF stables, reads every configured chain successfully before publishing, converts total supply through the current fresh/static FX reference, and tags the result supplySource = "onchain-total-supply". The same fail-closed source tag covers Movement USDCx only after its pinned-ledger fungible-asset supply and resource decimals reconcile to Circle's Ethereum xReserve balance for domain 10005 within one basis point; either provider failing or a wider mismatch leaves the supply unresolved.
Operationally, the 15-minute lane publishes after the full primary consensus, pool challenge, authoritative overrides, validation, and replay-cache continuity. It does not wait on the five fallback passes or exact-address transport. The hourly phase runs in the separate status-check invocation ahead of the :15 publication and is best effort. Protocol overrides remain under their bounded wall-clock budget with per-candidate fairness caps. Within the hourly cohort, missing rows precede low-depth priced rows, while provider cursors rotate only inside a priority cohort.
A narrow DEX refresh runs after the status monitors every 15 minutes (:09, :24, :39, :54) within the existing price-corroboration budget entry. It refreshes active missing-price and currently dexscreener-exact-priced rows (missing rows covered by a valid price-gap review — reviewed as having no admissible market — are left to the hourly corroboration passes, which still probe them) using previously successful exact chain/address targets, including secondary deployments discovered by hourly fallback; missing assets without a reviewed hint use their first supported registered deployment. Hints are routing metadata only and must still match active contracts or traded contracts. Raw provider addresses cannot bypass this check. The same exact-address executor preserves token/symbol matching, the $50,000 liquidity floor, peg-aware admission and actual local-fetch timestamps. It issues sequential batches of at most 30 addresses, at most seven batches within 45 seconds (five-second request cap plus pacing), with no retries; consecutive batches are paced by the shared DexScreener rate-limit interval inside the same budget, and a hard 429/Cloudflare-1015 refusal stops the slot's remaining batches, which stay deferred with the rate-limited class. The lane consults and records its own dexscreener-prices-refresh circuit, so refresh failures cannot open the hourly corroboration breaker or vice versa. An overflow cursor advances across batches; unsupported, deferred, failed and unresolved coverage remains explicit rather than being reported healthy, and the price-corroboration budget surface degrades only on transport failures, timeouts, phase errors, failed hourly passes, deferrals that exhaust the batch budget, or a slot in which previously resolvable (hinted) exact routes were attempted and none produced a quote — cohort rows with no reviewed DEX target or no DEX market stay telemetry metadata. A slot refused only by DexScreener's shared-egress throttle (rate-limited alone) is recorded as a refresh-circuit failure rather than a degraded slot: three consecutive throttled slots open the dexscreener-prices-refresh circuit, and the resulting circuit-open class degrades the surface until a probe succeeds.
A batch counts against the dexscreener-prices-refresh breaker only once its outcome is definitive. A batch whose fetch the lane's own 45-second deadline aborts never reached a provider verdict: it stays deferred (timedOut keeps the slot degraded) and is not recorded as a provider attempt, so a local wall-clock abort cannot open the breaker while DexScreener itself is healthy. Only definitive outcomes — hard 429/1015 refusals, HTTP or transport failures, five-second request timeouts, and successful batches — are recorded against the circuit; half-open probes retry automatically every 30 minutes, so an egress-throttling episode ends by itself once a probe succeeds. A refresh with no candidate batch to probe — nothing missing or dexscreener-exact-priced, or no supported reviewed deployment for the rows that remain — consumes no half-open probe and records no provider verdict: it applies the hourly exact pass's documented no-candidate recovery (recoverProviderOnNoCandidates), which closes a non-closed breaker without an upstream request, so a temporarily empty cohort cannot pin dexscreener-prices-refresh open.
The same slot also runs a coverage refresh for the coingecko-onchain-address lane. That lane's quotes have a one-publication lifetime (maxTrustedAgeSec of 900 s) and no price_cache replay path, so a row it prices would otherwise sit missing in three of every four publications while the hour is the only collection cadence. Its cohort is deliberately narrower than the hourly one: rows whose published priceSource includes that lane — their quote lapses this generation — plus rows with no publishable price at all; missing rows under a valid price-gap review stay with the hourly passes, as in the DEX refresh. Lane-priced rows sort ahead of merely-missing rows and of the hourly cohort's low-depth ordering, because only their price is about to disappear, and the provider's per-network batching then leaves the remaining request cap to the missing cohort in priority order. A previously successful deployment is persisted as a routing hint in the same payload and narrows that row to one chain/address instead of one batch per registered deployment (USDa alone has eighteen); a hint that no longer matches the canonical contract or traded-contract list is ignored rather than inventing a target. Admission is unchanged — the $50,000 liquidity floor, reviewed-target overrides, symbol/address identity matching, per-batch address limits, pacing and the five-request cap all stay with the provider — and targets that cap leaves unqueried are reported as cappedTargets telemetry instead of being treated as resolved. A definitive 404 ("this deployment is not indexed on that network") is coverage information, not an outage: it never records a provider failure nor opens the lane's circuit, while throttles, HTTP errors and transport failures still do. The coverage refresh keeps its own 25-second deadline and its own coingecko-onchain circuit, so it neither delays the DEX lane nor shares DexScreener breaker state; a wall-clock abort leaves the circuit untouched because it is not a provider verdict. The price-corroboration budget surface degrades when this lane's circuit is open, its deadline is spent, or a request fails — those are exactly the generations in which the rows only it can price lose their quote.
Fresh DEX observations enter a separate slot-fenced price:dex-refresh:v1 cache. The same payload carries this slot's exact-address observations and the address routing hints. Publication merges it with hourly staging by asset/source and selects the newer actual observation timestamp, preserving other sources. The existing 15-minute DexScreener source-age limit remains unchanged; routing hints and cache reads cannot renew it. Failed refreshes do not publish a renewed quote. Separate keys prevent the hourly writer from erasing quarter-hour observations, and each input cache retains its own availability diagnostic. Broad fallback/address corroboration stays hourly and outside the critical publication path. Operational acceptance requires successive refresh/publication pairs across an hour boundary, not one restored price.
Open USD (ousd-open-standard, DefiLlama 443) has a verified CoinGecko identity, open-usd. On 2026-09-30 at 18:46 UTC, CoinGecko's coin detail returned a full listing, all four official contracts, a positive USD price, and zero market cap; the DefiLlama list still returned a null price and $477.31M of USD-denominated circulating supply. Primary consensus already collects CoinGecko simple prices using the list's gecko_id; the verified registry geckoId now pins that identity and enables metadata-driven CoinGecko ticker discovery for DEX coverage and historical CoinGecko price backfill. It does not itself establish independent multi-source agreement: two list aggregators alone remain single-source after hardening. Exact-contract recovery remains an hourly fallback, not OUSD's current primary price path. DefiLlama list supply remains authoritative; CoinGecko's zero market cap cannot replace it, enter positive-market-cap fallback intake, or seed historical supply rows.
OUSD needs no pool-specific promoted-price allowlist: orca-dex and uniswap-v4-dex are already registered independent families, but the 2026-09-30 18:51 UTC retained surface prices only Orca, while the Base DeFiLlama row has no embedded price and the V4 subgraph supplies execution identity only, so a lone Orca candidate remains lacked_corroboration until a second admitted protocol price or agreeing hard source is available.
Critical sync-stablecoins metadata contains only publication-path provider diagnostics. Hourly corroboration logs aggregate cohort, cache-write, and provider-diagnostic counts separately so a corroboration failure cannot degrade the publication status.
DIA is currently research-only. npm run audit:dia-provider -- --input internal working notes probes DIA's exact-address GET /v1/assetQuotation/{blockchain}/{address} endpoint for below-target rows and records hit rate, timestamp quality, source metadata, and agreement vs the current Pharos price. The probe does not publish prices, change consensusSources, alter circuit state, or participate in depeg confirmation. Any future production integration requires false-positive review, capacity/circuit approval, and a methodology update.
Jupiter fallback uses the official https://api.jup.ag/price/v3 gateway and sends x-api-key when JUPITER_API_KEY is configured. The previous Lite gateway is no longer used after Worker egress received repeated Cloudflare 403 block pages from that host. Quote freshness uses one cached current-slot result per pass and at most three sequential RPC attempts. Configured Solana primary/fallback RPCs run first with their existing authentication, followed by PublicNode; without configured RPCs, the existing api.mainnet-beta.solana.com, api.mainnet.solana.com, and solana-rpc.publicnode.com roster applies. Both hourly and emergency fallback paths pass runtime RPC configuration. Successful references appear in provider diagnostics as well as failed attempts; complete reference failure still rejects quotes.
Binance ticker fetches try the market-data mirror first (data-api.binance.vision) and then fall back to the main public API host (api.binance.com) before recording the source as failed. Both hosts use the same tracked USDTUSD / USDCUSD market mapping.
If every attempted Binance host returns a Worker-side 403/451 block, Pharos records the diagnostics and treats Binance as a no-contribution provider block rather than a source outage. Binance contributes zero prices in that state; it does not keep the source-wide breaker open.
When authoritative pricing removes every Jupiter fallback candidate, the run closes stale-open jupiter-prices breaker state without making a provider health request. Future eligible Solana fallback candidates still use the normal circuit breaker and diagnostics path.
CoinGecko low-volume lane
Some tracked stablecoins trade at low enough volume that CoinGecko's upstream last_updated_at for the ticker sits hours-to-days behind real time. The strict 15-minute freshness gates used elsewhere reject these prices, leaving the assets with priceSource: "missing" even though CoinGecko has a valid USD quote.
fetchFiatCoinGeckoTokens in worker/src/cron/sync-stablecoins/supplemental-assets/fiat-cg.ts runs a relaxed fallback for CoinGecko-only supplemental assets:
- Try
resolveSupplementalPricefirst (the standard 15-minute gate). - If there is no
geckoIdbut the asset has one unambiguous supported supply contract, try the same DefiLlama coins endpoint with an exactchain:contractkey. Accepted quotes must include a matching DefiLlama symbol, confidence of at least0.8, a fresh upstream timestamp, and pass the shared peg-aware reasonableness bounds before publishing assource: "defillama-contract"or normalizing the same on-chain total-supply fallback. - If that returns null but
cgData[geckoId].usdis a positive finite number, build a resolution withsource: "coingecko-low-volume",priceConfidence: "fallback", andpriceObservedAtMode: "upstream"when CG returnedlast_updated_at(otherwise"local_fetch"). - NAV/yield-bearing assets (
flags.navToken || flags.yieldBearing) are never par-valued. An admitted reserve NAV may use trusted FX conversion for its denomination; an FX reference alone never values supply. When all three market lanes return null, an asset with a registered vault NAV route resolves a supply-valuation-only price throughresolveVaultNavSupplyPrice(worker/src/lib/authoritative-price-sources/erc4626-nav.ts): the exact protocol-redeem route — parent-trust gate on the previous published payload's parent row (30-minute synced-at ceiling), liveconvertToAssets(1 share)read, and the bounded 24-hourprotocol-redeem-cached-ratedegradation lane — reused pre-intake. The resolved price values on-chain total supply only; the row publishes withprice: nulland the live override stage re-prices it in the same run. Without a trusted NAV the asset stays out of the payload (fail-closed), which is what surfaced the 2026-08-30sbold-k3-capital/eearn-emberpublication-coverage degradation after CoinGecko and DefiLlama dropped their market rows. - Assets configured for a registered reserve NAV adapter can also use a matched successful snapshot through
loadReserveNavSupplyPrice(worker/src/lib/reserve-nav-price.ts) before they have a previous cache row. The decoder checks fetch age and source age independently; positive native supply is valued at observed NAV, with trusted FX conversion for non-USD classes. Primary reserve-NAV consensus supplies the published live price.
The fallback enrichment pipeline also has a narrow coingecko-low-volume pass for selected tracked assets with audited usable CoinGecko rows. That pass runs only after DefiLlama contract, CMC, Jupiter, and DexScreener fallback recovery fail. It is explicitly allowlisted for deuro-deuro, usdn-smardex, cadm-mento, tryb-bilira, btcusd-btcfi, dllr-sovryn, gbpm-mento, audm-mento, copm-mento, chfm-mento, hchf-hedera-swiss-franc, money-defi-money (added after the 2026-09-28 audit found DefiLlama had dropped every MONEY price lane), and hbd-hive (added after the 2026-09-30 audit found CG-only intake could not admit its stale market cap even while its price remained inside the low-volume budget); it preserves the row's admitted supply source and can only fill missing price fields, so it cannot overwrite a price that primary consensus or an earlier fallback already accepted.
The 2026-09-30 review recovers HBD's real CoinGecko price with its original upstream observation timestamp, not a renewed local-fetch clock, while preserving carried supply. TRYB's September 23 observation has instead expired the existing seven-day budget and its reviewed venues are below admission floors. Its owned price-gap acknowledgement in STABLECOIN_PRICE_GAP_REVIEWS expires on October 7, 2026: TRYB remains explicitly missing, can clear immediately on a new admissible observation, and re-alerts at expiry if still missing. Neither the TRY FX reference nor Hive's conversion peg is substituted for a market price.
The lane is registered in shared/lib/pricing-source-registry-aggregators.ts with a 7-day maxTrustedAgeSec and defaultWeight: 0.5. Downstream treatment is intentionally weaker than primary coingecko:
priceConfidence: "fallback"flows intopriceValidationModeForAsset → "fallback_enrichment"andclassifyPrimaryDepegTrust → "confirm_required", so the low-volume lane publishes for display but cannot single-handedly open, extend, or confirm a depeg alert.- The new source belongs to the same CG lineage family in
worker/src/lib/price-publish-policy.tsandworker/src/lib/depeg-trust-policy.ts, so severe-downside corroboration still requires an independent non-CG source. - Hourly fallback recoveries retain
priceConfidence: "fallback"when staged inprice_cache; every later publication revalidates the candidate before it can fill a missing row, and it remains non-depeg-authoritative.
Strict primary CoinGecko admission everywhere else is untouched.
Zephyr Scanner supplemental lane
zsd-zephyr-protocol and zys-zephyr-protocol are native Zephyr-chain assets, so Pharos cannot derive supply from a supported EVM/Solana contract. The supplemental fiat-CoinGecko path fetches https://zephyrprotocol.com/api/v1/livestats once per run when either asset is active:
- ZSD uses official
zsd_circfor circulating supply and keeps CoinGecko as the preferred market price when available; if CoinGecko is missing, the protocol's reportedzsd_priceis used as a scopedzephyr-scannerfallback. - ZYS uses official
zys_circandzys_pricebecause neither CoinGecko nor DefiLlama exposes the yield-share wrapper. - The emitted
zephyr-scannerpricing source is registered as protocol telemetry with no dedicated circuit breaker and is only reachable from this narrow supplemental path. - Scanner-supplied
captured_at/block_timestamp/timestampvalues are validated against the registeredzephyr-scannertrust window before they become price or supply provenance: a timestamp future-skewed beyond the shared 10-minute pricing-source allowance, or older than the four-hour window, drops the provenance tonull(supply still publishes) with a logged machine-readable reason, so a faulty scanner clock can never publish an impossible observation time or poison V9 evidence.
The enrichment path is intentionally narrower than primary pricing:
- it exists to fill holes, not overrule good consensus
- fallback results are validated before they can enter hourly corroboration provenance or later claim an asset during publication
- the global price cache stores publication-safe continuity plus hourly fallback candidates with explicit fallback confidence; fallback candidates are revalidated on read and remain non-depeg-authoritative, while the verified CMC targeted cache remains a separate provider-local lane that never refreshes the original observation time or upgrades fallback confidence. Its staleness budget is calibrated against the producer cadence instead of equal to it: live targeted and category quotes stay admissible for one fetch cadence (one hour) plus a five-minute grace because CMC rolls
last_updatedhourly and the consumer fetch lands just after each roll boundary, and a cached verified quote bridges up to two cadences plus grace so a single missed CMC roll or one rotation-skipped fetch hour (the 25-slug request cap rotates candidates) does not blank the asset; anything older still goes missing rather than publishing stale. Thecoinmarketcapregistry entry's publicationmaxTrustedAgeSec(two hours twenty minutes) covers that bridge plus one 15-minute publication slot, so a quote admitted during the :09 probe survives the staging handoff to the :15 publication instead of being discarded assourceExpiredafter it had already suppressed the fresh retrieval that would have replaced it - the effective replay age is the smaller of that six-hour ceiling and every component source's registry
maxTrustedAgeSec; any non-replay-safe component makes the cache entry immediately ineligible - previous-trusted continuity now merges the last authoritative stablecoins publication with fresh replay-safe
price_cacherows, so a temporarilylowor unusable publication does not make an already-confirmed severe depeg forget its prior corroborated state on the next run - replay-safe cached fallback is applied to any asset that is still missing after post-validation, including assets that became missing later in the same sync run because a current-run candidate was rejected
- invalid or severely depegged single-source fallback prints are dropped instead of poisoning later runs
Current Operator Limitations
As of 2026-07-18, the unresolved active rows include these intentionally fail-closed cases:
- NXUSD: no ordinary-holder protocol redemption path was verified, and current exact pools report zero transactions and volume. The material Avalanche Curve NXUSD/av3CRV pool is severely imbalanced at roughly 392,127 NXUSD versus 10,413 av3CRV; executable
get_dy_underlyingquotes return only about 0.5968 underlying quote units for one NXUSD and about 0.4732 per NXUSD at a 10,000-NXUSD notional. The next exact pool is only about $5,600 and Polygon has no exact pool, so no deterministic NXUSD adapter is admitted.
Specialty protocol and exact-pool routes are availability paths, not price guarantees. Kava, Citrea, Ethereum, Avalanche, or Plasma API/RPC failure; stale heads; identity, wrapper, or code-hash drift; unavailable or underfunded redemption; unavailable trusted parent prices; and insufficient executable depth all leave the asset missing and keep activePriceCoverage incomplete. Operators should investigate the route-specific diagnostic instead of extending replay age, filling nominal parity, or lowering global liquidity thresholds.
Timestamp Semantics
priceObservedAt: effective observation time attached to the selected source pricepriceObservedAtMode: freshness provenance forpriceObservedAtpriceObservedAtMode = "upstream":priceObservedAtcame from source-native freshness metadatapriceObservedAtMode = "local_fetch": the source exposed no trustworthy upstream observation timestamp, so Pharos uses local fetch time insteadpriceObservedAtMode = "unknown": legacy or carried-forward metadata did not preserve freshness provenance explicitlypriceSyncedAt: when Pharos selected and wrote the price during the current syncpriceUpdatedAt: compatibility alias for the effective observation timestamp, preserved so existing consumers do not interpret sync-write time as source freshness- high-confidence cluster labels can describe multiple agreeing sources even when the published price is the cluster median rather than any one constituent source price
Downstream Trust Semantics
- Source labels are normalized through the pricing-source registry before replay safety, pool-challenge eligibility, fallback-only classification, severe-downside corroboration, and depeg-authority checks run. Composite labels such as
coingecko+geckoterminalare expanded into their component sources instead of being treated as unknown standalone sources. - Every registered source declares a
depegSourceFamily. CoinGecko-derived sources collapse to thecoingeckofamily, DefiLlama list/detail/contract sources collapse to thedefillamafamily, hard market/oracle/protocol sources keep provider-specific families, and promoted DEX lanes keep protocol-specificdex:*families. The same family map now defines independence before ordinary consensus clustering as well as severe-downside corroboration and downstream depeg confirmation. - Fallback/search lanes remain non-authoritative even when their source labels appear inside composite strings.
coinmarketcap,defillama-contract, and CoinGecko mirror/low-volume-style sources are treated as list aggregators for independence checks; Jupiter, DexScreener exact/search/address, DexPaprika, CoinGecko Onchain address augmentation, Alchemy Prices, Moralis, Birdeye, and cached replay cannot satisfy single-source depeg authority. - Soft single-source prices are never depeg-authoritative
- Soft-only multi-source agreement can still publish, but it remains
confirm_requireddownstream unless a hard authoritative source is present - Hard single-source prices are only depeg-authoritative when their freshness is source-native (
priceObservedAtMode = "upstream"); local-fetch hard single-source prices remainconfirm_required - Supported non-USD fiat assets can require a fresh direct native-peg corroboration step before a derived USD/FX move is allowed to publish, or to open, extend, or confirm downstream depeg state; when that native-implied mark is published, it remains a non-replay-safe fallback lane rather than cached consensus continuity
- Weak fixed-peg price jumps versus the previous trusted price are withheld until corroboration arrives
Confidence Model
The final cached price can carry one of four confidence states:
| Value | Meaning |
|---|---|
high | 2 or more independent sources agree, or a validated authoritative override succeeded |
single-source | only one live source produced a usable price, or a 2-source agreeing cluster was downgraded because every agreeing member is a list-style aggregator |
low | multiple sources existed but failed to form a strong agreeing cluster |
fallback | price came from enrichment rather than primary consensus |
Downstream consumers use these tags for display, depeg confirmation, and risk handling.
After consensus, applyListAggregatorDowngrade() expands composite source labels and downgrades 2-source clusters made entirely of list-style aggregators such as CoinGecko, DefiLlama-list, DefiLlama-contract, and CoinMarketCap from high to single-source, because those feeds can re-export overlapping upstream list data and are not treated as independent corroboration by themselves.
sync-stablecoins metadata also records, alongside the row counts, the circulating-value weight of each confidence bucket (confidenceMarketCapUsd plus the pricedMarketCapUsd denominator, active scope included) and the count of missing active prices covered by a valid, unexpired price-gap review (acknowledgedMissingCount). These are status-display inputs for the admin Price Source Health card — the value-weighted view exists because the row-count distribution is dominated by the single-source long tail by design (see Status Dashboard); they do not alter price selection, publication, or confidence assignment.
Update Rules
When changing live pricing behavior, update all relevant surfaces in the same change:
- runtime implementation in
worker/src/cron/sync-stablecoins/enrich-prices-primary.ts,worker/src/cron/sync-stablecoins/enrich-prices-fallback.ts, or related provider modules - this document for canonical pricing behavior
- Supply Snapshot, Depeg Detection, Pharos Stability Index, or Blacklist Tracker when the corresponding pipeline semantics changed
/methodologypricing copy insrc/app/methodology/sections/core-sections-pricing.tsxshared/lib/methodology-versions/registry.tsand the matching entry undershared/data/methodology-changelogs/pricing-pipeline/if methodology semantics changed- about-page.md and
src/lib/about-content.tswhen externally visible data sources change
Data Integrity Guardrails
The sync pipeline includes multiple layers of validation to prevent bad data from reaching users:
- Structural validation: DefiLlama response must contain
MIN_VALID_ASSET_COUNT(50) assets with validid,name,symbol, andcirculatingfields. Malformed objects are dropped before caching - Price validation ordering: sync-time validation rejects prices before
savePriceCache(), not after. Fixed pegs use canonical tracked metadata (pegType,navToken,commodityOunces) during validation, NAV tokens still use broad positive-price checks, fiat FX pegs share the USD upside tolerance when a usable reference exists, fractional commodity tokens are always scaled bycommodityOuncesand keep their broader reference band, and weak fallback/search-family address-provider quotes cannot publish uncorroborated fixed-peg depeg-sized prices - Concurrent cron guard:
setCacheIfNewer()uses a compare-and-swap pattern — a slow sync run can't overwrite a newer run's data. UsessyncStartSecas CAS guard. Applied to cache-writing crons such as stablecoins, stablecoin-charts, FX rates, bluechip ratings, and USDS status. - Detail JSON validation:
stablecoin-detail.tsparses response JSON before caching; skips cache on parse failure - Detail history freshness guard:
/api/stablecoin/:idrejects CoinGecko-derived history whose latest point is more than 72 hours old and falls back to D1supply_historyinstead of caching stale chart data - fetchWithRetry: Default 15s timeout prevents hanging Workers. HTTP retries are limited to
408,429, and5xxresponses;Retry-Afteris honored for429and5xx. Non-retryable HTTP errors terminate immediately unless passthrough or final-response semantics are configured.404is not passed through by default; callers must opt in via{ passthrough404: true }. Timeout and passthrough behavior are configurable per call ({ timeoutMs: N },{ passthroughStatuses: [...] }) - Depeg dedup:
UNIQUE INDEX (stablecoin_id, started_at, source)prevents duplicate depeg events. Partial index onended_at IS NULLspeeds up open-event queries - Depeg interval merge:
computePegScore()andcomputePegStability()merge overlapping depeg intervals before summing duration - Depeg direction handling: If a coin flips from below-peg to above-peg (or vice versa) without recovering, the old event is closed and a new one opened with the correct direction
- Peg score consistency: Both the detail page and peg-summary API use the same tracking-window start helper:
coinTrackingStart(...), which appliesmax(firstSeen, fourYearsAgo)when first-seen data exists. First-seen data is anchored by curated launch date first, then earliestsupply_history, then a durable first valid-price observation for priced assets that have not yet written supply history. - Backfill batch safety:
backfill-depegs.tsbundles the per-coin DELETE together with the first chunk of up to 99 inserts in one atomicdb.batch()(reserving one slot for the delete so it never exceeds D1's 100-statement batch limit), so a partial crash cannot leave the coin event-less; any remaining inserts then ship in sequential groups of 100 - OFFSET/LIMIT safety: SQL queries use
LIMIT -1when offset > 0 but no limit is set (bare OFFSET is invalid SQLite). Values are parameterized, not interpolated - Freshness header:
/api/stablecoinsreturnsX-Data-Age(seconds since last cache write) - Cloudflare Access admin auth: Admin endpoints are gated by the
ops-api.pharos.watchorigin lane. WhenCF_ACCESS_OPS_API_AUDis configured, the worker cryptographically verifies the Cloudflare Access JWT (worker/src/lib/auth.ts). Timing-safe HMAC comparison (timingSafeCompare) is used for the Telegram webhook secret, not for admin endpoints. - Pagination defaults:
/api/depeg-eventsdefaultslimitto 100 and caps at 1000;/api/blacklistdefaultslimitto 1000, caps at 1000, and treatslimit=0as "use default". The blacklist frontend fetches a single events page viasrc/lib/blacklist-api.ts(fetchBlacklistEvents, limit/offset params), and the chart/summary stats are served by the dedicated/api/blacklist-summaryendpoint (fetchBlacklistSummary) rather than by client-side multi-page hydration. - Unbounded query guard:
/api/peg-summarybounds via the 4-yearstarted_at >filter on the depeg_events query - Cache-empty 503:
/api/peg-summaryreturns HTTP 503 (not 200) when cache is empty, signaling data unavailability - Orphan depeg cleanup:
detectDepegEvents()closes open depeg events whose stablecoin was not processed during the current run (removed from tracked list, failed validation, etc.) - Cron history pruning:
logCronRun()no longer prunes old rows inline. The dailyprune-cron-historyjob on0 3 * * *deletescron_runsrows older than 7 days andcron_slot_executionsrows older than 14 days. - Security headers: Worker adds
X-Content-Type-Options: nosniffto all responses - Admin cache bypass: cache bypass is declared by each endpoint's
cacheBypassflag inshared/lib/api-endpoints/definitions.tsand exposed throughisCacheBypassPath(). This covers admin status/API-key/action-log reads plus mutating repair, backfill, and control endpoints; use the shared endpoint registry instead of maintaining a hand-copied route list. - Per-asset schema guard (stablecoins, DEC-03):
syncStablecoins()validates every main and fallback row on its own against the publishedStablecoinDataSchemabeforesetCacheIfNewer(). A schema-invalid row is quarantined — logged with its issues, written with the other quarantined rows tostablecoins:invalid-last, and removed from the run's downstream cohort — while valid peers publish. Only a global failure holds the whole list: an invalid envelope (fxFallbackRates), a duplicate published id, or a quarantine that leaves fewer thanMIN_VALID_ASSET_COUNTrows. A hold does not overwrite the canonicalstablecoinscache; it writes the rejected payload tostablecoins:invalid-last, returns cronstatus: "degraded", and alerts with validation context (main/fallback) plus last-known-good cache age. Intake applies the matching per-row admission before any transform (supply-snapshot.md) - Strict cache payload validation (yield rankings):
syncYieldData()validates theyield-rankingscache payload againstYieldRankingsResponseSchemabeforesetCache(). On schema failure, cache write is skipped,validationFailuresis incremented in cron metadata, and the run returnsstatus: "degraded"so status surfaces do not mark it healthy - Fail-closed transformed cache reads: cache-backed endpoints that must parse and reshape stored JSON now return HTTP
503when the cached payload is malformed instead of serving a200with raw cached bytes. This currently applies to/api/yield-rankingsand the cached fallback path in/api/mint-burn-flows. - Canonical V9 safety guard (yield):
syncYieldData()reads the acceptedreport-cards:v9publication through the shared current-safety loader. The post-V9 publisher requires a complete, identity-valid V9 active set; missing, held, stale, or incompatible safety data produces an empty degraded safety snapshot while the yield cache remains available.provenance.safetySnapshotrecords the accepted V9 generation, methodology, policy, and publish time. API-time hydration may use a newer complete current V9 publication with compatible identity. When live hydration is unusable (missing, held, or evaluator-incompatible publication) and the cached payload carries a stamped identity no older than the 24-hour stale-coherent window (YIELD_SAFETY_STALE_COHERENT_MAX_AGE_SECinshared/lib/yield-safety-fallback.ts),/api/yield-rankingsserves the payload's own coherent publish-time safety values unchanged, emittingyield-safety-hydration-staleandprovenance.liveSafetyHydration.fallback: "publish-time-snapshot". It clears safety fields to explicit NR (yield-safety-hydration-degraded) only when no coherent stamped snapshot exists — stamped identity missing — or the fallback aged past the window; either state also degrades/api/healthviayield-safety-unrated-serving:*warnings. Immediately before publishing, the yield path rechecks the active V9 identity; a mid-run identity change no longer blocks publication — the run publishes its coherent loaded-identity results and recordssafetyIdentityChangedBeforePublishin cron metadata, and the read path serves them as a publish-time snapshot until the next run re-aligns. No yield path reads the retired V8 compact cache or recomputes report cards. - Shared stablecoins cache loader: Consumers that read
stablecoins(/api/status,/api/peg-summary,/api/mint-burn-flows,daily-digest,compute-dews,stability-index,backfill-depegs) useworker/src/lib/stablecoins-cache.tsinstead of ad-hocJSON.parselogic. The loader separates cache read tolerance (mode: "strict" | "lenient") from cache contract validation (contract: "published" | "critical-fields"). Both modes require an object-shaped{ peggedAssets: [...] }payload and fail closed on missing cache, malformed JSON, invalid top-level shape, schema-invalid published-contract objects, or filtered malformed entries. Lenient mode may returnkind: "degraded"with a usable payload only for whole-entry critical-field filtering, includingreasonandfilteredCount. - DEWS source-failure accounting:
computeAndStoreDEWS()records upstream read failures as structuredsourceFailuresmetadata and emitsstatus: "degraded"when non-bootstrap-critical inputs fail. Metadata now includes source coverage and validation-failure counts. - Stage-structured stablecoins sync:
syncStablecoins()keeps the same output contract but now delegates intake/fallback gating toworker/src/cron/sync-stablecoins/intake.ts, shared post-enrichment/cache/depeg steps toworker/src/cron/sync-stablecoins/post-enrichment.ts, final run metadata shaping toworker/src/cron/sync-stablecoins/metadata.ts, helper contracts toworker/src/cron/sync-stablecoins/shared.ts, and normalization/filtering/staleness/supply-history fill toworker/src/cron/sync-stablecoins/stages.ts, whilesupplemental-assets.tsowns commodity and CG-only overlay fetches. - DefiLlama ID remap before enrichment/cache writes: in
syncStablecoins(), assets are remapped viaREGISTRY_BY_LLAMA_IDimmediately afternormalizeChainCirculating()and before supplemental merges/applyTrackedAssetOverrides(). This ensures downstream maps and keys (primaryPriceResults.get(asset.id),savePriceCache, cached-price fallback lookups, supply-history fill inputs, and final stablecoins cache payload) consistently use canonical IDs. - Post-remap canonical dedupe: if DefiLlama emits duplicate rows that collapse onto the same canonical Pharos ID,
syncStablecoins()now keeps a single preferred row before caching or enrichment. This prevents duplicate canonical assets from double-counting supply in the finalstablecoinspayload. - Stage-structured yield sync:
syncYieldData()now delegates source evaluation and previous-best normalization toworker/src/cron/yield-sync/evaluation.ts, rankings/cache publication and persistence helpers toworker/src/cron/yield-sync/publication.ts, and batched history preload plus stale/orphan cleanup toworker/src/cron/yield-sync/history.ts, keeping resolution logic separate from D1 housekeeping and payload assembly. - Stage-structured mint/burn run-state:
syncMintBurn()now delegates disabled-config normalization, lane rotation, and run-state persistence toworker/src/cron/mint-burn/run-state.ts; the two 30-minute scheduled handlers already shareworker/src/handlers/scheduled/mint-burn-slot.tsfor slot-specific dispatch. - Stage-structured blacklist EVM ingestion:
syncBlacklist()now delegates EVM event fetch/parsing and RPC fallback target selection toworker/src/cron/blacklist/evm-source.ts, isolating the Tron path and downstream balance enrichment from the source-ingest stage. - Stablecoins stale-publication guard:
syncStablecoins()now emits stage-level progress and compares fresh prices against the previous published cache before writing. If at least 50 comparable prices exist and >=98% are identical, the run returnsstatus: "degraded", recordsstaleWriteBlocked=true, and skips the canonicalstablecoinscache write instead of republishing a stale snapshot as fresh. - Fail-closed PSI dependency handling:
computeAndStoreStabilityIndex()no longer treats an unavailabledepeg_eventsquery as "no active depegs". The run now also requires a non-empty latest DEWS row set with usablecomputed_atno older than twocompute-dewsintervals before deriving stress breadth. These dependency failures degrade and skip publication so PSI remains anchored to the last valid sample. - DEWS bootstrap + freshness guard:
computeAndStoreDEWS()now uses a dedicateddews:bootstrap-completesentinel to end bootstrap grace after the first successful publication, and staledex_liquidityinputs (>2 hours old) now count as a hard degraded source failure. - Yield publication guardrails:
syncYieldData()now degrades on invalid/empty direct DeFiLlama payloads, on total deterministic on-chain failure, and blocksyield-rankingscache writes when the new rankings payload shrinks severely versus the last published cache. - DEWS blacklist coverage parity:
computeAndStoreDEWS()now derives blacklist-signal coverage from the shared blacklist contract registry (CONTRACT_CONFIGSinworker/src/lib/blacklist-contracts.ts, keyed bystablecoinId) instead of a local hardcoded subset, soPYUSDandUSD1receive the sameblacklist_events-driven stress input as the other live blacklist-tracked coins. - DEWS thin-peg FX parity:
computeAndStoreDEWS()now passes cachedfxFallbackRatesintoderivePegRates(), matching live depeg detection and peg-summary behavior for thin non-USD peg groups. - Recent-only chart FX repair:
syncStablecoinCharts()still corrects obvious recenttotalCirculatingUSDcorruption with the live FX cache, but it no longer rewrites deep historical points with today's FX reference. - Completed-day supply history reads:
snapshotSupply()requires exact active-registry coverage rather than a percentage threshold. Every active ID must have usable supply or an owned, reasoned, unexpired waiver; incomplete writes do not advancesnapshot-supply:last-write, remain hidden from completed-day reads, and can be retried on the same UTC day. Publicsupply-historyandnon-usd-sharereads cap rows to the completion marker when present and emitX-Data-Agefrom the latest completed supply snapshot run. The marker only fences the latest day, and the cron never back-writes a missed UTC day, so a missed day can later hold only per-coin admin-backfill rows.non-usd-sharetherefore omits any day on which core assets observed both before and after it but absent on it (valued at their previous row) leave less than 95% of the total or less than 50% of the commodity or fiat non-USD cohort observed, rather than charting a partial market as a share. As of 2026-09-29, genuine per-coin holes stay at or above 99.4% total and 82% cohort coverage, and the only omitted day is 2026-08-06, when XNK's stale quote blocked every snapshot run before #785. - Single-deployment on-chain supply fallback: CoinGecko-detail supplemental assets may use on-chain
totalSupply * priceonly when exactly one supported deployment can represent global supply. Multi-deployment assets skip that fallback instead of treating one chain's supply as the total market cap unless they are in the curated aggregate on-chain supply list, where every configured chain is read and the fallback fails closed if any configured chain cannot be read. Reviewed zero-supply native deployments may contribute zero only when their aggregate config explicitly allows it. Configured protocol-inventory exclusions may subtract live holder balances from that single deployment before publishingonchain-circulating-supply; if any configured balance read fails, the on-chain fallback is skipped for that run. Movement USDCx reads0x1::fungible_asset::ConcurrentSupplyandMetadataat one pinned Movement ledger, requires the resource decimals to match the tracked contract metadata, and admits the row only when Circle xReserve's EthereumbalanceOfNativeCollateral(USDC, 10005)is at least the Movement supply and differs by no more than one basis point. Missing ledgers, malformed resources, provider failures, reserve deficits, and wider mismatches publish no repaired supply. - CAS outcome visibility and cadence buckets:
setCacheIfNewer()returns whether a cache row was actually published or skipped because a newer canonical row already exists. FX and stablecoin-chart producers claim generation-fenced scheduled cadence buckets, complete them only after successful canonical publication (or readback-confirmed newer publication), and leave failures retryable. This avoids 45/90-minute drift caused by elapsed write-age checks. - Freshness sentinel validation:
/api/healthand/api/statustrustfreshness:dex-liquidity,freshness:yield-data, andfreshness:dewsonly when the cache row contains a valid JSON producer assertion:updatedAt, expectedsource, andpublishStatus: "ok"with optionalrowsWritten/coverageRatio. Malformed, stale, future-dated, wrong-source, or non-oksentinels fall back to table freshness and then latest successful producer cron freshness, withfreshnessSource,sentinelValidationReason, and a warning surfaced in the cache status.compute-dewspublishes the DEWS freshness sentinel only after a non-degraded run persists at least one current row, so zero-result or degraded runs cannot mark stale stress-signal tables fresh. - Restored supply is independent of price freshness:
mergeSupplementalLastKnownGood()andrestoreMissingTrackedAssets()may retain eligible previous positive supply within the supply restore ceiling, marking itsupplyRestored: truewith its originalsupplyObservedAt. A price carried forward from an earlier generation must independently pass its source registry freshness budget using the originalpriceObservedAt(or legacypriceUpdatedAt). The restore helpers apply this check when they carry the previous row, andapplyConsensusResults()applies it again when it retains an existing price because a candidate is missing or rejected. The published row stores only a composite's conservative, oldest-member observation time and no per-member times, so a carried composite is checked against every component's budget using that time. Prices accepted in the current run already passed per-source freshness when they were collected, sovalidatePublishedAssetPrice()does not age them again at publication. Consensus composites therefore stay published when their oldest member is an hourly DEX quote within its own budget, native-implied fills keep the fetch lane's 1,800-second admission, and protocol-redeem FX-par rows are not changed here. Restored rows additionally require an original observation timestamp; a freshly fetched, non-restored list quote retains its registry-defined missing-timestamp semantics without inventing an observation time. An ordinary CoinGecko quote older than 900 seconds becomes unavailable without discarding valid supply; it is never relabelled low-volume or given a new observation time. Existing low-volume and NAV budgets remain unchanged. Resulting active-price gaps are reported separately from supply coverage. - Exact stablecoin publication capability: every published stablecoins run compares cache IDs to the active registry. Named unwaived omissions degrade the run and clear the downstream-safe cache capability. The default waiver roster is empty; any future exception must be explicit, owned, reasoned, and time-bounded. Persistent no-supply impossibility is handled through a reviewed quarantine rather than a silent active-coverage waiver. Price gaps and genuine depegs remain active monitoring failures. The data-invariant canary uses the same exact evaluator.
- Bounded cron diagnostics: stablecoins cron rows retain counts and bounded diagnostics but never the deduplicated asset objects. Their producer-level guard compacts below 60 KiB before the scheduled wrapper appends lease and slot identity, preserving top-level coverage evidence beneath the global 64 KiB persistence ceiling; since the compacted shape still scales with the missing-asset count, the guard enforces the budget through an ordered degradation ladder (duplicate ledger ID list, verbose gap details, attempt records — counts and truncation markers retained) and fails closed with a scalar-only envelope when no rung fits. Mint/burn rows retain totals, top laggers, and at most 12 config samples; normalized full-run drilldown and per-config attempt age live in latest-only cache records.
- Independent active-price coverage: every stablecoins run records exact active price coverage separately from row coverage. A published active row with a missing or invalid price is reported exactly and, once the gap persists for two consecutive published generations, raises an
/api/healthwarning — it never degrades public health status, downgrades a successfully completed cron execution, or blocks the rest of the cache — and the health parser revalidates the complete priced-ID set against the current active registry before reportingcomplete. - Publishable-price recovery targeting: authoritative live overrides and exact-address augmentation no longer treat every positive numeric incumbent as resolved. Recovery targeting requires publishable price provenance before a row leaves the missing cohort, live override candidates have per-candidate fairness caps inside the unchanged shared budget, and CoinGecko exact-address batches round-robin across networks before spending another request on one network. One stalled protocol route or large network cannot consume the full active-price repair window.
- Stablecoins publication heap handoff: the main sync computes active-price coverage and reduces the prior publication to its ID set before cache normalization, schema validation, and serialization. This preserves previous-price provenance in coverage metadata and tracked-addition notices without retaining the full prior asset graph across the publication heap peak.
- Bounded, as-of historical supply lookups: every nearest-supply lookup carries an explicit distance policy. The PSI replay lookup (
findNearestSupplySnapshot) is as-of — it returns the newestsupply_historysnapshot on or before the replayed day, within 14 days, so a replayed day can never take market cap or price from a later observation. The backfill extraction and authoritative-price-source twins (findNearestSupply) share the same 14-day bound (MAX_SUPPLY_SNAPSHOT_DISTANCE_SECinshared/lib/rate-series.ts, beside the sharedfindAsOfSnapshot) and returnnullbeyond it, so a price point outside supply coverage is skipped instead of being gated by a snapshot months away. DEX price rows are trusted only whenupdated_atis a safe integer at or before now with finite non-negative TVL (isTrustedDexPriceRow), matching the existing primary-price rejection of future timestamps. - Bounded-lane authoritative override scheduling: the live override stage pulls candidates in its existing priority order onto at most four lanes — the
sync-stablecoinsmaxConnectionsdeclaration inshared/lib/cron-jobs.ts— instead of running them strictly serially. Measured production runs on 2026-09-23 spent the whole 10-second budget on the first few chain-RPC candidates (Jupiter 2-3 s, Kava 3-5 s, Mento broker/FPMM 1-3 s each) and then budget-skipped 8-41 of 64 candidates, which is exactly the cohort of override-only assets that flicker out of active-price coverage. Lanes take the next candidate as soon as they free, so the same budget now covers the whole queue: the shared budget, per-candidate fairness caps, provider interleaving, missing-before-priced partitioning, and circuit admission (memoized as one in-flight read per source per pass) are unchanged, and only candidates the budget never reached stay skip-neutral. Candidates that price from a tracked parent row declareliveParentByAssetIdand are dispatched only after that parent's same-pass attempt settles, so parallel lanes cannot read a parent row that has no price yet; blocked candidates never idle a lane while ready work remains. The attempt ledger is staged per candidate and flushed once per pass so parallel completion order cannot scramble diagnostics, andauthoritativeOverrideStats.perAdapterrecords per-adapter attempted/resolved/empty/failed/circuit-open/budget-skip counts with total and maximum attempt duration for budget attribution.
The Kava USDX authoritative quote uses a six-second candidate deadline for its four serial, body-consumed requests (each remains capped at 2.2 seconds). The shared live-override budget and oracle identity, expiry, and dispersion guards still apply.
Gold & Silver Spot Prices (gold-api.com)
syncFxRates() in worker/src/cron/sync-fx-rates.ts fetches gold and silver spot prices from the gold-api.com API for commodity-pegged stablecoin peg validation (XAUT, PAXG, KAU, KAG, etc.).
Why gold-api.com?
The previous source (DefiLlama's coingecko:gold / coingecko:silver coins API) silently returns empty data, producing garbage peg references and phantom trillion-BPS depegs in backfilled events. gold-api.com requires no API key, and the worker only performs two live spot requests in each claimed 30-minute sync-fx-rates cadence bucket.
Live Sync (sync-fx-rates.ts)
- Endpoint:
GET https://api.gold-api.com/price/XAU(gold),GET https://api.gold-api.com/price/XAG(silver) - Source time: Preserve the provider's ISO
updatedAtas the observation time, including old observations; missing, malformed, nonpositive, or more-than-60-second future timestamps remainnull. Fetch time never substitutes for source time. - Request volume: 2 requests per claimed
sync-fx-ratescadence bucket (gold + silver), with no repo-level rate limiter; the quarter-hourly trigger maps to one upstream-work bucket every 30 minutes. - Validation: Same
isValidFxRate()bounds + delta checks as FX rates (gold: $500-$10,000/oz, silver: $5-$500/oz, max 20% change from previous value). When a fresh commodity peer median exists, gold-api.com metal spots must also stay within 5% of that peer reference; divergent spots are rejected before they can refresh the shared FX cache. - Fallback: If the gold-api.com live fetch fails or fails the peer-median cross-check,
sync-fx-rates.tsnow derives a fresh commodity reference from the just-writtenstablecoinscache (peer median across tracked gold tokens; single tracked silver token for silver) before inheriting the previous cached metal rate. Only contributors passing their pricing-source freshness policy enter this median; its timestamp is the oldest admitted quote observation, never the cache publication time. This keeps/api/healthanchored to an actually fresh commodity reference when the anonymous metals endpoint is blocked from Workers. - Chainlink overlay: XAU/XAG Chainlink quotes still have to be fresh under their feed-level staleness window and within 5% of the current metal reference. Because the metals lane preserves the gold-api.com provider time or oldest admitted peer quote observation, older but still feed-fresh Chainlink metal quotes are used as validation-only cross-checks instead of being skipped before divergence detection or replacing fresher spot metadata.
For fiat FX, Frankfurter remains the preferred ECB-backed source for the business-day set, including the tracked HKD and INR peg groups. The worker targets the maintained hosted endpoint at https://api.frankfurter.dev/v1, which replaced the retired frankfurter.app host. The existing fawazahmed0/currency-api mirror still owns the currencies keyed in SECONDARY_FX_CURRENCY_TO_PEG, and it can also backstop the wider fiat set when Frankfurter is temporarily unavailable so the cron can keep publishing live dated FX references instead of immediately dropping to a cached-only run. If both Frankfurter and the existing secondary mirrors are unavailable, sync-fx-rates.ts falls through to ExchangeRate-API's daily USD reference snapshot as a tertiary full-set fallback before reusing cached rates. If none of those live fetches respond but the previously persisted daily references are still within their expected publish cadence, the cron carries them forward as a live success instead of incrementing the cached-fallback streak. Even after a cached-fallback run begins, the independent OXR, Chainlink, and metals probes still execute; if they restore fresh full-set fiat coverage, the run exits cached fallback immediately instead of waiting for the primary Frankfurter path to recover first.
Backfill (backfill-depegs.ts)
backfill-depegs.tsnow asks the same authoritative-price registry used by live sync for historical series first. If a coin has an authoritative historical provider and that provider cannot return enough coverage, the backfill preserves existingsource='backfill'rows instead of rebuilding from a known-weaker fallback source.- Supported non-USD fiat backfills prefer direct CoinGecko native-fiat history and compare it to the native
1.0peg before they fall back to USD history plus historical FX. - Commodity backfill does not call a gold-api.com timeseries endpoint.
- Instead, it builds daily GOLD/SILVER peg references from CoinGecko historical prices across tracked commodity tokens (
buildCommodityMedianSeriesFromCg()), normalized to per-troy-ounce and median-aggregated per day. - The resulting
{ GOLD: FxTimeSeries[], SILVER: FxTimeSeries[] }series feedsbuildFxLookup()for time-varying commodity peg references. - Fiat backfill uses Frankfurter historical ranges from
api.frankfurter.dev/v1for ECB-covered currencies and date-addressedfawazahmed0/currency-apisnapshots for non-ECB currencies such as CNH, RUB, UAH, ARS, KGS, NGN, XOF, and VND. - Secondary historical FX snapshots are cached in D1 by year (
fx-history-secondary:<year>) so repeated admin backfills do not re-fetch the same daily files. - Fallback behavior: if series data is sparse/missing for a timestamp,
buildFxLookup()falls back to the current peg reference derived from live rates. - Historical depeg extraction validates each price point against the direct peg reference for that timestamp (
historical_backfillmode). That preserves confirmed catastrophic downside moves without weakening the tighter fallback/DEX filters used for noisy live sources. - Dry-run backfill audits now accept
startDay/endDayplus optionalcontextDays, replay only that UTC window with the requested context pad, and keep long-history non-USD repairs belowops-apitimeout limits without changing the full-coin mutation path.
Budget
The live /price/ endpoint requires no API key and is called only in claimed sync-fx-rates cadence buckets (2 requests: gold + silver), roughly 2,880 requests/month. Backfills source commodity history from CoinGecko market-chart data (via existing CoinGecko integration), so there is no separate gold-api.com historical-request budget.
Treasury Benchmark Rates
fetch-tbill-rate runs daily at 08:00 UTC and fetches every benchmark descriptor on each run: USD 3-month Treasury, USD/EFFR, EUR, CHF, GBP, JPY, MXN, BRL, AUD, CAD, RUB, and TRY.
Each descriptor owns an independent circuit breaker key in the form TREASURY_RATES:<descriptor> (for example, TREASURY_RATES:EUR). An open descriptor circuit produces its retained or hardcoded fallback while every other descriptor continues through its own breaker and provider path. The daily publication preserves the structured risk_free_rates and legacy risk_free_rate cache shapes.
Stale Data Monitoring (Frontend)
The StaleDataBanner component (src/components/stale-data-banner.tsx) warns users when data from selected critical queries is degraded or stale. Its named budgets come from DATA_HEALTH_PRESETS in src/lib/data-health-config.ts, which projects API_FRESHNESS_MAX_AGE_SEC; they are endpoint/UI health budgets, not necessarily producer intervals. Frontend freshness uses the shared FRESHNESS_RATIOS thresholds from shared/lib/status-thresholds.ts: fresh through 8x staleTime, degraded through 12x staleTime, then stale. When a hook uses apiFetchWithMeta(), backend freshness metadata (_meta.status, X-Data-Age, stale Warning) takes precedence over browser fetch time so a fresh client refetch cannot mask stale server data. Which presets a page monitors is owned by that route's own client model — each page passes its StaleQuery set to StaleDataBanner — and the minute values behind each preset are owned by API_FRESHNESS_MAX_AGE_SEC in shared/lib/api-freshness.ts, which derives most of them from CRON_INTERVALS and DATA_SURFACE_DESCRIPTORS. Read both from source rather than from a table here: a producer-cadence change moves the budgets without touching any prose. Screener, for example, monitors Prices, Peg Data, Report Cards, DEWS, and Liquidity, while Blacklist monitors only Blacklist. Some routes also render additional detail queries that are handled locally rather than by the page-level banner.
Homepage KPI cards also consume PSI, mint/burn, and DEWS data, while Compare can fetch supply-history and per-coin mint/burn detail queries. Those additional queries are not part of the current page-level stale banner contract.
Cron-backed hooks normally derive polling from FRONTEND_API_QUERY_DESCRIPTORS: staleTime uses the producer interval and refetchInterval uses twice that interval. Endpoint and banner freshness budgets can intentionally be tighter or looser—for example, prices warn after 10 minutes, Report Cards after 15 minutes despite a 30-minute V9 producer, and Liquidity after four hours despite an hourly scoring producer. Local browser age becomes degraded after 8x the selected banner preset and stale after 12x, while hook-level freshness metadata can mark data degraded/stale sooner when the Worker explicitly reports old cache age or stale-table warnings.
Tracked NAV classification is authoritative in both intake normalization and price-validation context: an omitted curated NAV flag means false, and a provider's navToken: true cannot bypass fixed-peg validation. Untracked rows retain their source classification.