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.
Base 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
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.
| State | Condition | Why it matters |
|---|---|---|
HEALTHY | complete, positive, inside this feed’s max age | Safe to price against — for this round, not for the next one. |
STALE | age > maxAge | The 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_ANSWER | answer <= 0 | answer 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_TIMESTAMP | updatedAt > block.timestamp | Computing now - updatedAt reverts under checked arithmetic, or underflows to an enormous age without it. Unlike staleness, waiting does not fix it. |
INCOMPLETE_ROUND | updatedAt == 0 | The round was started and never answered, so there is no timestamp to measure an age from at all. |
UNAVAILABLE | the feed reverted, has no code, or answered in the wrong shape | Reported 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.
Live data unavailable — showing the last known values.
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
| Verdict | HEALTHY |
|---|---|
| Answer | 64820.00500000 (8 decimals) |
| Answer, as stored | 6482000500000 |
| Round | 18446744073709845412 |
| Updated at | 1785433866 (unix seconds) |
| Age | 18 minutes 2 seconds (1082 seconds) |
| Max age for this feed | 1 hour 15 minutes (4500 seconds, immutable) |
| Headroom | 56 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
| Verdict | HEALTHY |
|---|---|
| Answer | 1920.46000000 (8 decimals) |
| Answer, as stored | 192046000000 |
| Round | 18446744073709823363 |
| Updated at | 1785433848 (unix seconds) |
| Age | 18 minutes 20 seconds (1100 seconds) |
| Max age for this feed | 1 hour 15 minutes (4500 seconds, immutable) |
| Headroom | 56 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
| Verdict | HEALTHY |
|---|---|
| Answer | 8.50500000 (8 decimals) |
| Answer, as stored | 850500000 |
| Round | 18446744073709818890 |
| Updated at | 1785434848 (unix seconds) |
| Age | 1 minute 40 seconds (100 seconds) |
| Max age for this feed | 1 hour 15 minutes (4500 seconds, immutable) |
| Headroom | 1 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
| Verdict | HEALTHY |
|---|---|
| Answer | 0.99980507 (8 decimals) |
| Answer, as stored | 99980507 |
| Round | 18446744073709552751 |
| Updated at | 1785409184 (unix seconds) |
| Age | 7 hours 9 minutes (25764 seconds) |
| Max age for this feed | 1 day 1 hour (90000 seconds, immutable) |
| Headroom | 17 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.
- a verdict is always the most severe applicable state
- time passing never makes a verdict healthier
- a complete, positive, in-date round is healthy
- a non-positive answer is never healthy, at any age
- an incomplete round is reported as one
- a future timestamp is reported as one
- the staleness boundary is exact in both directions
- an unreadable feed yields
UNAVAILABLEand never reverts the caller - reading a real feed agrees with the pure logic on the same numbers
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.