Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Valid

Double debt scaling in `StabilityPool::liquidateBorrower()` leads to failed liquidations

Summary

The StabilityPool::liquidateBorrower() function incorrectly scales the user's debt twice, leading to an inflated balance check and approval amount. This causes legitimate liquidations to fail due to insufficient balance checks, even when the StabilityPool has enough funds to cover the actual debt.

Vulnerability Details

In StabilityPool.sol line 453:

uint256 userDebt = lendingPool.getUserDebt(userAddress);
uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());

The LendingPool::getUserDebt() function already returns a scaled debt value (multiplied by the normalized debt index). However, the StabilityPool then scales this value again by multiplying it with getNormalizedDebt(). This results in the debt being scaled twice, making scaledUserDebt much larger than it should be.

For example:

  • If a user has 100 tokens of raw debt

  • And the normalized debt index is 1.1

  • getUserDebt() will return 110 tokens (100 * 1.1)

  • The StabilityPool then multiplies 110 * 1.1 again

  • Resulting in 121 tokens of debt instead of the correct 110

Impact

When a liquidation is attempted through the StabilityPool, the function checks if the pool has enough crvUSD to cover the incorrectly scaled debt amount (if (crvUSDBalance < scaledUserDebt) revert InsufficientBalance()). Due to the double scaling, this check requires the pool to have more crvUSD than actually needed, causing legitimate liquidations to fail even when the pool has enough funds to cover the real debt amount. Additionally, the function approves an unnecessarily high amount of crvUSD to the LendingPool.

The likelihood is HIGH because it affects every liquidation through the StabilityPool when the normalized debt index is not exactly 1, which is almost always due to interest accrual.

The impact is HIGH because it prevents the protocol from executing legitimate liquidations, potentially leaving the protocol with bad debt that should have been liquidated.

Tools Used

Manual review

Recommendations

Remove the additional scaling operation. Simply use the scaledUserDebt already returned from getUserDebt(), which is the correct value directly as it's already properly scaled by the LendingPool:

- uint256 userDebt = lendingPool.getUserDebt(userAddress);
- uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
+ uint256 scaledUserDebt = lendingPool.getUserDebt(userAddress);
Updates

Lead Judging Commences

inallhonesty Lead Judge 2 months ago
Submission Judgement Published
Validated
Assigned finding tags:

StabilityPool::liquidateBorrower double-scales debt by multiplying already-scaled userDebt with usage index again, causing liquidations to fail

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.