Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Valid

Pass Supply Reset Allows Exceeding Maximum Supply Limits

Root + Impact

Description

The configurePass() function is meant to allow event organizers to update the pricing and supply limits of a given pass type (e.g., General, VIP, or Backstage). However, it resets the internal counter tracking how many passes have already been minted (passSupply[passId] = 0) without burning or revoking the previously issued tokens.

This causes the contract to "forget" how many tokens were already distributed, which allows new passes to be minted beyond the configured maxSupplyviolating the pass type’s intended scarcity.

function configurePass(
uint256 passId,
uint256 price,
uint256 maxSupply
) external onlyOrganizer {
require(passId == GENERAL_PASS || passId == VIP_PASS || passId == BACKSTAGE_PASS, "Invalid pass ID");
require(price > 0, "Price must be greater than 0");
require(maxSupply > 0, "Max supply must be greater than 0");
passPrice[passId] = price;
passMaxSupply[passId] = maxSupply;
passSupply[passId] = 0; // @> This line resets current supply to 0
}

Risk Assessment

Likelihood:

  • Organizers will naturally want to adjust pricing as demand changes during festival sales

  • The function is designed for configuration updates, making calls highly probable

  • No warnings or documentation about the supply reset side effect

Impact:

  • Breaks tokenomics by allowing unlimited minting beyond intended scarcity limits

  • Devalues existing pass holders' investments through artificial inflation

  • Violates smart contract invariants regarding maximum supply constraints

Proof of Concept

// Initial state: Configure BACKSTAGE passes
configurePass(3, 1 ether, 100); // Max supply: 100
// Users purchase all 100 BACKSTAGE passes
// passSupply[3] = 100, passMaxSupply[3] = 100
// Organizer updates price (normal operation)
configurePass(3, 2 ether, 100); // Updates price to 2 ETH
// BUG: passSupply[3] is now 0, but holders still have their 100 passes
// Attack: Can now mint 100 MORE passes
// Total circulating: 200 BACKSTAGE passes (100 over maximum)

Recommended Mitigation

function configurePass(
uint256 passId,
uint256 price,
uint256 maxSupply
) external onlyOrganizer {
require(passId == GENERAL_PASS || passId == VIP_PASS || passId == BACKSTAGE_PASS, "Invalid pass ID");
require(price > 0, "Price must be greater than 0");
- require(maxSupply > 0, "Max supply must be greater than 0");
+ require(maxSupply >= passSupply[passId], "Max supply cannot be less than current supply");
passPrice[passId] = price;
passMaxSupply[passId] = maxSupply;
- passSupply[passId] = 0; // Reset current supply
+ // Remove this line - preserve current supply counter
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 28 days ago
Submission Judgement Published
Validated
Assigned finding tags:

configurePass resets the current pass supply circumventing the max supply check

This is not acceptable as high because any attack vectors related to organizer trying to milk ETH from participants is voided by the fact that the organizer is trusted.

Support

FAQs

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