Oracle Health Explorer

Protocols rarely lose money because an oracle was attacked. They lose it because the consuming contract called latestRoundData(), took the answer, and used it — and the answer was old, or negative, or from a round that never finished. This page reads live Chainlink Data Feeds through a contract on Base Sepolia that performs those checks and names the result.

BaseBase Sepolia · testnet only

This demo is read-only. There is no wallet to connect and nothing to sign: every function on the contract is a view, so the whole page is built from eth_call. You will not be asked for a signature and you will not spend any gas. The page also takes no input at all — there is no field, no slider and no query parameter — so nothing you send can change what it reads.

Checker 0x67F7…Bd40 · Deployed in block 44827955, tx 0xa306…8578 · Source on GitHub

Watch: 30 seconds

The six things that can be wrong

A price feed does not usually fail by disappearing. It fails by continuing to answer, politely, with something a consumer should not act on. The contract distinguishes six states, and the order matters: they are declared least to most severe, and when more than one is true at once a consumer is told the worst of them rather than whichever the code happened to check first.

StateConditionWhy it matters
HEALTHYcomplete, positive, inside this feed’s max ageSafe to price against — for this round, not for the next one.
STALEage > maxAgeThe commonest failure and the quietest. The feed still answers and the data is well-formed; nobody has updated it. Code that only checks for a revert sees nothing wrong.
NON_POSITIVE_ANSWERanswer <= 0answer is a signed integer, and a production feed has printed a negative one. Cast to an unsigned type without checking, a small negative price becomes a number near 2256.
FUTURE_TIMESTAMPupdatedAt > block.timestampComputing now - updatedAt reverts under checked arithmetic, or underflows to an enormous age without it. Unlike staleness, waiting does not fix it.
INCOMPLETE_ROUNDupdatedAt == 0The round was started and never answered, so there is no timestamp to measure an age from at all.
UNAVAILABLEthe feed reverted, has no code, or answered in the wrong shapeReported as a verdict rather than propagated as a revert — a contract whose job is reporting broken feeds must not break on one.

What the chain says right now

Every figure below is read from the deployed contract on each page load: the verdict, the round data it was derived from, and the threshold it was judged against. Nothing here is a number this page decided. The raw values are shown beside each verdict so you can redo the arithmetic instead of trusting it.

Read at block 44833330. Worst state across all feeds: Healthy.

BTC / USD Healthy

Aggregator 0x0fb9…4298 · the pair name above is read from the aggregator itself, not from a label on this page

VerdictHEALTHY
Answer64820.00500000 (8 decimals)
Answer, as stored6482000500000
Round18446744073709845412
Updated at1785433866 (unix seconds)
Age18 minutes 2 seconds (1082 seconds)
Max age for this feed1 hour 15 minutes (4500 seconds, immutable)
Headroom56 minutes 58 seconds before this round goes stale

What this means for a consumer: For a protocol consuming this BTC/USD feed, “HEALTHY” with the shown round age means the data is fresh relative to your configured staleness limit, so you can safely read and use the price without triggering fallback or disabling trading/actions tied to that feed. Continue normal operation using the latest answered round.
Off-chain commentary, written by the Pigfox analysis engine. It is not an on-chain value and nothing about it is consensus-verified — the verdict and the figures above are.

ETH / USD Healthy

Aggregator 0x4adc…7cb1 · the pair name above is read from the aggregator itself, not from a label on this page

VerdictHEALTHY
Answer1920.46000000 (8 decimals)
Answer, as stored192046000000
Round18446744073709823363
Updated at1785433848 (unix seconds)
Age18 minutes 20 seconds (1100 seconds)
Max age for this feed1 hour 15 minutes (4500 seconds, immutable)
Headroom56 minutes 40 seconds before this round goes stale

What this means for a consumer: For a protocol consuming this ETH/USD feed, “HEALTHY” means the latest oracle update is fresh enough to pass your deployed checker’s staleness requirements. Your on-chain logic should therefore accept the price for computations (e.g., collateral valuation, risk checks, or mint/burn logic) without falling back to older data or pausing due to staleness.
Off-chain commentary, written by the Pigfox analysis engine. It is not an on-chain value and nothing about it is consensus-verified — the verdict and the figures above are.

LINK / USD Healthy

