Розробка yield vault з автокомпаундингом
Yield vault — це контракт, який бере пользовательские кошти, розміщує їх в протоколі (Aave, Compound, Curve, Convex), збирає накопичені reward-токени й реінвестує їх без участі користувача. Звучить просто. На практиці — кілька нетривіальних проблем: хто платить за compound, як не потерять на проскальзуванні при продажі rewards, та як не попасти під reentrancy через токени з хуками.
Harvest і compound: архітектурне рішення, яке визначає все
Хто викликає harvest і за чий рахунок
Автокомпаундинг вимагає періодичного виклику функції harvest() — збір rewards і реінвестування. Цю транзакцію хто-то повинен оплатити.
Модель 1: Permissionless harvest. Будь-хто може викликати harvest() і отримати bounty (зазвичай 0.5-1% від collected rewards). Економіка працює, коли rewards достатньо великі, щоб покрити gas + bounty. При низькому TVL vault — harvest трапляється рідко, APY нижче заявленого.
Модель 2: Keeper-based harvest. Chainlink Automation або Gelato Network викликають harvest() по розписанню або умові (accumulated rewards > threshold). Надійніше, але вимагає фінансування keeper. Gelato бере оплату з самих rewards через IAutomate інтерфейс — елегантно.
Модель 3: Harvest при кожному deposit/withdraw. Найпростіший варіант: _harvest() викликається автоматично при кожній пользовательской операції. Не потрібен keeper. Проблема: при першому депозиті в новий vault ви платите gas за harvest нульових rewards.
Ми зазвичай використовуємо гібрид: keeper по розписанню + permissionless harvest з bounty як fallback.
Математика компаундингу й помилки накопляння
APY при компаундингу: (1 + r/n)^n - 1, де r — річна ставка, n — кількість компаундингів в рік. При щоденному компаундингу (n=365) та 20% APR — APY складе 22.13%. При щотижневому (n=52) — 21.94%. Розбіжність невелика, але маркетинговий APY на frontend повинен відображати реальну частоту, інакше користувачі розчаровані.
Помилка округлення: при кожному compound shares * pricePerShare не завжди ділиться нацло на totalAssets. Накопичувальна помилка за тисячі compound-операцій може стати значимою. Рішення: зберігати totalAssets як точне значення (не вичислювати кожен раз з балансів), оновлювати його атомарно при кожній операції.
Стратегія — ядро vault
Інтеграція з Curve + Convex
Класична стратегія: депозит USDC → Curve 3pool (USDC/USDT/DAI) → отримання 3CRV → стейкинг в Convex Finance → отримання CRV + CVX rewards → продаж CRV/CVX за USDC → додавання обратно в 3pool.
Криві взаємодії в Solidity:
// Депозит у Curve
ICurvePool(POOL_3CRV).add_liquidity([amount, 0, 0], minLpTokens);
// Stake LP у Convex
IConvexBooster(BOOSTER).deposit(pid, lpAmount, true);
// Claim rewards
IConvexRewardPool(REWARD_POOL).getReward(address(this), true);
// Sell CRV через Uniswap V3
ISwapRouter(UNISWAP_ROUTER).exactInputSingle(params);
Ризик проскальзування при продажі rewards: CRV/CVX — не найликвідніші токени. Якщо vault накопив 50,000 CRV і продає все за один своп — price impact 2-5%. Рішення: розбити продаж на кілька частин (slicedSell) або використовувати 1inch для оптимального маршруту через кілька пулів.
Управління комісіями
Стандартна структура fee для yield vault:
- Performance fee: 10-20% від накопленого yield при кожному harvest. Йде treasury протоколу.
- Management fee: 0.5-2% років від TVL. Начисляється безперервно через збільшення totalAssets на користь treasury.
- Withdrawal fee: 0-0.1%. Опціонально, discourage короткострокових депозитів.
Реалізація management fee без окремих транзакцій: при кожному totalAssets() call вичислювати elapsed_seconds * annualFeeRate / SECONDS_IN_YEAR * tvl і вичитати з повертаного значення. Treasury shares мінтяться при harvest.
Помилка, яку ми бачили: performance fee рахувалась від gross yield без вычету management fee. Це double-counting: користувач платить management fee плюс performance fee з суми, з якої вже вичена management fee.
Безпека автокомпаундингу
Reentrancy через ERC-777 і callback-токени
Harvest функція робить кілька зовнішніх викликів: claim → swap → deposit. ERC-777 токени (CRV не є, але трапляються в екзотичних стратегіях) викликають tokensReceived хук у отримувача. Якщо vault приймає ERC-777, під час claim хук може викликати deposit або withdraw в vault до завершення harvest.
Паттерн захисту: nonReentrant на harvest, deposit, withdraw. Для vault, який працює з кількома токенами — nonReentrant через єдиний _locked флаг на рівні контракту, не функції.
Sandwich-атаки на harvest своп
Публічний harvest() видно в mempool. MEV-боти роблять sandwich: купляють CRV до harvest своп, продають після, забираючи частину реінвестованого yield.
Захист: deadline на своп (максимум 1-2 блоки), жорсткий minAmountOut через TWAP oracle замість spot price. amountOutMin = twapPrice * amount * (1 - maxSlippage). TWAP за 30 хвилин — Uniswap V3 observe(). Це робить sandwich непрофітабельним: атакуючий не може сдвинути TWAP за 1 транзакцію.
Альтернатива: Flashbots Private Transactions для harvest — транзакція не попадає в публічний mempool, MEV-боти не бачать.
Price manipulation через flash loan
Стратегія, яка читає spot price з AMM для розрахунку compound ratio, уязвима: flash loan тимчасово сдвигає ціну, harvest рахує неверний ratio, частина TVL йде в неоптимальну сторону.
Рішення: ніколи не використовувати spot AMM price для прийняття рішень про суми. Тільки TWAP або Chainlink для розрахунків. Spot price — тільки для slippage protection на рівні мінімального виводу, не для бізнес-логіки.
Стек розробки
Solidity 0.8.x + OpenZeppelin 5.x (ERC-4626, ReentrancyGuard, Pausable, AccessControl). Foundry для тестування: fork-тесты на Ethereum mainnet з реальними Curve/Convex станами. Chainlink Automation для keeper. 1inch Fusion API для оптимальних свапів rewards.
| Ризик | Вектор | Захист |
|---|---|---|
| Reentrancy | ERC-777 хуки в claim | nonReentrant на всі state-changing функції |
| Sandwich | Публічний harvest | TWAP minAmountOut + Flashbots |
| Price manipulation | Spot AMM ціни | Chainlink / TWAP для розрахунків |
| Накопичувальна помилка fee | Неточний totalAssets | Exact accounting, оновлення при кожній операції |
| Занадто рідкий compound | Недостатній TVL | Keeper + permissionless з bounty |
Процес роботи
Аналітика (2-3 дні). Цільові протоколи для стратегії, структура fee, частота compound, вимоги до keeper.
Проектування (3-5 днів). ERC-4626 vault архітектура, стратегія контракт (окремий від vault для апгрейдовности стратегії без міграції користувачів), fee accounting.
Розробка (2-4 тижні). Vault → стратегія → keeper інтеграція → frontend (wagmi + viem).
Тестування. Foundry fork-тесты: повний цикл deposit → earn → harvest → withdraw на mainnet стані. Fuzz на суми, timing, послідовність операцій.
Аудит. Yield vault — високоприоритетна ціль для аудиту. Зовнішній аудит обов'язків при TVL > $500k.
Орієнтири по термінам
Простий vault на один протокол з Chainlink Automation — 1-2 тижні. Мультистратегійний vault з оптимальним роутингом rewards та sandwich-захистом — 4-8 тижнів.
Вартість рахується після визначення стратегії й вимог до безпеки.







