Core Contracts

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

StabilityPool::liquidateBorrower Miscalculates User Debt

Summary

StabilityPool::liquidateBorrower miscalculates user debt

Vulnerability Details

In contracts/core/pools/StabilityPool/StabilityPool.sol the liquidateBorrower function retrieves the user’s debt from the LendingPool contract and then applies a ray‑multiplication:

https://github.com/Cyfrin/2025-02-raac/blob/main/contracts/core/pools/StabilityPool/StabilityPool.sol#L449-L470

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());
...
}

However, in LendingPool.sol, the getUserDebt function already computes the debt using rayMul:

https://github.com/Cyfrin/2025-02-raac/blob/main/contracts/core/pools/LendingPool/LendingPool.sol#L579-L582

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

Since userDebt is already the result of user.scaledDebtBalance.rayMul(reserve.usageIndex), applying another ray-multiplication in liquidateBorrower leads to the following incorrect computation:

scaledUserDebt = user.scaledDebtBalance * reserve.usageIndex * reserve.usageIndex

Impact

The incorrect scaling of debt can result in overestimation of the user’s debt during liquidation. This may lead to Excessive crvUSD balance requirements for the StabilityPool to process liquidations.

Tools Used

N/A

Recommendations

Remove the extra rayMul operation in liquidateBorrower. Instead, the function should use the debt value returned by getUserDebt as is, without further modifications:

function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
_update();
// Get the user's debt from the LendingPool.
+ uint256 scaledUserDebt = lendingPool.getUserDebt(userAddress);
- uint256 userDebt = lendingPool.getUserDebt(userAddress);
- uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
...
}
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.