BriVault

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

Deposit mints shares to msg.sender but records stake for receiver (deposit-on-behalf mismatch)

Root + Impact

Description

  • Normally, when a depositor deposits on behalf of another user (a “receiver”), the vault should credit the receiver with both the deposited stake record and the newly minted vault shares.

  • In this contract, deposit(assets, receiver) records the stake under stakedAsset[receiver] but mints the shares to msg.sender instead of receiver, creating an ownership mismatch between recorded stake and actual tokenized shares.

// Root cause in the codebase with @> marks to highlight the relevant section
pragma solidity ^0.8.24;
...
function deposit(uint256 assets, address receiver) public override returns (uint256) {
require(receiver != address(0));
if (block.timestamp >= eventStartDate) {
revert eventStarted();
}
uint256 fee = _getParticipationFee(assets);
if (minimumAmount + fee > assets) {
revert lowFeeAndAmount();
}
uint256 stakeAsset = assets - fee;
@> stakedAsset[receiver] = stakeAsset; // stake recorded for receiver
...
@> _mint(msg.sender, participantShares); // shares minted to msg.sender (not receiver)
...
emit deposited (receiver, stakeAsset);
return participantShares;
}

Risk

Likelihood:

  • This mismatch occurs every time a depositor uses the receiver parameter to deposit on behalf of another address (e.g., gifting or onboarding), because the code always writes stake to receiver but mints to msg.sender.

  • It also occurs whenever front-end logic or integrators assume receiver receives both stake and shares — their UI and back-end will show inconsistent ownership.

Impact:

  • Impact 1: The receiver may be recorded as having a stake but will not own the minted shares — only msg.sender holds withdrawable shares. This can permanently lock the receiver out of withdrawals or create theft scenarios.

  • Impact 2: Refunds, cancellations, or accounting (e.g., cancelParticipation, joinEvent) rely on stakedAsset and balanceOf consistency; the mismatch can cause refunds to go to the wrong party or cause irreconcilable accounting and loss of funds.

Proof of Concept

Observed effect: After deposit, stakedAsset[receiver] == amt - fee, but vault.balanceOf(msg.sender) == participantShares (shares belong to depositor). The receiver has no minted shares to withdraw.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract PoC_DepositMismatch {
BriVault public vault;
IERC20 public token;
constructor(BriVault _vault, IERC20 _token) {
vault = _vault;
token = _token;
}
function demo(address receiver, uint256 amt) external {
// Attacker (msg.sender) approves & deposits on behalf of "receiver"
token.transferFrom(msg.sender, address(this), amt); // or ensure allowance
token.approve(address(vault), amt);
vault.deposit(amt, receiver); // stake is recorded for receiver...
// ...but vault.minted shares are given to msg.sender, not receiver.
}
}

Recommended Mitigation

Explanation: Record and accumulate stake under stakedAsset[receiver] and mint the corresponding shares to the same receiver. This keeps on-chain records and tokenized ownership consistent and prevents accidental loss/theft of shares.

- stakedAsset[receiver] = stakeAsset;
+ // accumulate stake for receiver (support repeated deposits on behalf)
+ stakedAsset[receiver] += stakeAsset;
- ...
- _mint(msg.sender, participantShares);
+ // mint shares to the intended receiver (not the depositor)
+ _mint(receiver, participantShares);
Updates

Appeal created

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

Shares Minted to msg.sender Instead of Specified Receiver

Support

FAQs

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

Give us feedback!