BriVault

First Flight #52
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Users who deposited and did not join before event started cannot cancel nor participate; users' funds lost.

Root + Impact

Description

  • The BriVault contract enables users to deposit assets via the overridden deposit function, which sets the stakedAsset mapping to track participation eligibility. Users must then call joinEvent before eventStartDate to select a country and participate in the tournament. Before the event starts, users can call cancelParticipationto refund their staked assets, burn shares, and reset the mapping.

  • However, if a user deposits but fails to call joinEvent before eventStartDate, after the event begins, joinEvent reverts due to the timestamp check, and cancelParticipation also reverts for the same reason, regardless of whether the user joined, permanently locking the user's funds in the vault with no way to withdraw or participate.

// Root cause in the codebase with @> marks to highlight the relevant section
function cancelParticipation() public {
@> if (block.timestamp >= eventStartDate) {
revert eventStarted();
}
uint256 refundAmount = stakedAsset[msg.sender];
stakedAsset[msg.sender] = 0;
uint256 shares = balanceOf(msg.sender);
_burn(msg.sender, shares);
IERC20(asset()).safeTransfer(msg.sender, refundAmount);
}

Risk

Likelihood:

  • Users deposit during the pre-event period but overlook or delay calling joinEvent due to UI/UX issues or misinformation.

  • The event starts on schedule, triggering the timestamp checks that block both joining and canceling.

Impact:

  • Users suffer permanent loss of deposited funds, as they cannot access or recover assets post-event start without participation.

  • Protocol faces denial of service complaints, reduced user trust, and potential legal/regulatory scrutiny for locked funds.

Proof of Concept

Add the following code snippet to the briVault.t.sol test file.This test verifies that user cannot join the tournament if the eventStarted nor cancel the participation.

function test_missingTheStartDateTimeCausesDOS() public {
vm.startPrank(owner);
briVault.setCountry(countries);
vm.stopPrank();
vm.startPrank(user1);
mockToken.approve(address(briVault), 5 ether);
uint256 user1Shares = briVault.deposit(5 ether, user1);
vm.warp(eventStartDate + 1);
vm.expectRevert(BriVault.eventStarted.selector);
briVault.joinEvent(10);
vm.expectRevert(BriVault.eventStarted.selector);
briVault.cancelParticipation();
vm.stopPrank();
}

Recommended Mitigation

Possible mitigation is to allow users to cancel participation if they have not joined the event.

function cancelParticipation() public {
+ if (block.timestamp >= eventStartDate && bytes(userToCountry[msg.sender]).length > 0) {
- if (block.timestamp >= eventStartDate) {
revert eventStarted();
}
uint256 refundAmount = stakedAsset[msg.sender];
stakedAsset[msg.sender] = 0;
uint256 shares = balanceOf(msg.sender);
_burn(msg.sender, shares);
IERC20(asset()).safeTransfer(msg.sender, refundAmount);
}
Updates

Appeal created

bube Lead Judge 19 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
alexscherbatyuk Submitter
19 days ago
bube Lead Judge
15 days ago
bube Lead Judge 14 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!