Core Contracts

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

Incorrect collateral seize calculation in `finalizeLiquidation` function may lead to lost of users all collaterals

Summary

The LendingPool contract’s liquidation mechanism contains a high-severity vulnerability. When a user’s health factor falls below the liquidation threshold, the finalizeLiquidation function transfers all of the user’s collateral (NFT tokens) to the stability pool. This occurs even if the total collateral value significantly exceeds the outstanding debt. As a result, users lose all of collaterals than necessary to cover their debt.

Vulnerability Details

The issue is found in the finalizeLiquidation function. Once a user is marked as liquidatable (i.e., their health factor is less than 1), the function iterates through all of the user’s deposited NFT token IDs and transfers each one to the stabilityPool:

for (uint256 i = 0; i < user.nftTokenIds.length; i++) {
uint256 tokenId = user.nftTokenIds[i];
user.depositedNFTs[tokenId] = false;
raacNFT.transferFrom(address(this), stabilityPool, tokenId); // FOUND
}
delete user.nftTokenIds; // FOUND

The health factor is calculated as the ratio of the collateral threshold (collateral * liquidation threshold) to the user’s debt.

function calculateHealthFactor(address userAddress) public view returns (uint256) {
uint256 collateralValue = getUserCollateralValue(userAddress);
uint256 userDebt = getUserDebt(userAddress);
if (userDebt < 1) return type(uint256).max;
uint256 collateralThreshold = collateralValue.percentMul(liquidationThreshold); // <-
return (collateralThreshold * 1e18) / userDebt; // <-
}

liquidationThreshold is settable and cannot be more than 100%. However if threshold is less than 100% all the collateral of user is seized.

require(newValue <= 100_00, "Invalid liquidation threshold");`

If this ratio is less than 1, the account is deemed liquidatable. However, this implementation erroneously seizes all NFTs as collateral, regardless of whether the total collateral value is substantially higher than the outstanding debt. Only an amount of collateral equivalent to the debt should be liquidated.

Impact

Each user whose account is under liquidation may lose all collaterals due to incorrect seize calculation.

Unjust Collateral Loss: Users may lose more collateral than necessary, as the entire collateral is seized instead of an amount proportional to their debt.

Loss of User Trust: Such an aggressive liquidation approach could significantly undermine user confidence in the protocol and discourage participation.

Tools Used

• Manual code review

Recommendations

Proportional Liquidation Logic: Modify the finalizeLiquidation function to calculate and seize only the portion of collateral that is equivalent to the outstanding debt. This might involve determining the value of each NFT and liquidating just enough to cover the debt.

Review Health Factor Mechanics: Reevaluate the health factor calculation and liquidation criteria to ensure that liquidation is fair and proportional, protecting users from excessive collateral loss.

It is important to note that users collaterals are in nft form which may not be certainly equal to debt. This way developers should implement a function where remaining of seized collateral should be sent to user. Advise with experts before taking this considerations into account.

For example:

  1. User have 5 NFTS in list (300K$)

  2. Account under liquidation

  3. Debt is 200K$

  4. Assume sum of 3 NFTs is 220K$

  5. Protocol cannot seize 220K should be returned to user.

Updates

Lead Judging Commences

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

LendingPool::finalizeLiquidation() never checks if debt is still unhealthy

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!