Banked from a real staged candidate (cycle267 doc 06). The highest-yield oracle audit lenses, all proven on EtherFi Cash.
A "maxStaleness is dead on the non-Chainlink branch" finding is only EXPLOITABLE if a configured non-Chainlink oracle actually returns a STALE value without reverting. Many do NOT: Pyth price() wrappers self-revert (getPriceNoOlderThan), and a token priced via a base-asset chain inherits the base-asset oracle's freshness. So the PriceProvider-level dead-code can be fully REDUNDANT in practice.
THE DECISIVE CHEAP CHECK = a forge mainnet-fork warp test: fork, record price(token) for each non-Chainlink collateral, vm.warp(+30 days), call price(token) again. If it REVERTS -> the underlying oracle self-protects -> the dead-code is MOOT for that token (informational only). If it RETURNS A STALE VALUE -> real exposure. EtherFi Cash OP result: ALL non-Chainlink collaterals (sETHFI/ETHFI/WHYPE/beHYPE/EURC) reverted on warp (Pyth self-revert; sETHFI via its ETHFI base-asset Pyth feed) -> the source-level dead-code finding DOWNGRADED to informational/WALK. LESSON: NEVER score an oracle-dead-code/missing-staleness finding without the fork-warp confirmation that a LIVE configured oracle actually returns-stale-without-reverting. Source says "no staleness check"; the configured oracle may enforce it anyway. Verify-and-walk > false-submit. (This is also why the candidate started Medium-High from source and ended WALK after on-chain verification - the floor working.)
- Corollary: getRate() (pause-ignoring) is only exploitable if (a) the Accountant actually pauses AND (b) the rate it freezes is VOLATILE. A slow non-volatile staking-yield rate (sETHFI->ETHFI) frozen during a pause drifts sub-bps; combined with a fresh base-asset price, the mispricing is negligible. Score the rate's VOLATILITY, not just the pause-ignore.
If a price Config struct carries a safety field (maxStaleness, minPrice, maxPrice, heartbeat), EVERY consumer branch that reads that config MUST enforce it - else the field is SECURITY THEATER and a latent bug. Grep test: a config field referenced in only K of N branches = dead code on the other N-K.
- EtherFi: PriceProviderV2._fetchRawPrice reads config.maxStaleness ONLY in the if (config.isChainlinkType) branch; the non-Chainlink staticcall branch returns the price with only a >0 check. The configs SET maxStaleness: 2-7 days on non-Chainlink feeds (Liquid getRate, sETHFI latestAnswer, Pyth wrappers) -> silently ignored. Config validation even enforces maxStaleness != 0 ONLY for Chainlink configs (:349), cementing the asymmetry.
- Audit move on ANY multi-source oracle (Chainlink + Pyth + ERC4626-rate + custom): for EACH source branch, list which of {price>0, staleness, answeredInRound>=roundId, min/max bound, decimals} it enforces. The branch missing staleness on a feed that can freeze = the lead. The "config sets it but the branch doesn't read it" is the strongest single tell (intent-without-enforcement = defect, not by-design).
BoringVault / EtherFi AccountantWithRateProviders (and similar rate providers) expose BOTH getRate() (returns the last stored exchangeRate EVEN WHEN PAUSED) and getRateSafe() (reverts when the accountant is paused). A collateral oracle that wires the UNSAFE getRate keeps pricing collateral during a pause - the exact incident-condition pause exists to handle (depeg, rate dispute). ILayerZeroTeller.getRate() returns only uint256 rate (no timestamp) so the consumer COULDN'T staleness-check even if it wanted to. Audit reflex: grep oracle configs for getRate.selector vs getRateSafe.selector; the unsafe variant + no PriceProvider staleness = stale-price-on-pause exposure. The existence of getRateSafe is the proof the safe path was available and not taken (wrong-variant defect).
Admin/oracle-PUSHED exchange-rate feeds (Liquid getRate = a stored rate, NOT a spot convertToAssets) MITIGATE the donation/first-deposit-inflation/share-spot-manipulation seams ([[2026-06-11-metamorpho-totalassets-is-share-accounted-not-donation-inflatable]], [[seams-lst-selfrate-donation-empirical-test-2026-06-11]] do NOT apply) - BUT make STALENESS/freshness the dominant risk, and it's easy to overlook precisely because the value "looks trusted." So: when collateral is priced by a push-rate (not spot), STOP hunting donation/inflation and PIVOT to "does the rate go stale, and does anything enforce freshness?"
When an RWA/"Liquid" (ERC4626-shaped) collateral is priced via a DEDICATED EXTERNAL oracle address (a Chainlink-type AggregatorV3, e.g. Midas RWA) rather than an on-chain convertToAssets/exchangeRate() read, the donation/share-inflation primitives don't apply - pivot to oracle staleness + feed cadence. Always grep the price config for oracle: (external addr) vs an on-chain rate call before assuming the 4626 seam is live.
When the SAME PriceProvider uses 1-day staleness for liquid tokens but 7-day for an RWA, the loosened window is the deliberate-but-risky seam. Bank: "compare maxStaleness/heartbeat per collateral; the outlier (longest window, or the dead-code branch) is the lead."
Certora/formal specs routinely SUMMARIZE price() (e.g. cvlPrice.spec returns an arbitrary valid uint) to make proofs tractable. So a formally-"verified" lending protocol can still have an unguarded oracle - the freshness logic is ABSTRACTED AWAY, never proven. When a target ships Certora proofs, check whether the price oracle is a SUMMARY (abstracted) vs verified; if summarized, the oracle-consumer hardening (staleness/decimals/bounds) is un-audited surface worth a manual pass. Related: [[META-collateral-oracle-seam-hunting-playbook-2026-06-11]], [[2026-06-21-cycle267-layerzero-oft-and-crosschain-native-mint-invariants]] (the Chainlink-consumer 4-point checklist).