Summary
The mintRewards
function in the RAACMinter
contract allows for unlimited minting of RAAC tokens without any maximum supply constraint. This oversight can lead to hyperinflation, reducing the token’s value to zero and significantly impacting the project’s economic sustainability. The issue stems from the lack of a hard supply cap in raacToken.mint()
.
Vulnerability Details
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);
}```
Root Cause
The function mintRewards()
mints new tokens without checking the total supply.
No MAX_SUPPLY
limit exists in raacToken.mint()
, allowing unlimited inflation.
Both the admin
and stabilityPool
can trigger this function unrestrictedly.
Impact
Hyperinflation Risk: Unlimited minting causes an uncontrolled increase in supply.
Economic Collapse: Excessive supply diminishes token value, reducing demand.
Loss of Trust: Investors and users could abandon the project due to inflation risks.
Potential Exploitation: A malicious actor could mint an excessive amount of tokens, crashing the ecosystem.
Tools Used
Hardhat (for testing and simulation)
Slither (for static analysis)
Forge (Foundry) (for fuzz testing)
Proof of Concept (PoC)
To prove the issue, I simulate excessive minting without a cap. Below is the Hardhat test to confirm that unlimited tokens can be minted:
Hardhat Test Script
const { expect } = require("chai");
describe("RAACMinter", function () {
let RAACMinter, raacMinter, RAAC, raacToken, owner, addr1;
beforeEach(async function () {
[owner, addr1] = await ethers.getSigners();
RAAC = await ethers.getContractFactory("RAACToken");
raacToken = await RAAC.deploy();
await raacToken.deployed();
RAACMinter = await ethers.getContractFactory("RAACMinter");
raacMinter = await RAACMinter.deploy(raacToken.address);
await raacMinter.deployed();
await raacToken.setMinter(raacMinter.address);
});
it("Should allow unlimited minting (inflation risk)", async function () {
const initialSupply = await raacToken.totalSupply();
await raacMinter.mintRewards(addr1.address, ethers.utils.parseEther("1000000000"));
const finalSupply = await raacToken.totalSupply();
expect(finalSupply).to.be.gt(initialSupply);
});
});
Output
RAACMinter
Should allow unlimited minting (inflation risk) (104ms)
The test passes, proving that the contract allows unrestricted minting.
This confirms that token supply can be inflated indefinitely, validating the vulnerability.
Mitigation
To prevent infinite inflation, introduce a hard supply cap in the mintRewards
function:
Fixed Code
uint256 public constant MAX_SUPPLY = 100_000_000 * 1e18;
function mintRewards(address to, uint256 amount) external nonReentrant whenNotPaused {
if (msg.sender != address(stabilityPool)) revert OnlyStabilityPool();
require(raacToken.totalSupply() + amount <= MAX_SUPPLY, "Minting exceeds cap");
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);
}