Core Contracts

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

No liquidation incentive leads to the accumulation of bad debt for the protocol

Summary

The protocol allows borrowing crvUSD against NFT collateral that users deposit. However, there is no liquidation incentive in the protocol for users to liquidate underwater borrowers, leading to the accumulation of bad debt.

Vulnerability Details

In the LendingPool.sol, there is a process to initiate the liquidation of borrowers whose health factor is below the threshold. This process is then finalized by the StabilityPool if the borrower does not repay their debt during the grace period (3 days). During the liquidation finalization process, the StabilityPool transfers reserve assets to cover the debt, but also gets the NFT from the LendingPool and according to the sponsor, in that way, links generated revenue to flow across the protocol (which is ultimately beneficial for RAAC holders as a group).

However, users who should initiate the liquidation process (any user) get nothing in return, but still need to pay gas fees (which can be high, but nevertheless) for the transaction. This lead to the situation where no one but protocol itself would want to call initiateLiquidation as there is no any incentive for them to do so.

/**
* @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);
}

This will, further on, lead to the accumulation of a bad debt for protocol, especially if the accumulated debt gets larger than the StabilityPool's balance. Furthermore, should the balance be smaller than the current accumulated debt, every liquidation try will be reverted, leading to the huge loss for protocol, as it can be seen from the function below:

/**
* @notice Liquidates a borrower's position.
* @dev This function can only be called by a manager or the owner when the contract is not paused.
* @param userAddress The address of the borrower to liquidate.
* @custom:throws InvalidAmount If the user's debt is zero.
* @custom:throws InsufficientBalance If the Stability Pool doesn't have enough crvUSD to cover the debt.
* @custom:throws ApprovalFailed If the approval of crvUSD transfer to LendingPool fails.
* @custom:emits BorrowerLiquidated when the liquidation is successful.
*/
function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
// Get the user's debt from the LendingPool.
uint256 userDebt = lendingPool.getUserDebt(userAddress);
uint256 scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
if (userDebt == 0) revert InvalidAmount();
uint256 crvUSDBalance = crvUSDToken.balanceOf(address(this));
if (crvUSDBalance < scaledUserDebt) revert InsufficientBalance();
// Approve the LendingPool to transfer the debt amount
bool approveSuccess = crvUSDToken.approve(address(lendingPool), scaledUserDebt);
if (!approveSuccess) revert ApprovalFailed();
// Call finalizeLiquidation on LendingPool
lendingPool.finalizeLiquidation(userAddress);
emit BorrowerLiquidated(userAddress, scaledUserDebt);
}

Impact

High as the bad debt can be easily accumulated since there is no incentive for users to liquidate another users

Tools Used

Manual Review

Recommendations

There needs to be some kind of incentive mechanism built in for users who call initiateLiquidation.

Another possible approach: Compound V3's mechanism works in a similiar manner, where the debt is absorbed from the reserves and the collateral is taken into the protocol. Then, excess collateral is immediately put on sale at a discount. This collateral can then be bought at a discount to current market prices to incentivize liquidators.

Updates

Lead Judging Commences

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

Appeal created

avoloder Submitter
6 months ago
inallhonesty Lead Judge 6 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.

Give us feedback!