Подписки WebSocket

Подключайтесь к WebSocket-эндпоинтам BlockVectra для eth_subscribe newHeads и logs. Изучите методы подключения, правила фильтрации, задержки при переподключении и восстановление.

BlockVectra предоставляет защищенные соединения WebSocket (wss://) для потоковой передачи подписок на события Ethereum в реальном времени наряду со стандартными запросами JSON-RPC.

Выбор WebSocket, Webhook или опроса

Используйте WebSocket для получения newHeads и отфильтрованных логов logs в реальном времени, когда ваше приложение способно поддерживать постоянное соединение. Используйте Blockchain Webhook API для получения активности отслеживаемых кошельков на HTTPS-эндпоинт с проверкой подписи исходного тела запроса, повторными попытками и повтором (replay) сохраненных совпадений. Используйте HTTP-опрос для периодического мониторинга платежей ERC-20 и довыгрузки исторических логов. В руководстве по стейблкоинам также показан приемник Webhook для USDT / USDC. Архитектурное сравнение поддержки сетей, требований к приемнику и компромиссов восстановления для разработчиков и AI Agent см. в руководстве по выбору Webhooks, WebSocket или RPC-опроса.

Поддержка WebSocket определяется полями ws и subscriptions в ответе GET /v1/chains; поддержка Push определяется списком аутентифицированного запроса GET /v1/push/chains. Сеть без поддержки WebSocket может использовать адресные Webhooks, если она присутствует в этом списке.

Отключения WebSocket требуют повторной подписки и довыгрузки данных; они не генерируют управляющие Push-события subscription.gap или chain.reorg. В случае Webhooks пропуск требует сканирования диапазона; уведомление о реорганизации требует пометить или отбросить замененные события перед сохранением автоматически повторно доставленных канонических событий. Replay в Push повторно отправляет сохраненные совпадения, но не данные до добавления адреса или сети либо за время нахождения подписки в статусе offline. При реализации восстановления ознакомьтесь с правилами тарификации и справочником ошибок.

Доступные сети

Вы можете проверить активность подписок WebSocket в сети, прочитав поля ws (boolean) и subscriptions (массив поддерживаемых типов) в ответе GET /v1/chains.

В таблице ниже представлены сети, в которых включена поддержка WebSocket:

СетьКонечная точка WebSocket (Key в пути)
Robinhood Chainwss://api.blockvectra.com/v1/robinhood_mainnet/{api_key}
Robinhood Chain Testnetwss://api.blockvectra.com/v1/robinhood_testnet/{api_key}

Подключение и аутентификация

Клиенты устанавливают защищенное соединение TLS WebSocket (wss://). API key можно передать двумя способами:

  • Ключ в пути (Path key): wss://api.blockvectra.com/v1/{chain}/{api_key}
  • Ключ в заголовке (Header key): wss://api.blockvectra.com/v1/{chain} с заголовком x-api-key: {api_key} или Authorization: Bearer {api_key} во время рукопожатия HTTP Upgrade.

При наличии ключа в пути используется именно он, а оба заголовка аутентификации игнорируются. Без ключа в пути непустой заголовок x-api-key имеет приоритет над Authorization: Bearer. API WebSocket в браузерах не могут устанавливать эти заголовки; используйте URL с ключом в пути.

Проверки при рукопожатии

Рукопожатие может завершиться с ошибкой:

  • Аутентификация: отсутствие API key возвращает HTTP 401 (missing_api_key); неизвестный, отключенный или отозванный API key возвращает HTTP 401 (invalid_api_key); если аутентификация временно недоступна, возвращается HTTP 503 (auth_unavailable).
  • Баланс аккаунта: аккаунт с нулевым или отрицательным предоплаченным балансом возвращает HTTP 402 (balance_exhausted); если состояние тарификации не может быть подтверждено, возвращается HTTP 503 (billing_unavailable).
  • Лимиты подключений: превышение лимита на ключ (20 подключений) или лимита на аккаунт (50 подключений) возвращает HTTP 429 (ws_connection_limit).
  • Доступность сети: запрос неизвестной или неподдерживаемой сети возвращает HTTP 404 (unknown_chain).
  • Емкость сервера: когда сервер занят или перегружен, рукопожатие возвращает HTTP 503 (overloaded) с заголовком Retry-After.

После подключения клиенты могут отправлять стандартные запросы JSON-RPC 2.0 (такие как eth_blockNumber или eth_call) и методы управления подписками, оформленные в виде текстовых фреймов UTF-8.

Правила тарификации

  • Установление соединения, поддержание открытого неактивного соединения и ping/pong heartbeats не тарифицируются.
  • Успешные вызовы eth_subscribe и eth_unsubscribe тарифицируются, включая отписку, вернувшую false; вызовы, завершившиеся ошибкой, не тарифицируются. Обычные вызовы JSON-RPC следуют правилам тарификации JSON-RPC.
  • Уведомления newHeads учитываются один раз на хэш блока для каждого соединения независимо от того, сколько подписок newHeads открыто в этом соединении.
  • Уведомления logs учитываются один раз на подписку для каждого хэша блока и фазы с совпадающими логами; блоки без совпадений не тарифицируются. Несколько совпадающих логов в одном блоке и фазе не увеличивают плату. Раздельные подписки учитываются отдельно, даже если их фильтры пересекаются. Логи реорганизации (removed: true) составляют отдельную единицу тарификации; замещающий блок на той же высоте имеет другой хэш и является другой единицей.
  • Уведомления тарифицируются только после успешного сброса в буфер отправки сокета; находящиеся в очереди или отброшенные уведомления, которые не были отправлены в сокет, не тарифицируются. Уведомления, поставленные в очередь до ответа на eth_unsubscribe, учитываются, если они были отправлены в сокет. Сообщения WebSocket не содержат заголовков тарификации HTTP; проверяйте потребление учтенных CU в разделе использования аккаунта.

Методы подписки

API реализует стандартный интерфейс pub/sub Ethereum: eth_subscribe и eth_unsubscribe.

newHeads

Генерирует объект заголовка нового блока при каждом добавлении блока к вершине цепочки.

  • Запрос на подписку:
    {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}
  • Ответ на подписку: возвращает непрозрачный шестнадцатеричный идентификатор подписки:
    {"jsonrpc":"2.0","id":1,"result":"0x1"}
  • Фрейм push-уведомления:
    {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1","result":{"number":"0x1b4","hash":"0x..."}}}

logs

Генерирует события логов, соответствующие заданным критериям фильтра.

  • Требование к фильтру: каждый фильтр подписки logs должен указывать address (адрес контракта или массив адресов) или topic0 (первая позиция в topic, не null). Фильтр, не указывающий ни того, ни другого (например, {} или {"topics":[null,"0x..."]}), отклоняется с кодом ошибки -32602 (logs_filter_required).

  • Лимиты фильтра: не более 100 адресов; не более 4 позиций topic с максимум 16 хэшами-кандидатами на позицию.

  • Емкость фильтров: если активные фильтры логов достигают предела емкости, подписка возвращает код ошибки -32022 (ws_filter_capacity).

  • Реорганизации цепочки: если блок удаляется из-за реорганизации сети, уведомления для удаленных логов содержат "removed": true.

  • Запрос на подписку:

    {"jsonrpc":"2.0","id":2,"method":"eth_subscribe","params":["logs",{"address":"0x1234567890123456789012345678901234567890"}]}

eth_unsubscribe

Завершает активную подписку с использованием ее идентификатора подписки.

  • Запрос на отписку:
    {"jsonrpc":"2.0","id":3,"method":"eth_unsubscribe","params":["0x1"]}
  • Ответ на отписку:
    {"jsonrpc":"2.0","id":3,"result":true}

Примеры для запуска

Подключайтесь с помощью viem v2 через createPublicClient и транспорт webSocket. Замените {chain} на идентификатор целевой сети, а {api_key} — на ваш API key:

import { createPublicClient, webSocket } from 'viem';

const apiKey = process.env.BLOCKVECTRA_API_KEY || '{api_key}';
const url = `wss://api.blockvectra.com/v1/robinhood_mainnet/${apiKey}`;

const client = createPublicClient({
  transport: webSocket(url),
});

// 1. Subscribe to new block headers (newHeads)
const unwatchBlocks = client.watchBlocks({
  onBlock: (block) => {
    console.log('New block header received:', block.number, block.hash);
  },
  onError: (error) => {
    console.error('watchBlocks error:', error);
  },
});

// 2. Subscribe to contract event logs (filter requires address or topic0)
const unwatchEvents = client.watchEvent({
  address: '0x1234567890123456789012345678901234567890',
  onLogs: (logs) => {
    console.log('Matching logs received:', logs);
  },
  onError: (error) => {
    console.error('watchEvent error:', error);
  },
});

Коды закрытия и действия клиента

Когда сервер завершает сессию WebSocket, он отправляет фрейм Close с определенным кодом закрытия и короткой причиной. В таблице ниже перечислены коды закрытия, отправляемые сервером, и рекомендуемые действия:

Код закрытияПричина (reason)ОписаниеПовторяемыйДействие клиента
1001idleНеактивное соединение без подписок или сообщений в течение 3600 секунд (1 час)ДаПереподключайтесь по мере необходимости.
1003binary frames are not acceptedПолучен бинарный фрейм WebSocket; поддерживаются только текстовые фреймы UTF-8НетНе переподключайтесь автоматически. Обновите клиент для отправки текстовых фреймов.
1009message too largeРазмер входящей полезной нагрузки превысил 1 МиБНетНе переподключайтесь автоматически. Разделите крупные запросы или уменьшите размер полезной нагрузки.
1012service restartПерезапуск сервера или сессия достигла максимального времени жизни (24 часа)ДаПереподключитесь со случайной задержкой (jitter backoff), восстановите подписки и довыгрузите пропущенные данные.
1013chain unavailableСеть недоступнаДаПереподключитесь с полной экспоненциальной задержкой со случайным разбросом (full-jitter), восстановите подписки и довыгрузите пропущенные данные.
1013overloadedСервер временно перегруженДаПереподключитесь с полной экспоненциальной задержкой со случайным разбросом (full-jitter), восстановите подписки и довыгрузите пропущенные данные.
4402insufficient balanceБаланс аккаунта исчерпанНетНе переподключайтесь автоматически. Пополните баланс, затем переподключитесь.
4404invalid api keyAPI key неизвестен, отключен или отозванНетНе переподключайтесь автоматически. Проверьте или обновите API key в консоли перед переподключением.
4408slow consumerСервер закрывает сессию, чья очередь push превышает 512 КиБ, и сбрасывает ожидающие уведомления; клиенты могут не получить фрейм закрытия (браузер сообщает 1006)ДаОбрабатывайте непредвиденные отключения (фрейм закрытия не получен, браузер сообщает 1006) аналогично коду 4408: переподключитесь с задержкой, восстановите подписки и довыгрузите сброшенные данные с помощью eth_getLogs; уменьшите количество подписок или считывайте быстрее.
4429push rate exceededСкорость уведомлений превысила 1 000 push/секундуДаУменьшите число подписок или сузьте фильтры; переподключитесь с задержкой, подпишитесь заново и довыгрузите данные.
4503billing unavailableТарификация временно недоступнаДаПереходное состояние; переподключитесь с полной экспоненциальной задержкой со случайным разбросом (full-jitter).

Переподключение и экспоненциальная задержка

Для предотвращения шторма синхронных переподключений при обрыве соединений клиенты должны реализовать экспоненциальную задержку с полным случайным разбросом (full jitter):

  • Формула задержки: перед n-й попыткой переподключения (n = 0, 1, 2, ...) подождите промежуток времени, выбранный равномерно случайным образом:
    delay = random(0, min(20s, 0.5s * 2^n))
  • Сброс счетчика: сбрасывайте счетчик повторов n в 0 только после поддержания непрерывного стабильного соединения в течение не менее 60 seconds.
  • Код закрытия 1012: добавьте случайную начальную задержку перед первой попыткой переподключения, чтобы избежать синхронных пиков повторных подключений.
  • Неповторяемые коды: не переподключайтесь автоматически при кодах 4402, 4404, 1003 или 1009.

Довыгрузка пропущенных данных после переподключения

Подписки WebSocket не сохраняются между соединениями; уведомления, отправленные во время отключения, не сохраняются на сервере. После переподключения клиенты должны реализовать стратегию наверстывания (catch-up):

  1. Довыгрузка логов с помощью eth_getLogs:
    • Сохраняйте максимальный номер блока, успешно обработанный вашей системой (last_processed_block).
    • Сразу после переподключения вызовите eth_subscribe("logs", ...), чтобы захватывать события в реальном времени.
    • Запрашивайте пропущенные блоки через eth_getLogs с параметрами fromBlock: last_processed_block + 1 и toBlock: "latest" (или первый блок, полученный из потока в реальном времени).
    • Если разрыв при отключении превышает лимит сети max_logs_block_range (из GET /v1/chains), разделите запросы на фрагменты, не превышающие этот лимит.
    • Дедуплицируйте записи логов на границе запросов, используя уникальный кортеж (blockHash, transactionHash, logIndex).
  2. Довыгрузка заголовков блоков с помощью eth_getBlockByNumber:
    • Зафиксируйте последний номер блока и его хэш, полученные перед отключением.
    • Подпишитесь заново на newHeads.
    • Запросите eth_getBlockByNumber("latest", false) и последовательно получите недостающие промежуточные блоки. Проверяйте непрерывность цепочки по parentHash для выявления реорганизаций.

Лимиты

ЛимитЗначениеРезультат при превышении
Подписок на соединение WebSocket100-32022 subscription_limit
Подписок newHeads на соединение WebSocket4-32022 subscription_limit
Требования к фильтру подписки logsНеобходимо указать address или topic0 (первая позиция в topics)-32602 logs_filter_required

Следующие шаги

Последнее обновление:

На этой странице