Pass token IDs and memorabilia token IDs share the same uint256 space with no enforced lower-bound guard. If a collision is achieved, hasPass() returns true for the holder, granting access to attendPerformance() and all BEAT reward flows without ever purchasing a pass.
Pass token IDs are raw constants: GENERAL_PASS = 1, VIP_PASS = 2, BACKSTAGE_PASS = 3. Memorabilia token IDs are encoded as:
For collectionId = 0: (0 << 128) + 1 = 1 — identical to GENERAL_PASS. While nextCollectionId is initialised at 100 (making collectionId 0–3 unreachable via createMemorabiliaCollection today), the vulnerability is structural:
encodeTokenId is public pure — any external contract or user can call it with (0, 1) and receive token ID 1, which hasPass() treats as a valid GENERAL_PASS.
collections[1], collections[2], collections[3] are uninitialised structs with priceInBeat = 0. Nothing at the contract level prevents a future upgrade or organizer action from populating those slots.
The separation is a runtime convention (nextCollectionId = 100), not a protocol invariant enforced by the type system or require guards.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.