The cancelParticipation() function allows users to cancel their participation and receive a refund before the event starts. Under normal behavior, when a user cancels, all their participation data should be cleared, including their country selection and share mappings, ensuring a clean state if they decide to rejoin later.
However, the cancelParticipation() function on lines 275-289 only clears stakedAsset[msg.sender] and burns shares, but does not clear the userToCountry[msg.sender] mapping, the userSharesToCountry[msg.sender][countryId] mapping, or remove the user from the usersAddress[] array. This leaves stale data in the contract state. If a user cancels and then re-deposits and joins again, the old country mapping and share values remain, which can interfere with new participation and cause inconsistencies in winner calculations, as _getWinnerShares() will process stale share values for canceled users.
Likelihood:
This vulnerability occurs when users call cancelParticipation() and then re-deposit and join the event again, as the function does not clear the participation-related mappings before refunding
The bug manifests during winner calculation when _getWinnerShares() iterates through usersAddress[] and processes canceled users who still have stale share values in userSharesToCountry, causing incorrect calculations
Impact:
Canceled users remain in usersAddress[] array, causing _getWinnerShares() to process entries for users who are no longer participants, leading to incorrect winner share calculations
Stale userSharesToCountry mappings can cause canceled users' old share values to be included in totalWinnerShares calculations even though they have no shares
If a user cancels and rejoins, old country mappings may interfere with new participation, potentially causing confusion in winner verification
Explanation of PoC:
This proof of concept demonstrates how stale state remains after cancellation. The test shows that when a user cancels, their mappings are not cleared, and canceled users remain in the usersAddress[] array, causing stale share values to be included in winner calculations.
Test Results:
✅ userToCountry mapping not cleared after cancel
✅ userSharesToCountry mapping not cleared after cancel
✅ User remains in usersAddress[] array after cancel
✅ Stale shares included in totalWinnerShares calculation
Explanation:
The recommended mitigation clears all participation-related state when a user cancels. This ensures canceled users don't affect winner calculations and users who rejoin start with clean state.
CancelParticipation burns shares but leaves the address inside usersAddress and keeps userSharesToCountry populated.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.