Hoarder Can Deny Other Users from Buying Passes in FestivalPass.sol::buyPass
Description
- 
The buyPassfunction lets users purchase festival passes by sending the exact ETH price for the selected tier.
 
- 
However, there is no limit on the number of passes one wallet can buy. As a result, one user or bot can buy the entire available supply for a pass tier, preventing other participants from purchasing passes fairly. This creates an economic Denial-of-Service (DoS) condition where fair distribution is impossible. 
function buyPass(uint256 collectionId) external payable {
    require(collectionId == GENERAL_PASS || collectionId == VIP_PASS || collectionId == BACKSTAGE_PASS, "Invalid pass ID");
    
    require(msg.value == passPrice[collectionId], "Incorrect payment amount");
    require(passSupply[collectionId] < passMaxSupply[collectionId], "Max supply reached");
    
    _mint(msg.sender, collectionId, 1, "");
    ++passSupply[collectionId];
    uint256 bonus = (collectionId == VIP_PASS) ? 5e18 : (collectionId == BACKSTAGE_PASS) ? 15e18 : 0;
    if (bonus > 0) {
        BeatToken(beatToken).mint(msg.sender, bonus);
    }
    emit PassPurchased(msg.sender, collectionId);
}
Risk
Likelihood:
Impact:
- 
Legitimate users are denied access once supply is exhausted by the hoarder 
- 
The hoarder can resell passes at a markup, harming the fairness and reputation of the festival ecosystem 
Proof of Concept
Add this code to your FestivalPassTest.sol  file to confirm the proof
...
address public attacker = makeAddr("attacker");
...
function setUp() public {
       ...
       
       vm.deal(attacker, 1000 ether);
}
function test_Economic_DoS() public {
        
        vm.startPrank(attacker);
        for (uint256 i = 0; i < BACKSTAGE_MAX_SUPPLY; i++) {
            festivalPass.buyPass{value: BACKSTAGE_PRICE}(3);
        }
        vm.stopPrank();
        
        vm.startPrank(user1);
        vm.expectRevert("Max supply reached");
        festivalPass.buyPass{value: BACKSTAGE_PRICE}(3);
        vm.stopPrank();
        
    }
Recommended Mitigation
This change uses a single hasPurchasedTier flag to ensure each address can only purchase one pass in total, regardless of the tier. Once a buyer purchases any pass, further purchases are blocked, enforcing a strict one-tier-per-user limit.
+ mapping(address => bool) public hasPurchasedTier;
function buyPass(uint256 collectionId) external payable {
    require(collectionId == GENERAL_PASS || collectionId == VIP_PASS || collectionId == BACKSTAGE_PASS, "Invalid pass ID");
+   require(!hasPurchasedTier[msg.sender], "Address can only purchase one tier");
    require(msg.value == passPrice[collectionId], "Incorrect payment amount");
    require(passSupply[collectionId] < passMaxSupply[collectionId], "Max supply reached");
+   hasPurchasedTier[msg.sender] = true;
    _mint(msg.sender, collectionId, 1, "");
    ++passSupply[collectionId];
    uint256 bonus = (collectionId == VIP_PASS) ? 5e18 : (collectionId == BACKSTAGE_PASS) ? 15e18 : 0;
    if (bonus > 0) {
        BeatToken(beatToken).mint(msg.sender, bonus);
    }
    emit PassPurchased(msg.sender, collectionId);
}