Normal behavior:
After the tournament ends and the owner sets the winner, the vault should either allow winning participants to withdraw their proportional share or — if there are no winners for the selected team — safely refund participants or handle the pool deterministically.
Specific issue:
setWinner() computes totalWinnerShares and sets finalizedVaultAsset, then withdraw() uses:uint256 assetToWithdraw = Math.mulDiv(shares, vaultAsset, totalWinnerShares);
If the owner selects a winner that has no participants (i.e., totalWinnerShares == 0), two problems arise:
Logical lock: no-one can withdraw — participants of other teams are not winners and are denied; there is no fallback path (refunds) for the case where the winner bucket is empty, so funds become effectively unusable.
Division-by-zero: if due to some bug/edge-case (totalWinnerShares == 0) a withdraw() call reaches the Math.mulDiv(..., totalWinnerShares) path, it will revert (division by zero), causing more unexpected errors.
Likelihood:
Reason 1: This occurs whenever the owner (maliciously or accidentally) sets the winner to a country index that has zero participants.
Reason 2: This occurs if participants forgot to join the actual winning team (e.g., mismatch between team names) or if totalWinnerShares was incorrectly aggregated to zero due to prior logic bugs (duplicate joins, incorrect bookkeeping).
Impact:
Funds can become effectively locked: honest participants who bet on other teams cannot withdraw because they "did not win", and there are no refunds or fallback.
If a legit winner exists but totalWinnerShares is zero due to aggregation bugs, legitimate winners cannot withdraw because the withdraw formula will revert.
High severity because all participants’ funds may be stuck with no on-chain recovery path.
User A deposits and joins country 0 (only participant).
Owner maliciously (or by mistake) calls setWinner(1) — country 1 has zero participants.
_getWinnerShares() yields totalWinnerShares == 0 and _setFinallizedVaultBalance() snapshots the vault balance.
No participant can call withdraw():
Non-winners (e.g., A) will revert with didNotWin() — they did not bet on the (incorrect) winner.
There are no winners to call withdraw() — even if someone attempts a withdraw (edge-case), division-by-zero or zero-division logic will revert.
Short explanation
Ensure setWinner() checks whether the chosen winner has participants (i.e., totalWinnerShares > 0) and provide a safe fallback for the "no winners" case — e.g., automatic refunds to all participants, allow owner to choose a different resolution, or burn fees and allow proportional refunds. Also guard withdraw() against division-by-zero by asserting totalWinnerShares > 0.
When no one bet on the winning team, making totalWinnerShares = 0, causing division by zero in withdraw and preventing any withdrawals.
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.