Core Contracts

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

Wrong calculation in finalizeLiquidation will lead to protocol insolvency

Summary

State variables are updated with the wrong values in `finalizeLiquidation` which will lead to protocol becoming insolvent

Vulnerability Details

The LendingPool allows the stability pool to finalize a liquidation after the grace period has passed. The grace period is the period when a borrower can cover their possition.

As we can see in `finalizeLiquidation`

delete user.nftTokenIds;
// Burn DebtTokens from the user
(uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned,) // <=
= IDebtToken(reserve.reserveDebtTokenAddress).burn(userAddress, userDebt, reserve.usageIndex);
// @audit-issue M: amountScaled and amountBurned are reversed
// 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);
  1. The user's nft position is removed.

  2. Reserve debt tokens are burned.

  3. Reserve assets are transferred from the stability pool

  4. User debt balance and total usege are updated

All seems good, but actually the amountScaled and amountBurned values are reversed when returned from IDebtToken::burn()

As can be seen in DebtToken::burn the return values are:

function burn(
address from,
uint256 amount,
uint256 index
) external override onlyReservePool returns (uint256, uint256, uint256, uint256) {
...
_burn(from, amount.toUint128());
emit Burn(from, amountScaled, index);
return (amount, totalSupply(), amountScaled, balanceIncrease); // <=
}

The first return value is actually the burned amount but in the lending pool it is set as the scaled amount, when the 3rd value is the scaled amount, but is set as the burned amount.

These values are used to update the user scaled balance, interest rates and liquidity.

Updating the interest rates with the wrong value will lead to unexpected consequences. One of which is protocol insolvency since the rate will be lower and this would let users borrow for less.

Impact

High, Insolvency

Tools Used

Manual Review

Recommendations

function finalizeLiquidation(address userAddress) external nonReentrant onlyStabilityPool { // seen
...
// Burn DebtTokens from the user
- (uint256 amountScaled, uint256 newTotalSupply, uint256 amountBurned, uint256 balanceIncrease)
= IDebtToken(reserve.reserveDebtTokenAddress).burn(userAddress, userDebt, reserve.usageIndex);
+ (uint256 amountBurned, uint256 newTotalSupply, uint256 amountScaled, uint256 balanceIncrease)
= IDebtToken(reserve.reserveDebtTokenAddress).burn(userAddress, userDebt, reserve.usageIndex);
...
emit LiquidationFinalized(stabilityPool, userAddress, userDebt, getUserCollateralValue(userAddress));
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

LendingPool functions incorrectly assign DebtToken::burn return values, swapping amountScaled and amountBurned, causing wrong token transfers and debt accounting

This is confusing naming but functionally correct. The variable names are misleading, but that's not a vulnerability.

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

LendingPool functions incorrectly assign DebtToken::burn return values, swapping amountScaled and amountBurned, causing wrong token transfers and debt accounting

This is confusing naming but functionally correct. The variable names are misleading, but that's not a vulnerability.

Support

FAQs

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