No check for round completeness in could lead to stale prices and wrong price return value, or outdated price. The functions rely on accurate price feed might not work as expected, sometimes can lead to fund loss.
If there is a problem with chainlink starting a new round and finding consensus on the new value for the oracle (e.g. chainlink nodes abandon the oracle, chain congestion, vulnerability/attacks on the chainlink system) consumers of this contract may continue using outdated stale data (if oracles are unable to submit no new round is started).
This could lead to stale prices and wrong price return value, or outdated price.
As a result, the functions rely on accurate price feed might not work as expected, sometimes can lead to fund loss. The impacts vary and depends on the specific situation, ranging from incorrect liquidations to bad swaps.
Validate data feed for round completeness.
File: contracts/utils/PriceCalculator.sol
function avgPrice(uint8 _hours, Chainlink.AggregatorV3Interface _priceFeed) private view returns (uint256) {
uint256 startPeriod = block.timestamp - _hours * 1 hours;
uint256 roundTS;
uint80 roundId;
int256 answer;
(roundId, answer,, roundTS, answeredInRound) = _priceFeed.latestRoundData();
+ if (answeredInRound < roundId) revert("round not complete");
uint256 accummulatedRoundPrices = uint256(answer);
uint256 roundCount = 1;
while (roundTS > startPeriod && roundId > 1) {
roundId--;
try _priceFeed.getRoundData(roundId) {
(, answer,, roundTS,) = _priceFeed.getRoundData(roundId);
accummulatedRoundPrices += uint256(answer);
roundCount++;
} catch {
continue;
}
}
return accummulatedRoundPrices / roundCount;
}
function tokenToEurAvg(ITokenManager.Token memory _token, uint256 _tokenValue) external view returns (uint256) {
Chainlink.AggregatorV3Interface tokenUsdClFeed = Chainlink.AggregatorV3Interface(_token.clAddr);
uint256 scaledCollateral = _tokenValue * 10 ** getTokenScaleDiff(_token.symbol, _token.addr);
uint256 collateralUsd = scaledCollateral * avgPrice(4, tokenUsdClFeed);
- (, int256 eurUsdPrice,,,) = clEurUsd.latestRoundData();
+ (uint80 roundId, int256 eurUsdPrice,,, uint80 answeredInRound) = clEurUsd.latestRoundData();
+ if (answeredInRound < roundId) revert("round not complete");
return collateralUsd / uint256(eurUsdPrice);
}
function tokenToEur(ITokenManager.Token memory _token, uint256 _tokenValue) external view returns (uint256) {
Chainlink.AggregatorV3Interface tokenUsdClFeed = Chainlink.AggregatorV3Interface(_token.clAddr);
uint256 scaledCollateral = _tokenValue * 10 ** getTokenScaleDiff(_token.symbol, _token.addr);
- (,int256 _tokenUsdPrice,,,) = tokenUsdClFeed.latestRoundData();
+ (uint80 roundIDTU,int256 _tokenUsdPrice,,, uint80 answeredInRoundTU) = tokenUsdClFeed.latestRoundData();
+ if (answeredInRoundTU < roundIdTU) revert("round not complete");
uint256 collateralUsd = scaledCollateral * uint256(_tokenUsdPrice);
- (, int256 eurUsdPrice,,,) = clEurUsd.latestRoundData();
+ (uint80 roundIdEU, int256 eurUsdPrice,,, uint80 answeredInRoundEU) = clEurUsd.latestRoundData();
+ if (answeredInRoundEU < roundIdEU) revert("round not complete");
return collateralUsd / uint256(eurUsdPrice);
}
function eurToToken(ITokenManager.Token memory _token, uint256 _eurValue) external view returns (uint256) {
Chainlink.AggregatorV3Interface tokenUsdClFeed = Chainlink.AggregatorV3Interface(_token.clAddr);
- (, int256 tokenUsdPrice,,,) = tokenUsdClFeed.latestRoundData();
+ (uint80 roundIdTU, int256 tokenUsdPrice,,, uint80 answeredInRoundTU) = tokenUsdClFeed.latestRoundData();
+ if (answeredInRoundTU < roundIdTU) revert("round not complete");
- (, int256 eurUsdPrice,,,) = clEurUsd.latestRoundData();
+ (uint80 roundIdEU, int256 eurUsdPrice,,, uint80 answeredInRoundEU) = clEurUsd.latestRoundData();
+ if (answeredInRoundEU < roundIdEU) revert("round not complete");
return _eurValue * uint256(eurUsdPrice) / uint256(tokenUsdPrice) / 10 ** getTokenScaleDiff(_token.symbol, _token.addr);
}