The joinEvent() function blindly pushes msg.sender into usersAddress every time it is called:
There is no check preventing a single address from calling joinEvent() multiple times:
usersAddress grows with repeated duplicates
totalParticipantShares increases multiple times for the same participant
numberOfParticipants increments misleadingly
totalWinnerShares becomes inflated because _getWinnerShares() iterates through every occurrence of the duplicated address
withdraw() then pays out multiple times more than intended
Because _getWinnerShares() loops over the array and sums:
Thus, a malicious user can drastically reduce totalWinnerShares (by being the only winner) OR inflate their own influence—both resulting in stolen funds or skewed distribution.
This breaks the core vault math and lets an attacker receive more tokens than they deposited.
Likelihood:
Reason 1: joinEvent() is a public function with no protections; any user can call it multiple times without restrictions.
Reason 2: No state variable checks if a user has already joined, and no mapping tracks whether a user has previously selected a country.
Impact:
Impact 1: Attackers inflate totalParticipantShares, numberOfParticipants, and usersAddress artificially, breaking winner-share math.
Impact 2: _getWinnerShares() sums the same user's shares multiple times, allowing attackers to manipulate payouts and extract a disproportionate share of the vault’s rewards.
Severity: HIGH (incorrect accounting → loss of user funds / protocol manipulation)
Add this PoC to the existing test suite.
It shows one user joining 3 times and tripling their effective winner-shares, giving them a massively inflated payout.
What the test proves
User joins 3 times
_getWinnerShares() counts the same user's shares 3 times
StotalWinnerShares is 3× higher
Reward formula becomes manipulated
User receives excess tokens
Duplicate entries directly cause an overpayment.
Option 1 — Prevent duplicate joins (preferred)
Add a boolean mapping:
And modify joinEvent():
Option 2 — Replace array with correct accounting
Remove usersAddress entirely and track:
Update on join:
Then compute winner shares instantly:
Option 3 — Allow re-joining but update instead of pushing
If multiple join-attempts should overwrite:
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.