Beatland Festival

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

attendPerformance uses a single global lastCheckIn cooldown, so attending one performance blocks attending any other for an hour

Global per-user check-in cooldown blocks attending more than one performance per hour

Description

attendPerformance enforces the cooldown against a single global lastCheckIn[msg.sender] value (FestivalPass.sol:110) and updates that same global slot on every attendance (line 112). The cooldown is therefore not scoped to a performance, so attending any one performance blocks the user from attending every other performance for a full COOLDOWN (1 hour), including performances that run simultaneously.

require(block.timestamp >= lastCheckIn[msg.sender] + COOLDOWN, "Cooldown period not met");
hasAttended[performanceId][msg.sender] = true;
lastCheckIn[msg.sender] = block.timestamp; // @> one global timestamp gates ALL performances

Risk

Likelihood:

High. The festival is explicitly designed for multiple performances, and overlapping or back-to-back sets are normal. Any pass holder attending two events within an hour hits this on ordinary use.

Impact:

Low. No funds are at risk and the hasAttended mapping already prevents double-claiming a single performance. The harm is functional: legitimate attendees are denied BEAT rewards they earned by attending distinct, possibly concurrent, performances, degrading the core reward mechanic the protocol promises.

Proof of Concept

Attend one active performance, then immediately attempt a second concurrent one and observe the revert.

pass.attendPerformance(0); // succeeds, sets global lastCheckIn
vm.expectRevert("Cooldown period not met");
pass.attendPerformance(1); // reverts even though perf 1 is active and not yet attended

Recommended Mitigation

Scope the cooldown per performance, or rely on the existing per-performance hasAttended guard.

- require(block.timestamp >= lastCheckIn[msg.sender] + COOLDOWN, "Cooldown period not met");
- lastCheckIn[msg.sender] = block.timestamp;
+ require(block.timestamp >= lastCheckIn[performanceId][msg.sender] + COOLDOWN, "Cooldown period not met");
+ lastCheckIn[performanceId][msg.sender] = block.timestamp;
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!