Мы разрабатываем дашборды DeFi-портфелей под ключ. Представьте: пользователь держит USDC в Aave, ETH-USDC LP в Uniswap V3, wstETH в Lido, и открытую перпетуал позицию на GMX. Четыре разных протокола, четыре разных способа представления позиций, четыре разных API или субграфа. Задача дашборда — агрегировать всё это в единый экран с реальными цифрами P&L. Наш опыт показывает, что 80% усилий уходит на data layer: нормализацию данных из разных источников и корректный расчёт stale/live балансов. Мы гарантируем точность до блока и минимальную задержку. Свяжитесь с нами, чтобы оценить проект для вашего стека.
Как рассчитывается P&L и impermanent loss?
Самая сложная часть — корректный расчёт unrealized P&L по LP позициям. Для Uniswap V3 позиция — это NFT с определёнными tickLower, tickUpper, liquidity. Текущие amounts token0 и token1 зависят от текущего sqrtPriceX96 пула. Формула нетривиальна:
function getAmountsFromLiquidity(
sqrtPriceX96: bigint,
sqrtRatioAX96: bigint,
sqrtRatioBX96: bigint,
liquidity: bigint
): [bigint, bigint] {
if (sqrtPriceX96 <= sqrtRatioAX96) {
const amount0 = (liquidity * (sqrtRatioBX96 - sqrtRatioAX96) * Q96)
/ (sqrtRatioBX96 * sqrtRatioAX96)
return [amount0, 0n]
} else if (sqrtPriceX96 < sqrtRatioBX96) {
const amount0 = (liquidity * (sqrtRatioBX96 - sqrtPriceX96) * Q96)
/ (sqrtRatioBX96 * sqrtPriceX96)
const amount1 = (liquidity * (sqrtPriceX96 - sqrtRatioAX96)) / Q96
return [amount0, amount1]
} else {
const amount1 = (liquidity * (sqrtRatioBX96 - sqrtRatioAX96)) / Q96
return [0n, amount1]
}
}
Impermanent loss считается как разница между текущей стоимостью позиции и стоимостью, если бы те же активы просто держались с момента входа. Для дашборда нужно хранить entry price и initial amounts при открытии позиции.
Какие источники данных мы используем?
On-chain direct calls vs индексаторы. Самый точный способ получить баланс — прямой eth_call к контракту. Для токен-баланса — balanceOf(). Для позиции Aave — getUserAccountData(). Это всегда актуально, но медленно: каждый протокол требует отдельных вызовов, а при нескольких десятках протоколов латентность растёт линейно. Решение — Multicall3 (контракт 0xC...A11, деплой на всех major EVM чейнах): батч из 50+ вызовов в одной транзакции. Время ответа — как один RPC вызов вместо 50. Сравнение: Multicall3 в 20 раз быстрее последовательных вызовов.
import { multicall } from 'viem'
const results = await multicall(client, {
contracts: [
{ address: AAVE_POOL, abi: aavePoolAbi, functionName: 'getUserAccountData', args: [userAddress] },
{ address: USDC_TOKEN, abi: erc20Abi, functionName: 'balanceOf', args: [userAddress] },
{ address: UNISWAP_POSITION_MANAGER, abi: nftAbi, functionName: 'balanceOf', args: [userAddress] },
]
})
Для исторических данных (история транзакций, PnL по времени) прямые вызовы не работают — нужны индексаторы.
The Graph для исторических данных. Uniswap, Aave, Compound, Curve, Balancer — все имеют официальные subgraphs в The Graph Network. Subgraph предоставляет GraphQL API для запроса исторических событий: deposits, withdrawals, swaps, liquidations.
query UserPositions($user: String!) {
aaveV3_deposits(where: { user: $user }, orderBy: timestamp, orderDirection: desc) {
amount
reserve { symbol, decimals, priceInUSD }
timestamp
}
aaveV3_borrows(where: { user: $user }) {
amount
reserve { symbol }
currentVariableBorrowRate
}
}
Проблема: у разных версий протоколов разные subgraphs. Aave V2 на Ethereum, Aave V3 на Polygon, Aave V3 на Arbitrum — три разных субграфа с разными схемами. Нормализация — основная инженерная задача дашборда. Мы используем единый слой абстракции, который маппит все схемы в одну модель данных.
Alchemy и Moralis как API-over-RPC. Alchemy API предоставляет готовые методы: getTokenBalances() возвращает все ERC-20 балансы адреса без перебора контрактов. getAssetTransfers() — историю трансферов. Это значительно упрощает начальную реализацию, но стоит денег при высокой нагрузке. Moralis дополнительно агрегирует данные о NFT позициях и DeFi protocol positions через их DeFi API — платный, но экономит месяцы разработки кастомного data layer. Для MVP оправдан Alchemy + The Graph для ключевых протоколов. Для production с десятками тысяч пользователей — собственный indexer.
| Источник | Свежесть | Стоимость | Сложность интеграции |
|---|---|---|---|
| Multicall3 | Live (до блока) | Бесплатно (газ RPC) | Средняя |
| The Graph | Отставание ~1 мин | Бесплатно (лимиты) | Высокая (разные схемы) |
| Alchemy API | Live | Платный (по запросам) | Низкая |
| Moralis | Live | Платный (подписка) | Низкая |
Мультичейн агрегация
Типичный пользователь активен на Ethereum mainnet, Arbitrum, Polygon, Base. Дашборд должен показывать суммарный портфель поперёк чейнов. Схема: параллельные запросы к RPC каждого чейна через Promise.all(), нормализация балансов в USD через единый price oracle. Coingecko API или DefiLlama Price API для получения актуальных цен по token address + chain ID. Проблема cross-chain identity: адрес пользователя одинаков на всех EVM чейнах (ECDSA), но смарт-контракт кошелёк (Safe, Argent) может иметь разные адреса на разных чейнах при несинхронизированном деплое. Мы поддерживаем multi-address mode: пользователь может добавить несколько адресов в один профиль.
Как мы обеспечиваем производительность?
Backend: Node.js + TypeScript с viem для RPC. Redis для кэша балансов (TTL 30 секунд для live данных, 5 минут для исторических). PostgreSQL для хранения исторических snapshot-ов портфеля (для построения equity curve).
Frontend: React + wagmi v2 для wallet connection, Recharts или TradingView Lightweight Charts для графиков, Tanstack Query для data fetching с автоматическим refetch каждые 30 секунд.
WebSocket для real-time обновлений: подписка на eth_subscribe("newHeads") для триггера обновления балансов при новом блоке — выглядит живо без лишних poll-запросов.
Пример настройки WebSocket
const transport = http('https://mainnet.infura.io/v3/YOUR_KEY')
const client = createPublicClient({ transport })
Что входит в работу
- Документация по data layer (схемы данных, описание нормализации).
- Доступ к репозиторию с полным кодом дашборда.
- Обучение команды заказчика (2 часа онлайн).
- 2 недели бесплатной поддержки после деплоя.
- Возможность SLA на дальнейшее сопровождение.
Наш опыт: 5 лет на рынке blockchain-разработки, 20+ успешных проектов в DeFi. Мы гарантируем, что дашборд будет работать без простоев и с точностью до последнего блока.
Процесс работы
- Аналитика (1–2 дня). Список целевых протоколов и чейнов, приоритизация по popular use cases аудитории.
- Data layer (5–7 дней). Multicall агрегатор, интеграция The Graph для ключевых протоколов, нормализация в единую схему позиции.
- Backend API (3–5 дней). REST/GraphQL API для фронтенда, кэширование, история портфеля.
- Frontend (5–7 дней). Wallet connection, суммарный balance view, детали по протоколам, графики.
Ориентиры по срокам
| Этап | Срок |
|---|---|
| MVP (5–7 протоколов, 2–3 чейна) | 2–3 недели |
| Полноценный дашборд (история, IL, алерты, мобильный) | 6–8 недель |
Получите консультацию по вашему проекту — оценим сложность и сроки бесплатно.







