Algo Ssstablecoinsss

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

Collateral valuation assumes every collateral token uses 18 decimals

Root + Impact

Description

  • The README states that someone can fork the codebase and swap WETH and WBTC for any basket of assets. Collateral valuation should therefore normalize each collateral token according to its actual decimals.

  • _get_usd_value assumes the raw token amount is already scaled to 18 decimals. For collateral tokens with 6 or 8 decimals, the engine calculates the wrong USD value. This can make collateral unusable or can misprice mint, redeem, and liquidation logic depending on the asset decimals.

# src/dsc_engine.vy
PRECISION: public(constant(uint256)) = 1 * (10**18)
ADDITIONAL_FEED_PRECISION: public(constant(uint256)) = 1 * (10**10)
@internal
@view
def _get_usd_value(token: address, amount: uint256) -> uint256:
price_feed: AggregatorV3Interface = AggregatorV3Interface(
self.token_address_to_price_feed[token]
)
...
@> return (
@> (convert(price, uint256) * ADDITIONAL_FEED_PRECISION) * amount
@> ) // PRECISION

Risk

Likelihood:

  • This occurs when the codebase is forked with a collateral token whose decimals are not 18, such as many BTC-style or stablecoin-style tokens.

  • The README explicitly describes swapping WETH and WBTC for another basket of assets as an intended use case.

Impact:

  • The protocol uses incorrect collateral values for minting, redemption, and liquidation.

  • Users can receive incorrect borrow limits or become unexpectedly liquidatable due to incorrect accounting.

Proof of Concept

def test_six_decimal_collateral_is_mispriced(dsce, weth):
# This arithmetic mirrors _get_usd_value for a $1 token with 6 decimals.
# price = 1e8 because the engine assumes an 8-decimal USD feed.
price = 1 * 10**8
additional_feed_precision = 10**10
precision = 10**18
one_token_with_6_decimals = 1 * 10**6
actual = (price * additional_feed_precision * one_token_with_6_decimals) // precision
# Expected internal USD value for $1 is 1e18.
expected = 1 * 10**18
assert actual != expected
assert actual == 10**6

Recommended Mitigation

- return (
- (convert(price, uint256) * ADDITIONAL_FEED_PRECISION) * amount
- ) // PRECISION
+ token_decimals: uint256 = staticcall IERC20Detailed(token).decimals()
+ normalized_amount: uint256 = amount * PRECISION // (10 ** token_decimals)
+ return (normalized_price * normalized_amount) // PRECISION

Store or read each collateral token's decimals and normalize token amounts to 18 decimals before converting to USD.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours 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!