oracle_lib is the protocol's single line of defence against bad Chainlink data — its whole stated purpose is "used to check the Chainlink Oracle for stale data ... If a price is stale, functions will revert." It validates staleness but never validates that the returned price is positive:
latestRoundData() returns price as an int256. Chainlink aggregators can, under fault conditions (a broken feed, a mis-configured aggregator, or a min/max-answer floor being hit), return price == 0. There is no assert price > 0 here, so a zero answer flows straight into the engine.
Downstream in dsc_engine.vy, both consumers convert the price to uint256 and use it directly:
With price == 0:
convert(0, uint256) is 0 (no revert), so _get_usd_value returns 0 for every collateral token. Every user's _get_account_collateral_value becomes 0, their health factor collapses to 0, and liquidate's starting_user_health_factor < MIN_HEALTH_FACTOR gate passes for everyone — perfectly healthy positions become liquidatable at a zero valuation, letting a liquidator seize collateral for near-nothing.
_get_token_amount_from_usd divides by (0 * 1e10) == 0 and reverts, so redemptions and liquidations that route through it are simultaneously bricked.
(A negative price is caught only incidentally: Vyper 0.4.0's convert(price, uint256) reverts on a negative int256, which is itself an uncontrolled revert / DoS rather than a clean handled error.)
The one guard the library exists to provide — rejecting an invalid price — is missing for the most basic invalid value.
Impact: Medium. A zero feed answer is silently accepted as a real price. It either values all collateral at zero (enabling unjust liquidation of healthy users and mispriced seizes) or reverts core flows, depending on which path is hit. The library's stated safety guarantee does not hold.
Likelihood: Low. Requires the Chainlink feed to actually return a non-positive answer, which is uncommon but is exactly the fault condition this library was written to defend against.
Expected: a zero/negative price is rejected and the engine freezes safely (the intended "revert on bad data" behaviour). Actual: a zero price is treated as valid and prices all collateral at zero.
Add a positive-price assertion in _stale_check_latest_round_data, and prefer the feed heartbeat over the deprecated answeredInRound field:
With price > 0 enforced, a zero (or negative) answer reverts as the library intends, freezing the engine instead of valuing collateral at zero.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.