Algo Ssstablecoinsss

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

Rebasing Token Support

Root + Impact

The protocol records collateral balances at deposit time but rebasing tokens (stETH, aTokens) automatically change balances over time, causing recorded amounts to diverge from actual holdings and creating protocol insolvency or unfair liquidations.

Description

  • The protocol records user collateral deposits in a state mapping and uses this recorded amount for all collateral value calculations, health factor checks, and liquidations.

  • Rebasing tokens automatically adjust their balances in holder wallets (increase for rewards, decrease for slashing), but the protocol's recorded amounts remain static, causing mismatches between actual contract balance and accounting records.

# dsc_engine.vy lines 223-225
self.user_to_token_address_to_amount_deposited[msg.sender][
token_collateral_address
] += amount_collateral # @> Records static amount at deposit time
# dsc_engine.vy lines 335-340
@internal
@view
def _get_account_collateral_value(user: address) -> uint256:
total_collateral_value_in_usd: uint256 = 0
for token: address in COLLATERAL_TOKENS:
amount: uint256 = self.user_to_token_address_to_amount_deposited[user][token]
# @> Uses recorded amount, not actual token balance
total_collateral_value_in_usd += self._get_usd_value(token, amount)
return total_collateral_value_in_usd
# @> No check against actual balanceOf(this)

Risk

Likelihood:

  • Protocol whitelists rebasing tokens like stETH (Lido staked ETH with daily rewards) or aTokens (Aave interest-bearing tokens), where balance changes occur automatically every block.

  • Negative rebasing events occur during network slashing (stETH), liquidations (aTokens), or algorithmic adjustments, reducing actual token balances below recorded amounts.

Impact:

  • Negative rebase reduces contract's actual token balance below recorded amounts, creating phantom collateral where users can mint DSC against value that no longer exists, causing protocol insolvency.

  • Positive rebase increases contract balance above recorded amounts, with excess tokens becoming locked forever as accounting doesn't reflect gains, preventing users from claiming earned rewards.

Proof of Concept

# Scenario 1: Negative Rebase (stETH slashing event)
# User deposits 100 stETH
with boa.env.prank(user):
steth.approve(dsce, 100e18)
dsce.deposit_collateral(steth, 100e18)
# State after deposit:
contract_balance = steth.balanceOf(dsce) # 100 stETH
recorded = dsce.get_collateral_balance_of_user(user, steth) # 100 stETH
# Ethereum network slashing event: stETH rebases down 5%
# (This happens automatically via stETH contract)
# State after rebase:
contract_balance = steth.balanceOf(dsce) # 95 stETH (rebased down)
recorded = dsce.get_collateral_balance_of_user(user, steth) # Still 100 stETH
# INSOLVENCY:
# Protocol thinks it has: 100 stETH
# Protocol actually has: 95 stETH
# Phantom collateral: 5 stETH
# User can mint DSC based on 100 stETH recorded value
# But only 95 stETH available for liquidation
# Protocol is undercollateralized by 5%
# Scenario 2: Positive Rebase (stETH rewards)
# User deposits 100 stETH
# After 1 year, stETH rewards accrue: 100 → 105 stETH
contract_balance = steth.balanceOf(dsce) # 105 stETH (rebased up)
recorded = dsce.get_collateral_balance_of_user(user, steth) # Still 100 stETH
# User tries to withdraw all collateral:
dsce.redeem_collateral(steth, 100e18)
# Transfers 100 stETH, leaves 5 stETH locked in contract forever
# No accounting for the 5 stETH rewards
# Permanently locked value

Recommended Mitigation

# Document rebasing tokens are not supported
+# IMPORTANT: Do not whitelist rebasing tokens
+# Incompatible tokens:
+# - stETH (Lido Staked ETH) - rebases for rewards/slashing
+# - aTokens (Aave) - rebases for interest
+# - sTSLA (Synthetix) - rebases algorithmically
+# - Any token with elastic supply mechanics
+#
+# Only whitelist fixed-balance tokens:
+# - WETH (Wrapped ETH)
+# - WBTC (Wrapped Bitcoin)
+# - Standard ERC20 tokens without rebase logic
# Alternative: Query actual balance instead of recorded amount
@internal
@view
def _get_account_collateral_value(user: address) -> uint256:
total_collateral_value_in_usd: uint256 = 0
for token: address in COLLATERAL_TOKENS:
- amount: uint256 = self.user_to_token_address_to_amount_deposited[user][token]
+ # Use actual contract balance proportional to user's share
+ # This requires tracking shares instead of amounts (major refactor)
total_collateral_value_in_usd += self._get_usd_value(token, amount)
return total_collateral_value_in_usd

Recommended Approach: Explicitly document that rebasing tokens must NOT be whitelisted as collateral.

Updates

Lead Judging Commences

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