There is no checks for the time at which Chainlink returned the data. The returned data could be calculated too far behind from the moment that the latestRoundData()
is called.
Since the price of oracle could vary in the time, this could lead to outdated price.
Outdated/Stale data used.
Front runnable issue.
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,) = _priceFeed.latestRoundData();
+ if (roundTS < block.timestamp - THRESHOLD) revert("stale price feed");
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();
+ (, int256 eurUsdPrice,, uint256 updatedAt,) = clEurUsd.latestRoundData();
+ if (updatedAt < block.timestamp - THRESHOLD) revert("stale price feed");
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);
+ uint256 updatedPeriod = block.timestamp - THRESHOLD
- (,int256 _tokenUsdPrice,,,) = tokenUsdClFeed.latestRoundData();
+ (,int256 _tokenUsdPrice,, uint256 updatedAtTU,) = tokenUsdClFeed.latestRoundData();
+ if (updatedAtTU < updatedPeriod) revert("stale price feed");
uint256 collateralUsd = scaledCollateral * uint256(_tokenUsdPrice);
- (, int256 eurUsdPrice,,,) = clEurUsd.latestRoundData();
+ (, int256 eurUsdPrice,, uint256 updatedAtEU,) = clEurUsd.latestRoundData();
+ if (updatedAtEU < updatedPeriod) revert("stale price feed");
return collateralUsd / uint256(eurUsdPrice);
}
function eurToToken(ITokenManager.Token memory _token, uint256 _eurValue) external view returns (uint256) {
Chainlink.AggregatorV3Interface tokenUsdClFeed = Chainlink.AggregatorV3Interface(_token.clAddr);
+ uint256 updatedPeriod = block.timestamp - THRESHOLD
- (, int256 tokenUsdPrice,,,) = tokenUsdClFeed.latestRoundData();
+ (, int256 tokenUsdPrice,, uint256 updatedAtTU,) = tokenUsdClFeed.latestRoundData();
+ if (updatedAtTU < updatedPeriod) revert("stale price feed");
- (, int256 eurUsdPrice,,,) = clEurUsd.latestRoundData();
+ (, int256 eurUsdPrice,, uint256 updatedAtEU,) = clEurUsd.latestRoundData();
+ if (updatedAtEU < updatedPeriod) revert("stale price feed");
return _eurValue * uint256(eurUsdPrice) / uint256(tokenUsdPrice) / 10 ** getTokenScaleDiff(_token.symbol, _token.addr);
}