Core Contracts

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

Incorrect Debt Accounting in borrow() Function Leading to Underestimation of User Debt

Summary

The LendingPool::borrow() function does not correctly account for interest accrual when updating a user’s debt balance, leading to an inaccurate tracking of outstanding debt. Specifically, it only adds the scaled debt amount without considering prior balance increases due to accumulated interest. This incorrect debt tracking extends to multiple functions, including liquidation and repayment, causing miscalculations that can lead to protocol losses and inaccurate liquidations.

Vulnerability Details

The DebtToken::mint() function correctly accounts for accrued interest (balanceIncrease) when minting new debt tokens for users.

function mint(
address user,
address onBehalfOf,
uint256 amount,
uint256 index
) external override onlyReservePool returns (bool, uint256, uint256) {
if (user == address(0) || onBehalfOf == address(0)) revert InvalidAddress();
if (amount == 0) {
return (false, 0, totalSupply());
uint256 amountScaled = amount.rayDiv(index);
if (amountScaled == 0) revert InvalidAmount();
uint256 scaledBalance = balanceOf(onBehalfOf);
bool isFirstMint = scaledBalance == 0;
uint256 balanceIncrease = 0;
if (_userState[onBehalfOf].index != 0 && _userState[onBehalfOf].index < index) {
balanceIncrease = scaledBalance.rayMul(index) - scaledBalance.rayMul(_userState[onBehalfOf].index);
}
_userState[onBehalfOf].index = index.toUint128();
uint256 amountToMint = amount + balanceIncrease;
_mint(onBehalfOf, amountToMint.toUint128());
emit Transfer(address(0), onBehalfOf, amountToMint);
emit Mint(user, onBehalfOf, amountToMint, balanceIncrease, index);
return (scaledBalance == 0, amountToMint, totalSupply());
}

However, in the LendingPool::borrow() function, the user’s scaledDebtBalance is updated using:

user.scaledDebtBalance += scaledAmount; // @audit doesn't account for balance increase

This implementation fails to incorporate the accumulated interest on the user’s existing debt.

  • Debt Growth is Ignored: Interest accrues over time on a user's debt, but since scaledDebtBalance is updated incorrectly, the protocol does not factor in the interest accrued between borrow transactions.

  • Incorrect Borrowing Power Calculation: The function checks if the user has sufficient collateral using the condition:

    // Fetch user's total debt after borrowing
    uint256 userTotalDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex) + amount; // @audit doesn't account for balance increase
    // Ensure the user has enough collateral to cover the new debt
    if (collateralValue < userTotalDebt.percentMul(liquidationThreshold)) { // @audit Incorrect Undercollateralization check
    revert NotEnoughCollateralToBorrow();
    }

    Since user.scaledDebtBalance does not reflect the actual debt (because balance increase is ignored), userTotalDebt is underestimated. This means users may borrow more than their collateral should allow.

  • Excessive Borrowing & Protocol Insolvency Risks: By exploiting this issue, users can repeatedly borrow more than their effective collateralization, increasing the likelihood of insolvency for the protocol when liquidations fail to recover sufficient funds.

This issue also extends to:

  • getUserDebt(), which fetches a user’s total debt using the same flawed calculation.

  • initiateLiquidation(), which evaluates a user’s health factor based on an incorrect debt value.

  • closeLiquidation(), which incorrectly checks for debt clearance before allowing liquidation to close.

  • finalizeLiquidation(), which relies on an inaccurate debt calculation when burning DebtTokens.

  • _repay(), where the function mistakenly compares repayment amounts to userScaledDebt instead of the full debt amount, leading to improper repayments.

Impact

  • Users can borrow more than they should: Since the debt calculation underestimates the actual debt, borrowers may withdraw excessive amounts, leaving insufficient collateral to cover their obligations.

  • Liquidations may be delayed or skipped: Because the system underestimates debt, users who should be liquidated might not be flagged as undercollateralized in time.

  • Protocol insolvency risk: The incorrect debt calculations can lead to situations where debt surpasses collateral but is not properly recognized, causing financial losses to lenders and liquidity providers.

Tools Used

Manual Code Review – Identified incorrect debt tracking logic in borrow().

Recommendations

Implement a similar balanceIncrease calculation as used in the DebtToken::mint() function the correctly account for the balance increase in between borrows in the LendingPool.

Updates

Lead Judging Commences

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

LendingPool::borrow tracks debt as user.scaledDebtBalance += scaledAmount while DebtToken mints amount+interest, leading to accounting mismatch and preventing full debt repayment

Support

FAQs

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