deposit records stakedAsset[receiver] but mints shares to msg.sender, creating a mismatch between staking records and share ownership. Legitimate depositors can lose participation rights or cause payout logic to break (zero snapshots → panic / incorrect distribution).Normal behaviour:
A caller who deposits tokens into the vault should cause (1) the vault to record the economic stake for the actual participant, (2) mint corresponding vault shares to the same participant, and (3) allow that participant to later call joinEvent() and receive a proper snapshot used for payouts. In short: payer → stake recorded; payer (or designated beneficiary) → shares minted; owner of shares → able to join and collect payouts.
Actual issue:
deposit(assets, receiver) records the stake under stakedAsset[receiver] but mints vault shares to msg.sender. This creates a mismatch between who is recorded as staked and who holds the minted shares. As a result, the address that actually holds shares may not be able to join the event (because joinEvent() checks stakedAsset[msg.sender]), while the recorded staker has no shares — leading to zero snapshots, division-by-zero during withdrawal, wrong payouts, or denial/confusion of benefit.
Likelihood:
Misuse is trivial and reproducible in normal operation: any depositor can call deposit(amount, receiver) with a different receiver value; this pattern is part of the public interface, so the condition will occur during regular deposits initiated by any participant or front-end that uses a receiver parameter (for example: delegated deposits, meta-transactions, or mistaken UI fields).
Integrations and UX increase exposure: wallet integrations, custodial flows, meta-txs or automated scripts commonly use a receiver or beneficiary field — these flows make this mismatch likely in production deployments when caller ≠ receiver occurs.
Impact:
Logical denial / incorrect entitlement: the economic stake is recorded for one address while shares (ownership rights) are assigned to a different address. The recorded staker cannot claim their expected shares; the minted-share holder cannot participate because joinEvent() checks the staked mapping — causing denial of benefit or inconsistent ownership semantics.
Financial integrity / runtime failure: this mismatch can produce zero snapshots for winners (because the joiner had no shares), yielding totalWinnerShares == 0 and causing panics (division by zero) at withdrawal or causing incorrect payouts to all winners (payout dilution or incorrect distribution). This undermines payout correctness and can be used to confuse or grief honest participants.
Just paste in briVault.t.sol
Call deposit(1 ETH, receiver = user2) from msg.sender = user1.
Assert stakedAsset[user1] == 0 and stakedAsset[user2] == 1 ETH - fee.
Assert briVault.balanceOf(user1) == shares and briVault.balanceOf(user2) == 0.
Call joinEvent() as user2 and confirm userSharesToCountry[user2][cid] == 0 (snapshot = 0).
Advance time, call setWinner and then attempt withdraw() as user2 — expect a panic (division by zero) or zero payout; user1 cannot join (revert noDeposit), so the intended participant is excluded.
Replace the incorrect mint target so that the beneficiary receives the minted vault shares.
Specifically: change _mint(msg.sender, participantShares) → _mint(receiver, participantShares) so the recorded stakedAsset[receiver] and share ownership are consistent (prevents caller/receiver mismatch, zero-snapshot states and payout/division errors).
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.