In the integration of Chainlink oracles for price data retrieval, a vulnerability has been identified related to the adjustment of precision. This vulnerability could lead to inaccurate price conversions, potentially resulting in financial losses or incorrect system behavior. The vulnerability arises from the lack of proper adjustment for precision differences between the initial precision received from the Chainlink oracle (8 decimals) and the desired precision for the token (6 decimals).
The vulnerability stems from the code’s assumption regarding the precision received from the Chainlink oracle and its subsequent adjustment. In the provided code snippet, the adjustment factor calculation is based solely on the assumption of an initial precision of 8 decimals, without explicit consideration for the precision difference between 8 and the desired precision of 6 decimals. This oversight may lead to inaccurate precision adjustment, resulting in incorrect price conversions.
When minting fertilizer, fertilizer is gotten out with the amountIn of barn raise token which is used to calculate the min fertilizer and min fertilizer out… To get the usdPrice of barnRaiseToken which is with WETH/STETH using lookback for instantaneous price. The getPrice will not favour tokens WETH/STETH of 18 decimals since the goal of the getPrice of ETHUSD is to return a 6 decimal value but In the case when chain link variable varies they will be loss of precision or loss exact value returned to calculate to get use price of tokens(WETH/STETH)
Exploitation Scenario:
Chainlink Oracle Integration:
The system integrates Chainlink oracles to retrieve price data for token conversion. The oracle response includes
both the price and the decimal precision, with the precision being 8 decimals for the ETH/USD pair to return a
price against the bean stalk token which is 6 decimals.
if (token == C.WETH) {
uint256 ethUsdPrice = LibEthUsdOracle.getEthUsdPrice(lookback);
if (ethUsdPrice == 0) return 0;
return uint256(1e24).div(ethUsdPrice);
}
Affects
function _getMintFertilizerOut(
uint256 tokenAmountIn,
address barnRaiseToken
) public view returns (uint256 fertilizerAmountOut) {
fertilizerAmountOut = tokenAmountIn.div(
LibUsdOracle.getUsdPrice(barnRaiseToken)
);
}
Vulnerability Exploitation:
function getPrice(
address priceAggregatorAddress,
uint256 maxTimeout
) internal view returns (uint256 price) {
IChainlinkAggregator priceAggregator = IChainlinkAggregator(priceAggregatorAddress);
// First, try to get current decimal precision:
uint8 decimals;
try priceAggregator.decimals() returns (uint8 _decimals) {
// If call to Chainlink succeeds, record the current decimal precision
decimals = _decimals;
} catch {
// If call to Chainlink aggregator reverts, return a price of 0 indicating failure
return 0;
}
// Secondly, try to get latest price data:
try priceAggregator.latestRoundData() returns (
uint80 roundId,
int256 answer,
uint256 /* startedAt /,
uint256 timestamp,
uint80 / answeredInRound */
) {
// Check for an invalid roundId that is 0
if (roundId == 0) return 0;
if (checkForInvalidTimestampOrAnswer(timestamp, answer, block.timestamp, maxTimeout)) {
return 0;
}
// Adjust to 6 decimal precision.
return uint256(answer).mul(PRECISION).div(10 ** decimals);
} catch {
// If call to Chainlink aggregator reverts, return a price of 0 indicating failure
return 0;
}
}
The chainlink decimal is 8, and can vary depending on the oracle. We scale it to a constant 6 to make it easier to implement with the bean token (which also has 6 decimals).
The system attempts to adjust the received price to the desired precision of 6 decimals using a fixed adjustment factor. However, the adjustment factor calculation does not directly account for the precision difference between 8 and 6 decimals. As a result, the adjusted price will not accurately reflect the desired precision, leading to potential precision loss or incorrect price conversions
Inaccurate precision adjustment may lead to incorrect price conversions during token swaps or financial transactions. Users relying on the converted prices may experience financial losses or make incorrect decisions
based on inaccurate price data
Manual Review
Dynamically calculate the adjustment factor based on the difference between the initial precision and the desired precision to accurately scale the price.
try priceAggregator.latestRoundData() returns (
uint80 roundId,
int256 answer,
uint256 /* startedAt /,
uint256 timestamp,
uint80 / answeredInRound */
) {
// Check for an invalid roundId that is 0
if (roundId == 0) return 0;
if (checkForInvalidTimestampOrAnswer(timestamp, answer, block.timestamp, maxTimeout)) {
return 0;
}
// Adjust to 6 decimal precision.
uint256 adjustmentFactor = 10 ** (8 - 6); // Adjusting from 8 to 6 decimals
return uint256(answer).mul(PRECISION).div(adjustmentFactor);
} catch {
// If call to Chainlink aggregator reverts, return a price of 0 indicating failure
return 0;
}
A past report https://medium.com/@JohnnyTime/breaking-down-high-severity-decimals-issue-in-codehawks-sta
blecoin-contest-c81c041780ea
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.