Summary
The SDAIPriceFeed constructor does not call _fetchPricePrimary() to initialize lastGoodPrice, leaving it at zero and causing potential issues on first oracle failure.
Vulnerability Detail
Unlike other price feeds (GNOPriceFeed, WBTCPriceFeed, etc.), SDAIPriceFeed does not call _fetchPricePrimary() in its constructor. This means:
lastGoodPricestarts at 0- If the first
fetchPrice()call encounters an oracle failure,_shutDownAndSwitchToLastGoodPrice()returns 0 - A price of 0 could cause division by zero or allow infinite borrowing
Impact
If oracles fail on the first price fetch after deployment, the system returns a price of 0.
Code Snippet
constructor(address _daiUsdOracleAddress, address _eurUsdOracleAddress, uint256 _daiUsdStalenessThreshold, uint256 _usdEurStalenessThreshold, address _borrowerOperationsAddress, address _sdaiAddress)
MainnetPriceFeedBase(_daiUsdOracleAddress, _daiUsdStalenessThreshold, _borrowerOperationsAddress)
{
eurUsdOracle.aggregator = AggregatorV3Interface(_eurUsdOracleAddress);
eurUsdOracle.stalenessThreshold = _usdEurStalenessThreshold;
eurUsdOracle.decimals = eurUsdOracle.aggregator.decimals();
sdai = IERC4626(_sdaiAddress);
// @audit - missing: _fetchPricePrimary();
// @audit - missing: assert(priceSource == PriceSource.primary);
}Compare to GNOPriceFeed: https://github.com/sherlock-audit/2025-12-evro-finance-dec-26th/blob/main/evro/contracts/src/PriceFeeds/GNOPriceFeed.sol#L17-L18
_fetchPricePrimary();
assert(priceSource == PriceSource.primary);Tool Used
Manual Review
Recommendation
Add initialization in the constructor:
constructor(...) MainnetPriceFeedBase(...) {
eurUsdOracle.aggregator = AggregatorV3Interface(_eurUsdOracleAddress);
eurUsdOracle.stalenessThreshold = _usdEurStalenessThreshold;
eurUsdOracle.decimals = eurUsdOracle.aggregator.decimals();
sdai = IERC4626(_sdaiAddress);
_fetchPricePrimary();
assert(priceSource == PriceSource.primary);
}