Core Contracts

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

No liquidation incentives for the users means fewer positions will enter liquidation and it's more probable for insolvent positions

Summary

In LendingPool, addresses with health factor are supposed to get put under liquidation by users calling the initiateLiquidation function. However, when the liquidation gets finalized, the protocol pays the liquidated address' debt and gets its NFT. Users have 0 incentives to call the initiateLiquidation function.

Vulnerability Details

LendingPool.sol
/**
* @notice Allows anyone to initiate the liquidation process if a user's health factor is below threshold
* @param userAddress The address of the user to liquidate
*/
function initiateLiquidation(address userAddress) external nonReentrant whenNotPaused {
if (isUnderLiquidation[userAddress]) revert UserAlreadyUnderLiquidation();
// update state
ReserveLibrary.updateReserveState(reserve, rateData);
UserData storage user = userData[userAddress];
uint256 healthFactor = calculateHealthFactor(userAddress);
if (healthFactor >= healthFactorLiquidationThreshold) revert HealthFactorTooLow();
isUnderLiquidation[userAddress] = true;
liquidationStartTime[userAddress] = block.timestamp;
emit LiquidationInitiated(msg.sender, userAddress);
}
function finalizeLiquidation(address userAddress) external nonReentrant onlyStabilityPool {
if (!isUnderLiquidation[userAddress]) revert NotUnderLiquidation();
.
.
// 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);
.
.
}

As we can see initiateLiquidation is callable by everyone. However, finalizeLiquidation is only callable by the StabilityPool and the liquidated user's NFTs are transferred to it. It also pays the debt of the liquidation. This means that users have no incentive to put a user under liquidation since they only spend gas for it and don't gain anything.

Impact

If users are not incentivized to put users under liquidation it means that it's more probable that some positions become insolvent (their NFTs are valued less than their borrowed amount). Since the protocol pays their debt and gets their NFTs that means loss of funds for the protocol.

Tools Used

Manual review

Recommendations

Create a keeper bot to scan positions and call initiateLiquidation or allow everyone to finalizeLiquidation so there is incentive for users to liquidate other users.

Updates

Lead Judging Commences

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

Appeal created

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

No incentive to liquidate

Support

FAQs

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