Skip to content
Quantix Security Labs

The First Deposit

Show how the first depositor can make a later deposit mint zero shares, quantify the loss, and harden the vault.
IntermediateSolidity · EthereumFirst-depositor attack30–45 minutes

Briefing

The vault prices shares from its token balance. Anyone can transfer tokens to it directly, without calling `deposit`.

Assume an 18-decimal asset and a victim depositing 10 tokens shortly after launch.

TrainingVault.solsolidity
// Quantix Security Research Network — training challenge.// Intentionally vulnerable. Written for education; do not deploy.// SPDX-License-Identifier: MITpragma solidity 0.8.24; interface IERC20 {    function balanceOf(address) external view returns (uint256);    function transfer(address, uint256) external returns (bool);    function transferFrom(address, address, uint256) external returns (bool);} contract TrainingVault {    IERC20 public immutable asset;    uint256 public totalShares;    mapping(address => uint256) public sharesOf;     constructor(IERC20 _asset) {        asset = _asset;    }     function totalAssets() public view returns (uint256) {        return asset.balanceOf(address(this));    }     function deposit(uint256 assets) external returns (uint256 shares) {        uint256 supply = totalShares;        shares = supply == 0 ? assets : (assets * supply) / totalAssets();        require(asset.transferFrom(msg.sender, address(this), assets), "transfer failed");        totalShares = supply + shares;        sharesOf[msg.sender] += shares;    }     function redeem(uint256 shares) external returns (uint256 assets) {        assets = (shares * totalAssets()) / totalShares;        sharesOf[msg.sender] -= shares;        totalShares -= shares;        require(asset.transfer(msg.sender, assets), "transfer failed");    }}

Hints

Reveal one at a time. Each moves a step closer to the answer.

  1. H1

    Hidden

  2. H2

    Hidden

  3. H3

    Hidden

Solution

Try writing your own explanation and fix before opening it.

References