The StabilityPool contract is responsible for liquidating borrower positions through its liquidateBorrower() function. The user's debt is obtained from the LendingPool contract, which returns a value that is already scaled by the usage index. The usage index is a critical component that needs to be up-to-date for accurate debt calculations.
There are two interrelated issues in the liquidateBorrower() function that affect debt calculations:
Double Scaling: The user's debt is incorrectly scaled twice:
First in LendingPool.getUserDebt(): return user.scaledDebtBalance.rayMul(reserve.usageIndex);
Then again in StabilityPool.liquidateBorrower(): uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
Stale Usage Index: The user's debt is retrieved before updating the lending pool's state:
Since getUserDebt() uses the current usage index for calculation, using a stale index leads to incorrect debt values. The usage index should be updated before any debt calculations to reflect the most recent interest accrual.
High. These issues directly affect the core liquidation functionality of the protocol in two ways:
Double scaling inflates the debt value artificially
Stale usage index results in outdated debt calculations
This combination could prevent legitimate liquidations from being executed due to inflated values failing balance checks. Moreover, this could cause the liquidation to fail because the token approval from StabilityPool is insufficient to cover the user's debt in LendingPool.
High. Both issues affect every liquidation attempt made through the Stability Pool, making them systematic issues that will consistently produce incorrect results. The impact becomes more significant as the time between usage index updates increases.
Consider this scenario:
Last usage index update was 30 days ago at 1.1
User has a scaled debt balance of 100 tokens
Current interest rate would increase usage index to 1.2
Normalized debt is also 1.1
liquidateBorrower() is called
Current implementation:
getUserDebt() uses stale index: 100 * 1.1 = 110 tokens
This is then scaled again: 110 * 1.1 = 121 tokens
updateState() is called after, updating usage index to 1.2
Correct debt should be: 100 * 1.2 = 120 tokens
The actual liquidation attempts to process 121 tokens instead of 120 tokens, which could fail due to insufficient balance checks.
Update the lending pool state before calculating the user's debt and remove the second scaling operation:
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.