DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

Incorrect Price Scaling in KeeperProxy::_check Function

Summary

The _check function is responsible for verifying price accuracy by comparing a given price against Chainlink's latest price feed data. However, the function incorrectly scales down the price value. The current implementation attempts to adjust the price for different token decimal formats, but it miscalculates the scale factor, which can lead to incorrect price validation.

Vulnerability Details

The function attempts to scale a token's price from 30 decimals to 8 decimals using the formula:

uint256 decimals = 30 - IERC20Meta(token).decimals();
price = price / 10 ** (decimals - 8); // Chainlink price decimals is always 8.

If a token has 18 decimals, the calculation becomes

uint256 decimals = 30 - 18; // 12
price = price / 10 ** (12 - 8); // 10^4, reducing price by a factor of 10,000

This results in an incorrectly scaled-down price that is 10,000 times smaller than intended. Instead of scaling from 30 to 8 decimals, it scales incorrectly to 26 decimals.

Impact

The function compares an incorrectly scaled price against Chainlink's 8-decimal price, leading to false deviations.

The contract rejects valid transactions, causing unexpected failures.

Tools Used

Manual Review

Recommendations

Modify the scaling formula to always ensure conversion from 30 decimals to 8 decimals by dividing the price by 10^(30 - 8 = 22).

function _check(address token, uint256 price) internal view {
(, int chainLinkPrice, , uint256 updatedAt, ) = AggregatorV2V3Interface(dataFeed[token]).latestRoundData();
require(updatedAt > block.timestamp - maxTimeWindow[token], "stale price feed");
uint256 decimals = IERC20Meta(token).decimals();
// Correct scaling: Convert from 30 decimals to 8 decimals
price = price / 10 ** (30 - 8); // Always 10^22
require(
_absDiff(price, chainLinkPrice.toUint256()) * BPS / chainLinkPrice.toUint256() < priceDiffThreshold[token],
"price offset too big"
);
}
Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

invalid_prices_decimals

GMX github documentation: “Prices stored within the Oracle contract represent the price of one unit of the token using a value with 30 decimals of precision. Representing the prices in this way allows for conversions between token amounts and fiat values to be simplified, e.g. to calculate the fiat value of a given number of tokens the calculation would just be: token amount * oracle price, to calculate the token amount for a fiat value it would be: fiat value / oracle price.” Sponsor confirmed the keeper does the same, so price decimals change in function of the token, to be sure the above rule is true. Example for USDC (6 decimals): Prices will have 24 decimals → 1e6 * 1e24 = 1e30. Just a reminder for some submissions: shortToken == collateralTokens, so the decimals is 1e24 for shortToken prices.

Support

FAQs

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

Give us feedback!