Core Contracts

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

Double Scaling of Debt Amount Causes Liquidation DOS

Summary

In the StabilityPool.liquidateBorrower() function, the borrower's debt amount is incorrectly scaled twice by the usage index, causing the required crvUSD amount to be significantly inflated. This can make liquidateBorrower() revert with InsufficientBalance if the stability pool does not have enough balance to cover the inflated debt amount.

Vulnerability Details

The root cause of the issue is the double scaling of the user debt amount:

  1. First scaling in LendingPool.getUserDebt():

    contracts/core/pools/LendingPool/LendingPool.sol#L579

    function getUserDebt(address userAddress) public view returns (uint256) {
    UserData storage user = userData[userAddress];
    return user.scaledDebtBalance.rayMul(reserve.usageIndex);
    }
  2. Second incorrect scaling in StabilityPool.liquidateBorrower():

    contracts/core/pools/StabilityPool/StabilityPool.sol#L453

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

As a result, the required amount becomes significantly larger than the actual debt, which may unexpectedly exceed the crvUSDBalance in StabilityPool, causing the liquidation reverts.

Impact

The impact is high since:

  1. The required balance for liquidation is inflated, make it much harder to liquidate large bad debt.

  2. It puts the protocol's solvency at risk.

Tools Used

Manual review

Recommendations

Remove the second scaling in StabilityPool.liquidateBorrower(). The debt amount should only be scaled once:

function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
...
uint256 userDebt = lendingPool.getUserDebt(userAddress);
// uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
if (userDebt == 0) revert InvalidAmount();
uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
if (crvUSDBalance < userDebt) revert InsufficientBalance();
...
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 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.