joinEvent snapshots balanceOf(msg.sender) once and stores it in userSharesToCountry[msg.sender][countryId] (src/briVault.sol:260-262). Subsequent deposits increase the share balance, but the mapping remains stale. _getWinnerShares later uses the outdated value, so the denominator is smaller than the actual winning supply. The custom withdraw() multiplies the current (larger) share balance by the vault asset snapshot, causing SafeERC20.safeTransfer to revert because the computed amount exceeds available funds.
Likelihood: High – adding funds after joining is intuitive for users who want to scale their bet.
Any winner who tops up after joining becomes unable to withdraw.
All winners on the same team are locked out because totalWinnerShares is understated.
User deposits 5 ether, calls joinEvent(team).
User deposits another 2 ether before eventStartDate.
Owner finalizes in favour of that team.
withdraw() reverts with "ERC20: transfer amount exceeds balance".
(See test/BriVaultAttack.t.sol:202, testSecondDepositAfterJoinBreaksWinnerWithdrawal.)
Refresh userSharesToCountry whenever a participant’s share balance changes (additional deposits, mints, burns).
Alternatively, discard the cached mapping and compute payout shares from live balances during withdrawal.
Extend testing to cover multi-deposit scenarios and ensure the denominator tracks the circulating supply.
Proposed patch (Solidity-like pseudocode):
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.