Граббінг даних токеноміки проектів

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Граббінг даних токеноміки проектів
Середній
~2-3 дні
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1286
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1122
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    859

Парсинг даних токеномики проектів

Задача звучить просто, але дьявол в деталях: дані токеномики немає в одному місці. Vesting schedule одного проекту — у смарт-контракті, іншого — у PDF whitepaper, третього — лише у Discord пості. Circulating supply часто розходиться між CoinGecko, проектом та тим, що реально on-chain. Побудувати надійний джерело даних — це шар нормалізації поверх heterogeneous джерел.

Що входить у "токеномику" та де це лежить

Метрика Первинне джерело Вторинне джерело
Total supply ERC-20 totalSupply() on-chain CoinGecko API
Circulating supply Розрахунок (total - locked - burned) CoinGecko (часто неточен)
Holder distribution On-chain (Transfer события) Etherscan API
Vesting schedule Vesting контракт Документація
Unlock события Vesting контракт события TokenUnlocks.app, Vestlab
Burn события Transfer to 0x0...dead On-chain
Inflation/emission Контракт (mint события) Whitepaper

On-chain дані: прямі виклики

Для 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)

    # Burned токени: баланс на dead адресах
    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

Повна історія холдерів — через сканування Transfer подій:

def get_all_holders(token_address: str, w3: Web3, from_block: int = 0) -> dict[str, Decimal]:
    """Повертає словник {адреса: баланс} через replay Transfer подій"""
    TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"

    balances: dict[str, Decimal] = {}
    decimals = get_decimals(token_address, w3)

    # Чанкуємо по 2000 блоків (ліміт більшості RPC)
    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}

Для токенів з багаторічною історією це займе час та тисячі RPC запитів. Правильніше використовувати The Graph subgraph якщо він існує для токена, або Etherscan API з кешуванням.

Vesting контракти: парсинг розписання розблокувань

Більшість серйозних проектів деплоять vesting контракти. Стандартні реалізації — OpenZeppelin VestingWallet, Sablier, LlamaPay. Парсинг розписання:

# OpenZeppelin VestingWallet ABI (спрощено)
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)

    # Будуємо unlock розписання: скільки розблокується кожного місяця
    schedule = []
    step = 30 * 24 * 3600  # 30 днів
    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 параметри з їхніх контрактів.

CoinGecko API для агрегованих даних

Для 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.

Нормалізація та зберігання

Дані з розних джерел потрібно привести до єдиної моделі:

CREATE TABLE token_snapshots (
    id              BIGSERIAL PRIMARY KEY,
    token_address   TEXT NOT NULL,
    chain_id        INTEGER NOT NULL,
    snapshot_time   TIMESTAMPTZ NOT NULL,
    total_supply    NUMERIC,
    circulating_supply NUMERIC,
    burned_supply   NUMERIC,
    locked_supply   NUMERIC,
    price_usd       NUMERIC,
    market_cap_usd  NUMERIC,
    holder_count    INTEGER,
    source          TEXT NOT NULL  -- 'on-chain', 'coingecko', 'computed'
);

CREATE TABLE unlock_events (
    id              BIGSERIAL PRIMARY KEY,
    token_address   TEXT NOT NULL,
    vesting_contract TEXT,
    beneficiary     TEXT,
    unlock_time     TIMESTAMPTZ NOT NULL,
    amount          NUMERIC NOT NULL,
    label           TEXT  -- 'team', 'investors', 'ecosystem'
);

Повна система мониторингу токеномики для 50–100 токенів з щоденними снапшотами та алертами на крупні розблокування: 3–5 тижнів розробки.