Розроблення yield агрегатора
Vault розгорнувся, стратегія працює — фармить COMP на Compound, продає через Uniswap V3, реінвестує в позицію. На шостому тижні токен COMP різко виріс в ціні, і всі холдери почали одночасно виводити кошти. Стратегія тримала 80% активів у Compound, ліквідності для виводу не вистачало. Контракт почав екстрено виходити з протоколу, платячи 3-4% slippage на кожній операції. Користувачі, які виходили останніми, отримали на 6% менше. Це не баг в контракті — це архітектурна помилка в управлінні ліквідністю vault.
Головні проблеми в архітектурі yield агрегатора
Проблема liquidity buffer та bank run
Класичний vault за ERC-4626 зберігає shares та assets у співвідношенні totalAssets / totalSupply. Якщо 90% активів розгорнуто в стратегії (що правильно для дохідності), при масовому виводі контракт повинен екстрено закривати позиції.
Два варіанти, які ми використовуємо залежно від профілю стратегії:
Idle buffer (тримаємо 10-20% активів у vault). Простий підхід: невеликий відсоток активів не інвестується, служить буфером для малих виводів без взаємодії з протоколами. Yearn Finance використовує debtRatio — кожна стратегія отримує ліміт на управління активами, остача залишається у vault.
Черга виводу з затримкою. Для стратегій з довгими локапами (Curve gauge locks, Convex) — черга виводу з затримкою 24-72 години. Користувач отримує «withdraw ticket» — NFT або запис у маппінгу, який можна виконати після розблокування.
Harvest timing та MEV
Функція harvest(), яка збирає rewards та реінвестує, — лакомий кусок для MEV. Перед harvest() токен нагород коштує X, після продажу — X мінус slippage. Sandwich атакуючий фронтрунить harvest, отримує прибуток від руху ціни.
Рішення:
- Продаж rewards через приватний mempool (Flashbots Protect, MEV Blocker)
- TWAP-продаж: розділити продаж на кілька транзакцій над кількома блоками
- Використання CoW Protocol / 1inch Fusion для batch settlement
Другий варіант простіший в реалізації, але збільшує gas overhead на 30-50%.
Reentrancy у ERC-4626 через ERC-777 токени
Стандарт ERC-4626 не забороняє використовувати ERC-777 як underlying asset. При withdraw() → _burn(shares) → зовнішній трансфер → hook tokensReceived у отримувача є можливість повторно викликати deposit() або withdraw(). Якщо totalAssets оновлюється після трансфреру — share price маніпулюється в цей момент.
Стандартне рішення: nonReentrant на всіх функціях, які змінюють totalAssets або totalSupply. Додатково — перевірка totalAssets до та після операції як assertion.
Як ми будуємо yield агрегатор
Архітектура vault + strategy
Слідуємо паттерну Yearn v2/v3: vault відділений від стратегій. Vault управляє ERC-4626 логікою, обліком shares, лімітами. Стратегії — окремі контракти з єдиним інтерфейсом:
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);
}
Це дозволяє додавати нові стратегії без зміни контракту vault. Vault тримає список активних стратегій з debtRatio для кожної — відсоток від totalAssets, який стратегія може використовувати.
Розподіл по кількох стратегіях
Для vault з кількома стратегіями потрібен механізм розподілу. Простий варіант: фіксовані debtRatio через governance. Продвинутий: автоматичний rebalancer на основі даних APY.
Автоматичний rebalancer — складніше, тому що не можна надійно читати APY з протоколів on-chain. Aave повертає currentLiquidityRate в ray (1e27), Compound — supplyRatePerBlock. Потрібна нормалізація та конвертація в річний відсоток. І це лише поточний APY — не враховує reward токени, gas overhead на rebalance, slippage.
У більшості випадків ми реалізуємо off-chain keeper, який читає APY, розраховує оптимальний розподіл та вызивает rebalance() на vault раз на 6-24 години. On-chain контракт лише перевіряє, що виклик від авторизованого keeper.
Chainlink Automation для harvest
Замість ручного вызива harvest — Chainlink Automation (раніше Keepers). Контракт реалізує AutomationCompatibleInterface:
function checkUpkeep(bytes calldata) external view returns (bool upkeepNeeded, bytes memory);
function performUpkeep(bytes calldata performData) external;
checkUpkeep перевіряє: пройшло достатньо часу з останнього harvest, накопилося достатньо rewards для оплати газу. Якщо обидві умови — upkeepNeeded = true, вузол Chainlink вызивает performUpkeep. Це усуває залежність від ручного управління та гарантує регулярний harvest.
Облік performance fee
Performance fee — відсоток від прибутку, який йде до скарбниці протоколу. Технічно: при кожному harvest розраховуємо profit = totalAssets_after - totalAssets_before. Від прибутку беремо performanceFee (зазвичай 10-20%) та конвертуємо в shares, які мінтяться на fee recipient.
Важливий нюанс: fee повинна мінтитися в shares, а не відправлятися як assets. Інакше при великому обсязі fees протокол постійно ізимає ліквідність зі стратегій.
Підтримувані протоколи та стратегії
| Протокол | Тип стратегії | Складність інтеграції | Додаткові ризики |
|---|---|---|---|
| Aave V3 | Lending supply | Низька | Oracle risk |
| Compound V3 | Lending supply | Низька | Oracle risk |
| Uniswap V3 | LP (concentrated) | Висока | Impermanent loss |
| Curve + Convex | LP + gauge | Середня | Gauge lock |
| Pendle | Yield tokenization | Висока | PT/YT expiry |
| GMX | Perp liquidity | Висока | Directional risk |
Uniswap V3 LP — найбільш складна стратегія через управління діапазонами. Активна стратегія (ребалансування діапазонів) вимагає постійного моніторингу ціни та вызова rebalance() при виході позиції з діапазону, інакше LP позиція перестає заробляти fees. Ми використовуємо Arrakis або Gamma Protocol як базовий шар для керованих LP позицій замість реалізації з нуля.
Процес розроблення
Аналітика (3-5 днів). Вибір протоколів для інтеграції, визначення стратегій, оцінка APY та рисків. Документування інваріантів vault: totalAssets >= totalDebt, share price монотонно зростає при прибутковій роботі.
Розроблення vault core (2-3 тижні). Реалізація ERC-4626, система управління стратегіями, механізм комісій, emergency pause.
Розроблення стратегій (1-2 тижні кожна). Інтеграція з кожним протоколом, логіка harvest, тестування на mainnet fork.
Тестування (1-2 тижні). Fork-тести з моделюванням масових виводів, сценаріїв harvest, emergency exit. Fuzz-тести на інваріанти через Echidna.
Деплой та моніторинг. The Graph субграф для індексації подій vault, Grafana дашборд для моніторингу TVL, APY, частоти harvest.
Орієнтири по строкам
Vault з однією стратегією (Aave lending) — 3-4 тижні. Multi-strategy vault з автоматичним harvest через Chainlink — 6-8 тижнів. Повноцінний агрегатор з UI, кількома стратегіями та governance — 2-3 місяці. Вартість розраховується індивідуально.







