Summary
When converting USD and collateral, the decimal for DSC is fixed at 18, but the collateral decimal is not taken into account, which may not be 18, such as 8 for WBTC.
As a result, there is a huge error in the calculation of getTokenAmountFromUsd
/ getUsdValue
, resulting in funds loss for users.
Vulnerability Details
Taking the getUsdValue
function as an example, The POC below shows that BTC is less valuable than ETH due to decimal issues:
pragma solidity 0.8.19;
import {Test, console} from "forge-std/Test.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract DSCEngine1Test is Test {
address constant btcOracle = 0x1b44F3514812d835EB1BDB0acB33d3fA3351Ee43;
address constant ethOracle = 0x694AA1769357215DE4FAC081bf1f309aDC325306;
uint256 private constant ADDITIONAL_FEED_PRECISION = 1e10;
uint256 private constant PRECISION = 1e18;
function setUp() public {
vm.createSelectFork("https://eth-sepolia.g.alchemy.com/v2/WhC9CgJA77Pt1e59uRYcDXeq4xuK-CKU");
}
function testUSDValue() public view {
assert(getUsdValue(btcOracle, 1e8) < getUsdValue(ethOracle, 1e18));
}
function getUsdValue(address oracle, uint256 amount) public view returns (uint256) {
(, int256 price,,,) = AggregatorV3Interface(oracle).latestRoundData();
return ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION;
}
}
Impact
The decimal of the collateral is not taken into account, which can result in a miscalculation of the result and a huge loss for the user. For example, getUsdValue undervalues WBTC, and users deposit 1WBTC but cannot borrow any DSC.
Tools Used
Foundry
Recommendations
Taken into account the collateral decimals, provide a correct implementation of getUsdValue
:
function getUsdValue(address token, uint256 amount) public view returns (uint256) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(s_priceFeeds[token]);
(, int256 price,,,) = priceFeed.staleCheckLatestRoundData();
return ((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / 10 ** uint256(CERC20(token).decimals());
}