Core Contracts

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

When liquidating borrower his debt is not up to date

Summary

StabilityPool.liquidateBorrower is not called the lendingPool.updateState() at the start of the function caused lendingPool.getUserDebt(userAddress) to return user debit not relavant to current time. So incorrect debt amount is returned.

Vulnerability Details

The lendingPool.updateState() function is not called at the start of the function.

function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
_update();
// 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();
// Approve the LendingPool to transfer the debt amount
bool approveSuccess = crvUSDToken.approve(address(lendingPool), scaledUserDebt);
if (!approveSuccess) revert ApprovalFailed();
// Update lending pool state before liquidation
lendingPool.updateState();
// Call finalizeLiquidation on LendingPool
lendingPool.finalizeLiquidation(userAddress);
emit BorrowerLiquidated(userAddress, scaledUserDebt);
}

This is LendingPool.getUserDebt . It returned the user total debit amount to last updated borrowed interest. not for current time. To get the borrowed interest relavant to current time , lendingPool.updateState() should be called at start of the function.

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

Also lendingPool.getUserDebt(userAddress) returns the user debit to last updated index. it again multiply by last updated index. Meaning double conversion done. Its unnessary here.

Impact

User debit is less , so it caused less crvUSDToken approval to lending pool . Since here its updated interest rates which casused higher user debit compared to approval debit amount. So this transfer function is reverted due to less approval amount causing liquidation is failed.

Due to this double conversion , it might approve the higher debit amount than actual debit amount , but it should not be happend as always. Consider this senario : The index was last updated a long time ago, making the double conversion result in a smaller total debit compared to updating it to the current time. So Better follow the correct calculations.

Tools Used

Manual Review

Recommendations

Call lendingPool.updateState() function at the start of the function. Also fix the double converison of debit calculation.

Updates

Lead Judging Commences

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

StabilityPool: liquidateBorrower should call lendingPool.updateState earlier, to ensure the updated usageIndex is used in calculating the scaledUserDebt

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!