# Подписки WebSocket

> Source: https://docs.blockvectra.com/ru/guides/websocket-subscriptions/

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

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

Используйте WebSocket для получения `newHeads` и отфильтрованных логов `logs` в реальном времени, когда ваше приложение способно поддерживать постоянное соединение. Используйте [Blockchain Webhook API](https://docs.blockvectra.com/ru/guides/webhook-push/) для получения активности отслеживаемых кошельков на HTTPS-эндпоинт с [проверкой подписи исходного тела запроса](https://docs.blockvectra.com/ru/guides/webhook-push/#verify-signatures), повторными попытками и повтором (replay) сохраненных совпадений. Используйте [HTTP-опрос](https://docs.blockvectra.com/ru/guides/stablecoin-payments/) для периодического мониторинга платежей ERC-20 и довыгрузки исторических логов. В руководстве по стейблкоинам также показан [приемник Webhook для USDT / USDC](https://docs.blockvectra.com/ru/guides/stablecoin-payments/#receive-payments-with-webhooks). Архитектурное сравнение поддержки сетей, требований к приемнику и компромиссов восстановления для разработчиков и AI Agent см. в [руководстве по выбору Webhooks, WebSocket или RPC-опроса](https://docs.blockvectra.com/ru/guides/webhook-vs-websocket/).

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

Отключения WebSocket требуют повторной подписки и довыгрузки данных; они не генерируют управляющие Push-события `subscription.gap` или `chain.reorg`. В случае Webhooks пропуск требует сканирования диапазона; уведомление о реорганизации требует пометить или отбросить замененные события перед сохранением автоматически повторно доставленных канонических событий. [Replay в Push](https://docs.blockvectra.com/ru/guides/webhook-push/#delivery-retries-and-replay) повторно отправляет сохраненные совпадения, но не данные до добавления адреса или сети либо за время нахождения подписки в статусе offline. При реализации восстановления ознакомьтесь с [правилами тарификации](https://docs.blockvectra.com/ru/guides/billing-rules/) и [справочником ошибок](https://docs.blockvectra.com/ru/errors/).

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

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

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

| Сеть | Конечная точка WebSocket (Key в пути) |
| --- | --- |
| Robinhood Chain | `wss://api.blockvectra.com/v1/robinhood_mainnet/{api_key}` |
| Robinhood Chain Testnet | `wss://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`](https://docs.blockvectra.com/ru/errors/#missing_api_key)); неизвестный, отключенный или отозванный API key возвращает HTTP 401 ([`invalid_api_key`](https://docs.blockvectra.com/ru/errors/#invalid_api_key)); если аутентификация временно недоступна, возвращается HTTP 503 ([`auth_unavailable`](https://docs.blockvectra.com/ru/errors/#auth_unavailable)).
* **Баланс аккаунта**: аккаунт с нулевым или отрицательным предоплаченным балансом возвращает HTTP 402 ([`balance_exhausted`](https://docs.blockvectra.com/ru/errors/#balance_exhausted)); если состояние тарификации не может быть подтверждено, возвращается HTTP 503 ([`billing_unavailable`](https://docs.blockvectra.com/ru/errors/#billing_unavailable)).
* **Лимиты подключений**: превышение лимита на ключ (20 подключений) или лимита на аккаунт (50 подключений) возвращает HTTP 429 ([`ws_connection_limit`](https://docs.blockvectra.com/ru/errors/#ws_connection_limit)).
* **Доступность сети**: запрос неизвестной или неподдерживаемой сети возвращает HTTP 404 ([`unknown_chain`](https://docs.blockvectra.com/ru/errors/#unknown_chain)).
* **Емкость сервера**: когда сервер занят или перегружен, рукопожатие возвращает HTTP 503 ([`overloaded`](https://docs.blockvectra.com/ru/errors/#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](https://docs.blockvectra.com/ru/guides/billing-rules/).
* Уведомления `newHeads` учитываются один раз на хэш блока для каждого соединения независимо от того, сколько подписок `newHeads` открыто в этом соединении.
* Уведомления `logs` учитываются один раз на подписку для каждого хэша блока и фазы с совпадающими логами; блоки без совпадений не тарифицируются. Несколько совпадающих логов в одном блоке и фазе не увеличивают плату. Раздельные подписки учитываются отдельно, даже если их фильтры пересекаются. Логи реорганизации (`removed: true`) составляют отдельную единицу тарификации; замещающий блок на той же высоте имеет другой хэш и является другой единицей.
* Уведомления тарифицируются только после успешного сброса в буфер отправки сокета; находящиеся в очереди или отброшенные уведомления, которые не были отправлены в сокет, не тарифицируются. Уведомления, поставленные в очередь до ответа на `eth_unsubscribe`, учитываются, если они были отправлены в сокет. Сообщения WebSocket не содержат заголовков тарификации HTTP; проверяйте потребление учтенных CU в разделе использования аккаунта.

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

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

### `newHeads`

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

* **Запрос на подписку**:
  ```json
  {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}
  ```
* **Ответ на подписку**: возвращает непрозрачный шестнадцатеричный идентификатор подписки:
  ```json
  {"jsonrpc":"2.0","id":1,"result":"0x1"}
  ```
* **Фрейм push-уведомления**:
  ```json
  {"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`](https://docs.blockvectra.com/ru/errors/#logs_filter_required)).

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

* **Емкость фильтров**: если активные фильтры логов достигают предела емкости, подписка возвращает код ошибки `-32022` ([`ws_filter_capacity`](https://docs.blockvectra.com/ru/errors/#ws_filter_capacity)).

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

* **Запрос на подписку**:
  ```json
  {"jsonrpc":"2.0","id":2,"method":"eth_subscribe","params":["logs",{"address":"0x1234567890123456789012345678901234567890"}]}
  ```

### `eth_unsubscribe`

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

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

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

**viem v2 (TypeScript)**

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

```ts
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);
  },
});
```


  **Command line (websocat / wscat)**

Подключайтесь с помощью инструментов командной строки, таких как `websocat` или `wscat`, и отправляйте исходные фреймы JSON-RPC:

```bash
# Connect with path-based API key using websocat
websocat "wss://api.blockvectra.com/v1/{chain}/{api_key}"

# Alternatively, pass the key via request header
websocat -H="x-api-key: {api_key}" "wss://api.blockvectra.com/v1/{chain}"

# Or connect using wscat
wscat -c "wss://api.blockvectra.com/v1/{chain}/{api_key}"
```

Отправляйте команды подписки в интерактивную сессию:

```json
{"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}
{"jsonrpc":"2.0","id":2,"method":"eth_subscribe","params":["logs",{"address":"0x1234567890123456789012345678901234567890"}]}
{"jsonrpc":"2.0","id":3,"method":"eth_unsubscribe","params":["0x1"]}
```


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

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

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

## Лимиты

| Лимит                                       | Значение                                                              | Результат при превышении                                            |
| ------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Подписок на соединение WebSocket            | 100                                                                   | `-32022` [`subscription_limit`](https://docs.blockvectra.com/ru/errors/#subscription_limit)     |
| Подписок `newHeads` на соединение WebSocket | 4                                                                     | `-32022` [`subscription_limit`](https://docs.blockvectra.com/ru/errors/#subscription_limit)     |
| Требования к фильтру подписки `logs`        | Необходимо указать `address` или `topic0` (первая позиция в `topics`) | `-32602` [`logs_filter_required`](https://docs.blockvectra.com/ru/errors/#logs_filter_required) |

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

* [Изучите каталог наборов данных](https://blockvectra.com/ru/data/), чтобы увидеть все наборы данных, индексируемые BlockVectra.
* [Посмотрите бесплатный тариф и цены](https://blockvectra.com/ru/pricing/#free), чтобы узнать, что включено в ваш аккаунт.
* [Войдите в консоль](https://console.blockvectra.com/login/?next=%2Fkeys%2F), чтобы создать API key.
