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

Hardcoded timeout for considering price as the stale may block users using the protocol for some tokens used as deposit

Summary

In case of price is not updated for a long time, the protocol may deny service to the user as the price is considered stale when not updated for 3 hours.

Vulnerability Details

OracleLib expects that the value of updatedAt returned by latestRoundData should be in the range of the block.timestamp and block.timestamp - 3 hours. The issue here is that if the price was not updated in the last 3 hours, the protocol would mark the price as stale and therefore revert the execution, even though the price may still be valid.
There are two parameters that must trigger price updates:

  • deviation threshold - the percentage change in an asset price

  • heartbeat period - the time period after which the price must be updated

These two triggers are the ones that should be considered as the ones that must update the price. In case there is no big change in the price, for example, when the market is calm, there is no guarantee the price will be updated. Also, the hardcoded value of 3 hours may be enough for most of the tokens which have a heartbeat of 1 hour, but there are some that have a heartbeat period of 1 day (for example, USDC / USD pair and DAI / USD pair). In the case of these pairs, the price would be considered stale after 3 hours, even though the price is still valid, therefore the users would not be able to use the protocol until the price is updated. Even then, they will have a guaranteed 3 hours where they can use protocol, but the rest of the time window between 3 and 24 hours may be unavailable for them.

Impact

This vulnerability can be used to deny service to the users, as the protocol would not allow them to use it in case the price is not updated for 3 hours. It may be specific only for some feeds, like the ones mentioned above, but beware of the market share of these tokens, as they are considered as stablecoins used widely in DeFi as collateral.

Tools Used

Manual review.

Recommendations

The check of the period elapsed between block.timestamp and updatedAt should be left in the code, as it does act as additional protection for stale prices, but the value of 3 hours should be reconsidered and changed based on the token which price is checked. Based on the above mentioned, in the case of USDC and DAI, the value should be => 24 hours. The following may be an example of the code that should be used:

// Instead of using `TIMEOUT` as constant, but to set it up per deposit token.
function staleCheckLatestRoundData(address depositToken, AggregatorV3Interface priceFeed)
public
view
returns (uint80, int256, uint256, uint256, uint80)
{
uint256 timeout = _timeouts[depositToken];
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
priceFeed.latestRoundData();
if (timeout > 0) {
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.