Normal behavior:
Users should be able to join the event only once after depositing. The contract should store each participant uniquely and later aggregate shares to calculate rewards for the winning country.
Issue:
The joinEvent() function pushes msg.sender into the usersAddress array without checking for duplicates. Attackers can repeatedly call joinEvent() before the event starts, inflating the array. During setWinner(), the contract loops through this array in _getWinnerShares(), causing potential gas exhaustion (DoS) or incorrect share calculations due to duplicate counting.
Likelihood:
A participant can repeatedly call joinEvent() after a single deposit, since no guard limits re-entry.
The array usersAddress grows without restriction, and iteration in _getWinnerShares() depends directly on user-controlled input.
Impact:
The setWinner() function can run out of gas and fail, permanently locking vault finalization and withdrawals.
Duplicate entries inflate totalWinnerShares, breaking payout fairness and allowing reward manipulation.
Sets up a local Foundry test instance, deploying a mock token and the BriVault.
Mints tokens to an attacker address and has the attacker deposit once.
The attacker spam-calls joinEvent(0) 3,000 times (via vm.prank(attacker) so each call appears from the attacker).
The test advances time past the eventEndDate and expects vault.setWinner(0) to revert.
Enforce single join per user.
Replace user iteration with per-country aggregation
Fix deposit mint mismatch
The _getWinnerShares() function is intended to iterate through all users and sum their shares for the winning country, returning the total.
The _getWinnerShares() function is intended to iterate through all users and sum their shares for the winning country, returning the total.
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.