Summary
The SmartVault3::euroCollateral() calls the PriceCalculator::tokenToEurAvg() function to get average price of token in Euros. In the function, collateralUsd calls PriceCalculator::avgPrice which does checks for stale price. However in the eurUsdPrice did not check for stale prices. Eventhough, the contract is not specifically stated out of scope, thus reported them.
Furthermore, other functions did not check for stale prices. These functions in PriceCalculator.sol will be used in calculating the token's price in EUROs and USD. I have link them above.
Vulnerability Details
function euroCollateral() private view returns (uint256 euros) {
ITokenManager.Token[] memory acceptedTokens = getTokenManager().getAcceptedTokens();
for (uint256 i = 0; i < acceptedTokens.length; i++) {
ITokenManager.Token memory token = acceptedTokens[i];
euros += calculator.tokenToEurAvg(token, getAssetBalance(token.symbol, token.addr));
}
}
/
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();
return collateralUsd / uint256(eurUsdPrice);
}
Impact
Price oracle could get a stale price without checking roundId.
Tools Used
Manual Review
Recommendations
Check for stale price for eurUsdPrice.
(uint80 roundId, int256 eurUsdPrice, , uint256 updatedAt, uint80 answeredInRound) = clEurUsd.latestRoundData();
require(updatedAt > 0, "Round is not complete");
require(answer >= 0, "Malfunction");
require(answeredInRound >= roundID, "Stale price");