BriVault

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

Users who deposited but did not join the event cannot withdraw their assets

[M-1] Users who deposited but did not join the event cannot withdraw their assets

Description

The withdraw() function currently allows only participants who selected the winner country to withdraw rewards.
However, users who deposited assets but never joined the event (i.e., userToCountry[msg.sender] is unset) have no way to retrieve their deposited tokens once the event ends.
These users remain permanently locked out of their funds.

Impact

  • Loss of funds for users who accidentally deposited but didn’t call the join function.

  • Causes user dissatisfaction and potential trust issues.

  • Contradicts expected UX behavior — deposits should be refundable if participation was never completed.

Proof of Concepts

function withdraw() external winnerSet {
if (block.timestamp < eventEndDate) {
revert eventNotEnded();
}
// Users who never joined have userToCountry[msg.sender] == ""
// This will revert in didNotWin() and permanently lock their funds.
if (
keccak256(abi.encodePacked(userToCountry[msg.sender])) !=
keccak256(abi.encodePacked(winner))
) {
revert didNotWin();
}
uint256 shares = balanceOf(msg.sender);
...
}

Recommended mitigation

Add a branch allowing users who deposited but never joined to withdraw their assets after event ends.
For example:

function withdraw() external winnerSet {
if (block.timestamp < eventEndDate) {
revert eventNotEnded();
}
string memory joinedCountry = userToCountry[msg.sender];
// Allow refund if user never joined
if (bytes(joinedCountry).length == 0) {
uint256 refund = stakedAsset[msg.sender];
stakedAsset[msg.sender] = 0;
IERC20(asset()).safeTransfer(msg.sender, refund);
emit Refund(msg.sender, refund);
return;
}
// Otherwise only winners can withdraw reward
if (
keccak256(abi.encodePacked(joinedCountry)) !=
keccak256(abi.encodePacked(winner))
) {
revert didNotWin();
}
uint256 shares = balanceOf(msg.sender);
uint256 assetToWithdraw =
Math.mulDiv(shares, finalizedVaultAsset, totalWinnerShares);
_burn(msg.sender, shares);
IERC20(asset()).safeTransfer(msg.sender, assetToWithdraw);
emit Withdraw(msg.sender, assetToWithdraw);
}

This ensures:

  • Depositors who never joined can still get refunds.

  • Participants follow the winner/loser withdrawal logic.

Updates

Appeal created

bube Lead Judge 19 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!