BriVault

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

# When No User Guesses the Winning Team, Deposited Funds Become Locked in the Protocol

When No User Guesses the Winning Team, Deposited Funds Become Locked in the Protocol

Description

  • The protocol documentation states that users can deposit an ERC20 asset to bet on a team. After the tournament concludes, winners will share the prize pool proportionally based on their deposited amount.

  • However, when no user guesses the winning team, users have no way of knowing from the rules that they can retrieve their funds.

  • Notably, this vulnerability is predicated on the protocol having already fixed the loophole that allowed users to withdraw funds at any time (i.e., directly using the original ERC4626 withdraw and redeem functions).

Risk

Likelihood:

  • Once the event ends with no user guessing the winning team, this issue will inevitably occur.

Impact:

  • Permanent locking of user funds.

  • Severe damage to protocol credibility.

Proof of Concept

  • Add the following function to test/BriVaultTest.t.sol and run forge test --mt test__whenNoWinner -vv:

function test__whenNoWinner() public {
// Admin sets national team information
vm.startPrank(owner);
briVault.setCountry(countries);
vm.stopPrank();
// User1 deposits all funds and bets on a national team index
vm.startPrank(user1);
uint256 balanceBeforuser1_Before = mockToken.balanceOf(user1);
mockToken.approve(address(briVault), 20 ether);
uint256 user1Shares = briVault.deposit(20 ether, user1);
briVault.joinEvent(10);
uint256 balanceBeforuser1 = mockToken.balanceOf(user1);
vm.stopPrank();
// User2 deposits all funds and bets on a national team index
vm.startPrank(user2);
mockToken.approve(address(briVault), 20 ether);
uint256 user2Shares = briVault.deposit(20 ether, user2);
briVault.joinEvent(11);
uint256 balanceBeforuser2 = mockToken.balanceOf(user2);
vm.stopPrank();
// User3 deposits all funds and bets on a national team index
vm.startPrank(user3);
mockToken.approve(address(briVault), 20 ether);
uint256 user3Shares = briVault.deposit(20 ether, user3);
briVault.joinEvent(12);
uint256 balanceBeforuser3 = mockToken.balanceOf(user3);
vm.stopPrank();
// User4 deposits all funds and bets on a national team index
vm.startPrank(user4);
mockToken.approve(address(briVault), 20 ether);
uint256 user4Shares = briVault.deposit(20 ether, user4);
briVault.joinEvent(13);
uint256 balanceBeforuser4 = mockToken.balanceOf(user4);
vm.stopPrank();
// Advance timestamp to after the event ends
vm.warp(eventEndDate + 1);
// Admin declares the winning team index
vm.startPrank(owner);
briVault.setWinner(5);
vm.stopPrank();
// At this point, no user has guessed the winning team.
vm.prank(user1);
vm.expectRevert(abi.encodeWithSignature("didNotWin()"));
briVault.withdraw();
vm.prank(user2);
vm.expectRevert(abi.encodeWithSignature("didNotWin()"));
briVault.withdraw();
vm.prank(user3);
vm.expectRevert(abi.encodeWithSignature("didNotWin()"));
briVault.withdraw();
vm.prank(user4);
vm.expectRevert(abi.encodeWithSignature("didNotWin()"));
briVault.withdraw();
}
  • Console output:

Ran 1 test for test/briVault.t.sol:BriVaultTest
[PASS] test__whenNoWinner() (gas: 2298139)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.90ms (1.25ms CPU time)

Recommended Mitigation

  • Implement a simple mechanism to track the number of times each national team index is selected.

  • After the winning team is announced, if the winning team index has zero selections, allow any user to retrieve their funds.

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!