Avci - latestRoundData is not checking if returns stale result
Closed this issue · 0 comments
sherlock-admin commented
Avci
medium
latestRoundData is not checking if returns stale result
Summary
On Oracle.sol, the contract uses the latestRoundData, but there is no check for return value round completeness and it will impact the project logic.
Vulnerability Detail
The _setPricesFromPriceFeeds function in the contract Oracle.sol fetches the asset price from a Chainlink aggregator using the latestRoundData function. However, there are no checks on roundID or timeStamp, resulting in stale prices/not completeness rounds. The oracle wrapper calls out to a chainlink oracle receiving the latestRoundData(). It then checks freshness by verifying that the answer is indeed for the last known round. The returned updatedAt timestamp is not checked.
Impact
may oracle return stale price.
Code Snippet
solidity function _setPricesFromPriceFeeds(DataStore dataStore, EventEmitter eventEmitter, address[] memory priceFeedTokens) internal {
for (uint256 i = 0; i < priceFeedTokens.length; i++) {
address token = priceFeedTokens[i];
if (!primaryPrices[token].isEmpty()) {
revert PriceAlreadySet(token, primaryPrices[token].min, primaryPrices[token].max);
}
IPriceFeed priceFeed = getPriceFeed(dataStore, token);
(
/* uint80 roundID */,
int256 _price,
/* uint256 startedAt */,
/* uint256 timestamp */,
/* uint80 answeredInRound */
) = priceFeed.latestRoundData();
uint256 price = SafeCast.toUint256(_price);
uint256 precision = getPriceFeedMultiplier(dataStore, token);
price = price * precision / Precision.FLOAT_PRECISION;
if (price == 0) {
revert EmptyFeedPrice(token);
}
uint256 stablePrice = getStablePrice(dataStore, token);
Price.Props memory priceProps;
if (stablePrice > 0) {
priceProps = Price.Props(
price < stablePrice ? price : stablePrice,
price < stablePrice ? stablePrice : price
);
} else {
priceProps = Price.Props(
price,
price
);
}
primaryPrices[token] = priceProps;
tokensWithPrices.add(token);
emitOraclePriceUpdated(eventEmitter, token, priceProps.min, priceProps.max, true, true);
}
Tool used
Manual Review
Recommendation
Validating Feed Correctly
solidity function _setPricesFromPriceFeeds(DataStore dataStore, EventEmitter eventEmitter, address[] memory priceFeedTokens) internal {
for (uint256 i = 0; i < priceFeedTokens.length; i++) {
address token = priceFeedTokens[i];
if (!primaryPrices[token].isEmpty()) {
revert PriceAlreadySet(token, primaryPrices[token].min, primaryPrices[token].max);
}
IPriceFeed priceFeed = getPriceFeed(dataStore, token);
(
uint80 roundID, int256 price, uint256 timeStamp, unit 80 answeredInRound
) = priceFeed.latestRoundData();
uint256 price = SafeCast.toUint256(_price);
uint256 precision = getPriceFeedMultiplier(dataStore, token);
price = price * precision / Precision.FLOAT_PRECISION;
require(answeredInRound >= roundID, "...");
require(timeStamp != 0, "...");
if (price == 0) {
revert EmptyFeedPrice(token);
}
uint256 stablePrice = getStablePrice(dataStore, token);
Price.Props memory priceProps;
if (stablePrice > 0) {
priceProps = Price.Props(
price < stablePrice ? price : stablePrice,
price < stablePrice ? stablePrice : price
);
} else {
priceProps = Price.Props(
price,
price
);
}
primaryPrices[token] = priceProps;
tokensWithPrices.add(token);
emitOraclePriceUpdated(eventEmitter, token, priceProps.min, priceProps.max, true, true);
}