The vulnerability arises from the use of an inflated scaling factor (1e8) when minting the initial shares, combined with the reliance on the contract's collateral balance (via collateralToken.balanceOf(address(this))) to calculate new shares. An attacker can exploit this by transferring extra tokens directly to the contract. This manipulation inflates the reported collateral balance, resulting in subsequent depositors receiving fewer shares per token deposited and allowing the attacker to control a disproportionate share of the vault. This violates the invariant that a depositor’s share value should never decrease because of the actions of other participants. Also just one user will control a large amoun tof the shares just by depositing first.
When the vault is empty, the first deposit mints shares by multiplying the deposit amount by 1e8:
This logic means that an attacker depositing a minimal amount can obtain an enormous number of shares relative to the actual collateral value.
For the sake of an easier explanation we will assume the minDepositAmount is 0.5
For this attack path to work positionIsClosed = true
Initial Deposit by Attacker:
The attacker deposits 1 token when totalShares == 0 and receives:
Direct Transfer Manipulation:
The attacker then transfers an additional 9 tokens directly to the contract. The contract’s balance becomes 10 tokens.
Honest Depositor's Deposit:
An honest depositor later deposits 1 token. At that time, the contract calculates the total amount before the deposit as:
New shares are then minted as:
Resulting Impact:
Attacker’s Shares: 100,000,000
Honest Depositor’s Shares: 10,000,000
Total Shares: 110,000,000
Upon withdrawal, the attacker controls ~90.9% of the vault assets despite both parties depositing 1 token each.
The vulnerability centers on two critical flaws working together to create a severe exploitation vector:
Arbitrary 1e8 Share Scaling: By depositing even a minimal amount, the first user receives an extraordinarily high number of shares (scaled by 1e8), granting them disproportionate control over the vault.
Contract Balance Dependency: The contract calculates shares based on collateralToken.balanceOf(address(this)), which can be manipulated by directly transferring tokens to the contract outside the deposit function. This design flaw means any tokens in the contract—even those not properly accounted for through deposits—affect share calculations.
Manipulation of Share Value: When the attacker directly transfers additional tokens to the contract, they artificially inflate the contract's balance, causing subsequent depositors to receive fewer shares per token since the share calculation relies on the total balance rather than tracked deposits.
Theft of Investment Value: The attacker can strategically time their withdrawal to extract significantly more value than their actual contribution warrants, effectively stealing from other users.
MEV Exploitation: A sophisticated attacker could monitor the mempool for large incoming deposits, front-run these transactions with their withdrawal, and then back-run with a new deposit at the manipulated (more favorable) share ratio.
Destruction of Trust: Such manipulation fundamentally breaks the core invariant of the vault system—that a user's share value should represent a fair proportion of their contributed assets—making the protocol unusable for legitimate purposes.
Manual review
Use a dedicated variable to track deposits rather than relying solely on collateralToken.balanceOf(address(this)). This prevents unauthorized tokens from affecting share calculations.
When first initializing the contract, increase the total number of shares to 1e8 instead of minting them to the first depositor, that way the contract won't suffer any rounding issues and the shares will be calculated fairly.
Prevent tokens from getting stuck. This is a simple donation to every shareholder, and therefore, every share has more value (profit), leading to fewer shares for future users. If this is not intended by the user, it is merely a mistake! For totalAmountBefore > amount * totalShares, Here is the worst-case scenario (with the minimum amount): first deposit 0.001000 (e4) USDC → 1e12 shares. Second deposit: 0.001 (e4) USDC * 1e12 / 0.001 (e4) USDC. So, the attacker would need to donate 1e16 tokens, meaning 1e10 USDC → 10 billion $. Even in the worst case, it's informational.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.