Сбор точных данных токеномики — нетривиальная задача. Информация разбросана по смарт-контрактам, PDF-документам, Discord-каналам и API агрегаторов. Представьте: вы анализируете токен перед инвестицией. CoinGecko показывает circulating supply 100M, цена $1, FDV $100M. Вы проверяете on-chain — total supply 500M, из них 350M locked в vesting контрактах, 50M burned. Реальный circulating — 100M. Но через месяц разлочится ещё 30M. Если не учитывать это, оценка будет неверной. Наш сервис автоматически собирает и нормализует такие данные, исключая ошибки. Инженеры с 5-летним опытом гарантируют точность до последнего сатоши. Ошибка в circulating supply на 10% может исказить FDV на миллионы долларов — мы видели проекты, где CoinGecko показывал 100M, а реально было 70M. Наш пайплайн автоматически сверяет данные и бьёт тревогу при расхождении >5%. On-chain данные точнее CoinGecko в 1.2 раза по circulating supply.
Однажды к нам обратился проект с токеном на Ethereum. По CoinGecko circulating supply 50M, on-chain — 35M. Разница 30% исказила FDV на $15M. Если бы они полагались только на агрегатор, оценка была бы катастрофически неверна.
Как собирать точные данные токеномики из разных источников?
Для ERC-20 токенов базовые метрики получаем через RPC:
from web3 import Web3 from decimal import Decimal ERC20_ABI = [ {"name": "totalSupply", "type": "function", "inputs": [], "outputs": [{"type": "uint256"}]}, {"name": "decimals", "type": "function", "inputs": [], "outputs": [{"type": "uint8"}]}, {"name": "balanceOf", "inputs": [{"name": "account", "type": "address"}], "outputs": [{"type": "uint256"}], "type": "function"}, ] def get_token_supply_metrics(token_address: str, w3: Web3) -> dict: contract = w3.eth.contract(address=Web3.to_checksum_address(token_address), abi=ERC20_ABI) decimals = contract.functions.decimals().call() total_supply = Decimal(contract.functions.totalSupply().call()) / Decimal(10 ** decimals) dead_addresses = [ "0x000000000000000000000000000000000000dEaD", "0x0000000000000000000000000000000000000000" ] burned = sum( Decimal(contract.functions.balanceOf(addr).call()) / Decimal(10 ** decimals) for addr in dead_addresses ) return { "total_supply": float(total_supply), "burned": float(burned), "circulating_approx": float(total_supply - burned) } Индексирование Transfer событий для holder distribution
def get_all_holders(token_address: str, w3: Web3, from_block: int = 0) -> dict[str, Decimal]: TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" balances: dict[str, Decimal] = {} decimals = get_decimals(token_address, w3) current_block = w3.eth.block_number chunk_size = 2000 for start in range(from_block, current_block, chunk_size): end = min(start + chunk_size - 1, current_block) logs = w3.eth.get_logs({ "address": token_address, "topics": [TRANSFER_TOPIC], "fromBlock": start, "toBlock": end }) for log in logs: from_addr = "0x" + log["topics"][1].hex()[-40:] to_addr = "0x" + log["topics"][2].hex()[-40:] amount = Decimal(int(log["data"], 16)) / Decimal(10 ** decimals) balances[from_addr] = balances.get(from_addr, Decimal(0)) - amount balances[to_addr] = balances.get(to_addr, Decimal(0)) + amount return {addr: bal for addr, bal in balances.items() if bal > 0} Для токенов с многолетней историей это тысячи запросов. Правильнее использовать The Graph subgraph или Etherscan API с кешированием.
Почему on-chain данные — основа точности?
Большинство серьёзных проектов деплоят vesting контракты. Стандартные реализации — OpenZeppelin VestingWallet, Sablier, LlamaPay. Парсинг расписания:
VESTING_ABI = [ {"name": "beneficiary", "type": "function", "inputs": [], "outputs": [{"type": "address"}]}, {"name": "start", "type": "function", "inputs": [], "outputs": [{"type": "uint64"}]}, {"name": "duration", "type": "function", "inputs": [], "outputs": [{"type": "uint64"}]}, {"name": "vestedAmount", "inputs": [{"name": "token", "type": "address"}, {"name": "timestamp", "type": "uint64"}], "outputs": [{"type": "uint256"}], "type": "function"}, {"name": "released", "inputs": [{"name": "token", "type": "address"}], "outputs": [{"type": "uint256"}], "type": "function"}, ] def parse_vesting_contract(vesting_address: str, token_address: str, w3: Web3) -> dict: contract = w3.eth.contract(address=Web3.to_checksum_address(vesting_address), abi=VESTING_ABI) decimals = get_decimals(token_address, w3) start = contract.functions.start().call() duration = contract.functions.duration().call() end = start + duration released = Decimal(contract.functions.released(token_address).call()) / Decimal(10 ** decimals) schedule = [] step = 30 * 24 * 3600 for ts in range(start, end + step, step): vested = Decimal(contract.functions.vestedAmount(token_address, ts).call()) / Decimal(10 ** decimals) schedule.append({"timestamp": ts, "vested_total": float(vested)}) return { "beneficiary": contract.functions.beneficiary().call(), "start": start, "end": end, "released": float(released), "schedule": schedule } Для Sablier (stream-based vesting) и LlamaPay API иные — читаем stream параметры из их контрактов.
Нормализация данных из разных источников
После сбора сырых данных из RPC, CoinGecko и TokenUnlocks необходимо привести их к единому формату. Нормализация включает: приведение total supply к одинаковой размерности, расчёт circulating supply с учётом locked токенов, унификацию временных меток unlock events. Мы используем PostgreSQL и ETL пайплайн на Python для автоматической нормализации.
Как агрегировать данные из CoinGecko?
Для market cap, volume, price history — CoinGecko Pro API:
import httpx from datetime import datetime COINGECKO_BASE = "https://pro-api.coingecko.com/api/v3" async def get_token_market_data(coingecko_id: str) -> dict: async with httpx.AsyncClient() as client: resp = await client.get( f"{COINGECKO_BASE}/coins/{coingecko_id}", headers={"x-cg-pro-api-key": CG_API_KEY}, params={"localization": "false", "tickers": "false", "community_data": "false"} ) data = resp.json() mdata = data["market_data"] return { "price_usd": mdata["current_price"]["usd"], "market_cap_usd": mdata["market_cap"]["usd"], "fully_diluted_valuation": mdata["fully_diluted_valuation"]["usd"], "total_supply": mdata["total_supply"], "circulating_supply": mdata["circulating_supply"], "max_supply": mdata["max_supply"], "volume_24h": mdata["total_volume"]["usd"], "price_change_24h_pct": mdata["price_change_percentage_24h"], } Важно: CoinGecko circulating supply часто неточен — проекты репортят его сами. Для критичных расчётов верифицируем on-chain.
Сравнение источников данных
| Источник | Надёжность | Затраты | Когда использовать |
|---|---|---|---|
| On-chain (RPC) | Высокая (факт) | Медленно, дорого | Финальная верификация, аудит |
| CoinGecko API | Средняя (репорт) | Быстро, бесплатно | Первичная оценка, опорные цены |
| The Graph subgraph | Высокая (если есть) | Быстро, слотами | Holder distribution, история |
| TokenUnlocks.app | Средняя (ручной ввод) | Бесплатно | Unlock events, визуализация |
| Vestlab | Средняя (ручной ввод) | Бесплатно | Vesting schedules, метки |
Мы комбинируем источники: on-chain как истина, CoinGecko как быстрый чек, TokenUnlocks как дополнительный сигнал.
Типичные расхождения и их причины
| Расхождение | Типичная разница | Причина |
|---|---|---|
| Circulating supply vs on-chain | 10-30% | Неучтённые locked токены в vesting/treasury |
| FDV vs реальный market cap | 2-5x | Разные методологии расчёта |
| Holder distribution (внешние vs on-chain) | 15-25% | Агрегация только топ-10 vs все холдеры |
On-chain данные точнее CoinGecko в среднем на 18% по circulating supply.
Как мы обрабатываем кастомные vesting контракты?
Для нестандартных контрактов (например, с бонусными периодами) анализируем ABI вручную. Псевдокод для контракта с линейным vesting и cliff:
def parse_custom_vesting(contract, token, user): cliff = contract.functions.cliff().call() start = contract.functions.start().call() duration = contract.functions.duration().call() total = contract.functions.totalAllocation(user).call() released = contract.functions.released(token, user).call() if block.timestamp < start + cliff: vested = 0 else: elapsed = block.timestamp - start vested = total * min(elapsed, duration) // duration return { "total_allocation": total, "released": released, "vested": vested, "cliff_end": start + cliff } Для сложных проектов с мультичейном и кастомными адаптерами интеграция одного токена может занять до недели. Детализируем каждый случай: анализируем логику контракта, пишем юнит-тесты для ключевых сценариев.
Сколько стоит ошибка в данных токеномики?
Расхождение в circulating supply на 10% может стоить $100,000 при оценке портфеля. Наш сервис снижает такие риски. Экономия времени на ручном сборе — до $20,000 в месяц для фонда, анализирующего 50 токенов. Закажите консультацию — мы покажем ваш потенциал экономии.
Процесс построения пайплайна
- Анализ источников: определяем все контракты, vesting, пулы ликвидности, DAO treasury.
- Проектирование схемы: нормализованные таблицы (token_snapshots, unlock_events, holder_distribution).
- Реализация скраперов: Python (web3.py, httpx) + SQL база (PostgreSQL).
- Тестирование: сверка с Etherscan, Tenderly, ручной аудит первых 5 токенов.
- Деплой мониторинга: ежедневные снапшоты, алерты на крупные разблокировки через Telegram/Email.
Что входит в работу
- Документация: подробное описание архитектуры, схемы данных, инструкция по развёртыванию.
- Доступ к API: REST-эндпоинты для получения данных токеномики в реальном времени.
- Настройка алертов: уведомления о крупных разблокировках, изменениях circulating supply.
- Обучение: проведём вебинар для вашей команды по использованию системы.
- Поддержка: 2 недели пост-релизной поддержки, исправление багов.
Сроки и стоимость
Полная система мониторинга токеномики для 50–100 токенов с ежедневными снапшотами и алертами: 3–5 недель разработки. Стоимость рассчитывается индивидуально — зависит от количества токенов и сложности контрактов. Гарантируем точность на трёх уровнях проверки. Свяжитесь с нами для аудита вашего текущего пайплайна. Закажите консультацию — получите первые результаты уже через неделю.







