Розробка кастомних аналітик дашбордів на Dune
Dune Analytics — це SQL-движок поверх decoded blockchain даних. Якщо ви умієте писати SQL і розумієте структуру Ethereum транзакцій, ви можете побудувати будь-яку аналітику по будь-якому протоколу без власної інфраструктури. Складність не в інструменті — вона у розумінні того, як дані блокчейну устроєні в реляційній моделі, і у написанні запитів, які віддають результат за розумний час, а не по 5 хвилин.
Структура даних у Dune
Dune працює з двома рівнями даних:
Raw tables — сирі дані ланцюга:
-
ethereum.transactions— всі транзакції (from, to, value, gas, data...) -
ethereum.logs— всі подій (address, topics[0..3], data) -
ethereum.traces— внутрішні транзакції (call traces) -
ethereum.blocks— блоки
Decoded tables — decoded подій конкретних протоколів. Якщо ABI контракту завантажений у Dune, всі його подій доступні як нормалізовані таблиці:
-
uniswap_v3_ethereum.Pair_evt_Swap— всі своопи Uniswap V3 -
aave_v3_ethereum.Pool_evt_LiquidationCall— ліквідації Aave -
erc20_ethereum.evt_Transfer— всі ERC-20 переводи
Завжди надавайте перевагу decoded таблицям — вони швидше і не потребують ручного парсингу data і topics.
Написання ефективних запитів
Основні антипаттерни
Повний скан без партиціонування за датою — найчастіша причина таймаутів:
-- ПОГАНО: повний скан всієї історії
SELECT date_trunc('day', block_time), count(*)
FROM ethereum.transactions
WHERE "to" = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 -- USDC контракт
-- ДОБРЕ: завжди фільтруємо по block_time
SELECT date_trunc('day', block_time), count(*)
FROM ethereum.transactions
WHERE "to" = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
AND block_time >= date_trunc('day', now()) - interval '90 days'
Надлишкові JOIN на великі таблиці — erc20_ethereum.evt_Transfer містить мільярди рядків. JOIN без жорсткої фільтрації убиває запит:
-- ПОГАНО: JOIN на всю історію переводів
SELECT t.from, SUM(t.value)
FROM erc20_ethereum.evt_Transfer t
JOIN my_users u ON t."from" = u.address
WHERE t.contract_address = 0xdAC17F958D2ee523a2206206994597C13D831ec7 -- USDT
GROUP BY 1
-- ДОБРЕ: додаємо часовий діапазон
SELECT t.from, SUM(t.value / 1e6) as usdt_sent
FROM erc20_ethereum.evt_Transfer t
JOIN my_users u ON t."from" = u.address
WHERE t.contract_address = 0xdAC17F958D2ee523a2206206994597C13D831ec7
AND t.evt_block_time >= now() - interval '30 days'
GROUP BY 1
ORDER BY 2 DESC
Паттерни для DeFi аналітики
TVL (Total Value Locked) — потребує відстеження балансів токенів на контракті:
-- TVL протоколу через подій Deposit/Withdraw
WITH deposits AS (
SELECT
date_trunc('day', evt_block_time) AS day,
token,
SUM(amount / POWER(10, decimals)) AS amount
FROM protocol_ethereum.Pool_evt_Deposit
JOIN tokens.erc20 ON token = contract_address AND blockchain = 'ethereum'
WHERE evt_block_time >= now() - interval '180 days'
GROUP BY 1, 2
),
withdrawals AS (
SELECT
date_trunc('day', evt_block_time) AS day,
token,
-SUM(amount / POWER(10, decimals)) AS amount
FROM protocol_ethereum.Pool_evt_Withdraw
JOIN tokens.erc20 ON token = contract_address AND blockchain = 'ethereum'
WHERE evt_block_time >= now() - interval '180 days'
GROUP BY 1, 2
),
daily_flows AS (
SELECT day, token, SUM(amount) AS net_flow
FROM (SELECT * FROM deposits UNION ALL SELECT * FROM withdrawals)
GROUP BY 1, 2
)
SELECT
day,
token,
SUM(net_flow) OVER (PARTITION BY token ORDER BY day) AS cumulative_tvl
FROM daily_flows
ORDER BY day DESC, token
DAU (Daily Active Users) — рахуємо унікальні адреси:
SELECT
date_trunc('day', block_time) AS day,
COUNT(DISTINCT "from") AS dau,
COUNT(*) AS transactions,
COUNT(*) / COUNT(DISTINCT "from") AS avg_txs_per_user
FROM ethereum.transactions
WHERE "to" IN (
SELECT DISTINCT contract_address FROM protocol_ethereum.Pool_evt_Swap
)
AND block_time >= now() - interval '90 days'
AND success = TRUE
GROUP BY 1
ORDER BY 1
Revenue / Fee аналітика:
-- Комісії протоколу через PoolFeeUpdated і Swap подій
SELECT
date_trunc('week', s.evt_block_time) AS week,
SUM(
CASE
WHEN s.token0 = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 -- USDC
THEN ABS(s.amount0) / 1e6 * p.fee / 1e6
ELSE ABS(s.amount1) / 1e6 * p.fee / 1e6
END
) AS protocol_fees_usd
FROM uniswap_v3_ethereum.Pair_evt_Swap s
JOIN uniswap_v3_ethereum.Factory_evt_PoolCreated p
ON s.contract_address = p.pool
WHERE s.evt_block_time >= now() - interval '52 weeks'
GROUP BY 1
ORDER BY 1
Робота з Spellbook (Dune V2)
Spellbook — це dbt проект з переиспользуємими моделями даних. Замість того щоб кожен раз писати логіку декодування цін або нормалізації токенів, використовуємо готові таблиці:
-- prices.usd — нормалізовані USD ціни для всіх токенів по годинам
-- dex.trades — всі DEX своопи в єдиному форматі (Uniswap, Curve, Balancer...)
-- tokens.erc20 — символи, decimals всіх ERC-20 токенів
SELECT
date_trunc('day', dt.block_date) AS day,
SUM(dt.amount_usd) AS volume_usd,
COUNT(DISTINCT dt.taker) AS unique_traders,
COUNT(*) AS trade_count
FROM dex.trades dt
WHERE dt.project = 'uniswap'
AND dt.version = '3'
AND dt.blockchain = 'ethereum'
AND dt.block_date >= now() - interval '30 days'
GROUP BY 1
ORDER BY 1 DESC
Таблиця dex.trades вже містить нормалізовані суми в USD — не потрібно самостійно джойниться з price feeds.
Дашборд: архітектура і UX
Гарний Dune дашборд — це продукт, а не набір графіків. Практичні принципи:
Ієрархія метрик: top-level KPI (TVL, Volume, Users) → drill-down (по мережам, токенам, часовим періодам) → деталі (топ адреси, конкретні транзакції).
Параметри для інтерактивності: Dune підтримує параметри у запитах ({{token_address}}, {{days_back}}). Це робить один дашборд застосовним для різних активів:
SELECT *
FROM dex.trades
WHERE token_bought_address = {{token_address}}
AND block_date >= now() - interval '{{days_back}} days'
Кешовані запити: важкі історичні запити (> 30 днів історії) краще розділити на:
- Історичний snapshot (пересчитується раз у день через scheduled query)
- Delta за останні 24h (швидкий, оновлюється часто)
Об'єднуємо через UNION у фінальному запиті — користувач бачить актуальні дані без очікування 5-хвилинного пересчету.
Що входить в роботу
- Аналіз протоколу і визначення ключових метрик для дашборду
- Написання SQL запитів з оптимізацією за часом виконання
- Візуалізація: вибір типів графіків, настройка осей, кольорів, легенд
- Параметризація запитів для інтерактивності
- Настройка розпорядження оновлення для важких запитів
- Документація логіки розрахунків для команди







