Core Contracts

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

`DebtToken::totalSupply` returns the total supply with wrong scaled calculation

Summary

The DebtToken::totalSupply function incorrectly scales the total supply using a division (rayDiv) instead of a multiplication (rayMul) by the normalized debt. This results in an underestimation of the total debt supply, which in turn can lead to inaccurate accounting in the lending pool reserve and potential downstream complications.

Vulnerability Details

The current implementation is:

function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
uint256 scaledSupply = super.totalSupply();
return scaledSupply.rayDiv(ILendingPool(_reservePool).getNormalizedDebt());
}

Since the user's balance function scales the stored value by multiplying with the normalized debt, the totalSupply should also use a multiplication (rayMul) on the stored scaled total to yield the correct underlying debt amount. Using rayDiv underestimates the debt supply.
Proof of Concept

In DebtToken.test.js, add or refer to the following test case:

it("wrong calculation of total supply in debt token", async function () {
const mintAmount = ethers.parseEther("100");
const index = RAY; // Initial index
console.log("Attempting to mint", mintAmount.toString(), "tokens with index", index.toString());
const tx = await debtToken.connect(mockLendingPoolSigner).mint(user1.address, user1.address, mintAmount, index);
await tx.wait();
const totalSupply = await debtToken.totalSupply();
console.log("totalSupply:", totalSupply.toString());
expect(totalSupply).to.equal(mintAmount);
const index2 = index * ethers.getBigInt("2");
await mockLendingPool.setNormalizedDebt(index2);
// With correct scaling, after updating the normalized debt, the total supply should double.
const totalSupplyAfter = await debtToken.totalSupply();
console.log("totalSupply:", totalSupplyAfter.toString());
// The test will currently fail because totalSupplyAfter is less than mintAmount, demonstrating the bug.
expect(totalSupplyAfter).to.greaterThan(mintAmount);
});

Impact

  • Inaccurate Debt Accounting: The total supply is underestimated, which can lead to an incorrect value for total usage in the reserve struct.

  • Downstream Miscalculations: Since the total supply is used in risk and liquidation calculations, this error might lead to improper collateralization assessments or liquidation events.

Tools Used

  • Manual Code Review

  • Unit Testing with Hardhat/Foundry

Recommendations

Modify the total supply calculation to use multiplication (rayMul) with the normalized debt. Below is the suggested patch:

function totalSupply() public view override(ERC20, IERC20) returns (uint256) {
uint256 scaledSupply = super.totalSupply();
- 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.