Algo Ssstablecoinsss

AI First Flight #2
Beginner FriendlyDeFi
EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

WBTC Collateral Severely Undervalued Due to Missing Decimal Normalization

WBTC Collateral Severely Undervalued Due to Missing Decimal Normalization

Description

The _get_usd_value function assumes all collateral tokens have 18 decimals. WBTC has 8 decimals, causing the protocol to undervalue WBTC collateral by a factor of 10^10.

The vulnerable calculation in _get_usd_value (line 314-316):

return (
(convert(price, uint256) * ADDITIONAL_FEED_PRECISION) * amount
) // PRECISION

This formula works correctly only when amount is in 18-decimal format:

  • WETH (18 decimals): 1 WETH = 10^18 units. (2000e8 * 1e10 * 1e18) / 1e18 = 2000e18 = $2,000 in 18-decimal format. Correct.

  • WBTC (8 decimals): 1 WBTC = 10^8 units. (60000e8 * 1e10 * 1e8) / 1e18 = 60000e8 = 6e12 = $0.000006 in 18-decimal format. Off by 10^10.

The same issue propagates to _get_token_amount_from_usd (line 360-364), which calculates inverse token amounts during liquidation:

return (
(usd_amount_in_wei * PRECISION) // (
convert(price, uint256) * ADDITIONAL_FEED_PRECISION
)
)

For a $1 DSC debt covered against WBTC, this returns 1e18 * 1e18 / (60000e8 * 1e10) = 1e18 / 60000 = ~1.67e13 WBTC units, which is ~167,000 WBTC instead of ~0.0000167 WBTC.

Risk

Likelihood: High -- WBTC is explicitly listed as a supported collateral token. Any user depositing WBTC will be affected immediately.

Impact: High -- Users depositing WBTC as collateral effectively have worthless collateral in the system. They cannot mint any meaningful amount of DSC. The protocol's core functionality is broken for one of its two supported tokens.

Real-World Precedent: Uranium Finance ($57M, 2021) suffered from a similar arithmetic miscalculation. Token decimal mismatches are a well-documented vulnerability class (CWE-682).

Proof of Concept

How the attack works:

  1. User deposits 1 WBTC (worth $60,000) as collateral

  2. Protocol calculates collateral value as $0.000006 instead of$60,000

  3. Health factor: (0.000006 * 50/100 * 1e18) / dsc_minted -- virtually zero borrowing power

  4. User can mint at most ~0.000003 DSC (worth $0.000003) for$60,000 of collateral

  5. WBTC collateral is essentially unusable

Expected outcome: Users depositing WBTC cannot mint any meaningful amount of DSC. The protocol's 200% overcollateralization requirement becomes ~200,000,000,000% for WBTC.

def test_wbtc_undervalued_by_1e10(self, dsce, wbtc):
one_wbtc_real = 10**8 # 1 WBTC in 8-decimal (real on-chain)
one_token_mock = 10**18 # 1 token in 18-decimal (mock assumption)
usd_8dec = dsce.get_usd_value(wbtc, one_wbtc_real)
usd_18dec = dsce.get_usd_value(wbtc, one_token_mock)
# 18-decimal amount: correct ($2,000)
assert usd_18dec == to_wei(2_000, "ether")
# 8-decimal amount: WRONG — 10^10 times too small
assert usd_18dec // usd_8dec == 10**10

Recommended Mitigation

Read the token's decimals dynamically and normalize all amounts to 18 decimals:

@internal
@view
def _get_usd_value(token: address, amount: uint256) -> uint256:
price_feed: AggregatorV3Interface = AggregatorV3Interface(
self.token_address_to_price_feed[token]
)
(round_id, price, started_at, updated_at, answered_in_round) = (
oracle_lib._stale_check_latest_round_data(price_feed.address)
)
# Normalize token amount to 18 decimals
token_decimals: uint256 = convert(IERC20Detailed(token).decimals(), uint256)
normalized_amount: uint256 = amount * (10 ** (18 - token_decimals))
return (
(convert(price, uint256) * ADDITIONAL_FEED_PRECISION) * normalized_amount
) // PRECISION

Apply the same normalization in _get_token_amount_from_usd to convert the result back to the token's native decimal format.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 5 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!