Decimals of the data in the function latestRoundData
Description
The number of decimals in different price feeds may vary. The function getOraclePrice
can retrieve the latest price from a price feed, and the function getOracleDecimals
can retrieve the number of decimals used in the corresponding price feed.
Because Balancer math works with price data with 18 decimals, the decimals of the price data need to be converted from the corresponding price-feed decimals to 18 decimals. But in the current implementation, the price data undergoes decimals conversion based on the BalancerOracleAdapter's state variable decimals
. This may result in incorrect prices being used in the calculation of the pool's fair price and could potentially lead to prices becoming zero due to precision loss.
function latestRoundData()
external
view
returns (uint80, int256, uint256, uint256, uint80){
// [...]
for(uint8 i = 0; i < tokens.length; i++) {
! prices[i] = getOraclePrice(address(tokens[i]), ETH).toBaseUnit(decimals); // balancer math works with 18 dec
}
// [...]
}
function toBaseUnit(uint256 amount, uint8 decimals) internal pure returns (uint256) {
return amount / (10 ** decimals);
}
Since BalancerOracleAdapter inherits from AggregatorV3Interface, the decimals in the return price of the function latestRoundData
should be consistent with the state variable decimals
. Thus, the decimals of fairUintUSDPrice
need to be converted to align with the state variable decimals
.
function latestRoundData()
external
view
returns (uint80, int256, uint256, uint256, uint80){
// [...]
uint256 fairUintETHPrice = _calculateFairUintPrice(prices, weights, pool.getInvariant(), pool.getActualSupply());
! uint256 fairUintUSDPrice = fairUintETHPrice.mulDown(getOraclePrice(ETH, USD));
if (fairUintUSDPrice > uint256(type(int256).max)) {
revert PriceTooLargeForIntConversion();
}
return (uint80(0), int256(fairUintUSDPrice), block.timestamp, block.timestamp, uint80(0));
}
Impact
The function latestRoundData
may return an incorrect price.
Recommendations
Consider converting the price decimals involved in the calculation of the pool's fair price from the corresponding price-feed decimals to 18 decimals.
Consider converting the decimals of fairUintUSDPrice
to match the decimals
defined in the BalancerOracleAdapter.
Remediation
This issue has been acknowledged by Plaza Finance, and fixes were implemented in the following commits: