Разработка системы сравнения APY/APR протоколов
Пользователь сравнивает доходность вручную: Aave показывает 4.2% APY на USDC, Compound — 5.1%, Morpho — 5.8%. Но эти цифры нельзя напрямую сравнивать: Aave учитывает compound interest (APY), Compound исторически показывал APR (без учёта реинвестирования), Morpho агрегирует с дополнительными MORPHO rewards. Без нормализации данных любое сравнение misleading.
Главная проблема: нормализация метрик
APR vs APY и разные периоды начисления
APR (Annual Percentage Rate) — простая ставка без учёта реинвестирования. APY (Annual Percentage Yield) — с учётом compound effect. Разница при частом начислении существенна:
APY = (1 + APR/n)^n - 1
При APR = 10% и начислении каждый блок (≈ 2190 раз в год на Ethereum) APY ≈ 10.52%. При начислении раз в день — 10.516%. Разница небольшая, но при сравнении протоколов с разными периодами начисления нужна единая база.
Для корректного сравнения система нормализует всё в Daily APR: divide annualized rate на 365. Пользователю показываем APY (более понятная метрика), внутри считаем в Daily APR.
Rewards tokens: как включать в расчёт
Многие протоколы поверх базовой ставки платят собственные токены (COMP, AAVE, MORPHO). Их нужно включать в total APY, но с учётом:
- Текущая цена reward токена (volatile — цена может упасть к моменту claim)
- Vesting/lock: AAVE Safety Module rewards вестируются 10 дней
- Emission decay: снижение emissions со временем (многие протоколы снижают emissions каждые N дней)
Подход: показывать base APY (без rewards) и total APY (с rewards) раздельно. Пользователь сам решает, учитывать ли нестабильные rewards.
Получение данных on-chain
Каждый протокол предоставляет данные по-своему:
Aave V3: getReserveData(asset) возвращает currentLiquidityRate в ray (1e27). Конвертация: APR = rate / 1e27 * SECONDS_PER_YEAR. Rewards через getRewardsData() в RewardsController.
Compound V3 (Comet): getSupplyRate(utilization) возвращает rate per second. APR = ratePerSecond * SECONDS_PER_YEAR.
Morpho: агрегирует рынки Aave и Compound с оптимизацией. Base rate = underlying market rate, но с peer-to-peer matching может быть выше. supplyAPR() из Morpho Lens контракта.
Curve: доходность из нескольких источников — trading fees (автоматически реинвестируются), CRV emissions (через gauge), boost. CRV APY зависит от veCRV баланса пользователя. Для comparison системы берём «base» APY без boost.
Pendle: YT (Yield Token) implied APY рассчитывается через цену PT — не прямое значение из контракта, а расчёт через market price.
Архитектура системы
Data fetching layer
On-chain вызовы дорогие (в смысле RPC requests). При 10 протоколах × 20 assets × 5 метрик = 1000 calls при каждом обновлении. Решение: Multicall3 батчинг — все calls в одну транзакцию.
const calls = protocols.flatMap(protocol =>
assets.map(asset => ({
target: protocol.address,
callData: protocol.interface.encodeFunctionData("getReserveData", [asset])
}))
);
const results = await multicall.aggregate(calls);
Для исторических данных — The Graph субграфы. Большинство крупных протоколов (Aave, Compound, Uniswap) имеют официальные субграфы с историческими hourly rate данными.
Кэширование и обновление
APY в DeFi меняется с каждым блоком, но значимо — раз в несколько минут. Агрессивное кэширование:
- L1 cache (Redis): текущие rates, TTL 60 секунд
- L2 cache (PostgreSQL): hourly snapshots для исторических графиков
- Real-time updates: WebSocket для подписки на изменения (при наличии events)
Background job каждые 60 секунд fetches on-chain данные, обновляет Redis, раз в час записывает snapshot в PostgreSQL.
Историческая динамика
Текущий APY — моментальный снимок. Для принятия решений нужна историческая динамика: как менялся APY за последние 7/30/90 дней, как реагировал на рыночные события.
Из hourly snapshots строятся:
- Moving average (7d, 30d) для понимания средней доходности
- Volatility метрика (std deviation APY) — насколько стабильна доходность
- Min/max за период — floor и ceiling ожидаемой доходности
Volatile APY (меняется в 5x диапазоне за месяц) — другой профиль риска, чем stable APY (±0.5%). Это важная информация для пользователя.
Ранжирование и сравнение
Просто отсортировать по APY — недостаточно. Ранжирование должно учитывать:
- Risk-adjusted yield: разные протоколы имеют разный risk profile (audit history, TVL, age)
- Liquidity: доступный deposit capacity — важно для крупных сумм
- Stable vs volatile APY: rewards в нативных токенах менее надёжны
- Сеть: gas расходы на Ethereum vs L2 — важны для небольших депозитов
Простое решение: показывать фильтры (только stable APY, только audited protocols, minimum TVL) без попытки создать единый «score» — пользователь сам взвешивает приоритеты.
Технический стек
Backend: Node.js + TypeScript. Viem для on-chain calls (легче ethers.js, tree-shakeable). PostgreSQL для исторических данных. Redis для кэширования. Bullmq для очереди background jobs.
Frontend: Next.js, TanStack Query для data fetching с кэшированием и refetch intervals. Recharts или Tremor для графиков исторической доходности.
Subgraphs: The Graph для исторических данных Aave, Compound, Uniswap. Для протоколов без официального субграфа — собственный event indexer через Ponder или самописный indexer.
Ориентиры по срокам
MVP с 3-5 протоколами, текущими rates без истории — 3-5 дней. Полноценная система с историческими данными, нормализацией, фильтрами и визуализацией — 2-3 недели. Стоимость рассчитывается индивидуально.







