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

Value of collateral whose decimal is less than 18 is significantly undervalued because of wrong assumption about collateral token's decimal.

Summary

DecentralizedStableCoin's decimal is 18 as it inherits default ERC20 decimal, but collateral token's decimal might be less than DecentralizedStableCoin's decimal.

For example,

  • WBTC's decimal = 8

  • WETH's decimal = 18

For 1 WBTC (~29091$)

  • getUsdValue(WBTC, 100_000_000) = 2909103464482 * 100_000_000 / 100_000_000 = 0.00029e18

For 1 WETH(~1836$)

  • getUsdValue(WBTC, 10 ** 18) = 183644510000 * 10 ** 18 / 100_000_000 = 1836e18

1 WBTC's USD value is wrongly calculated as 0.00029$

function getTokenAmountFromUsd(address token, uint256 usdAmountInWei) public view returns (uint256) {
// price of ETH (token)
// $/ETH ETH ??
// $2000 / ETH. $1000 = 0.5 ETH
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
(, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
// ($10e18 * 1e18) / ($2000e8 * 1e10)
return (usdAmountInWei * PRECISION) / (uint256(price) * ADDITIONAL_FEED_PRECISION); // @audit - wrong assumption about collateral token decimal
}
function getAccountCollateralValue(address user) public view returns (uint256 totalCollateralValueInUsd) {
// loop through each collateral token, get the amount they have deposited, and map it to
// the price, to get the USD value
for (uint256 i = 0; i < s_collateralTokens.length; i++) {
address token = s_collateralTokens[i];
uint256 amount = s_collateralDeposited[user][token];
totalCollateralValueInUsd += getUsdValue(token, amount);
}
return totalCollateralValueInUsd;
}
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; // @audit - wrong assumption about collateral token decimal
}

Impact

Value of collaterals whose decimal is not 18 might be wrongly calculated.

A user with WBTC collateral might be unfairly liquidated as collateral value is significantly underestimated.

Tool used

Manual Review

Recommendation

  • It should use collateral token's decimal rather than PRECISION to convert token amount into USD amount and vice versa.

  • It should use price feed's decimal rather than PRECISION / ADDITIONAL_FEED_PRECISION for future safety.

+ function safeDecimals(address token) internal pure returns (uint8) {
+ (bool success, bytes memory data) = token.staticcall(abi.encodeWithSelector(SIG_DECIMALS));
+ return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
+ }
function getTokenAmountFromUsd(address token, uint256 usdAmountInWei) public view returns (uint256) {
// price of ETH (token)
// $/ETH ETH ??
// $2000 / ETH. $1000 = 0.5 ETH
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
(, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
// ($10e18 * 1e18) / ($2000e8 * 1e10)
- return (usdAmountInWei * PRECISION) / (uint256(price) * ADDITIONAL_FEED_PRECISION); // @audit - wrong assumption about collateral token decimal
+ return usdAmountInWei * (10 ** (priceFeed.decimals() + safeDecimals(token))) / uint256(price) / PRECISION;
}
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;
+ return uint256(price) * amount * PRECISION / (10 ** (priceFeed.decimals() + safeDecimals(token)));
}

Support

FAQs

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