Yield aggregator development
Vault deployed, strategy working — farming COMP on Compound, selling through Uniswap V3, reinvesting in position. By week six the COMP token surged in price and all holders started withdrawing simultaneously. Strategy held 80% of assets in Compound, didn't have enough liquidity to withdraw. Contract started emergency exit from protocol, paying 3-4% slippage on each operation. Users withdrawing last received 6% less. This isn't a contract bug — it's an architectural mistake in vault liquidity management.
Main problems in yield aggregator architecture
Liquidity buffer problem and bank run
Classic vault per ERC-4626 stores shares and assets in ratio totalAssets / totalSupply. If 90% of assets deployed in strategy (correct for yield), on mass withdrawal the contract must emergency close positions.
Two approaches we use depending on strategy profile:
Idle buffer (keep 10-20% of assets in vault). Simple approach: small percentage of assets not invested, serves as buffer for small withdrawals without protocol interaction. Yearn Finance uses debtRatio — each strategy gets limit on asset management, remainder stays in vault.
Withdrawal queue with delay. For strategies with long locks (Curve gauge locks, Convex) — withdrawal queue with 24-72 hour delay. User gets "withdraw ticket" — NFT or mapping entry, executable after unlock.
Harvest timing and MEV
harvest() function that collects rewards and reinvests — is an MEV target. Before harvest() reward token costs X, after selling — X minus slippage. Sandwich attacker frontruns harvest, profits from price movement.
Solutions:
- Sell rewards through private mempool (Flashbots Protect, MEV Blocker)
- TWAP selling: split sale into several transactions over several blocks
- Use CoW Protocol / 1inch Fusion for batch settlement
Second option simpler to implement, but increases gas overhead 30-50%.
Reentrancy in ERC-4626 through ERC-777 tokens
ERC-4626 standard doesn't forbid using ERC-777 as underlying asset. On withdraw() → _burn(shares) → external transfer → receiver's tokensReceived hook, there's opportunity to re-enter deposit() or withdraw(). If totalAssets updates after transfer — share price manipulated at that moment.
Standard solution: nonReentrant on all functions changing totalAssets or totalSupply. Additionally — check totalAssets before and after operation as assertion.
How we build yield aggregator
Vault + strategy architecture
Follow Yearn v2/v3 pattern: vault separated from strategies. Vault manages ERC-4626 logic, share accounting, limits. Strategies — separate contracts with single interface:
IStrategy {
function deposit(uint256 assets) external;
function withdraw(uint256 assets) external returns (uint256 loss);
function totalAssets() external view returns (uint256);
function harvest() external returns (uint256 profit, uint256 loss);
}
This allows adding new strategies without changing vault contract. Vault keeps list of active strategies with debtRatio for each — percentage of totalAssets the strategy can manage.
Multi-strategy allocation
For vault with several strategies you need allocation mechanism. Simple variant: fixed debtRatio through governance. Advanced: automatic rebalancer based on APY data.
Automatic rebalancer — more complex because you can't read APY from protocols on-chain reliably. Aave returns currentLiquidityRate in ray (1e27), Compound — supplyRatePerBlock. Need normalization and conversion to annualized percent. And that's just current APY — doesn't account for reward tokens, gas overhead on rebalance, slippage.
In most cases we implement off-chain keeper that reads APY, calculates optimal distribution and calls rebalance() on vault once per 6-24 hours. On-chain contract only checks call is from authorized keeper.
Chainlink Automation for harvest
Instead of manual harvest calls — Chainlink Automation (formerly Keepers). Contract implements AutomationCompatibleInterface:
function checkUpkeep(bytes calldata) external view returns (bool upkeepNeeded, bytes memory);
function performUpkeep(bytes calldata performData) external;
checkUpkeep checks: enough time passed since last harvest, enough rewards accumulated to pay for gas. If both conditions — upkeepNeeded = true, Chainlink node calls performUpkeep. This removes manual management dependency and guarantees regular harvest.
Accounting for performance fee
Performance fee — percentage of profit going to protocol treasury. Technically: on each harvest calculate profit = totalAssets_after - totalAssets_before. From profit take performanceFee (usually 10-20%) and convert to shares minted to fee recipient.
Important nuance: fee must be minted in shares, not sent as assets. Otherwise with large fees the protocol constantly withdraws liquidity from strategies.
Supported protocols and strategies
| Protocol | Strategy type | Integration complexity | Additional risks |
|---|---|---|---|
| Aave V3 | Lending supply | Low | Oracle risk |
| Compound V3 | Lending supply | Low | Oracle risk |
| Uniswap V3 | LP (concentrated) | High | Impermanent loss |
| Curve + Convex | LP + gauge | Medium | Gauge lock |
| Pendle | Yield tokenization | High | PT/YT expiry |
| GMX | Perp liquidity | High | Directional risk |
Uniswap V3 LP — most complex strategy due to range management. Active strategy (range rebalancing) requires constant price monitoring and calling rebalance() when position exits range, otherwise LP stops earning fees. We use Arrakis or Gamma Protocol as base layer for managed LP positions instead of building from scratch.
Development process
Analytics (3-5 days). Choose protocols for integration, define strategies, estimate APY and risks. Document vault invariants: totalAssets >= totalDebt, share price monotonically increases on profitable operation.
Vault core development (2-3 weeks). ERC-4626 implementation, strategy management system, fee mechanism, emergency pause.
Strategy development (1-2 weeks each). Integration with each protocol, harvest logic, testing on mainnet fork.
Testing (1-2 weeks). Fork-tests simulating mass withdrawals, harvest scenarios, emergency exit. Fuzz-tests on invariants through Echidna.
Deployment and monitoring. The Graph subgraph for vault event indexing, Grafana dashboard for monitoring TVL, APY, harvest frequency.
Timeline estimates
Vault with one strategy (Aave lending) — 3-4 weeks. Multi-strategy vault with automatic harvest via Chainlink — 6-8 weeks. Full aggregator with UI, several strategies and governance — 2-3 months. Cost calculated individually.







