Core Contracts

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

`DebtToken::mint()` function incorrectly adds `balanceIncrease` to amount when minting new tokens, causing excess debt to be minted

Summary

The mint function in DebtToken.sol incorrectly adds the balanceIncrease to the amount when minting new tokens. This results in double-counting of interest accrual since the balanceIncrease is already accounted for in the user's balance through the index scaling mechanism.

Vulnerability Details

In the mint function, after calculating the balanceIncrease (which represents accrued interest), the function adds it to the original amount to create amountToMint:

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());
}

This is incorrect because the balanceIncrease is already accounted for in the user's balance through the index scaling mechanism. The balance increase due to interest accrual is automatically reflected when the user's balance is queried through balanceOf(), which scales the stored balance by the current index.

Impact

This vulnerability leads to users being minted more debt tokens than they should receive, effectively double-counting the interest accrual. This results in users owing more debt than they actually borrowed, which could lead to:

  • Unfair liquidations

  • Protocol insolvency due to accounting errors

  • Users paying more interest than they should

The impact is HIGH severity because it directly affects the core accounting mechanism of the protocol and results in direct loss of funds for users through excess debt.

Tools Used

Manual Review

Proof of Concept

Add the following test case to the test/unit/core/tokens/DebtToken.test.js file:

it("debt mint bigger amount", async function () {
const mintAmount = ethers.parseEther("100");
const index = RAY; // Initial index
await debtToken.connect(mockLendingPoolSigner).mint(user1.address, user1.address, mintAmount, index);
{
const userBalance = await debtToken.balanceOf(user1.address);
expect(userBalance).to.equal(mintAmount);
console.log("User1 balance after mint:", userBalance.toString()); // 100,000000000000000000
}
const newIndex = RAY * 11n / 10n; // 1.1 RAY = 10% increase
// Setting new normalized debt
await mockLendingPool.setNormalizedDebt(newIndex);
{
const userBalance = await debtToken.balanceOf(user1.address);
const expectedBalance = mintAmount * newIndex / RAY;
expect(userBalance).to.equal(expectedBalance);
console.log("User1 balance after interest:", userBalance.toString()); // 110,000000000000000000
}
// Increase User1 debt by the same initial amount
await debtToken.connect(mockLendingPoolSigner).mint(user1.address, user1.address, mintAmount, newIndex);
{
const userBalance = await debtToken.balanceOf(user1.address);
const accumulatedDebt = mintAmount * newIndex / RAY;
const expectedNewBalance = accumulatedDebt + mintAmount;
console.log("User1 balance after second mint:", userBalance.toString()); // 221,000000000000000000
console.log("Expected balance:", expectedNewBalance.toString()); // 210,000000000000000000
console.log("Accumulated debt:", accumulatedDebt.toString()); // 110,000000000000000000
console.log("Minted amount:", mintAmount.toString()); // 100,000000000000000000
expect(userBalance).to.gt(expectedNewBalance); // Should be equal but userBalance is greater because of the extra minted amount
}
});

Recommendations

Remove the addition of balanceIncrease when minting new tokens. The mint function should simply mint the requested amount:

function mint(
address user,
address onBehalfOf,
uint256 amount,
uint256 index
) external override onlyReservePool returns (bool, uint256, uint256) {
// ... existing code ...
- _mint(onBehalfOf, amountToMint.toUint128());
+ _mint(onBehalfOf, amount.toUint128());
// ... existing code ...
}
Updates

Lead Judging Commences

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

DebtToken::mint miscalculates debt by applying interest twice, inflating borrow amounts and risking premature liquidations

Support

FAQs

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