When a user calls briVault::joinEvent, their address is added to the briVault::usersAddress array via push(), and they are registered as a participant. When a user calls briVault::cancelParticipation, they should be removed from the participant list and their address should be removed from the briVault::usersAddress array to prevent them from being counted in future calculations.
The briVault::cancelParticipation function does not remove the user's address from the briVault::usersAddress array. When a user cancels and then rejoins the event, their address is pushed to the array again, creating duplicate entries. This directly impacts winner share calculations: the briVault::_getWinnerShares function iterates through the entire briVault::usersAddress array and accumulates userSharesToCountry[user][winnerCountryId] for each address into briVault::totalWinnerShares. If a user appears multiple times due to cancel/rejoin cycles, their shares are counted multiple times, artificially inflating briVault::totalWinnerShares. This inflated value is then used as the divisor in the withdrawal calculation (assetToWithdraw = Math.mulDiv(shares, vaultAsset, totalWinnerShares)), causing legitimate winners to receive less than their fair share of the prize pool.
Likelihood:
This occurs whenever a user calls briVault::joinEvent, then calls briVault::cancelParticipation and then calls briVault::joinEvent again before the event starts, as the briVault::usersAddress array is never cleaned up during cancellation.
This occurs every time briVault::cancelParticipation is executed, since the function lacks any logic to remove the user's address from the usersAddress array, leaving stale entries that accumulate over time.
Impact:
Winners receive less than their fair share of the prize pool because duplicate entries in briVault::usersAddress cause briVault::totalWinnerShares to be artificially inflated. When briVault::_getWinnerShares iterates through the array, users who canceled and rejoined have their shares counted multiple times, increasing the divisor in the withdrawal calculation (assetToWithdraw = Math.mulDiv(shares, vaultAsset, totalWinnerShares)), which reduces each winner's payout proportionally. If gas costs were ignored this process could lead to the winner's payout nearing a sum of 0.
The unbounded loop in briVault::_getWinnerShares processes duplicate entries unnecessarily, increasing gas costs for the briVault::setWinner function. As more users cancel and rejoin, the array grows with duplicates, making each winner calculation more expensive and potentially causing a denial of service attack due to prohibitively expensive gas costs/limits.
Add the following test and helper function to briVault.t.sol
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.