Developing Vault Contracts
A vault is a contract that accepts tokens from users, places them in strategies (Aave, Compound, Curve, Convex, Yearn), and issues share tokens in return. Sounds simple. In practice, 80% of vault architecture problems are not errors in the strategy itself, but errors in share calculation logic during deposits/withdrawals, or incorrect fee accounting during harvesting. Several protocols lost user funds precisely here, not in the strategy itself.
The Inflation Attack Problem and ERC-4626
First Deposit and Share Attack
ERC-4626 is the tokenized vault standard, adopted in April 2022. Before it, each vault reinvented the wheel for convertToShares / convertToAssets calculations. After — consensus emerged, but vulnerabilities didn't disappear.
Classic vault attack without ERC-4626 protection: attacker makes first deposit of 1 wei, gets 1 share. Then directly donates (not via deposit) 1000 USDC to vault, increasing totalAssets without changing totalSupply. Next user deposits 1999 USDC — when dividing 1999e6 * 1 / 1000e6 they get 1 share (integer division). The attacker with 1 share gets half the pool on withdrawal — 1499 USDC. The victim lost 500 USDC.
OpenZeppelin in ERC4626.sol closed this through virtual shares and assets: _decimalsOffset() adds 10^N to the denominator, making the attack economically unfeasible. But this works only if _decimalsOffset is chosen considering the token's decimals.
We use ERC-4626 as the base and add _decimalsOffset = 3 for most ERC-20 tokens, making attack cost ~1000x higher than profit.
Harvest and Fee Calculation — Where Mistakes Usually Happen
During harvesting, a vault collects rewards (e.g., CRV + CVX from Convex), converts them to underlying asset, and adds to the pool. Between harvest and re-deposit, share price grows. If fee is taken after this growth as a percentage of profit — everything is correct. If fee is taken at harvest before re-deposit — management fee eats principal, not just profit.
Typical error: performanceFee = (totalAssets() - lastHarvestTotalAssets) * feePercent / 10000. Here totalAssets() may include unrealized gains that will disappear with market volatility. Better: fee from realized profit after reward conversion.
Reentrancy in Vault via ERC-777 / ERC-1363
If underlying asset is a token with transfer hook (ERC-777 or ERC-1363), the hook may be called during deposit() before totalSupply updates. An attacker can call deposit() again in the hook — getting shares at old rate before their first deposit was counted.
Protection: nonReentrant on deposit, withdraw, redeem, mint. Foundry fuzz-test with mock ERC-777 token that calls repeated deposit from transfer hook.
How We Build Vault Contracts
Architecture: Vault + Strategy Separation
Vault holds assets and manages shares. Strategy is a separate contract with placement logic. This isn't just architectural cleanliness: if strategy is compromised, vault can be paused (pausable), and funds — in theory — evacuated via emergencyWithdraw. If everything is in one contract — it can't.
Vault (ERC-4626)
└── Strategy
├── Aave v3 supply/withdraw
├── Curve LP deposit
└── Convex staking
Yearn v2/v3 uses this same concept. We adapt to specific requirements, not copy Yearn — it has ~5000 lines, clients usually need a third.
Stack
Solidity 0.8.x + OpenZeppelin 5.x (ERC4626, AccessControl, Pausable, ReentrancyGuard). Integrations: Aave v3 via IPool, Compound v3 via IComet, Curve via ICurvePool, Convex via IConvex. Oracles for reward swaps: Chainlink or Uniswap v3 TWAP depending on token liquidity.
Tests in Foundry: fork mainnet Ethereum/Arbitrum, 100+ fuzz runs on deposit/withdraw/harvest with random amounts and sequences. Property-based invariant: convertToAssets(totalSupply()) >= totalUserDeposits after any operation.
Vault Types Table
| Type | Strategy | Complexity | Typical APY Source |
|---|---|---|---|
| Simple lending | Aave/Compound | Low | Supply rate |
| LP vault | Curve + Convex | Medium | CRV + CVX rewards |
| Multi-strategy | 3+ protocols | High | Weighted allocation |
| Leverage vault | Aave self-borrow | High | Leveraged yield |
Work Process
Design (3-5 days). Choose strategy, analyze integrations, storage layout vault + strategy. Define fee structure: management fee (% of AUM per year), performance fee (% of profit), withdrawal fee (rare but happens).
Development (1-3 weeks). Vault core + one or two strategies. Fork-tests on Ethereum mainnet or Arbitrum (cheaper gas for testing).
Audit preparation. Slither, test coverage >95%, check all fee calculations on edge cases (0 assets, 1 wei deposit, harvest right after deployment).
Deployment. Gnosis Safe for owner/admin functions. Timelock on strategy change — minimum 24 hours.
Timeline Guidelines
Vault with one strategy (Aave/Compound): 1-2 weeks. Multi-strategy vault with rebalancing: 3-5 weeks. Complex leverage strategies with liquidation protection: 6-8 weeks.







