Разработка платформы крипто-сбережений
Пользователь хочет положить USDC и получать проценты, не разбираясь в том, как работает Aave или Compound. Это задача агрегации yield-стратегий за простым интерфейсом. Сложность — не в UI, а в том, что под капотом нужно правильно управлять ликвидностью, не потерять пользовательские средства на переключении стратегий и корректно начислять проценты без накопительных ошибок округления.
Начисление процентов: где теряются деньги пользователей
Continuous compounding vs discrete accrual
Aave V3 использует liquidityIndex — накопительный множитель, который растёт непрерывно. Баланс пользователя — это scaledBalance * currentLiquidityIndex. При каждом действии (deposit, withdraw) scaledBalance пересчитывается через текущий индекс.
Собственная реализация начисления процентов, которую нам принесли на ревью, считала проценты иначе: balance += balance * rate * blocks_elapsed / YEAR_IN_BLOCKS. Казалось бы, эквивалентно. Проблема возникла при депозитах: если пользователь довносил средства, старый баланс пересчитывался по новой ставке, а не по ставке на момент первого депозита. За 6 месяцев накопилась ошибка ~0.3% — небольшая в процентах, но значимая при TVL $5M.
Правильный подход: хранить scaledBalance и глобальный growthIndex. Все операции — через индекс. Это стандарт ERC-4626, который именно для этого и создавался.
ERC-4626 как основа
ERC-4626 (Tokenized Vault Standard) решает задачу крипто-сбережений элегантно: пользователь вносит assets (USDC), получает shares (sUSDC). convertToAssets(shares) всегда возвращает текущую стоимость с учётом накопленного yield. Не нужно изобретать своё.
Критичный момент: функция previewRedeem должна возвращать точно то количество assets, которое получит пользователь при redeem. Расхождение между preview и actual — нарушение стандарта и потенциальный front-running вектор. EIP-4626 специально требует previewRedeem быть pessimistic (округление вниз для пользователя), чтобы протокол никогда не уходил в долг.
Стратегии yield — агрегация и ребалансировка
Интеграция с Aave V3
Aave Pool контракт принимает supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode). В ответ пользователь получает aToken (aUSDC), который автоматически начисляет проценты через увеличение liquidityIndex. При withdraw — withdraw(asset, amount, to).
Для платформы сбережений vault контракт выступает onBehalfOf — держит все aTokens у себя, а пользователям выдаёт shares своего ERC-4626 vault.
Ребалансировка между протоколами
Если yield на Compound выше Aave — протокол должен перевести ликвидность. Это звучит просто, но есть нюансы:
- Withdraw из Aave может занять время если utilization rate высокий (нет свободной ликвидности)
- Gas на ребалансировку съедает часть yield для мелких депозитов
- Flash ребалансировка через flash loan — можно переместить весь депозит атомарно: взять flash loan USDC → supply в новый протокол → withdraw из старого → вернуть flash loan. Платишь 0.09% flash loan fee вместо двух отдельных транзакций.
Порог ребалансировки: разница yield должна покрывать gas + flash loan fee с запасом. Иначе ребалансировка убыточна. Типичный порог — 0.5-1% годовых разницы.
Chainlink для ценовых расчётов
При мультивалютных сбережениях (депозит ETH, yield в USDC) нужны price feeds для корректного отображения баланса в базовой валюте. Chainlink Data Feeds на Ethereum mainnet и L2 — стандарт. Важно: проверять updatedAt timestamp feed-а, чтобы не использовать stale price. Если block.timestamp - updatedAt > heartbeat * 2 — цена устаревшая, нужен fallback.
Гибкие условия: fixed vs flexible terms
Flexible deposits (как в обычном сберегательном счёте): вывод в любой момент, ставка переменная. ERC-4626 vault — стандартная реализация.
Fixed-term deposits (как депозит в банке): пользователь блокирует средства на 30/90/180 дней, получает более высокую ставку. Реализация: lockedUntil timestamp в маппинге, withdraw проверяет его. Ставка — фиксируется при депозите через snapshot yieldIndex.
Проблема с fixed-term: если нужна liquidity до срока, пользователь теряет накопленный yield и/или платит penalty. Это нужно явно обозначить в UI и смарт-контракте — earlyWithdrawPenalty в базисных пунктах.
Безопасность
Rug pull protection: admin не должен иметь возможности вывести пользовательские средства произвольно. onlyOwner на withdraw — красный флаг для аудиторов. Timelock на изменение стратегий (минимум 24-48 часов) через Gnosis Safe + TimelockController.
Паузируемость: при обнаружении проблемы с протоколом, в который инвестированы средства (Aave exploit) — нужна быстрая пауза новых депозитов. Pausable от OpenZeppelin + Emergency withdrawal режим, который позволяет пользователям вывести средства даже в паузе (только выводы, никаких новых депозитов).
Максимальный депозит per user: защита от концентрации риска и whale domination, которая мешает другим пользователям выводить средства.
Процесс работы
Аналитика (2-3 дня). Выбор базовых протоколов, tokenomics (есть ли reward token платформы), multi-chain или single chain, требования к KYC/compliance.
Разработка (1-2 недели). ERC-4626 vault → стратегия интеграции → ребалансировщик → timelock + governance.
Тестирование. Fork-тесты на Ethereum mainnet: реальные ставки Aave, реальный USDC. Сценарии: депозит → yield → ребалансировка → вывод. Fuzz на deposit/withdraw/redeem с случайными суммами и временными параметрами.
Ориентиры по срокам
Простой vault на базе одного протокола (Aave) с ERC-4626 — 1 неделя. Мультистратегийная платформа с ребалансировкой, fixed-term deposits и governance — 4-6 недель.
Стоимость рассчитывается индивидуально.







