Algo Ssstablecoinsss

AI First Flight #2
Beginner FriendlyDeFi
EXP
View results
Submission Details
Severity: medium
Valid

Fixed 72-Hour Oracle Timeout Creates Incorrect Staleness Assumptions Across Collateral Assets

Root + Impact

Impact

Medium

  • Stale oracle prices may remain trusted longer than intended

  • Valid oracle responses may incorrectly revert

  • Collateral valuation can become inaccurate

  • Liquidation timing may become unreliable


Likelihood

Medium–High

  • Different Chainlink feeds naturally use different heartbeat intervals

  • The issue occurs during normal operation

  • No attacker permissions or special setup required

Description

The protocol validates oracle freshness using a single globally fixed timeout:

TIMEOUT = 72 * 3600

Whenever price data is requested, the protocol checks:

seconds_since <= TIMEOUT

If the timeout is exceeded, the transaction reverts and the price is considered stale.

The issue is that all collateral assets share the same timeout assumption, despite different oracle feeds having different heartbeat/update expectations.

Some Chainlink feeds update more frequently than others depending on:

  • asset volatility

  • liquidity profile

  • feed configuration

  • heartbeat settings

Using one hardcoded timeout for every asset introduces inconsistent oracle validation behavior.


Vulnerability Details

Current implementation:

TIMEOUT = 72 hours

applies equally to:

WETH/USD
WBTC/USD
future collateral assets

However, oracle feeds are not standardized to a single update cadence.

As a result:

Scenario A — Stale prices trusted too long

A feed intended to update frequently experiences delayed updates.

The protocol still accepts pricing data for up to 72 hours, even though the feed may already be economically stale.

During volatile markets:

collateral value != market reality

This can distort:

  • health factor calculations

  • liquidation eligibility

  • minting limits


Scenario B — Valid prices incorrectly rejected

A slower-updating collateral feed behaves according to its intended heartbeat.

However, if its normal update cadence exceeds protocol expectations, the system unnecessarily reverts:

DSCEngine_StalePrice

This can interrupt:

  • deposits

  • minting

  • liquidation

  • redemption

even when the oracle behaves correctly.

Risk

Likelihood

  • Different Chainlink feeds inherently have different heartbeat configurations

  • The protocol explicitly supports replacing collateral assets

  • Forked deployments increase the probability of incompatible timeout assumptions

Impact

  • Stale pricing may remain trusted

  • Correct oracle updates may be rejected

  • Liquidation timing becomes unreliable

  • Protocol risk assumptions degrade

Proof of Concept

Assume the protocol is forked to support multiple collateral assets with different Chainlink feed configurations.

Asset A updates every hour.

Asset B updates every 24 hours.

Both assets are forced into:

72-hour timeout validation

Case 1 — High-volatility asset

Asset A experiences rapid price movement, but oracle updates stop.

The protocol continues accepting the last known price for up to 72 hours.

During this period:

  1. User deposits collateral

  2. Asset price drops significantly

  3. Health factor still uses stale valuation

  4. User remains solvent longer than intended

  5. Liquidation occurs too late


Case 2 — Different feed cadence

Asset B follows a slower heartbeat design.

The protocol incorrectly treats expected feed timing as stale:

DSCEngine_StalePrice

This unnecessarily blocks protocol functionality despite valid oracle behavior.

Recommended Mitigation

Replace the global timeout constant with per-feed configurable heartbeat validation.

Instead of:

TIMEOUT = 72 hours

store timeout values per collateral asset:

feed_timeout[token]

and validate each oracle using its expected heartbeat configuration.

Example approach:

  • WETH/USD → shorter timeout

  • WBTC/USD → asset-specific timeout

  • future collateral → configurable at deployment

Additionally:

  • validate feed heartbeat during deployment

  • emit events when stale thresholds are exceeded

  • include tests for mixed-feed timing behavior

This ensures oracle freshness assumptions match actual feed design rather than relying on a universal constant.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 2 days ago
Submission Judgement Published
Validated
Assigned finding tags:

[M-01] The TIMEOUT is set as a fixed constant of 72 hours, which makes it inflexible in adapting to the market price.

## Description In this contract, the TIMEOUT is set as a fixed constant (72 hours, or 259200 seconds). This means that if the oracle price data is not updated within 72 hours, the data will be considered outdated, and the contract will trigger a revert. ## Vulnerability Details At this location in the code, <https://github.com/Cyfrin/2024-12-algo-ssstablecoinsss/blob/4cc3197b13f1db728fd6509cc1dcbfd7a2360179/src/oracle_lib.vy#L15> ```Solidity TIMEOUT: constant(uint256) = 72 * 3600 ``` the timeout is directly set to 72 hours. For an oracle, which cannot dynamically adjust the price updates, this is a suboptimal approach. ## Impact - Fixed Timeout: The TIMEOUT is hardcoded to 72 hours. In markets with frequent fluctuations or assets that require more frequent price updates, 72 hours might be too long. Conversely, if the timeout is too short, it could cause frequent errors due to the inability to update data in time, disrupting normal contract operations. - Non-adjustable Timeout: If the contract's requirements change (e.g., market conditions evolve or the protocol requires more flexibility), the fixed TIMEOUT cannot be dynamically adjusted, leading to potential mismatches with current needs. - Lack of Flexibility: The current timeout mechanism is static and cannot be adjusted based on market volatility or the frequency of oracle updates. In volatile markets, a shorter TIMEOUT might be necessary, while in stable markets, a longer timeout would be more appropriate. \##Tools Used Manual review ## Recommendations Introduce a dynamic price expiration mechanism that adjusts based on market conditions. Use volatility data (such as standard deviation or market price fluctuation) to dynamically adjust the timeout period. This can be achieved by monitoring market volatility and adjusting the TIMEOUT accordingly: ```Solidity # Monitor market volatility and dynamically adjust TIMEOUT @external def adjustTimeoutBasedOnVolatility(volatility: uint256): if volatility > HIGH_VOLATILITY_THRESHOLD: self.TIMEOUT = SHORTER_TIMEOUT # In high volatility, decrease TIMEOUT else: self.TIMEOUT = LONGER_TIMEOUT # In stable market, increase TIMEOUT log TimeoutAdjusted(self.TIMEOUT) ```

Support

FAQs

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

Give us feedback!