Aggregator 0xb113…5a61 · the pair name above is read from the aggregator itself, not from a label on this page

VerdictHEALTHY
Answer8.50500000 (8 decimals)
Answer, as stored850500000
Round18446744073709818890
Updated at1785434848 (unix seconds)
Age1 minute 40 seconds (100 seconds)
Max age for this feed1 hour 15 minutes (4500 seconds, immutable)
Headroom1 hour 13 minutes before this round goes stale

What this means for a consumer: A HEALTHY verdict means the Chainlink round freshness requirements your deployed checker enforces are satisfied for the LINK/USD price. For your protocol, you can safely proceed to read and use the feed for valuation, pricing, and liquidation logic without triggering the checker’s fallback or revert path. Continue normal operation.
Off-chain commentary, written by the Pigfox analysis engine. It is not an on-chain value and nothing about it is consensus-verified — the verdict and the figures above are.

USDC / USD Healthy

Aggregator 0xd30e…5165 · the pair name above is read from the aggregator itself, not from a label on this page

VerdictHEALTHY
Answer0.99980507 (8 decimals)
Answer, as stored99980507
Round18446744073709552751
Updated at1785409184 (unix seconds)
Age7 hours 9 minutes (25764 seconds)
Max age for this feed1 day 1 hour (90000 seconds, immutable)
Headroom17 hours 50 minutes before this round goes stale

What this means for a consumer: HEALTHY means your protocol’s configured freshness requirement for the USDC/USD round is satisfied, so it can safely use the latest reported price for calculations that depend on up-to-date data. No special fallback or circuit breaker action is required purely due to feed staleness; continue normal pricing logic and consider re-checking on the next update cycle.
Off-chain commentary, written by the Pigfox analysis engine. It is not an on-chain value and nothing about it is consensus-verified — the verdict and the figures above are.


Why the thresholds are not the same number

Each feed is judged against its own max age, fixed at deployment. That is a measurement, not a preference.

The three crypto feeds above republish every few minutes, driven by price deviation. USDC / USD does not: it moves on a roughly 24-hour heartbeat, confirmed across two consecutive intervals before this contract was written. A single estate-wide threshold cannot describe both. Tight enough for BTC, and USDC reads stale for almost all of every day. Loose enough for USDC, and a BTC feed frozen for twenty hours reads healthy. Neither of those is a staleness check.

So the threshold lives with the feed, and it is immutable — there is no owner, no setter and no upgrade path, which is why the max age shown in each table above is read from the contract rather than written into this page.

The boundary is the bug

maxAge is inclusive. An age of exactly maxAge is healthy; stale begins one second later. The comparison is age > maxAge, never >=.

That single character is the whole bug class, and it is invisible to testing that samples the middle of a range — both wrong versions agree with the right one everywhere except on one value. So the property harness constructs both sides of the threshold on every run rather than waiting to stumble onto it, and a companion test demonstrates that an interior sample does not catch the substitution.

A check that is still copied, and cannot fire

Older Chainlink documentation suggested guarding staleness with answeredInRound < roundId, and that line is still copied into consumer code today. All four aggregators here still expose answeredInRound — and on every one of them it comes back equal to roundId, so the guard can never fire.

The contract documents that in a comment instead of implementing it. A check that always passes is worse than no check: it reads as though staleness were covered when the only thing actually covering it is the updatedAt comparison.

How it is verified

The contract passes the Pigfox Solidity pipeline on every push: formatting, build, tests, 100% line, statement, branch and function coverage with no exclusions, Slither at fail-on: low, and property fuzzing under both Echidna and Medusa.

Nine properties are claimed. The verdict logic is a pure function — it takes the round data and the observation time as arguments and reads neither state nor clock — which is what makes them provable at all: a live aggregator cannot be asked to go stale, answer negative, or hand back an incomplete round, so those states are reached with mock feeds built from source inside the fuzzers’ own in-process EVM.

The property count is declared, not counted: the harness states it as a literal, a static gate counts the predicates, and each fuzzer is checked against what it actually registered at runtime — so a property that quietly stops being picked up fails the build instead of reporting a smaller green run. The harness’s own detectors are tested too, by installing a checker built to break each property and requiring the matching predicate to notice, with a control that requires a faithful checker to trip nothing. Full detail, including the measured heartbeats and the live read-back, in the repository.