StabilityPool's liquidation flow contains a critical accounting error where the debt repayment amount doesn't match the actual debt cleared. When liquidating a borrower, the contract transfers the scaled user debt but updates state with the unscaled amount, leading to accounting inconsistencies.
When a borrower is liquidated:
Their debt should be cleared (set to 0)
The StabilityPool balance should decrease by exactly the scaled debt amount
Looking at the code flow:
The issue arises because:
The StabilityPool calculates the scaled debt correctly using normalized debt
But when calling finalizeLiquidation(), the LendingPool clears the user's raw debt instead of scaled debt
This creates a mismatch between tokens transferred and debt cleared
The root cause is in the debt accounting during liquidation, the StabilityPool pays the scaled amount but the LendingPool clears the unscaled amount.
The protocol's stability relies heavily on precise accounting between the StabilityPool and LendingPool during liquidations. However, I've identified a critical accounting mismatch in this interaction.
When a borrower faces liquidation, the StabilityPool steps in to handle debt repayment. The process seems straightforward, calculate the debt, scale it appropriately, and clear it. However, i notice how the scaling creates a unforsen divergence.
And this is what happens, the StabilityPool correctly calculates the scaled debt using ray math (multiplication with high precision): function liquidateBorrower
This scaled amount is what gets transferred to the LendingPool. However, the LendingPool's finalizeLiquidation function operates on raw debt values: function finalizeLiquidation
You can see here we're mixing scaled and unscaled values. Think of it like paying a $100 debt with €100, the numbers match but the actual value doesn't.
Which sims means that the StabilityPool could end up paying more or less than the actual debt being cleared, breaking the core invariant that debt cleared must equal debt paid. In a protocol handling real estate value, such accounting mismatches could cascade into larger stability issues.
As the main issue emerges during liquidation flows. When a borrower defaults on their RAAC-backed loan, the StabilityPool steps in to handle debt repayment. The protocol calculates debt in two different ways
This means the StabilityPool transfers the precisely scaled amount. However, when we look at how the LendingPool handles this
This creates a fundamental mismatch, imagine paying a $150 debt with €100. The numbers might look similar, but the actual value transfer is incorrect. In RAAC's case, this could lead to the StabilityPool consistently overpaying or underpaying liquidations, directly impacting the protocol's ability to maintain stable real estate backing.
manual
Either scale the transfer amount down to match the raw debt
Or update the LendingPool to clear the scaled debt amount
This vulnerability could lead to accounting errors in the protocol's debt tracking system, potentially affecting the overall stability mechanism.
The issue is more complex than initially suggested
Our fix needs to focus on:
Ensuring the burn amount matches the scaled debt received
Maintaining consistency between StabilityPool's scaledUserDebt and LendingPool's amountBurned
Properly updating reserve.totalUsage with the scaled values
Fix structure
This way we maintain consistency between debt scaling across both pools while properly handling the ray math calculations.
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.