Normal: The health factor check should work correctly regardless of which collateral token the user deposits. MIN_HEALTH_FACTOR should account for the different decimal precisions of WETH (18 decimals) and WBTC (8 decimals).
Bug: _get_usd_value() returns the USD value of collateral at different precisions depending on the token's decimals — WETH returns 18-decimal precision while WBTC returns 8-decimal precision. MIN_HEALTH_FACTOR = 1 * 10**18 is calibrated for WETH's 18-decimal output. For WBTC, the health factor is ~10^10 times smaller than it should be, causing the _revert_if_health_factor_is_broken check to unfairly reject healthy WBTC positions.
Likelihood:
WBTC is one of only two supported collateral tokens (COLLATERAL_TOKENS: public(immutable(address[2])))
Every WBTC depositor is affected — the precision mismatch is deterministic
The bug activates on every _revert_if_health_factor_is_broken call for WBTC positions
Impact:
WBTC depositors cannot mint DSC even with sufficient collateral — health factor appears ~10^10 times too low
Healthy WBTC positions are incorrectly flagged for liquidation
WBTC as a collateral type is functionally broken — users are forced to use only WETH
Protocol fails its core design goal of supporting multiple collateral types
Trace the precision through the code for a WBTC deposit:
User deposits 1 WBTC = 1 * 10**8 (8 decimals)
Chainlink WBTC price = 60000 * 10**8 (8 decimals, standard)
_get_usd_value calculation:
Result: 60000e8 — USD value with 8-decimal precision
Compare with WETH (1 WETH deposit):
Result: 2000e18 — USD value with 18-decimal precision
Health factor calculation uses usd_value * 10**18 / total_dsc_minted:
WETH with $2000 collateral, 1000 DSC minted: health_factor = 1000e36 / 1000e18 = 1e18 ✅ (passes)
WBTC with $60000 collateral, 1000 DSC minted: health_factor = 30000e26 / 1000e18 = 3e9 ❌ (fails!)
WBTC user has 30× more collateral value but the health factor is ~3×10^8 times smaller — the position is healthy but the protocol rejects it.
Add a precision normalization factor for WBTC in the health factor check. Scale the health factor to a consistent 18-decimal precision for all collateral types:
Or more robustly, fix _get_usd_value to return a consistent 18-decimal precision regardless of the input token's decimals:
## Description The `_revert_if_health_factor_is_broken` function is responsible for ensuring that a user's health factor meets the minimum required standard. There is only implementation for WETH. ## Vulnerability Details In the function, there is only implementation for WETH. ```Solidity @internal def _revert_if_health_factor_is_broken(user: address): user_health_factor: uint256 = self._health_factor(user) assert ( user_health_factor >= MIN_HEALTH_FACTOR ), "DSCEngine__BreaksHealthFactor" ``` Value of the `MIN_HEALTH_FACTOR=10^18`is higher than the Satoshi factor which is 10^8. As a result, for WBTC, the `user_health_factor` can be inflated to more than 101010^{10} times its normal value. ## Impact Bigger value of MIN_HEALTH_FACTOR for WBTC allows on bigger value of `user_health_factor`and wrong value when function should revert. ## Recommendations Add MIN_HEALTH_FACTOR also for WBTC. ```Solidity @internal def _revert_if_health_factor_is_broken(user: address): user_health_factor: uint256 = self._health_factor(user) # Check if the user's token is WBTC and adjust health factor accordingly if user_health_factor >= (MIN_HEALTH_FACTOR * 10**10): # If user health factor is higher due to WBTC precision, still ensure it meets the minimum assert user_health_factor >= MIN_HEALTH_FACTOR, "DSCEngine__BreaksHealthFactor" else: assert user_health_factor >= MIN_HEALTH_FACTOR, "DSCEngine__BreaksHealthFactor" ```
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.