Beatland Festival

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

# L-2: Unvalidated constructor addresses can permanently brick the protocol

Root + Impact

The constructor stores _organizer and _beatToken without any zero-address or wiring validation. organizer == address(0) bricks every organizer-only function (pass config, performances, collections), and a wrong/zero beatToken makes all VIP/BACKSTAGE bonuses, attendance rewards, and memorabilia redemptions revert — with no setter to correct the token address afterwards. A single deployment mistake is unrecoverable.

Description

// src/FestivalPass.sol:45-48
constructor(address _beatToken, address _organizer) ERC1155("ipfs://beatdrop/{id}") Ownable(msg.sender){
@> setOrganizer(_organizer);
@> beatToken = _beatToken;
}

organizer can later be fixed by setOrganizer (onlyOwner), so the zero-organizer case is recoverable by the owner. However beatToken is written only here and has no setter (grep beatToken shows the constructor is the sole write). If _beatToken is zero or not the deployed BeatToken, calls at FestivalPass.sol:82, :115, and :197 revert, and the token binding cannot be repaired. Combined with BeatToken's one-shot setFestivalContract (see I-1), a mismatched pair is permanent.

Risk

Likelihood: Low. Requires a deploy-script error or a wrong argument; not reachable by an external attacker.

Impact: Deployment-time denial of service for rewards and redemptions (and organizer functions if organizer is zero until fixed). Recoverable for organizer; unrecoverable for beatToken. No external funds are at risk, but a misconfigured launch is non-functional.

Proof of Concept

Deploying with _organizer = address(0) and then attempting to configure a pass reverts with "Only organizer can call this", showing the zero-organizer start state is unusable without an owner intervention.

// test/Exploits.t.sol
function test_ZeroOrganizerBricksConfiguration() public {
FestivalPass fp2 = new FestivalPass(address(beat), address(0));
vm.expectRevert("Only organizer can call this");
fp2.configurePass(1, GENERAL_PRICE, 10); // no organizer can ever call this until owner fixes it
}

Recommended Mitigation

Validate both addresses at construction and add a guarded setter for the BEAT token (or require the reciprocal binding to be established atomically) so a mistake is recoverable. At minimum reject the zero address.

constructor(address _beatToken, address _organizer) ERC1155("ipfs://beatdrop/{id}") Ownable(msg.sender){
+ require(_beatToken != address(0), "beatToken required");
+ require(_organizer != address(0), "organizer required");
setOrganizer(_organizer);
beatToken = _beatToken;
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 4 hours 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!