15,000 USDC
View results
Submission Details
Severity: medium
Valid

ChainlinkOracle may provide inaccurate asset prices when the underlying assets hits the minimum price.

Summary

A similar incident occurred on the Binance Smart Chain (BSC) with Venus when LUNA experienced a significant decline.

Vulnerability Details

Stablecoin protocols is really tested when the collateral backing the stable coin becomes very volatile specially in negative direction (depreciating in value).

WBTC and WETH being key collateral it is very likely that asset price may drop suddenly during which chainlik oracle may not return the accurate price for below reasons :

OracleLib.sol uses ChainlinkFeedRegistry to fetch the price of the requested tokens

function staleCheckLatestRoundData(AggregatorV3Interface priceFeed)
public
view
returns (uint80, int256, uint256, uint256, uint80)
{
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
priceFeed.latestRoundData();
uint256 secondsSince = block.timestamp - updatedAt;
if (secondsSince > TIMEOUT) revert OracleLib__StalePrice();
return (roundId, answer, startedAt, updatedAt, answeredInRound);
}
  • These Chainlink aggregators incorporate a built-in circuit breaker mechanism designed to activate when the price of an asset deviates beyond a pre-defined price band.

  • Consequently, in scenarios where an asset experiences a substantial decrease in value, like the case of LUNA crash, the oracle's price will persistently report the minimum price instead of the actual asset price.

  • Essentially over-valuing users collateral.

Impact

Liquidators wont be able to liquidate many underwater users despite not having enough collateral to back.

Tools Used

Manual Review

Recommendations

The ChainlinkAdapterOracle needs to validate the received answer by comparing it against the predefined minPrice and maxPrice boundaries, and in case the answer falls outside these bounds, it should revert.

function staleCheckLatestRoundData(AggregatorV3Interface priceFeed)
public
view
returns (uint80, int256, uint256, uint256, uint80)
{
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
priceFeed.latestRoundData();
++++++ if (answer >= maxPrice or answer <= minPrice) revert();
uint256 secondsSince = block.timestamp - updatedAt;
if (secondsSince > TIMEOUT) revert OracleLib__StalePrice();
return (roundId, answer, startedAt, updatedAt, answeredInRound);
}

Support

FAQs

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