QuantAMM

QuantAMM
49,600 OP
View results
Submission Details
Severity: low
Invalid

Missing Oracle Price Staleness Checks in Chainlink and MultiHop Oracle

Summary

The ChainlinkOracle and MultiHopOracle contracts lack staleness checks when fetching oracle price data. This omission could allow the use of outdated or stale price data, leading to incorrect weight adjustments and mispricing in the AMM. The issue stems from the _getData() function in both contracts, which retrieves and aggregates data without verifying its freshness.

Vulnerability Details

Affected Contracts and Functions:

  1. ChainlinkOracle.sol:

    • Function: _getData()

    • Code snippet:

      function _getData() internal view override returns (int216, uint40) {
      (, int data, , uint timestamp, ) = priceFeed.latestRoundData();
      require(data > 0, "INVLDDATA");
      data = data * int(10 ** normalizationFactor);
      return (int216(data), uint40(timestamp));
      }
    • Issue: The function fetches price data using latestRoundData() but does not check whether the timestamp is within an acceptable range, allowing stale data to be used.

  2. MultiHopOracle.sol:

    • Function: _getData()

    • Code snippet:

      function _getData() internal view override returns (int216 data, uint40 timestamp) {
      HopConfig memory firstOracle = oracles[0];
      (data, timestamp) = firstOracle.oracle.getData();
      if (firstOracle.invert) {
      data = 10 ** 36 / data;
      }
      uint256 oracleLength = oracles.length;
      for (uint i = 1; i < oracleLength; ) {
      HopConfig memory oracleConfig = oracles[i];
      (int216 oracleRes, uint40 oracleTimestamp) = oracleConfig.oracle.getData();
      if (oracleTimestamp < timestamp) {
      timestamp = oracleTimestamp;
      }
      if (oracleConfig.invert) {
      data = (data * 10 ** 18) / oracleRes;
      } else {
      data = (data * oracleRes) / 10 ** 18;
      }
      unchecked {
      ++i;
      }
      }
      }
    • Issue: The function aggregates data from multiple oracles but does not perform staleness checks on the timestamps, potentially allowing outdated data to propagate.

Additional Context:

  • The lack of staleness checks may expose the protocol to risks associated with outdated price feeds. For example:

    • If Chainlink experiences delays in consensus or system vulnerabilities, the latestRoundData() function may return stale data.

    • If the timestamp is old, the returned price may no longer represent current market conditions.

Impact

  • Incorrect Pricing: Outdated prices can lead to significant mispricing in the AMM.

  • Risk of Exploitation: An attacker could exploit this weakness to manipulate the protocol’s pricing and weights.

  • Protocol Integrity: Using stale data undermines the integrity and reliability of the protocol.

Tools Used

  • Manual code review of the ChainlinkOracle.sol and MultiHopOracle.sol contracts.

  • Chainlink documentation to confirm behavior of latestRoundData().

Recommendations

  1. Implement Staleness Checks:

    • Add a condition to verify that the timestamp returned by latestRoundData() is within a reasonable range (e.g., not older than 1 hour).

    • Example:

      require(block.timestamp - timestamp < MAX_STALENESS_THRESHOLD, "STALE_DATA");
  2. Validate Round IDs:

    • Ensure that the round ID corresponds to the latest completed round to prevent using outdated data.

Updates

Lead Judging Commences

n0kto Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

invalid_chainlink_staled_data_updateAt_roundId_known_issue

LightChaser: ## [Medium-4] Insufficient oracle validation

Support

FAQs

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