DeFiHardhat
35,000 USDC
View results
Submission Details
Severity: low
Invalid

Chainlink oracle returns stale price due to 4 days timeout sent in `getWstethEthPrice` to get chainlinkPrice variable price

Summary

The getWstethEthPrice function in the LibWstethEthOracle library calculates the price of wstETH (Wrapped stETH) in terms of ETH (Ethereum). It calculates the price by considering the Chainlink price and Uniswap price, ensuring robustness and accuracy in the price calculation process.

Now getWstethEthPrice calls LibChainlinkOracle.getPrice/LibChainlinkOracle.getTwap with the timeout argument of LibChainlinkOracle.FOUR_DAY_TIMEOUT which is set to 345600 seconds (4 days). This duration is four times longer than the Chainlink heartbeat that is 86400 seconds (1 day), potentially introducing a significant delay in recognizing stale or outdated price data.

Vulnerability Details

See the following code:

function getWstethEthPrice(uint256 lookback) internal view returns (uint256 wstethEthPrice) {
uint256 chainlinkPrice = lookback == 0 ?
LibChainlinkOracle.getPrice(WSTETH_ETH_CHAINLINK_PRICE_AGGREGATOR, LibChainlinkOracle.FOUR_DAY_TIMEOUT) :
LibChainlinkOracle.getTwap(WSTETH_ETH_CHAINLINK_PRICE_AGGREGATOR, LibChainlinkOracle.FOUR_DAY_TIMEOUT, lookback);
// Check if the chainlink price is broken or frozen.
if (chainlinkPrice == 0) return 0;
uint256 stethPerWsteth = IWsteth(C.WSTETH).stEthPerToken();
chainlinkPrice = chainlinkPrice.mul(stethPerWsteth).div(CHAINLINK_DENOMINATOR);
// Uniswap V3 only supports a uint32 lookback.
if (lookback > type(uint32).max) return 0;
uint256 uniswapPrice = LibUniswapOracle.getTwap(
lookback == 0 ? LibUniswapOracle.FIFTEEN_MINUTES :
uint32(lookback),
WSTETH_ETH_UNIV3_01_POOL, C.WSTETH, C.WETH, ONE
);
// Check if the uniswapPrice oracle fails.
if (uniswapPrice == 0) return 0;
if (LibOracleHelpers.getPercentDifference(chainlinkPrice, uniswapPrice) < MAX_DIFFERENCE) {
wstethEthPrice = chainlinkPrice.add(uniswapPrice).div(AVERAGE_DENOMINATOR);
if (wstethEthPrice > stethPerWsteth) wstethEthPrice = stethPerWsteth;
wstethEthPrice = wstethEthPrice.div(PRECISION_DENOMINATOR);
}
}

Impact

The Chainlink heartbeat indicates the expected frequency of updates from the oracle. The Chainlink heartbeat for STETH / ETH is 86400 seconds (1 day).

https://docs.chain.link/data-feeds/price-feeds/addresses?network=ethereum&page=1&search=0x86392dC19c0b719886221c78AB11eb8Cf5c52812

But the defined FOUR_DAY_TIMEOUT in the LibChainlinkOracle is 345600 seconds (4 days).

This timeout which is significantly longer than the heartbeat can lead to scenarios where the LibChainlinkOracle library accepts data that may no longer reflect current market conditions. Also, in volatile markets, a 4-day window leads to accepting outdated prices, increasing the risk of price slippage.

Tools Used

Manual Review

Recommendations

Consider reducing the FOUR_DAY_TIMEOUT from 4 days to 1 day to align more closely with the Chainlink heartbeat on STETH/ETH, enhancing the relevance of the price data.

Updates

Lead Judging Commences

giovannidisiena Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

Chainlink timeout

Support

FAQs

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