Core Contracts

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

The calculation for scaledUserDebt in liquidateBorrower is incorrect

Summary

The liquidateBorrower function in the StabilityPool contract calls lendingPool.getUserDebt to retrieve the user's debt, which already includes the multiplication by reserve.usageIndex. However, the liquidateBorrower function then incorrectly multiplies the debt again by reserve.usageIndex (via lendingPool.getNormalizedDebt()), leading to a double multiplication of the debt by the same factor. This results in an incorrect and inflated debt value being used in the liquidation process.

Vulnerability Details

The getUserDebt function already multiplies the user's scaled debt balance (user.scaledDebtBalance) by reserve.usageIndex to calculate the user's debt:

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

The liquidateBorrower function incorrectly multiplies the debt again by reserve.usageIndex (via lendingPool.getNormalizedDebt()):

function getNormalizedDebt() external view returns (uint256) {
return reserve.usageIndex;
}
uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());

The debt is effectively multiplied twice by reserve.usageIndex, leading to an inflated and incorrect debt value.

Impact

The double multiplication results in a debt value that is much larger than the actual debt, so the allowance to be approved is larger than expected. The impact is Low, the likelihood is High, so the severity is Medium.

Tools Used

Manual Review

Recommendations

Remove the unnecessary multiplication in liquidateBorrower:

function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
_update();
// Get the user's debt from the LendingPool.
uint256 userDebt = lendingPool.getUserDebt(userAddress);
if (userDebt == 0) revert InvalidAmount();
uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
if (crvUSDBalance < userDebt) revert InsufficientBalance();
// Approve the LendingPool to transfer the debt amount
bool approveSuccess = crvUSDToken.approve(address(lendingPool), userDebt);
if (!approveSuccess) revert ApprovalFailed();
// Update lending pool state before liquidation
lendingPool.updateState();
// Call finalizeLiquidation on LendingPool
lendingPool.finalizeLiquidation(userAddress);
emit BorrowerLiquidated(userAddress, userDebt);
}
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!