Beatland Festival

AI First Flight #4
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Economic Attack via Unlimited BEAT Inflation

Root + Impact

Description

  • Normal behavior

    The attendPerformance function rewards users with BEAT tokens when they attend an active performance.
    The reward amount is calculated based on a base reward configured by the organizer and a multiplier determined by the type of festival pass held by the user.

  • Specific issue

    The BEAT reward mechanism has no upper bound or emission control.
    The organizer can set arbitrarily large baseReward values for performances, and users holding high-tier passes (e.g. BACKSTAGE) can repeatedly mint large amounts of BEAT tokens through attendance.
    This allows uncontrolled token inflation, enabling economic attacks that devalue BEAT and undermine the memorabilia redemption system.

// Root cause in the codebase with @> marks to highlight the relevant section
function attendPerformance(uint256 performanceId) external {
...
uint256 multiplier = getMultiplier(msg.sender);
// @> Unbounded BEAT emission controlled by organizer-defined baseReward
BeatToken(beatToken).mint(
msg.sender,
performances[performanceId].baseReward * multiplier
);
}

Risk

Likelihood:

  • The organizer can freely configure baseReward when creating performances, with no maximum or sanity checks.


  • Users can attend multiple performances over time, allowing cumulative BEAT minting without any global, per-user, or per-performance cap.

Impact:

  • BEAT token supply can inflate rapidly, leading to loss of token value.

  • Attackers can acquire large amounts of BEAT cheaply and redeem memorabilia NFTs at a fraction of their intended economic cost.

Proof of Concept

function test_EconomicAttack_UnboundedBEATInflation() public {
// Organizer configures BACKSTAGE pass
vm.prank(organizer);
festivalPass.configurePass(BACKSTAGE_PASS, 1 ether, 100);
// User buys BACKSTAGE pass
vm.deal(user1, 1 ether);
vm.prank(user1);
festivalPass.buyPass{value: 1 ether}(BACKSTAGE_PASS);
// Organizer creates a performance with very large reward
vm.prank(organizer);
uint256 performanceId = festivalPass.createPerformance(
block.timestamp + 1,
1 hours,
1_000_000e18 // extremely large base reward
);
// Fast forward to performance time
vm.warp(block.timestamp + 2);
// User attends performance and mints massive BEAT amount
vm.prank(user1);
festivalPass.attendPerformance(performanceId);
// User now owns an excessive amount of BEAT
assertGt(beatToken.balanceOf(user1), 1_000_000e18);
}

Uncontrolled token inflation, enabling economic attacks

Recommended Mitigation

Introduce explicit emission controls and caps on BEAT minting.

+ uint256 public constant MAX_TOTAL_BEAT_SUPPLY = 1_000_000_000e18;
+ uint256 public totalMinted;
function attendPerformance(uint256 performanceId) external {
...
- BeatToken(beatToken).mint(msg.sender, reward);
+ require(totalMinted + reward <= MAX_TOTAL_BEAT_SUPPLY, "Emission cap exceeded");
+ totalMinted += reward;
+ BeatToken(beatToken).mint(msg.sender, reward);
}
function createPerformance(
uint256 startTime,
uint256 duration,
uint256 reward
) external onlyOrganizer returns (uint256) {
+ require(reward <= MAX_BASE_REWARD, "Reward too large");
...
}

Alternative mitigations include:

  • Per-performance reward pools

  • Per-user lifetime reward caps

  • Epoch-based emission schedules with decay

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 2 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!