Core Contracts

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

LendingPool::finalizeLiquidation Does Not Handle Sudden Collateral Value Drop

Summary

The finalizeLiquidation function assumes that collateral will always be sufficient to cover the outstanding debt when liquidating a borrower. However, if the collateral's value drops suddenly due to market volatility or price manipulation, the protocol may fail to recover the full loan amount, leading to bad debt.

Vulnerability Details

function finalizeLiquidation(address userAddress) external nonReentrant onlyStabilityPool {
if (!isUnderLiquidation[userAddress]) revert NotUnderLiquidation();
// update state
ReserveLibrary.updateReserveState(reserve, rateData);
if (block.timestamp <= liquidationStartTime[userAddress] + liquidationGracePeriod) {
revert GracePeriodNotExpired();
}
UserData storage user = userData[userAddress];
uint256 userDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex);
isUnderLiquidation[userAddress] = false;
liquidationStartTime[userAddress] = 0;
// Transfer NFTs to Stability Pool
for (uint256 i = 0; i < user.nftTokenIds.length; i++) {
uint256 tokenId = user.nftTokenIds[i];
user.depositedNFTs[tokenId] = false;
raacNFT.transferFrom(address(this), stabilityPool, tokenId);
}
delete user.nftTokenIds;
// Burn DebtTokens from the user
(uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned, uint256 balanceIncrease) = IDebtToken(reserve.reserveDebtTokenAddress).burn(userAddress, userDebt, reserve.usageIndex);
// Transfer reserve assets from Stability Pool to cover the debt
IERC20(reserve.reserveAssetAddress).safeTransferFrom(msg.sender, reserve.reserveRTokenAddress, amountScaled);
// Update user's scaled debt balance
user.scaledDebtBalance -= amountBurned;
reserve.totalUsage = newTotalSupply;
// Update liquidity and interest rates
ReserveLibrary.updateInterestRatesAndLiquidity(reserve, rateData, amountScaled, 0);
emit LiquidationFinalized(stabilityPool, userAddress, userDebt, getUserCollateralValue(userAddress));
}

The function determines the amount of debt to burn based on the user's scaled debt balance. It then transfers the collateral (NFTs) to the Stability Pool, assuming that the collateral is sufficient to cover the debt. However, it does not check whether the current market value of the collateral is still sufficient to cover the outstanding debt.

If the collateral price drops significantly before liquidation, the Stability Pool may not receive enough value, causing protocol losses.

Impact

The protocol will not be able to recover the full debt from liquidated users, leading to a loss of funds.

POC

  1. Step 1: Deposit Collateral & Borrow Funds

    • User deposits an NFT worth $10,000 and borrows $5,000 worth of stablecoins.

  2. Step 2: Price Manipulation

    • The NFT price crashes to $3,000 .

  3. Step 3: Liquidation Attempt

    • The protocol triggers liquidation since the user's health factor is below 1.0.

    • However, the collateral is now worth less than the outstanding debt.

    • The protocol attempts to liquidate the user, but cannot fully recover the debt.

  4. Step 4: Protocol Loss

    • The Stability Pool absorbs the loss or the protocol accumulates bad debt.

Tools Used

Manual Review

Recommendations

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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