Core Contracts

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

Invalid Token Accounting In `RAACMinter::mintRewards` Leading to Failed Reward Distributions

Summary

The mintRewards function in the RAACMinter contract relies on an internal accounting variable excessTokens to determine whether to mint additional tokens before transferring rewards. In scenarios where excessTokens exceeds the reward amount, no minting occurs, potentially causing the subsequent token transfer to revert if the contract’s balance is insufficient.

Vulnerability Details

Faulty Accounting Logic

function tick() external {
// ...
excessTokens += amountToMint; // updates excessTokens
raacToken.mint(address(stabilityPool), amountToMint); // Mints to StabilityPool
}
function mintRewards(address to, uint256 amount) external {
uint256 toMint = excessTokens >= amount ? 0 : amount - excessTokens;
excessTokens = excessTokens >= amount ? excessTokens - amount : 0;
if (toMint > 0) {
raacToken.mint(address(this), toMint); // Mints to RAACMinter
}
raacToken.safeTransfer(to, amount); // Transfers from RAACMinter
}
  • Issue: The tick() function mints tokens directly to the StabilityPool but increases excessTokens as if held by RAACMinter.

  • Result: excessTokens becomes an invalid indicator of RAACMinter's actual balance.

Proof of Concept

Scenario Setup

  1. Initial State:

    • RAACMinter Balance: 0 RAAC

    • excessTokens: 0 RAAC

  2. Call tick():

    • amountToMint = 100 RAAC

    • excessTokens becomes 100

    • 100 RAAC minted directly to StabilityPool

  3. Current State:

    • RAACMinter Balance: 0 RAAC

    • StabilityPool Balance: 100 RAAC

    • excessTokens: 100 RAAC

  4. Call mintRewards(50):

    • toMint = 0 (since 100 >= 50)

    • excessTokens reduced to 50

    • Attempt to transfer 50 RAAC from RAACMinter (balance = 0)

Execution Result

// Fails with ERC20: transfer amount exceeds balance
raacToken.safeTransfer(to, 50);

Impact

  • Legitimate reward transfers may revert unexpectedly, disrupting the reward distribution process.

  • All reward distributions fail when tick() has been called previously

Tools Used

  1. Manual Review

  2. Hardhat Test:

it('Fails when excessTokens > actual balance', async () => {
await minter.tick(); // Mints to StabilityPool
await expect(minter.mintRewards(user.address, 50))
.to.be.revertedWith('ERC20: transfer amount exceeds balance');
});

Recommendations

function tick() external nonReentrant whenNotPaused {
if (emissionUpdateInterval == 0 || block.timestamp >= lastEmissionUpdateTimestamp + emissionUpdateInterval) {
updateEmissionRate();
}
uint256 currentBlock = block.number;
uint256 blocksSinceLastUpdate = currentBlock - lastUpdateBlock;
if (blocksSinceLastUpdate > 0) {
uint256 amountToMint = emissionRate * blocksSinceLastUpdate;
if (amountToMint > 0) {
- excessTokens += amountToMint;
lastUpdateBlock = currentBlock;
raacToken.mint(address(stabilityPool), amountToMint);
emit RAACMinted(amountToMint);
}
}
}
Updates

Lead Judging Commences

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

RAACMinter wrong excessTokens accounting in tick function

inallhonesty Lead Judge 4 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.