Summary
The MartenitsaEvent contract manages event logic within the protocol, primarily through functions like startEvent, joinEvent, and stopEvent.
The joinEvent function records participants and producers upon user entry to an event. However, the issue arises in the stopEvent function,
where proper reversal of changes made in joinEvent is not handled correctly.
While joinEvent mandates that users are not already participants, this status is not reset for future events during stopEvent execution,
rendering it impossible for a user to join subsequent events
Vulnerability Details
The function MartenitsaEvent::joinEvent updates a list of _participants and invokes the _addProducer function, temporarily
adding the user as a producer
function joinEvent() external {
require(block.timestamp < eventEndTime, "Event has ended");
@> require(!_participants[msg.sender], "You have already joined the event");
require(!isProducer[msg.sender], "Producers are not allowed to participate");
require(_healthToken.balanceOf(msg.sender) >= healthTokenRequirement, "Insufficient HealthToken balance");
_participants[msg.sender] = true;
participants.push(msg.sender);
emit ParticipantJoined(msg.sender);
(bool success) = _healthToken.transferFrom(msg.sender, address(this), healthTokenRequirement);
require(success, "The transfer is not successful");
_addProducer(msg.sender);
}
When it's time to conclude the event, the stopEvent function is invoked. However, it's noted that only the producer role is removed,
while the participants remain unaffected.
function stopEvent() external onlyOwner {
require(block.timestamp >= eventEndTime, "Event is not ended");
for (uint256 i = 0; i < participants.length; i++) {
@>
isProducer[participants[i]] = false;
}
}
A test is included to demonstrate a user joining an event, then attempting to join another event after the first one has concluded.
This results in a revert of the joinEvent function due to the requirement check: require(!_participants[msg.sender], "You have already joined the event");
function testUserLockedEvent() public {
vm.startPrank(address(marketplace));
healthToken.distributeHealthToken(bob, 10 * 10**18);
vm.stopPrank();
martenitsaEvent.startEvent(1 days);
vm.startPrank(bob);
healthToken.approve(address(martenitsaEvent), 10 ** 18);
martenitsaEvent.joinEvent();
vm.warp(block.timestamp + 1 days + 1);
vm.stopPrank();
martenitsaEvent.stopEvent();
vm.warp(block.timestamp + 10 days);
martenitsaEvent.startEvent(1 days);
vm.startPrank(bob);
marketplace.collectReward();
healthToken.approve(address(martenitsaEvent), 10 ** 18);
martenitsaEvent.joinEvent();
vm.stopPrank();
}
Output
Failing tests:
Encountered 1 failing test in test/MartenitsaMarketplace.t.sol:MartenitsaMarketplace
[FAIL. Reason: revert: You have already joined the event] testUserLockedEvent() (gas: 377973)
Impact
Users who have already participated in an event are unable to participate in future events.
Tools Used
Foundry and manual review
Recommendations
Removing participants from the list ensures they can join again in future events.
function stopEvent() external onlyOwner {
require(block.timestamp >= eventEndTime, "Event is not ended");
for (uint256 i = 0; i < participants.length; i++) {
isProducer[participants[i]] = false;
++ _participants[participants[i]] = false;
}
}