Core Contracts

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

Underwater borrowers can't get liquidated because `StabilityPool` holds no crvUSDToken amount

Summary

During liquidations, the wrong token, crvUSDToken, is approved to cover the borrower's debt. Unless the manager Or Owner makes a donation before calling liquidateBorrower, StabilityPool will hold 0 amount of crvUSDToken making liquidation impossible.

Vulnerability Details

There are a few ways users can interact with RAAC protocol:

  • Users can deposit liquidity, crvUsd, to LendingPool and, in exchange they receive rToken which accumulates interest.

  • Borrowers can deposit RAACNft as colateral and borrow against it.

  • Token holders can deposit it to StabilityPool for additional RAACToken rewards. A 1-to-1 deToken is minted to stability pool depositors.

In case a borrower's health factor is unhealthy, the liquidation process is started and, after the grace period expires, onlyManagerOrOwner can call liquidateBorrower to finalize the liquidation.

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());
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();
// Update lending pool state before liquidation
lendingPool.updateState();
// Call finalizeLiquidation on LendingPool
lendingPool.finalizeLiquidation(userAddress);
emit BorrowerLiquidated(userAddress, scaledUserDebt);
}

StabilityPool approves crvUSDToken to cover the borrower's debt. The problem is that StabilityPool doesn't hold any crvUsd. Unless onlyManagerOrOwner transfer the required amount to StabilityPool, practically making a donation to all depositors, liquidateBorrower reverts.

Since during liquidation the NFT collateral is transferred to StabilityPool, rToken should be used to cover the debt.

Impact

Underwater borrowers can't get liquidated.

Tools Used

Recommendations

  • use rTokens from StabilityPool to cover the liquidated position and transfer them to LendingPool. Distribute the rToken amount to all LendingPool depositors based on their contribution.

  • the StabilityPool should implement a share-based mechanism (instead of the 1-to-1 deToken minting) so that covered liquidation is supported by all StabilityPool depositors based on their contribution.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Validated
Assigned finding tags:

StabilityPool design flaw where liquidations will always fail as StabilityPool receives rTokens but LendingPool expects it to provide crvUSD

Support

FAQs

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