The Standard

The Standard
DeFiHardhat
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

Chainlink's `latestRoundData` may return a stale or incorrect result

Summary

Chainlink LatestRoundData can return stale price due to insufficient checks. As result protocol can make decisions based on not up to date prices, which can cause loses.

Vulnerability Details

function distributeAssets(ILiquidationPoolManager.Asset[] memory _assets, uint256 _collateralRate, uint256 _hundredPC) external payable {
consolidatePendingStakes();
(,int256 priceEurUsd,,,) = Chainlink.AggregatorV3Interface(eurUsd).latestRoundData(); //@audit chainlink issues, min/max, stale, sequencer
uint256 stakeTotal = getStakeTotal();
uint256 burnEuros;
uint256 nativePurchased;
for (uint256 j = 0; j < holders.length; j++) {
Position memory _position = positions[holders[j]];
uint256 _positionStake = stake(_position);
if (_positionStake > 0) {
for (uint256 i = 0; i < _assets.length; i++) {
ILiquidationPoolManager.Asset memory asset = _assets[i];
if (asset.amount > 0) {
(,int256 assetPriceUsd,,,) = Chainlink.AggregatorV3Interface(asset.token.clAddr).latestRoundData();
uint256 _portion = asset.amount * _positionStake / stakeTotal;
//Rest of the function

Chainlink classifies their data feeds into five different groups regarding how reliable is each source thus, how risky they are. The groups are Verified Feeds, Monitored Feeds, Provisional feeds , Custom Feeds and Specialized Feeds (they can be seen here). The risk is the lowest on the first one and highest on the last one.

A strong reliance on the price feeds has to be also monitored as recommended on the Risk Mitigation section. There are several reasons why a data feed may fail such as unforeseen market events, volatile market conditions, degraded performance of infrastructure, chains, or networks, upstream data providers outage, malicious activities from third parties among others.

Chainlink recommends using their data feeds along with some controls to prevent mismatches with the retrieved data. Along some recommendations, the feed can include circuit breakers (for extreme price events), contract update delays (to ensure that the injected data into the protocol is fresh enough), manual kill-switches (to cease connection in case of found bug or vulnerability in an upstream contract), monitoring (control the deviation of the data) and soak testing (of the price feeds).

The lastRoundData() interface parameters according to Chainlink are the following:.

function latestRoundData() external view
returns (
uint80 roundId, // The round ID.
int256 answer, // The price.
uint256 startedAt, // Timestamp of when the round started.
uint256 updatedAt, // Timestamp of when the round was updated.
uint80 answeredInRound // The round ID of the round in which the answer was computed.
)

Regarding The Standard itself, only the answer is used on thedistributeAssetsimplementation. The retrieved price of the priceFeed can be outdated and used anyways as a valid data because no timestamp tolerance of the update source time is checked while storing the return parameters of atestRoundData() inside distributeAssets as recommended by Chainlink in here. The usage of outdated data can have high impact on the protocol and the users

Impact

Protocol can make decisions based on not up to date prices, which can cause loses.

Tools Used

Manual Review

Recommendations

add checks on the return value of latestRoundData to ensure that the price is upto date

Updates

Lead Judging Commences

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Chainlink-price

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

Chainlink-price

Support

FAQs

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