Core Contracts

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

Small Debts might not get Liquidated since there are no Reward for Managers to liquidate Small Debts, who might end up spending more gas than the total debt

Summary

In the StabilityPool contract, managers have no direct reward for liquidating borrowers’ debts, especially small ones. When a manager calls the liquidateBorrower function, they spend gas but don’t get any reward, considering the high cost of gas. This makes it unlikely they’ll bother with small debts, which could leave the system with unhealthy positions that don’t get cleaned up.

Vulnerability Details

In liquidateBorrower function in the StabilityPool contract, Managers (or the owner) can use it to remove borrowers who owe money and can’t pay back properly, but they don’t get any reward for doing so. This is a big deal when the debt is small because the cost of running the function outweighs any benefit.

function liquidateBorrower(address userAddress) external onlyManagerOrOwner nonReentrant whenNotPaused {
_update();
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();
bool approveSuccess = crvUSDToken.approve(address(lendingPool), scaledUserDebt);
if (!approveSuccess) revert ApprovalFailed();
lendingPool.updateState();
lendingPool.finalizeLiquidation(userAddress);
emit BorrowerLiquidated(userAddress, scaledUserDebt);
}
  • A manager calls this to liquidate a borrower.

  • It checks the borrower’s debt (userDebt), makes sure the Stability Pool has enough crvUSD (crvUSDBalance), and then approves sending that crvUSD to the LendingPool.

  • The finalizeLiquidation function (in the LendingPool) takes the borrower’s NFTs (collateral) and sends them to the Stability Pool, while burning the debt by using the crvUSD.


    Nothing in this function gives the manager any crvUSD, RAAC tokens, or part of the NFTs. The manager spends gas to run this but gets nothing back right away.


    For example, if a borrower owes 10 crvUSD, the manager might spend $1 in gas (depending on the network), but they don’t get even 1 crvUSD as a reward. For big debts (like 100,000 crvUSD), a manager might hope to get some RAAC tokens later, but for small debts (like 10 crvUSD), the gas cost is more than any future reward is likely worth.


    In many systems like this, liquidators get a bonus to make it worthwhile. Here, the Stability Pool keeps the NFTs and sends crvUSD to the Lending Pool, but the manager walks away empty-handed.

If a borrower owes just 10 crvUSD and their position is unhealthy (e.g., not enough collateral), a manager has to pay gas to fix it but gets nothing back. They’ll skip it because it’s not worth their time or money. Over time, lots of small, bad debts could pile up if no one cleans them up.

Impact

  1. Small debts that should be liquidated might stay in the system, making it look weaker than it should.

  2. Managers will only liquidate big debts where they might get some indirect reward later (like RAAC tokens), leaving small ones untouched.

Tools Used

Manual Review

Recommendations

  • Pay the manager a little something for each liquidation, like 5% of the debt in RAAC tokens. Add this to liquidateBorrower:

    solidity

    uint256 reward = scaledUserDebt / 20; // 5% of the debt
    raacToken.safeTransfer(msg.sender, reward);
  • For a 10-crvUSD debt, that’s 0.5 RAAC. it is small, but enough to cover gas if RAAC has value.

Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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