Skip to content

Latest commit

 

History

History
56 lines (35 loc) · 2.39 KB

028.md

File metadata and controls

56 lines (35 loc) · 2.39 KB

Best Ceramic Yak

Medium

Price Validation Missing in Chainlink Data Feeds for MasterPriceOracle on Optimism

Summary

The missing validation checks for the return values of AggregatorV3Interface::latestRoundData in MasterPriceOracle.sol:83 may allow stale or invalid pricing data to be used. This can lead to inaccurate price feeds on Optimism, exposing users to potential under-collateralization, unnecessary liquidations, or over-collateralization risks.

Root Cause

In MasterPriceOracle.sol:83, there is no validation of the return values of latestRoundData, leaving the protocol vulnerable to using stale or invalid price data. Essential fields such as roundId, updatedAt, and price are not verified.

(, int256 price_, , , ) = oracle.latestRoundData();

Internal pre-conditions

  1. The oracles[underlying] mapping must point to a valid Chainlink AggregatorV3Interface.
  2. An admin initializes the MasterPriceOracle with valid underlyings and _oracles.

External pre-conditions

  1. The Chainlink data feed must provide stale, outdated, or invalid price data (e.g., price = 0 or updatedAt significantly in the past).
  2. A user or protocol interacts with the price function to fetch a price based on the faulty feed.

Attack Path

  1. A Chainlink oracle provides a stale price due to a lack of sequencer uptime or timeout checks.
  2. The MasterPriceOracle::_price function retrieves this stale price using latestRoundData.
  3. The invalid price is used in downstream calculations, causing inaccurate price-related operations such as borrowing.

Impact

The affected users or protocol components may experience:

  • Incorrect borrow liquidation based on stale or invalid prices.
  • deposit and withdraw usda

PoC

No response

Mitigation

To mitigate this issue, implement robust validation checks for the latestRoundData return values. A recommended implementation is:

(uint80 roundId, int256 price_, , uint256 updatedAt, ) = oracle.latestRoundData();
if (roundId == 0) revert("InvalidRoundId");
if (price_ <= 0) revert("InvalidPrice");
if (updatedAt == 0 || updatedAt > block.timestamp) revert("InvalidUpdate");
if (block.timestamp - updatedAt > TIMEOUT) revert("StalePrice");