15,000 USDC
View results
Submission Details
Severity: medium
Valid

Hardcoded Timeout Value in OracleLib.sol Leads to Inaccurate Timeout Settings for Different Price Feeds

Summary

The hardcoded TIMEOUT value in the OracleLib.sol contract may result in the system timing out constantly between heartbeats for price feeds with longer heartbeat durations. This approach is considered bad practice.
Can find the Heartbeat duration of Chainlink Data Feeds Here

Vulnerability Details

The getTimeOut function in the OracleLib.sol contract uses a hardcoded TIMEOUT value. This can be problematic, especially for price feeds with longer heartbeat durations, as the system may timeout consistently between heartbeats. Using the same TIMEOUT value for all price feeds, regardless of their individual heartbeat durations, is not optimal or safe.

function getTimeout(AggregatorV3Interface /* chainlinkFeed */ ) public pure returns (uint256) {
return TIMEOUT;
}

Impact

The impact of this issue could lead to constant system timeouts between heartbeats for price feeds that have longer heartbeat durations. This may result in data fetching failures or unnecessary system delays.

Tools Used

The issue was identified through manual code review.

Recommendations

Consider refactoring the getSepoliaEthConfig and getOrCreateAnvilEthConfig functions to include the heartbeat duration for each price feed:

function getSepoliaEthConfig() public view returns (NetworkConfig memory) {
return NetworkConfig({
wethUsdPriceFeed: 0x694AA1769357215DE4FAC081bf1f309aDC325306,
wbtcUsdPriceFeed: 0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43,
weth: 0xdd13E55209Fd76AfE204dBda4007C227904f0a81,
wbtc: 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063,
deployerKey: vm.envUint("PRIVATE_KEY"),
heartbeatWethUsd: /* heartbeat value for WETH/USD */,
heartbeatBtcUsd: /* heartbeat value for BTC/USD */
});
}
function getOrCreateAnvilEthConfig() public view returns (NetworkConfig memory) {
return NetworkConfig({
wethUsdPriceFeed: address(ethUsdPriceFeed),
wbtcUsdPriceFeed: address(btcUsdPriceFeed),
weth: address(wethMock),
wbtc: address(wbtcMock),
deployerKey: DEFAULT_ANVIL_KEY,
heartbeatWethUsd: /* heartbeat value for WETH/USD */,
heartbeatBtcUsd: /* heartbeat value for BTC/USD */
});
}

Refactor the constructor to accept an array of heartbeat values, and store them in a new mapping:

constructor(address[] memory tokenAddresses, address[] memory priceFeedAddresses, uint256[] memory heartbeats, address dscAddress) {
// USD Price Feeds
if (tokenAddresses.length != priceFeedAddresses.length || tokenAddresses.length != heartbeats.length) {
revert DSCEngine__TokenAddressesAndPriceFeedAddressesMustBeSameLength();
}
// For example ETH / USD, BTC / USD, MKR / USD, etc
for (uint256 i = 0; i < tokenAddresses.length; i++) {
s_priceFeeds[tokenAddresses[i]] = priceFeedAddresses[i];
s_heartbeats[tokenAddresses[i]] = heartbeats[i];
s_collateralTokens.push(tokenAddresses[i]);
}
i_dsc = DecentralizedStableCoin(dscAddress);
}

Finally, in OracleLib.sol, import NetworkConfig and use the specific timeout for each price feed in the staleCheckLatestRoundData and getTimeout functions:

function staleCheckLatestRoundData(AggregatorV3Interface priceFeed)
public
view
returns (uint80, int256, uint256, uint256, uint80)
{
(uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) =
priceFeed.latestRoundData();
uint256 secondsSince = block.timestamp - updatedAt;
if (secondsSince > getTimeout) revert OracleLib__StalePrice();
return (roundId, answer, startedAt, updatedAt, answeredInRound);
}
function getTimeout(AggregatorV3Interface priceFeed) public view returns (uint256) {
return NetworkConfig(priceFeed).heartbeat;
}

This refactoring allows the system to use the appropriate heartbeat duration for each price feed, preventing constant timeouts and improving system efficiency.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.