The liquidation process incorrectly applies interest twice when calculating debt amounts, causing the StabilityPool to overpay for liquidated positions. This error creates an exponential fund leakage vector that would lead to complete insolvency of the StabilityPool within hours of protocol operation under normal market conditions.
The root cause exists in the interaction between LendingPool.getUserDebt()
and StabilityPool.liquidateBorrower()
. The getUserDebt()
function returns a debt amount that has already been multiplied by the usageIndex
(interest accumulator):
The StabilityPool then incorrectly reapplies the interest index when processing liquidations:
This double application creates a squared interest effect where debt = scaledDebt × index² instead of the correct scaledDebt × index. The protocol's comment in LendingPool.getUserDebt()
states "returns the user's total debt including interest" but fails to account for how this value would be used in subsequent calculations.
The worst-case impact occurs during normal liquidation operations:
User borrows 100 ETH at 10% APR
After 1 year: Actual debt = 110 ETH (100 × 1.1)
StabilityPool calculates debt as 121 ETH (100 × 1.1 × 1.1)
Protocol permanently loses 11 ETH per liquidation
This error enables exponential fund drainage from the StabilityPool, with losses growing quadratically relative to interest rate changes. Even at modest 5% interest rates, liquidations would permanently remove 0.25% of pool funds per operation, making protocol insolvency inevitable within weeks.
Alice deposits 1000 crvUSD to LendingPool
Bob deposits NFT worth 100 crvUSD and borrows 100 crvUSD
After 1 year at 10% interest:
Actual debt: 110 crvUSD (getUserDebt()
returns 110)
Reserve's normalized debt index = 1.1e27
Oracle reports NFT price drop to 50 crvUSD
StabilityPool initiates liquidation:
Calculates debt as 110 × 1.1 = 121 crvUSD
Transfers 121 crvUSD from pool to cover 110 crvUSD debt
Protocol permanently loses 11 crvUSD (10% of actual debt)
manual review
Remove the second interest application in StabilityPool by using the direct scaled debt value. Modify StabilityPool.liquidateBorrower()
to use the already-interest-adjusted debt from LendingPool without additional multiplication:
This change aligns debt calculations with the protocol's intended single interest accrual model while maintaining existing accounting structures.
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.