Core Contracts

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

Incorrect excessTokens accounting in `RAACMinter::tick` corrupts reward minting logic

Incorrect excessTokens accounting in RAACMinter::tick corrupts reward minting logic

Description

The RAACMinter::mintRewards function improperly relies on the excessTokens value that's inflated by tick operations. While tokens are minted to StabilityPool, the Minter's tracking of excessTokens creates a phantom balance that enables double-counting and incorrect minting decisions:

  1. mintRewards uses excessTokens to determine if new minting is needed

  2. Actual tokens exist in StabilityPool while excessTokens suggests available balance in Minter

  3. Results in either:

    • Under-minting: If excessTokens > real Minter balance, transfers fail from empty contract

    • Over-minting: If excessTokens < real needed amount, incorrect mint amount is calculated

Proof of Concept

Add this test case to RAACMinter.test.js:

it("demonstrates mintRewards failure due to incorrect excessTokens", async function () {
// Setup initial conditions
await ethers.provider.send("evm_mine");
await raacMinter.tick();
// Get post-tick values
const excessAfterTick = await raacMinter.excessTokens();
const minterBalance = await raacToken.balanceOf(raacMinter.getAddress());
const stabilityPoolBalance = await raacToken.balanceOf(
stabilityPool.getAddress()
);
// Verify accounting mismatch
expect(excessAfterTick).to.be.gt(0);
expect(minterBalance).to.equal(0);
expect(stabilityPoolBalance).to.equal(excessAfterTick);
// Impersonate StabilityPool to call mintRewards
const stabilityPoolAddress = await stabilityPool.getAddress();
await hre.network.provider.request({
method: "hardhat_impersonateAccount",
params: [stabilityPoolAddress],
});
await ethers.provider.send("hardhat_setBalance", [
stabilityPoolAddress,
"0x1000000000000000000",
]);
const impersonatedPool = await ethers.getSigner(stabilityPoolAddress);
// Attempt transfer that should fail - update expectation
const transferAmount = excessAfterTick;
await expect(
raacMinter
.connect(impersonatedPool)
.mintRewards(user1.address, transferAmount)
).to.be.revertedWithCustomError(
raacToken,
"ERC20InsufficientBalance" // Matches OpenZeppelin's SafeERC20
);
// Verify failed state
expect(await raacToken.balanceOf(user1.address)).to.equal(0);
expect(await raacMinter.excessTokens()).to.equal(excessAfterTick); // No change
// ExcessTokens remains unchanged due to transaction revert
expect(await raacMinter.excessTokens()).to.equal(excessAfterTick);
});
Impact

High Severity - This mismatch between virtual and real balances can lead to:

  1. Failed reward distributions breaking core protocol functionality

  2. Unintended token inflation when minting to cover non-existent deficits

  3. Accounting errors that compound over time making reconciliation impossible

Recommendation
  • Primary Fix: Remove excessTokens tracking for StabilityPool-minted amounts

// In tick()
- excessTokens += amountToMint;
// Remove line entirely
  • Add balance validation in mintRewards

function mintRewards(address to, uint256 amount) external {
+ uint256 availableBalance = raacToken.balanceOf(address(this));
+ require(availableBalance >= amount, "Insufficient minted tokens");
uint256 toMint = excessTokens >= amount ? 0 : amount - excessTokens;
// ... rest of logic ...
}
Updates

Lead Judging Commences

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

RAACMinter wrong excessTokens accounting in tick function

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

RAACMinter wrong excessTokens accounting in tick function

Support

FAQs

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

Give us feedback!