context: DSCEngine.sol
The DSCEngine protocol aims to mint dsc stable coins for depositors based on the provided collateral token value in USD.
However, the protocol has limitations in accepting only two tokens as collateral, namely wETH and wBTC. When a depositor
attempts to provide wBTC as collateral and requests to mint dsc stable coins, the protocol encounters an issue because
wBTC has 8 decimals. As a result, the protocol's functionality fails to mint dsc stable coins, and the transaction is
reverted
The minting of dsc stable coins is determined by the health factor, which is calculated using 50% of the total
collateralized token value in USD (the remaining 50% is over-collateralized) and the total dsc stable coins held by the
user in the protocol.
Here's an example calculation of the health factor for a depositor Alice:
Alice deposits 1 wBTC as collateral and requests to mint 1000 dsc stable coins.
1 wBTC = 30,000 USD.
50% of 30,000 USD = 15,000 USD.
Health Factor = 15,000 USD / 1000 USD = 15.
Despite a health factor of 15, which is greater than the MIN_HEALTH_FACTOR (set to 1), the protocol fails to mint tokens
for Alice due to the vulnerability in the DSCEngine.getUsdValue() function.
The vulnerability lies in the DSCEngine.getUsdValue() function, which incorrectly returns the USD value for wBTC tokens
and tokens with decimals != 18. This erroneous value is used to calculate the health factor. As a result, the
computed health factor is always less than the MIN_HEALTH_FACTOR, leading to the failure of minting stable coins for
wBTC depositors.
Let's consider a user who wants to deposit 1 wBTC (wrapped Bitcoin) and requests to mint 1000 dsc coins (stable coins)
to the protocol.
Protocol Calculations:
uint256 private constant ADDITIONAL_FEED_PRECISION = 1e10;
uint256 private constant PRECISION = 1e18;
((uint256(price) * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION;
*wrapped BTC has 8 decimals
= ( ( 30000 * 1e8 ) * 1e10 * 1e8 ) / 1e18
= 30000 * 1e8
uint256 collateralAdjustedForThreshold = (collateralValueInUsd * LIQUIDATION_THRESHOLD) / LIQUIDATION_PRECISION;
= ( ( 30000 * 1e8 ) * 50 ) / 100
= 15000 * 1e8
return (collateralAdjustedForThreshold * 1e18) / totalDscMinted;
= ( ( 15000 * 1e8 ) * 1e18) / 1000 * 1e8
= 15 * 1e8
uint256 private constant MIN_HEALTH_FACTOR = 1e18;
uint256 userHealthFactor = 15 * 1e8
if (userHealthFactor < MIN_HEALTH_FACTOR) {
revert DSCEngine__BreaksHealthFactor(userHealthFactor);
}
= 15 * 1e8 < 1e18
forge test
function test_depositCollateralAndMintDsc() external {
deal(WBTC_ADDRESS, user, 1e8, true);
vm.startPrank(user);
IERC20(WBTC_ADDRESS).approve(address(dscEngine), 1e8);
vm.expectRevert();
dscEngine.depositCollateralAndMintDsc(WBTC_ADDRESS, 1e8, 10 ether);
vm.stopPrank();
}
output:
DSCEngine::depositCollateralAndMintDsc(StandardToken: [0xD0684a311F47AD7fdFf03951d7b91996Be9326E1], 100000000,
10000000000000000000)
← "DSCEngine__BreaksHealthFactor(145998936000)"
The current implementation of the code prevents wBTC depositors from minting dsc stable tokens by providing wBTC as
collateral. Consequently, the protocol loses wBTC depositors, which can be significant in the crypto market where wETH
and wBTC are crucial equity tokens.
This issue breaks the protocol's functionality.
Moreover, if any protocol forks this code to create a stable coin, the forked protocol will also be unable to accept
tokens with decimals != 18 as collateral.
Manual
Fix this issue by computing the getUsdValue() based on the token decimals, wBTC with 8 decimals will be correctly
multiplied by 1e18 to obtain the value in 18 decimals.
The below recommandation only for wBTC and wETH
//getUsdValue function in DSCEngine.sol
uint256 private constant PRECISION = 1e18;
uint256 private constant ADDITIONAL_FEED_PRECISION = 1e10;
uint256 decimals = IERC20(token_address).decimals();
if (decimals == 8) {
return ((price * ADDITIONAL_FEED_PRECISION) * amount * 1e10) / PRECISION;
} else {
return ((price * ADDITIONAL_FEED_PRECISION) * amount) / PRECISION;
}
By implementing this fix, the protocol can calculate getUsdValue() according to the token decimals.
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.