Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: high
Likelihood: medium
Invalid

BeatToken Minting Without Supply Cap Controls

Root + Impact

The BeatToken contract has no maximum supply limits or minting rate controls, allowing the festival contract to mint unlimited tokens and potentially cause severe inflation.

Description

  • The normal behavior should include supply caps or rate limiting to maintain token economic stability

  • The current implementation allows unlimited minting by the festival contract without any validation or constraints

function mint(address to, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Mint");
@> _mint(to, amount); // No supply limit validation
@> // No rate limiting
@> // No maximum supply check
}
function burnFrom(address from, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Burn");
@> _burn(from, amount); // Burns without validation
}

Risk

Likelihood:

  • Compromised or malicious festival contract could mint excessive tokens

  • Bugs in reward calculations could trigger large unexpected mints

Impact:

  • Hyperinflation destroying token value and ecosystem

  • Economic attack vector through excessive reward manipulation

  • Loss of user confidence in token stability

  • Potential market manipulation through controlled supply inflation

Proof of Concept

function testUnlimitedMinting() public {
// Festival contract can mint unlimited amounts
vm.startPrank(address(festival));
// Mint massive amount (simulating compromised contract)
uint256 excessiveAmount = type(uint256).max / 2; // Half of max uint256
beatToken.mint(attacker, excessiveAmount);
assert(beatToken.balanceOf(attacker) == excessiveAmount);
assert(beatToken.totalSupply() == excessiveAmount);
// This breaks the entire token economy
// Normal users' tokens become worthless due to inflation
vm.stopPrank();
// Simulate massive performance rewards causing inflation
vm.startPrank(address(festival));
for(uint i = 0; i < 1000; i++) {
beatToken.mint(address(uint160(i + 1000)), 1000000 ether);
}
// Total supply now extremely inflated
assert(beatToken.totalSupply() > 1000000000 ether);
vm.stopPrank();
}

Recommended Mitigation

+ uint256 public constant MAX_TOTAL_SUPPLY = 100_000_000 ether; // 100M tokens max
+ uint256 public constant MAX_MINT_PER_TX = 10_000 ether; // Per transaction limit
+ mapping(address => uint256) public dailyMinted; // Rate limiting
+ mapping(address => uint256) public lastMintDay; // Rate limiting
function mint(address to, uint256 amount) external {
require(msg.sender == festivalContract, "Only_Festival_Mint");
+ require(to != address(0), "Cannot mint to zero address");
+ require(amount > 0, "Amount must be greater than 0");
+ require(amount <= MAX_MINT_PER_TX, "Exceeds per-transaction limit");
+ require(totalSupply() + amount <= MAX_TOTAL_SUPPLY, "Exceeds maximum supply");
+
+ // Daily rate limiting per recipient
+ uint256 currentDay = block.timestamp / 1 days;
+ if (lastMintDay[to] < currentDay) {
+ dailyMinted[to] = 0;
+ lastMintDay[to] = currentDay;
+ }
+ require(dailyMinted[to] + amount <= MAX_MINT_PER_TX * 10, "Daily mint limit exceeded");
+ dailyMinted[to] += amount;
_mint(to, amount);
+ emit TokensMinted(to, amount, totalSupply());
}
+ function burnFrom(address from, uint256 amount) external {
+ require(msg.sender == festivalContract, "Only_Festival_Burn");
+ require(amount > 0, "Amount must be greater than 0");
+ require(balanceOf(from) >= amount, "Insufficient balance to burn");
+
_burn(from, amount);
+ emit TokensBurned(from, amount, totalSupply());
+ }
+ event TokensMinted(address indexed to, uint256 amount, uint256 newTotalSupply);
+ event TokensBurned(address indexed from, uint256 amount, uint256 newTotalSupply);
Updates

Lead Judging Commences

inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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