Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Not every user pass earns beat tokens for performance attendance

Summary

For user who buys multiple passes to attend performance with friends, only 1 pass will earn reward. Any extra passes that the user buys are found not being rewarded extra beat tokens when attending performance.

Description

In the attendPerformance function, the hasAttended only factors in the performance Id and user address without considering multiple passes that a single user has bought. This causes the reward is not granted for the extra passes that the user has paid for and attend performance together with friends.

function attendPerformance(uint256 performanceId) external {
require(isPerformanceActive(performanceId), "Performance is not active");
require(hasPass(msg.sender), "Must own a pass");
<@@>! require(!hasAttended[performanceId][msg.sender], "Already attended this performance");
require(block.timestamp >= lastCheckIn[msg.sender] + COOLDOWN, "Cooldown period not met");
hasAttended[performanceId][msg.sender] = true;
lastCheckIn[msg.sender] = block.timestamp;
uint256 multiplier = getMultiplier(msg.sender);
BeatToken(beatToken).mint(msg.sender, performances[performanceId].baseReward * multiplier);
emit Attended(msg.sender, performanceId, performances[performanceId].baseReward * multiplier);
}

Risk

Likelihood:

  • Happen to users who buy multiple passes for friends or family members who then attend together the same performance

Impact:

  • Beat tokens are not rewarded for every pass a single user has bought and used to attend performance with others, resulting an unfair reward system that user has no other way to get/claim back the tokens for their support to the event.

Proof of Concept

In test/FestivalPass.t.sol, add new test as follow:

function test_audit_noExtraBeatTokenForUserWithMultiplePasses() public {
// user buys multiple passes of same tier to attend performance with friends
vm.startPrank(user1);
festivalPass.buyPass{value: VIP_PRICE}(2);
festivalPass.buyPass{value: VIP_PRICE}(2);
festivalPass.buyPass{value: VIP_PRICE}(2);
vm.stopPrank();
assertEq(festivalPass.balanceOf(user1, 2), 3);
assertEq(festivalPass.passSupply(2), 3);
assertEq(beatToken.balanceOf(user1), 15e18); // earn 3 * 5e18 beatToken since user buys 3 VIP passes
// Organizer creates a performance
uint256 startTime = block.timestamp + 1 hours;
vm.prank(organizer);
uint256 perfId = festivalPass.createPerformance(startTime, 2 hours, 50e18);
// Warp to performance time
vm.warp(startTime + 30 minutes);
// User attends performance with friends
vm.prank(user1);
festivalPass.attendPerformance(perfId);
uint256 userBeatTokenAftAttendPerformance = beatToken.balanceOf(user1);
// user should earn beatToken via his 3 VIP passes upon purchase and attend performance
// (50e18 * 2 * 3) + (5e18 * 3)
uint256 userBeatTokenExpectedBalance = (50e18 * 2 * 3) + (5e18 * 3);
console.log("User beat token balance after attending performance: %d", userBeatTokenAftAttendPerformance);
console.log("User beat token expected balance: %d", userBeatTokenExpectedBalance);
assertTrue(festivalPass.hasAttended(perfId, user1));
assert(userBeatTokenAftAttendPerformance != userBeatTokenExpectedBalance);
}

Run the test in terminal as follow:

$ forge test --match-test test_audit_noExtraBeatTokenForUserWithMultiplePasses -vvv [12:31:19]
[⠊] Compiling...
[⠑] Compiling 1 files with Solc 0.8.25
[⠘] Solc 0.8.25 finished in 659.45ms
Compiler run successful!
Ran 1 test for test/FestivalPass.t.sol:FestivalPassTest
[PASS] test_audit_noExtraBeatTokenForUserWithMultiplePasses() (gas: 346865)
Logs:
User beat token balance after attending performance:
115000000000000000000
User beat token expected balance:
315000000000000000000
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.33ms (215.71µs CPU time)
Ran 1 test suite in 119.43ms (1.33ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

The test shows that the beat token balance of the user after attending performance is not tally to the expected balance that the user shall have.

Recommended Mitigation

To review the reward mechanism that considers multiple pass holders by either

  1. reward based on pass Id

  2. reward based on number of passes used per user/pass purchaser

or if the reward system is more towards by user basis, then during the buyPass stage, additional check to restrict only 1 pass tier per user shall be implemented.

Updates

Lead Judging Commences

inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.