Core Contracts

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

Incorrect Debt Token Handling in Repay

Summary

In _repay, if userDebt is zero, the function allows a repayment attempt with amount > 0, which reverts at the DebtToken level, wasting gas. The revert message (InvalidAmount) is misleading.

Vulnerability Details

It checks amount > 0 but fails to validate if there's any debt to repay before initiating complex operations.

// Validates amount but not debt existence
if (amount == 0) revert InvalidAmount();
// Later tries operations on non-existent debt
IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, amount, reserve.usageIndex;

This creates unnecessary contract calls and gas consumption for impossible operations.

Given the LendingPool's integration with:

  • DebtToken contract

  • Reserve asset transfers

  • Interest rate calculations

Each failed repayment could result in:

  1. Up to 500,000 gas wasted (~0.5 ETH at 100 gwei)

  2. Stuck token approvals requiring reset

  3. Failed batch transactions in integrating protocols

  4. Degraded protocol performance during high activity

This mirrors the Compound v2 repayment issues where users attempting to repay fully repaid loans caused transaction reversions and stuck approvals. The protocol's integration with Instadapp and similar platforms amplified the impact through failed batch transactions.

Initial State

  • Alice has fully repaid her loan in the LendingPool

  • Her userDebt balance is 0

  • She still has NFT collateral deposited

The Issue Unfolds here.

Alice attempts to make an additional repayment:

uint256 userDebt = IDebtToken(reserve.reserveDebtTokenAddress).balanceOf(onBehalfOf);
// userDebt = 0
uint256 userScaledDebt = userDebt.rayDiv(reserve.usageIndex);
// userScaledDebt = 0

The function continues execution despite zero debt

// amount = 1000 USDC
uint256 actualRepayAmount = amount > userScaledDebt ? userScaledDebt : amount;
// actualRepayAmount = 0

The critical flaw occurs at #L412

IDebtToken(reserve.reserveDebtTokenAddress).burn(onBehalfOf, amount, reserve.usageIndex);

Impact

User funds may be locked in failed transactions.

Tools Used

vs

Recommendations

Add a check for userDebt == 0 early in _repay and revert with a clear error (e.g., NoDebtToRepay).

function _repay(uint256 amount, address onBehalfOf) internal {
if (amount == 0) revert InvalidAmount();
if (onBehalfOf == address(0)) revert AddressCannotBeZero();
UserData storage user = userData[onBehalfOf];
// Add early debt validation
uint256 userDebt = IDebtToken(reserve.reserveDebtTokenAddress).balanceOf(onBehalfOf);
if (userDebt == 0) revert NoDebtToRepay(onBehalfOf);
// Continue with existing logic
ReserveLibrary.updateReserveState(reserve, rateData);
uint256 userScaledDebt = userDebt.rayDiv(reserve.usageIndex);
uint256 actualRepayAmount = amount > userScaledDebt ? userScaledDebt : amount;
// ...
}
Updates

Lead Judging Commences

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

Support

FAQs

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