MIN_HEALTH_FACTOR Is Calibrated Only for WETH — WBTC Positions Always Breach It
Description
_revert_if_health_factor_is_broken compares the computed health factor against MIN_HEALTH_FACTOR = 10**18. This constant was designed for WETH (18 decimals),
where the internal accounting naturally produces health factor values around 1e18. WBTC has 8 decimals (satoshi units). Because _get_usd_value does not
normalize token amounts to 18 decimals before dividing by PRECISION = 1e18, the health factor for any WBTC position is 1e10 times smaller than the equivalent
WETH position — permanently below MIN_HEALTH_FACTOR for any meaningful DSC balance.
@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 # 1e18 — only valid for 18-decimal tokens
), "DSCEngine__BreaksHealthFactor"
Impact
WBTC depositors cannot mint any meaningful amount of DSC. The maximum mintable DSC for 1 WBTC at $60,000 is 0.000003 DSC (3e12 wei). Any larger mint reverts in
_revert_if_health_factor_is_broken. WBTC is effectively non-functional as collateral despite being a supported token, rendering half the protocol's collateral
base unusable.
Proof of Concept
price (Chainlink BTC/USD) = 60,000 × 1e8 = 6e12
ADDITIONAL_FEED_PRECISION = 1e10
amount (1 WBTC in satoshis) = 1e8
_get_usd_value = (6e12 × 1e10 × 1e8) / 1e18 = 6e12
Expected value ($60,000 in 1e18) = 6e22
Actual result = 6e12 ← 1e10 times too small
collateral_adjusted = 6e12 × 50 / 100 = 3e12
health_factor = (3e12 × 1e18) / dsc_minted
...
decimals: uint256 = self.token_address_to_decimals[token]
normalized_amount: uint256 = amount * (10 ** (18 - decimals))
return (
(convert(price, uint256) * ADDITIONAL_FEED_PRECISION) * normalized_amount
) // PRECISION
Apply the same normalization in _get_token_amount_from_usd. With this fix, 1 WBTC (1e8 satoshis) is normalized to 1e18 before calculation, producing the
correct $60,000 valuation and a health factor in the expected 1e18 range.
## 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.