Core Contracts

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

DebtToken's totalSupply is incorrectly calculated

Summary

When calculating DebtToken's totalSupply, scaledSupply is divided by reserve.usageIndex. However, correct amount would be scaledSupply * reserve.usageIndex. This inconsistency affects calculation of reserve's utilizationRate, currentUsageRateand currentLiquidityRate. Thus, all three important calculations will be incorrect.

Vulnerability Details

DebtToken's totalSupply is calculated as the following:

function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
uint256 scaledSupply = super.totalSupply();
@> return scaledSupply.rayDiv(ILendingPool(_reservePool).getNormalizedDebt()); // @audit should be mul instead of div
}

In other words:

totalSupply = scaledSupply / reserve.usageIndex

However, individual account's debt is calculated differently:

function balanceOf(address account) public view override(ERC20, IERC20) returns (uint256) {
uint256 scaledBalance = super.balanceOf(account);
@> return scaledBalance.rayMul(ILendingPool(_reservePool).getNormalizedDebt());
}

In other words, account's balance is calculated as the following:

balance = scaledBalance * reserve.usageIndex

Indeed, multiplication is the correct operation. Because when DebtToken is minted or burned, scaledAmount is calculated as amount / reserve.usageIndex:

uint256 scaledAmount = amount.rayDiv(ILendingPool(_reservePool).getNormalizedDebt());
super._update(from, to, scaledAmount);
emit Transfer(from, to, amount);

To make it very clear, the following equation holds:

scaledAmount = amount / reserve.usageIndex

which implies that

amount = scaledAmount * reserve.usageIndex

Thus, account balance and total supply should be multiplied by reserve.usageIndex

Impact

reserve.totalUsage is equal to DebtToken's totalSupply. You can confirm that in borrow, repay and liquidation logic.

reserve.totalUsage is equal to pool's total debt. And pool's totalDebt takes part in calculating utilizationRate

And utilizationRate takes part in calculating currentUsageRate and currentLiquidityRate.

Thus important parameters will be calculated incorrectly and protocol will suffer from fund loss and reputation damage.

More specifically:

  • totalSupply is deflated

  • utilzationRate, is deflated

  • Thus borrowRate will be lower when actual utilizationRate is greater than optimizationUtilizationRate

Tools Used

Manual Review

Recommendations

- return scaledSupply.rayDiv(ILendingPool(_reservePool).getNormalizedDebt());
+ return scaledSupply.rayMul(ILendingPool(_reservePool).getNormalizedDebt());
Updates

Lead Judging Commences

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

DebtToken::totalSupply incorrectly uses rayDiv instead of rayMul, severely under-reporting total debt and causing lending protocol accounting errors

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

DebtToken::totalSupply incorrectly uses rayDiv instead of rayMul, severely under-reporting total debt and causing lending protocol accounting errors

Support

FAQs

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