Algo Ssstablecoinsss

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

oracle_lib never rejects a zero or negative Chainlink price, corrupting valuation and bricking liquidations via division by zero

oracle_lib never rejects a zero or negative Chainlink price, corrupting collateral valuation and bricking liquidations

Description

_stale_check_latest_round_data validates updated_at, answered_in_round, and the heartbeat timeout, but never checks that price > 0. A Chainlink feed returning 0 (or a negative answer) passes every assert and is consumed directly by the valuation math.

# oracle_lib.vy:44-48
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" # @> no `assert price > 0`

A price == 0 then reaches _get_usd_value (collateral worth $0) and _get_token_amount_from_usd, where it divides by price * ADDITIONAL_FEED_PRECISION == 0.

Risk

Likelihood:
Medium. Chainlink aggregators can report 0 during incident conditions / phase transitions, and convert(price, uint256) of a negative int256 wraps to a huge positive number rather than reverting, producing a silently wrong valuation.

Impact:
High. A zero price values all collateral of that type at $0, instantly making every borrower against it liquidatable, while _get_token_amount_from_usd reverts on division-by-zero, so liquidate itself bricks. A wrapped negative price massively overvalues collateral, letting an attacker mint unlimited DSC.

Proof of Concept

Set the mock aggregator answer to 0, then exercise both consumers.

mock_feed.updateAnswer(0)
# collateral now valued at zero -> position liquidatable
assert engine.get_usd_value(token, 1 * 10**18) == 0
# liquidation path reverts on division by zero
engine.liquidate(token, victim, 1 * 10**18) # reverts, no one can liquidate

Recommended Mitigation

Reject non-positive prices in the staleness wrapper.

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

Lead Judging Commences

ai-first-flight-judge Lead Judge about 5 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!