BriVault

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

Multiple Deposits Override stakedAsset (Loss of Data)

Root + Impact

Description

  • When a user makes multiple deposits into the vault before the event starts, each deposit should accumulate their total staked amount and issue additional vault shares proportionally. The stakedAsset[user] mapping should reflect the total net amount staked (after fees), so that cancelParticipation() can refund the correct amount and joinEvent() uses accurate share data.

  • The deposit() function overwritesstakedAsset[receiver] with each new deposit instead of adding to it. This causes the contract to permanently lose track of the user's full staked amount after the first deposit.

uint256 stakeAsset = assets - fee;
stakedAsset[receiver] = stakeAsset; //@audit Overwrites previous value

Risk

Likelihood:

  • Users will make multiple deposits during the pre-event phase to increase their position or correct underfunding, this is a normal and expected behavior in DeFi vaults.

  • The contract allows repeated calls to deposit() with no restriction, nothing prevents or warns about overwriting.

Impact:

  • On cancelParticipation(), only the laststakedAsset value is refunded, but all shares are burned resulting in permanent loss of assets from prior deposits.

  • If the user joins the event after multiple deposits, userSharesToCountry uses balanceOf(msg.sender) (correct total), but stakedAsset is wrong, creates inconsistency. If cancel is used, funds vanish.

Proof of Concept

The test demonstrates that two deposits (1 ETH + 2 ETH) result in stakedAsset[user] being incorrectly set to 1.97 ETH instead of the expected 2.955 ETH due to the = overwrite. When cancelParticipation() is called, only the last stake (1.97 ETH) is refunded despite burning all shares, causing a permanent loss of 0.985 ETH.

function test_multipleDeposits_overwriteStakedAsset_lossOfFunds() public {
vm.startPrank(user1);
mockToken.approve(address(briVault), type(uint256).max);
// Deposit 1: 1 ether → fee = 1.5% = 0.015 ether → stake = 0.985 ether
uint256 shares1 = briVault.deposit(1 ether, user1);
uint256 stake1 = 0.985 ether;
// Deposit 2: 2 ether → fee = 0.03 ether → stake = 1.97 ether
uint256 shares2 = briVault.deposit(2 ether, user1);
uint256 stake2 = 1.97 ether;
// BUG: stakedAsset[user1] = 1.97 (overwrites 0.985)
assertEq(briVault.stakedAsset(user1), stake2); // passes (shows overwrite)
assertEq(briVault.balanceOf(user1), shares1 + shares2); // total shares correct
// Expected total stake: 0.985 + 1.97 = 2.955 ether
uint256 expectedTotalStake = stake1 + stake2;
uint256 actualStakedAsset = briVault.stakedAsset(user1);
// Prove overwrite
assertTrue(actualStakedAsset < expectedTotalStake, "stakedAsset was overwritten");
// Cancel → only last stake refunded
uint256 balanceBefore = mockToken.balanceOf(user1);
briVault.cancelParticipation();
uint256 balanceAfter = mockToken.balanceOf(user1);
uint256 actualRefund = balanceAfter - balanceBefore;
// BUG: Only 1.97 refunded, 0.985 LOST
assertEq(actualRefund, stake2); // passes in buggy code
assertTrue(actualRefund < expectedTotalStake, "User lost funds due to overwrite");
// Final state
assertEq(briVault.stakedAsset(user1), 0);
assertEq(briVault.balanceOf(user1), 0);
vm.stopPrank();
}

Recommended Mitigation

The original = operator overwrites the previous stakedAsset value, causing prior deposits to be forgotten. Using += accumulates the total staked amount, ensuring cancelParticipation() refunds the full amount and internal accounting remains consistent with issued shares. This is required for safe multi-deposit support.

- stakedAsset[receiver] = stakeAsset;
+ stakedAsset[receiver] += stakeAsset;
Updates

Appeal created

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

`stakedAsset` Overwritten on Multiple Deposits

Vault tracks only a single deposit slot per user and overwrites it on every call instead of accumulating the total.

Support

FAQs

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

Give us feedback!