Core Contracts

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

Incorrect CrvUSD balance checking in StabilityPool for liquidation

Summary

Incorrect CrvUSD balance checking in StabilityPool for liquidation.

Vulnerability Details

StabilityPool owner or manager can call liquidateBorrower() to liquidate a borrower's position.

This function will first check if StabilityPool has sufficient CrvUSD balance, or the transaction will revert.

The checking is as below:

StabilityPool::liquidateBorrower()

// Get the user's debt from the LendingPool.
@> 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 < scaledUserDebt) revert InsufficientBalance();

userDebt is retrieved from LendingPool, the value is compounded:

LendingPool::getUserDebt()

function getUserDebt(address userAddress) public view returns (uint256) {
UserData storage user = userData[userAddress];
return user.scaledDebtBalance.rayMul(reserve.usageIndex);
}

However, userDebt is compounded again:

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

Then protocol wrongly compares StabilityPool CrvUSD balance with the double-compounded value.

uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
if (crvUSDBalance < scaledUserDebt) revert InsufficientBalance();

Moreover, LendingPool's updateState() is not called before getting user debt, and userDebt value might be wrong.

Impact

By comparing StabilityPool CrvUSD balance with the double-compounded value, it needs more CrvUSD balance than actual required (userDebt) to liquidate a borrower, this may wrongly block a liquidation.

Tools Used

Manual Review

Recommendations

The checking should be comparing StabilityPool CrvUSD balance with userDebt which has already been compounded.

// Get the user's debt from the LendingPool.
+ lendingPool.updateState();
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 < scaledUserDebt) revert InsufficientBalance();
+ if (crvUSDBalance < userDebt) revert InsufficientBalance();
Updates

Lead Judging Commences

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

Give us feedback!