Platform: Code4rena
Start Date: 26/08/2021
Pot Size: $200,000 USDC
Total HM: 17
Participants: 11
Period: 14 days
Judge: ghoulsol
Total Solo HM: 12
Id: 23
League: ETH
Rank: 10/11
Findings: 2
Award: $544.88
🌟 Selected for report: 0
🚀 Solo Findings: 0
136.2212 NOTE - $136.22
408.6635 USDC - $408.66
defsec
The latestRoundData function in the contract ExchangeRate.sol fetches the asset price from a Chainlink aggregator using the latestRoundData function. However, there are no checks on roundID nor timeStamp, resulting in stale prices. Stale prices could put funds at risk. Freshness of the returned price should be checked, since it affects an account's health (and therefore liquidations). Stale prices that do not reflect the current market price anymore could be used which would influence the liquidation pricing.
} else { address rateOracle = address(bytes20(data << 96)); // prettier-ignore ( /* uint80 */, rate, /* uint256 */, /* uint256 */, /* uint80 */ ) = AggregatorV2V3Interface(rateOracle).latestRoundData(); require(rate > 0, "ExchangeRate: invalid rate"); uint8 rateDecimalPlaces = uint8(bytes1(data << 88)); rateDecimals = int256(10**rateDecimalPlaces); if ( bytes1(data << 80) != Constants.BOOL_FALSE /* mustInvert */ ) { rate = rateDecimals.mul(rateDecimals).div(rate); } }
Consider to add checks on the return data with proper revert messages if the price is stale or the round is incomplete, for example:
(uint80 roundID, int256 price, , uint256 timeStamp, uint80 answeredInRound) = ETH_CHAINLINK.latestRoundData(); require(price > 0, "Chainlink price <= 0"); require(answeredInRound >= roundID, "..."); require(timeStamp != 0, "...");
#0 - jeffywu
2021-09-11T13:56:28Z
Duplicate of #92