The finalizeLiquidation()
function in LendingPool.sol
is responsible for finalizing the liquidation process of undercollateralized users. It transfers the user’s NFTs to the StabilityPool
and attempts to cover the user’s debt by pulling crvUSD (underlying asset) from the StabilityPool
.
However, the StabilityPool
does not hold or interact with crvUSD tokens, meaning the call to:
will always revert (msg.sender is the StabilityPool), as no crvUSD exists in the StabilityPool
. This prevents any liquidation from being finalized, leaving bad debt in the system and undermining protocol security.
Liquidation is initiated by marking the user as under liquidation.
After the grace period, finalizeLiquidation()
:
Transfers the user's NFTs to the StabilityPool
.
Burns the user's debt tokens:
Attempts to cover the burned debt by pulling crvUSD from the StabilityPool
:
StabilityPool
does not hold crvUSD, meaning this transfer will always revert.
Liquidation never completes, leaving bad debt unaddressed.
Incorrect Behavior Flow
User becomes undercollateralized → isUnderLiquidation[user] = true
.
After grace period, finalizeLiquidation()
is called.
NFTs are transferred to the StabilityPool.
Debt tokens are burned.
LendingPool tries to pull crvUSD from StabilityPool.
Fails because the StabilityPool never receives crvUSD.
Transaction reverts.
Liquidation is never finalized.
Undercollateralized users cannot be liquidated.
Bad debt remains on the protocol, leading to system insolvency.
Lenders and depositors face risks, as outstanding debt is never repaid.
manual review
In the liquidateBorrower()
function in StabilityPool.sol
call LendingPool.sol#withdraw(scaledUserDebt)
to acquire the necessary crvUSD
tokens.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.