Core Contracts

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

Lack of Access Control on mint() Function on RAACNFT

Summary

The mint() function allows any user to mint any token ID as long as they provide enough ERC-20 tokens. However, it does not restrict who can mint which token, potentially leading to front-running attacks, minting of reserved NFTs, and gas fee waste due to failed transactions.

Vulnerability Details

The contract does not enforce any restrictions on who can mint which tokenId. This creates two main issues:

  1. Users Can Attempt to Mint an Already Minted Token

    • The contract relies on the ERC-721 standard to prevent duplicate minting, meaning that failed mint attempts will still cost gas fees.

    • If two users try to mint the same tokenId, one will succeed, and the other will waste gas on a failed transaction.

  2. No Verification of Eligible Minters

    • The contract does not restrict minting to a whitelist or pre-sale buyers.

    • This allows anyone to mint any token, even if it was meant to be distributed via an auction or private sale.

Impact

  • Users attempting to mint already minted NFTs will pay gas fees for failed transactions.

  • Attackers can mint NFTs before intended users, leading to unfair distribution.

  • Anyone can mint any NFT, even if they were meant for specific users (pre-sale, auctions).

PoC

  • Alice submits a transaction to mint _tokenId = 10.

  • Bob sees Alice’s transaction in the mempool and front-runs it with a higher gas fee.

  • Bob’s transaction gets processed first, and he mints _tokenId = 10.

  • Alice’s transaction fails, and she loses gas fees.

const { expect } = require("chai");\
const { ethers } = require("hardhat");
describe("Unauthorized Minting in RAACNFT", function () {
let raacNFT, token, owner, alice, bob;
beforeEach(async function () {
[owner, alice, bob] = await ethers.getSigners();
const ERC20 = await ethers.getContractFactory("ERC20Mock");
token = await ERC20.deploy("Test Token", "TTK", owner.address, ethers.utils.parseEther("10000"));
await token.deployed();
const RAACNFT = await ethers.getContractFactory("RAACNFT");
raacNFT = await RAACNFT.deploy(token.address, owner.address);
await raacNFT.deployed();
await token.connect(alice).approve(raacNFT.address, ethers.utils.parseEther("100"));
await token.connect(bob).approve(raacNFT.address, ethers.utils.parseEther("100"));
});
it("should allow unauthorized users to mint the same NFT", async function () {
await raacNFT.connect(alice).mint(1, ethers.utils.parseEther("10"));
await expect(
raacNFT.connect(bob).mint(1, ethers.utils.parseEther("10"))
).to.be.revertedWith("ERC721: token already minted"); // ✅ Gas wasted due to lack of pre-check
});
});

Tools Used

Manual Review, Hardhat

Recommendations

Prevent Duplicate Minting Before Execution

Even though _safeMint() already prevents duplicate minting, explicitly checking _exists(tokenId) before execution will save gas fees on failed transactions.

function mint(uint256 _tokenId, uint256 _amount) public override {
// ✅ Explicit check uint256 price = raac_hp.tokenToHousePrice(_tokenId);
require(!_exists(_tokenId), "RAACNFT: Token ID already minted");
// ...
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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