Algo Ssstablecoinsss

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

[H-03] No Validation for Negative or Zero Oracle Prices Allows Manipulation

Root + Impact

Description

  • The oracle_lib._stale_check_latest_round_data() function validates staleness but does not check if the returned price is positive.

  • Chainlink oracles return int256 for price, which could be zero or negative during extreme market conditions or oracle malfunctions.

  • A zero or negative price would cause division by zero errors or allow users to mint unlimited DSC against minimal collateral.

// Root cause in the codebase with @> marks to highlight the relevant section
@internal
@view
def _stale_check_latest_round_data(
price_price_address: address,
) -> (uint80, int256, uint256, uint256, uint80):
# ... staleness checks ...
(
round_id, price, started_at, updated_at, answered_in_round
) = staticcall price_price.latestRoundData()
assert updated_at != 0, "DSCEngine_StalePrice"
assert answered_in_round >= round_id, "DSCEngine_StalePrice"
@> # No check for price > 0!
return (round_id, price, started_at, updated_at, answered_in_round)

Risk

Likelihood: Low

  • Reason 1 // Requires oracle malfunction or extreme market conditions

  • Reason 2 // Chainlink has safeguards but edge cases exist

Impact: Critical

  • Impact 1 // Zero price causes division by zero in _get_token_amount_from_usd

  • Impact 2 // Negative price converts to huge uint256, breaking all calculations

  • Impact 3 // Users could mint unlimited DSC with minimal collateral

  • Impact 4 // Complete protocol insolvency

Proof of Concept

The following demonstrates how a zero or negative oracle price breaks the protocol's core functionality. When the price converts from negative int256 to uint256, it becomes an astronomically large number, allowing users to mint massive amounts of DSC with tiny collateral deposits.

def test_negative_price_breaks_protocol():
# Oracle returns negative price (malfunction scenario)
eth_usd.updateAnswer(-1) # Negative price
with boa.env.prank(user):
weth.approve(dsce, 1) # Minimal collateral
# convert(-1, uint256) = max_value(uint256)
# This makes collateral appear infinitely valuable
# User can mint unlimited DSC
dsce.deposit_collateral_and_mint_dsc(weth, 1, HUGE_AMOUNT)
# Protocol is now insolvent

Recommended Mitigation

Add a check to ensure the oracle price is strictly positive before returning. This protects against oracle malfunctions and ensures all downstream calculations remain valid and bounded.

@internal
@view
def _stale_check_latest_round_data(
price_price_address: address,
) -> (uint80, int256, uint256, uint256, uint80):
price_price: AggregatorV3Interface = AggregatorV3Interface(
price_price_address
)
round_id: uint80 = 0
price: int256 = 0
started_at: uint256 = 0
updated_at: uint256 = 0
answered_in_round: uint80 = 0
(
round_id, price, started_at, updated_at, answered_in_round
) = staticcall price_price.latestRoundData()
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"
return (round_id, price, started_at, updated_at, answered_in_round)
Updates

Lead Judging Commences

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