Algo Ssstablecoinsss

AI First Flight #2
Beginner FriendlyDeFi
EXP
View results
Submission Details
Severity: high
Valid

`_revert_if_health_factor_is_broken` / `MIN_HEALTH_FACTOR` is only valid for WETH: non-18-dec collateral mis-computes the health factor

Description

The health-factor enforcement in _revert_if_health_factor_is_broken (and the MIN_HEALTH_FACTOR = 1e18 threshold it compares against) is only correct for 18-decimal collateral like WETH. For any collateral with a different number of decimals (e.g. WBTC, 8 decimals), the health factor is miscomputed, so the check passes or fails against a wrong value:

MIN_HEALTH_FACTOR: public(constant(uint256)) = 1 * (10**18) # @> implicitly assumes 18-dec collateral (WETH)
ADDITIONAL_FEED_PRECISION: public(constant(uint256)) = 1 * (10**10) # @> assumes 18-dec token + 8-dec feed
​
@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" # @> threshold only right for WETH

_health_factor -> _get_account_collateral_value -> _get_usd_value scales every token's amount with the fixed ADDITIONAL_FEED_PRECISION (1e10) and PRECISION (1e18), which only yields a correct 1e18-scaled USD value when the token has 18 decimals. For WBTC (amount in 1e8) the collateral USD value is under-counted by ~1e10, so _health_factor returns a wrong number and the >= MIN_HEALTH_FACTOR comparison is meaningless for non-WETH collateral.

Risk

Likelihood: Medium

  • WBTC (8 decimals) is the canonical second collateral for a DSC-style engine; any non-18-decimal collateral token makes every health-factor check in _revert_if_health_factor_is_broken wrong.

Impact: High

  • Because MIN_HEALTH_FACTOR / the health factor only work for WETH, a user depositing a non-18-decimal collateral is evaluated against a mis-scaled health factor: either they are blocked/liquidated despite being safe, or (depending on direction) they can mint far more DSC than their collateral backs and the health check still passes — breaking the over-collateralization invariant and the peg.

Proof of Concept

# COLLATERAL_TOKENS = [WETH(18dec), WBTC(8dec)].
# Deposit 1 WBTC (amount=1e8), BTC=$60,000: _get_usd_value = (60000e8*1e10*1e8)//1e18 = 60000e8 (not 60000e18).
# _health_factor uses this wrong value, so _revert_if_health_factor_is_broken compares a mis-scaled
# health factor to MIN_HEALTH_FACTOR (1e18) -> the check is invalid for WBTC collateral.

Recommended Mitigation

Normalize each collateral token to 18 decimals (using its decimals()) before computing USD value / the health factor, so MIN_HEALTH_FACTOR is a valid threshold for every collateral, not just WETH.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 7 days ago
Submission Judgement Published
Validated
Assigned finding tags:

[H-01] In the function \_revert_if_health_factor_is_broken constatnt variable MIN_HEALTH_FACTOR is only for WETH.

## 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" ```

Support

FAQs

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

Give us feedback!