Core Contracts

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

Unit Mismatch in Debt Token Balance Check Allows Incorrect Token Burns and wrong amount used in `_burn`

Summary

The burn function in the DebtToken contract compares an unscaled amount (in underlying asset units) with a scaled balance (in DebtToken units) without proper conversion, potentially allowing burning of more tokens than the user has.

Vulnerability Details

The vulnerability exists in the balance check:

// userBalance is in DebtToken units (scaled)
uint256 userBalance = balanceOf(from);
// amount is in underlying asset units (unscaled)
if(amount > userBalance){ // @audit Comparing incompatible units
amount = userBalance; // @audit Setting unscaled amount to scaled balance
}

The issue occurs because:

  • userBalance is in scaled DebtToken units

  • amount is in underlying asset units

  • Direct comparison of these different units is invalid

  • Setting amount = userBalance assigns scaled units to an unscaled variable

Correct comparison should be:

uint256 scaledAmount = amount.rayDiv(index);
if(scaledAmount > userBalance){
scaledAmount = userBalance;
amount = scaledAmount.rayMul(index);
}

Additionally, the amount used in _burn is in underlying assets units instead of scaled units leading to incorrect amount of tokens being burned

Impact

  • Allows burning incorrect amount of tokens

  • Leads to incorrect debt accounting

  • Potential for economic attacks

  • System invariants could be broken

Tools Used

Manual Review

Recommendations

Consider implementing the following changes:

function burn(
address from,
uint256 amount,
uint256 index
) external override onlyReservePool returns (uint256, uint256, uint256, uint256) {
if (from == address(0)) revert InvalidAddress();
if (amount == 0) {
return (0, totalSupply(), 0, 0);
}
uint256 userBalance = balanceOf(from);
uint256 scaledAmount = amount.rayDiv(index);
// Handle balance increase
uint256 balanceIncrease = 0;
if (_userState[from].index != 0 && _userState[from].index < index) {
uint256 borrowIndex = ILendingPool(_reservePool).getNormalizedDebt();
balanceIncrease = userBalance.rayMul(borrowIndex) - userBalance.rayMul(_userState[from].index);
scaledAmount += balanceIncrease.rayDiv(index);
}
_userState[from].index = index.toUint128();
// Compare in same units
if(scaledAmount > userBalance){
scaledAmount = userBalance;
amount = scaledAmount.rayMul(index);
}
if (scaledAmount == 0) revert InvalidAmount();
_burn(from, scaledAmount.toUint128());
emit Burn(from, scaledAmount, index);
return (scaledAmount, totalSupply(), amount, balanceIncrease);
}
Updates

Lead Judging Commences

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

Wrong balance check in RToken::burn

It's underlying vs underlying

Support

FAQs

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