Algo Ssstablecoinsss

AI First Flight #2
Beginner FriendlyDeFi
EXP
View results
Submission Details
Impact: medium
Likelihood: low
Invalid

Oracle does not validate that the returned price is positive, allowing zero/invalid prices

Root + Impact

Description

oracle_lib is the single trusted source of collateral prices for the entire protocol: every valuation, health-factor computation, and liquidation depends on the price it returns from latestRoundData(). It is therefore responsible for rejecting not just stale data but also invalid data before the price is used downstream.

The specific problem is that _stale_check_latest_round_data validates updated_at, answered_in_round, and the age of the price, but never checks that price > 0. Chainlink's latestRoundData can return a non-positive answer (zero during a faulty/incomplete round, or a negative value for some feed types or misconfigurations). Such a value passes all existing checks and is returned to the engine, where it is used directly in collateral math.

assert updated_at != 0, "DSCEngine_StalePrice"
assert answered_in_round >= round_id, "DSCEngine_StalePrice"
seconds_since: uint256 = block.timestamp - updated_at
assert seconds_since <= TIMEOUT, "DSCEngine_StalePrice"
@> # missing: assert price > 0
return (round_id, price, started_at, updated_at, answered_in_round)

Risk

Likelihood:

  • Occurs whenever the configured feed returns a zero or invalid answer, e.g. a faulty round, a deprecated/retired feed, or an operator incident. No attacker action is required.

Impact:

  • A zero price makes _get_usd_value return 0, so every user holding that collateral instantly appears undercollateralized and can be wrongfully liquidated, losing collateral and the bonus.

  • A zero price makes _get_token_amount_from_usd divide by zero and revert, bricking liquidations for that collateral exactly when they are most needed.

  • A single bad feed reading cascades into wrongful liquidations and/or a frozen liquidation path, threatening solvency.

Proof of Concept

Explanation: Because there is no price > 0 guard, a zero answer flows straight into the valuation formulas. The test below deploys a mock feed reporting price = 0 and shows the staleness check returns it instead of reverting:

import boa
​
def test_zero_price_passes_validation():
# Mock Chainlink feed reporting an INVALID price of 0 (faulty round / deprecated feed)
mock_feed = boa.load("src/mocks/MockV3Aggregator.vy", 8, 0)
oracle = boa.load("src/oracle_lib.vy")
​
# SAFE behavior would be to revert on the invalid price.
# ACTUAL behavior: it returns price == 0 with no revert, because there is no `price > 0` check.
_, price, _, _, _ = oracle.stale_check_latest_round_data(mock_feed.address)
​
assert price == 0 # zero price accepted and propagated to the engine -> BUG

Downstream this means: _get_usd_value(...) == 0 (collateral valued at $0 → wrongful liquidation), and _get_token_amount_from_usd(...) divides by zero → revert (liquidation path bricked).

Recommended Mitigation

Explanation: Add an explicit positivity check alongside the other validations, so any non-positive price reverts at the oracle boundary before it can reach collateral math. This matches Chainlink's guidance to validate the answer and fails safe (freeze) rather than mis-pricing collateral.

assert answered_in_round >= round_id, "DSCEngine_StalePrice"
+ assert price > 0, "DSCEngine_InvalidPrice"
seconds_since: uint256 = block.timestamp - updated_at
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!