Users call joinEvent(countryId) once to register their prediction and link their vault shares to a country. The function records userSharesToCountry[msg.sender][countryId] = balanceOf(msg.sender).
The function lacks a reentrancy guard or join-once check, allowing a user to call joinEvent() multiple times. Each call overwrites the same mapping entry but pushes the user to usersAddress[] again, causing _getWinnerShares() to double-count their shares in totalWinnerShares.
Likelihood:
Users call joinEvent() multiple times // occurs during UI errors, retries, or intentional abuse
setWinner() is called after multiple joins // standard post-event execution
Impact:
totalWinnerShares is inflated by duplicate entries // reduces payout per share
Legitimate winners receive significantly less than entitled // direct financial loss
Alice joins twice with 1000 shares. usersAddress[] contains Alice twice. _getWinnerShares() sums her shares twice, inflating totalWinnerShares to 3000. Bob, with 1000 shares and 2000 total assets, receives only 666 instead of 1000.
Requires userCountryId mapping. Add custom error: error AlreadyJoined();
Use a mapping to track joined users instead of pushing to array multiple times.
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.