Core Contracts

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

Double scaling with normalized debt leads to incorrect liquidation amounts

Summary

The StabilityPool::liquidateBorrower() function incorrectly multiplies the user's debt by the normalized debt index twice, resulting in liquidations that are much larger than they should be.

Vulnerability Details

In StabilityPool::liquidateBorrower(), the user's debt is fetched from the lending pool and then incorrectly multiplied again by the normalized debt:

// Get already normalized debt from lending pool
uint256 userDebt = lendingPool.getUserDebt(userAddress);
// Incorrectly multiply by normalized debt again
uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());

The issue is that LendingPool::getUserDebt() already returns the debt normalized by the usage index. By multiplying again by getNormalizedDebt(), the function is effectively squaring the normalization factor.

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

Proof of Concept

  1. Assume a user has borrowed 100 tokens and the normalized debt index is 1.1

  2. LendingPool::getUserDebt() returns 110 (100 * 1.1)

  3. StabilityPool::liquidateBorrower multiplies 110 by 1.1 again

  4. Final liquidation amount is 121 tokens (110 * 1.1) instead of the correct 110 tokens

Impact

This causes liquidations to be executed for amounts significantly higher than the actual user debt. The excess liquidation amount could prevent legitimate liquidations if the stability pool has insufficient balance due to inflated liquidation amounts

Recommendations

Remove redundant normalization

function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
_update();
uint256 userDebt = lendingPool.getUserDebt(userAddress);
- uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
+ uint256 scaledUserDebt = userDebt;
if (userDebt == 0) revert InvalidAmount();
// ... rest of function
}
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!