Algo Ssstablecoinsss

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

# Staleness check relies on the deprecated `answeredInRound` and omits `round_id`/`started_at` validation

Staleness check relies on the deprecated answeredInRound and omits round_id/started_at validation

Severity: Low · Impact: Low · Likelihood: Low

Description

  • A robust Chainlink freshness check should confirm the round actually started and completed and use non-deprecated fields.

  • oracle_lib validates answered_in_round >= round_id — a field Chainlink has deprecated and which no longer carries meaning on many feeds — while not checking round_id != 0 or started_at != 0.

assert updated_at != 0, "DSCEngine_StalePrice"
@> assert answered_in_round >= round_id, "DSCEngine_StalePrice" # deprecated field
@> # missing: assert round_id != 0 ; assert started_at != 0
seconds_since: uint256 = block.timestamp - updated_at

Risk

Likelihood:

  • Occurs on feeds where answeredInRound is not populated meaningfully, making the assertion a no-op that provides false assurance of freshness.

Impact:

  • Incomplete round validation can let a not-yet-finalized or malformed round through, feeding a bad price into collateral valuation.

Proof of Concept

Save the block below as tests/poc_l4.py inside the cloned repo and run mox test tests/poc_l4.py. A malformed round with round_id == 0 and started_at == 0 (a round that never properly started) is accepted and used, because the check only tests answered_in_round >= round_id (0 >= 0, always true) and never validates round_id or started_at.

import boa
from eth_utils import to_wei
from src import dsc_engine, decentralized_stable_coin
from src.mocks import mock_token, MockV3Aggregator
def test_invalid_round_is_accepted():
dsc = decentralized_stable_coin.deploy()
weth = mock_token.deploy()
other = mock_token.deploy()
eth_usd = MockV3Aggregator.deploy(8, 2_000 * 10**8)
other_usd = MockV3Aggregator.deploy(8, 2_000 * 10**8)
engine = dsc_engine.deploy(
[weth.address, other.address], [eth_usd.address, other_usd.address], dsc
)
dsc.set_minter(engine.address, True)
dsc.transfer_ownership(engine.address)
now = boa.env.evm.patch.timestamp
# Craft a malformed round: round_id = 0 and started_at = 0 (round never started),
# but a fresh updated_at timestamp and positive price.
# updateRoundData(_roundId, _answer, _timestamp(updatedAt), _startedAt)
eth_usd.updateRoundData(0, 2_000 * 10**8, now, 0)
# The staleness check only asserts updated_at != 0, answered_in_round >= round_id
# (0 >= 0, always true), and the 72h window. It never checks round_id != 0 or
# started_at != 0, so this invalid round is accepted and used.
value = engine.get_usd_value(weth.address, to_wei(1, "ether"))
assert value == to_wei(2_000, "ether") # malformed round used as a valid price

Recommended Mitigation

Drop the deprecated answeredInRound comparison and validate the round explicitly.

assert updated_at != 0, "DSCEngine_StalePrice"
- assert answered_in_round >= round_id, "DSCEngine_StalePrice"
+ assert round_id != 0, "DSCEngine_StalePrice"
+ assert started_at != 0, "DSCEngine_StalePrice"
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!