Core Contracts

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

Incorrect Accounting of Excess Tokens in RAACMinter Leading to Inability to Mint Rewards Using mintRewards

Finding Description and Impact

The tick function incorrectly increases the excessTokens variable when minting tokens to the Stability Pool. This creates a discrepancy in the accounting of excessTokens, as the contract assumes it holds these tokens when they have already been minted and transferred to the Stability Pool.

Additionally, the mintRewards function uses excessTokens to reduce the amount of tokens that need to be minted. However, due to the incorrect accounting in tick, the excessTokens variable may not accurately reflect the tokens held by the contract, leading to a revert when using safeTransferdue to insufficent funds.

Impact:

  • The protocol may inefficiently distribute tokens, leading to potential loss of funds or unfair advantages for certain users.


Proof of Concept

  1. Code Reference:

    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; // Increases excessTokens
    lastUpdateBlock = currentBlock;
    raacToken.mint(address(stabilityPool), amountToMint); // Mints and transfers tokens to stabilityPool
    emit RAACMinted(amountToMint);
    }
    }
    }
    function mintRewards(address to, uint256 amount) external nonReentrant whenNotPaused {
    if (msg.sender != address(stabilityPool)) revert OnlyStabilityPool();
    uint256 toMint = excessTokens >= amount ? 0 : amount - excessTokens;
    excessTokens = excessTokens >= amount ? excessTokens - amount : 0;
    if (toMint > 0) {
    raacToken.mint(address(this), toMint);
    }
    raacToken.safeTransfer(to, amount);
    emit RAACMinted(amount);
    }
  2. Steps to Reproduce:

    Initial Setup

    1. Deploy the RAACMinter contract.

    2. Set the emission rate to 100 RAAC per block.

    3. Assume the Stability Pool is empty initially, and excessTokens is 0.


    Step 1: Bob Deposits into Stability Pool

    • Bob deposits 1000 crvUSD into the Stability Pool.

    • The Stability Pool now has 1000 crvUSD in total deposits.


    Step 2: Call tick to Mint Tokens

    • The tick function is called after 10 blocks.

    • The amount to mint is calculated as:

      amountToMint = emissionRate * blocksSinceLastUpdate
      = 100 RAAC/block * 10 blocks
      = 1000 RAAC
    • The tick function increases excessTokens by 1000 RAAC:

      excessTokens += amountToMint; // excessTokens = 1000 RAAC
    • The function mints 1000 RAAC directly to the Stability Pool:

      raacToken.mint(address(stabilityPool), 1000 RAAC);

    Step 3: Stability Pool Requests Bobs Rewards

    • Stability Pool** **calls mintRewards to claim Bobs rewards.

    • The Stability Pool requests 500 RAAC to distribute to Bob.

    • The mintRewards function calculates:

      uint256 toMint = excessTokens >= amount ? 0 : amount - excessTokens;
      = 1000 >= 500 ? 0 : 500 - 1000
      = 0
      excessTokens = excessTokens >= amount ? excessTokens - amount : 0;
      = 1000 >= 500 ? 1000 - 500 : 0
      = 500
    • Since toMint = 0, the function does not mint any additional tokens.

    • The function attempts to transfer 500 RAAC to Bob from the RAACMinter contract:

      raacToken.safeTransfer(to, 500 RAAC);

What Happens Next?

The RAACMinter contract does not hold any RAAC tokens because they were minted directly to the Stability Pool.

The call to raacToken.safeTransfer(Bob, 500 RAAC) will revert because the RAACMinter contract has an insufficient balance of RAAC tokens.


Recommended Mitigation Steps

Remove excessTokens Entirely:

  • Since tokens are minted directly to the Stability Pool, the excessTokens variable is not needed.

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) {
lastUpdateBlock = currentBlock;
raacToken.mint(address(stabilityPool), amountToMint); // Mint to the stability pool contract
emit RAACMinted(amountToMint);
}
}
}
  • Modify the mintRewards function to always mint the full amount of tokens requested and transfer them to the recipient.

function mintRewards(address to, uint256 amount) external nonReentrant whenNotPaused {
if (msg.sender != address(stabilityPool)) revert OnlyStabilityPool();
// Always mint the full amount requested
raacToken.mint(address(this), amount);
raacToken.safeTransfer(to, amount);
emit RAACMinted(amount);
}
  1. Pros of This Approach:

    • Simpler logic, as excessTokens is no longer needed.

    • Tokens are minted on-demand when rewards are requested.

  2. Cons of This Approach:

    • This approach may lead to more frequent minting operations, which could increase gas costs.

    • It removes the optimization of using excessTokens to reduce the number of minting operations.

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!