Algo Ssstablecoinsss

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

H01. Self-liquidation bonus extraction

Root + Impact

Description

  • Normal behavior: The liquidate function allows external liquidators to repay a portion of an undercollateralized user’s DSC debt in exchange for collateral plus a 10% bonus, incentivizing third parties to maintain solvency.

  • Issue: The implementation does not prevent msg.sender == user, so an undercollateralized borrower can liquidate their own position and capture the liquidation bonus, extracting value that should go to independent liquidators.

// dsc_engine.vy (conceptual translation)
@external
def liquidate(collateral: address, user: address, debt_to_cover: uint256):
# ...
# @> no assertion preventing msg.sender from equaling user
# collateral + bonus sent back to msg.sender even when msg.sender == user

Risk

Likelihood:

  • Reason 1 // Underwater users routinely look for ways to minimize loss and will call any function that returns additional collateral in exchange for debt repayment.

  • Reason 2 // MEV and bots will quickly discover that self-liquidation yields a risk‑free 10% gain compared to pure repayment, making this behavior widespread once deployed.

Impact:

  • Impact 1 // Liquidation incentives for honest third‑party liquidators are significantly weakened, as the bonus is captured by the borrower instead of compensating others for providing capital and gas.

  • Impact 2 // In stressed market conditions, reduced external liquidation participation can lead to delayed or absent liquidations, increasing protocol bad debt and reducing confidence in the stablecoin.

Proof of Concept

A borrower opens a leveraged position and then self‑liquidates to farm the bonus:

  1. User deposits 10 WETH as collateral and mints the maximum DSC allowed.

  2. Price drops such that the user becomes undercollateralized (health factor < 1).

  3. User acquires some DSC from the market.

  4. User calls liquidate(weth, user, debt_to_cover) from their own address.

  5. The engine transfers collateral + 10% bonus back to the same user, giving them more value than they would get from simply repaying debt.

// Solidity-style Forge test pseudo-code
function testSelfLiquidationExtractsBonus() public {
address user = makeAddr("user");
// 1. User supplies collateral and mints DSC
vm.startPrank(user);
weth.approve(address(engine), 10 ether);
engine.deposit_collateral(address(weth), 10 ether);
engine.mint_dsc(9000e18); // example amount near limit
vm.stopPrank();
// 2. Price drops via oracle manipulation or mock
// assume we have a helper to set mock price
setPrice(address(weth), 900e18); // $900 down from $2000
// 3. User acquires DSC from market (mocked via mint in test)
dsc.mint(user, 1000e18);
// 4. User self-liquidates and earns 10% bonus
vm.startPrank(user);
dsc.approve(address(engine), type(uint256).max);
engine.liquidate(address(weth), user, 1000e18);
vm.stopPrank();
// 5. Assert user’s net position improved due to bonus
// @> assert user receives more collateral value than simple repayment path
}

Recommended Mitigation

Add an explicit check in liquidate to prevent self‑liquidation and keep the bonus reserved for external actors.

// dsc_engine.vy (conceptual diff)
@external
def liquidate(collateral: address, user: address, debt_to_cover: uint256):
- # no restriction on who can liquidate
+ assert msg.sender != user, "DSCEngine__CantSelfLiquidate"
# continue with liquidation flow
Updates

Lead Judging Commences

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