BriVault

First Flight #52
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: low
Valid

Setting a winner with no participants causes division-by-zero and vault lockup

[H-02] Setting a winner with no participants causes division-by-zero and vault lockup

Description

When the tournament owner sets a winner, the contract later calculates each participant’s reward proportionally to their shares using the formula:

uint256 assetToWithdraw = Math.mulDiv(shares, vaultAsset, totalWinnerShares);

Here, totalWinnerShares represents the total shares of users who selected the winning option.

However, if the owner sets a winner that no user has selected, then totalWinnerShares becomes 0.
Any subsequent call to withdraw() or other reward-claiming functions will revert with a division-by-zero error inside Math.mulDiv, permanently preventing anyone from claiming or withdrawing funds.

This leads to a complete vault lockup — no participants can withdraw, and the event’s assets remain frozen.

// briVault.sol
function withdraw() external {
...
// @audit totalWinnerShares can be zero if no one picked the chosen winner
uint256 assetToWithdraw = Math.mulDiv(shares, vaultAsset, totalWinnerShares);
...
}

Risk

Likelihood: Medium

  • The owner can accidentally (or maliciously) set a winner that no one chose.

  • The condition is rare but trivial to trigger intentionally.

Impact: High

  • Causes a global denial of service: all withdrawals revert.

  • Locks all user funds in the vault permanently.

  • No user or admin can recover assets unless a separate rescue mechanism exists.

Proof of Concept

Add this test to your test/briVault.t.sol suite:

function test_NoWinnerPickedDivisionByZero() public {
// Two users deposit different amounts
vm.startPrank(user1);
mockToken.approve(address(briVault), 10 ether);
briVault.deposit(10 ether, user1);
vm.stopPrank();
vm.startPrank(user2);
mockToken.approve(address(briVault), 5 ether);
briVault.deposit(5 ether, user2);
vm.stopPrank();
// Fast-forward to after event end
vm.warp(briVault.eventEndDate() + 1);
// Owner sets a winner that nobody picked
vm.startPrank(briVault.owner());
briVault.setWinner(0); // assume index 0 was never chosen
vm.stopPrank();
// Any withdrawal attempt will now revert due to division by zero
vm.startPrank(user1);
vm.expectRevert(); // Division by zero inside Math.mulDiv
briVault.withdraw();
vm.stopPrank();
}

Run with:

forge test --match-test test_NoWinnerPickedDivisionByZero -vvv

Recommended Mitigation

  • Add a guard clause to handle the edge case where no one picked the winning option:

function withdraw() external {
...
- uint256 assetToWithdraw = Math.mulDiv(shares, vaultAsset, totalWinnerShares);
+ if (totalWinnerShares == 0) {
+ revert NoWinnerParticipants(); // or refund logic for all participants
+ }
+ uint256 assetToWithdraw = Math.mulDiv(shares, vaultAsset, totalWinnerShares);
...
}
Updates

Appeal created

bube Lead Judge 19 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Division by Zero in Withdraw Function When No Winners Bet on Winning Team

When no one bet on the winning team, making totalWinnerShares = 0, causing division by zero in withdraw and preventing any withdrawals.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!