Beatland Festival

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

Pass Supply Reset Allows Over-Minting in FestivalPass

Root + Impact

Description

  • The configurePass function allows the organizer to reset the maxSupply of a pass at any time, even after the original cap has been reached. This enables the organizer to bypass the intended supply limit and sell unlimited passes for a given pass ID, violating scarcity guarantees and potentially harming users who purchased under the assumption of a fixed supply.

// FestivalPass.sol
function configurePass(uint256 passId, uint256 price, uint256 maxSupply) external onlyOrganizer {
passPrice[passId] = price;
passMaxSupply[passId] = maxSupply;
// No restriction on changing maxSupply after sales
@> passSupply[passId] = 0; // Reset current supply
}

Risk

Likelihood:

  • Whenever the organizer wants to sell more passes after reaching the original cap, they can simply call configurePass to reset or increase the supply.

  • This can occur at any time, especially if the organizer is malicious or under external pressure to generate more revenue.

Impact:

  • Users who purchased passes expecting a capped supply can have their assets diluted, losing value and trust.

  • The economic integrity of the pass system is compromised, leading to potential financial and reputational damage for the project.

Proof of Concept

The following test demonstrates the vulnerability:

function test_PassSupplyResetAllowsOverMint() public {
// Buy two passes (should reach max supply)
vm.deal(user, 1 ether);
vm.prank(user);
festival.buyPass{value: 0.1 ether}(1);
vm.prank(user);
festival.buyPass{value: 0.1 ether}(1);
// At this point, max supply is reached
vm.expectRevert();
vm.prank(user);
festival.buyPass{value: 0.1 ether}(1);
// Organizer resets pass supply
festival.configurePass(1, 0.1 ether, 2);
// Now user can buy two more passes, exceeding the original cap
vm.prank(user);
festival.buyPass{value: 0.1 ether}(1);
vm.prank(user);
festival.buyPass{value: 0.1 ether}(1);
// Should not be able to buy a fifth pass
vm.expectRevert();
vm.prank(user);
festival.buyPass{value: 0.1 ether}(1);
}

Recommended Mitigation

Make the maxSupply for each pass immutable after initial configuration, or only allow increasing (never decreasing) the supply, and never allow resetting it to a lower value than the current supply, Or, set maxSupply only once and make it immutable. For example:

OPTION1:
function configurePass(uint256 passId, uint256 price, uint256 maxSupply) external onlyOrganizer {
passPrice[passId] = price;
- passMaxSupply[passId] = maxSupply;
+ require(maxSupply >= passMaxSupply[passId], "Cannot decrease max supply");
+ require(passMaxSupply[passId] == 0 || maxSupply > 0, "Cannot reset after initial set");
+ passMaxSupply[passId] = maxSupply;
}
OPTION2:
function configurePass(uint256 passId, uint256 price, uint256 maxSupply) external onlyOrganizer {
passPrice[passId] = price;
- passMaxSupply[passId] = maxSupply;
+ require(passMaxSupply[passId] == 0, "maxSupply already set");
+ passMaxSupply[passId] = maxSupply;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months 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.