Summary
returned wstethEthPrice
from getWstethEthPrice
will wrong due to precision loss in division
Vulnerability Details
In getWstethEthPrice()
calculates wstETH in term of ETH with 6 decimal precision as mentioned in natspec comment.
wstethEthPrice = chainlinkPrice.add(uniswapPrice).div(AVERAGE_DENOMINATOR);
Here you can see that `chainlinkPrice` is scaled down by 10**6 and `uniswapPrice` is in scaled up by 10**6
and `wstethEthPrice` taking avarage of both of them by dividing these 2 addition by 2 `AVERAGE_DENOMINATOR = 2`
As both parameter to be added are of different decimal then their avarae could lead to huge precision loss and returned wstethEthPrice
will be wrong.
https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/main/protocol/contracts/libraries/Oracle/LibWstethEthOracle.sol#L94
Impact
refer Details section
Tools Used
Manual review
Recommendations
chainlinkPrice parameter should used directly without scaling it down.
https://github.com/Cyfrin/2024-05-beanstalk-the-finale/blob/main/protocol/contracts/libraries/Oracle/LibWstethEthOracle.sol#L78-L96
And at avarage calculation then it will scaled down by 10**6
chainlinkPrice = chainlinkPrice.mul(stethPerWsteth).div(CHAINLINK_DENOMINATOR);
uint256 stethPerWsteth = IWsteth(C.WSTETH).stEthPerToken();
chainlinkPrice = chainlinkPrice.mul(stethPerWsteth).div(CHAINLINK_DENOMINATOR);
if (lookback > type(uint32).max) return 0;
uint256 uniswapPrice = LibUniswapOracle.getTwap(
lookback == 0 ? LibUniswapOracle.FIFTEEN_MINUTES : uint32(lookback),
C.WSTETH_ETH_UNIV3_01_POOL,
C.WSTETH,
C.WETH,
ONE
);
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);