15,000 USDC
View results
Submission Details
Severity: high
Valid

`DSCEngine` miscalculates the USD value of tokens with other than 18 decimals.

Summary

The DSCEngine miscalculates the USD value of tokens with other than 18 decimals.

Note: I have submitted this issue once before, but it did not appear in my list of reports. If you see duplicates of this, please ignore them.

Vulnerability Details

The DSCEngine contract calculates the USD value of the underlying collateral in getUsdValue:

function getUsdValue(address token, uint256 amount) public view returns (uint256) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
(, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
// 1 ETH = $1000
// The returned value from CL will be 1000 * 1e8
return ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION;
}

As PRECISION is hardcoded to 1e18, this method assumes that the token has 18 decimals. If it does not however, as is the case with WBTC with 8 decimals which this system is explicity supposed to work with, the method will return the wrong USD value:

  • When the token has less than 18 decimals, the returned value is too low

  • When the token has more than 18 decimals, the returned value is too high

Proof of Concept

Assume a token TOKEN has 8 decimals and 1 TOKEN = $1000. Chainlink oracles always return their response with 8 decimals (for non-ETH pairs). Thus the calculation for 1 TOKEN would be:

((1000e8 * 1e10) * 1e8) / 1e18 = 100000000000 = 1e11

which is significantly below the intended result of 1000e18 = 1e21.

On the other hand, assuming TOKEN has 20 decimals, the calculation would be:

((1000e20 * 1e10) * 1e8) / 1e18 = 1e23

which in this case is significantly above the intended result of 1000e18 = 1e21.

Impact

When the decimals are less than 18, the contract calculates the value of the collateral as way too low, meaning users cannot mint the appropriate amount of DSC.

When the decimals are above 18, the contract calculates the value of the collateral as way too high, meaning users can mint too much DSC, which could lead to a depeg and loss of funds through selling of the cheaply accquired DSC on secondary markets.

Severity

I have selected High for this issue since it affects the explicit use-case of WETH/WBTC as collateral (where it would brick the contract to some degree), and since it has the potential for loss of funds for other collateral tokens with more than 18 decimals.

Tools Used

None

Recommendations

Adapt PRECISION based on the decimals of the given token:

PRECISION = 10**(token.decimals())

This should be done in both getUsdValue and getTokenAmountFromUsd.

Support

FAQs

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