# Đăng ký WebSocket

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

BlockVectra cung cấp kết nối WebSocket bảo mật (`wss://`) để nhận luồng đăng ký sự kiện Ethereum thời gian thực cùng với yêu cầu JSON-RPC tiêu chuẩn.

## Chọn WebSocket, Webhook hoặc thăm dò

Dùng WebSocket cho `newHeads` trực tiếp và `logs` đã lọc khi ứng dụng có thể duy trì kết nối. Dùng [Blockchain Webhook API](https://docs.blockvectra.com/vi/guides/webhook-push/) để nhận hoạt động ví được theo dõi tại endpoint HTTPS, với [xác minh chữ ký body gốc](https://docs.blockvectra.com/vi/guides/webhook-push/#verify-signatures), thử lại và phát lại các sự kiện khớp đã lưu giữ. Dùng [thăm dò HTTP](https://docs.blockvectra.com/vi/guides/stablecoin-payments/) để giám sát thanh toán ERC-20 theo lịch và truy xuất bổ sung log lịch sử. Hướng dẫn stablecoin cũng trình bày [bộ nhận Webhook USDT / USDC](https://docs.blockvectra.com/vi/guides/stablecoin-payments/#receive-payments-with-webhooks). Để so sánh kiến trúc về hỗ trợ chuỗi, yêu cầu bộ nhận và đánh đổi khi khôi phục cho nhà phát triển và AI Agent, xem [hướng dẫn chọn Webhook, WebSocket hoặc thăm dò RPC](https://docs.blockvectra.com/vi/guides/webhook-vs-websocket/).

Hỗ trợ WebSocket được xác định từ `ws` và `subscriptions` trong `GET /v1/chains`; hỗ trợ Push được xác định từ danh sách `GET /v1/push/chains` có xác thực. Chuỗi không có WebSocket vẫn có thể dùng Webhook địa chỉ nếu được liệt kê ở đó.

Ngắt kết nối WebSocket cần đăng ký lại và truy xuất bổ sung; không phát sự kiện điều khiển Push `subscription.gap` hay `chain.reorg`. Với Webhook, khoảng trống cần quét theo khoảng; thông báo tái tổ chức cần đánh dấu hoặc loại bỏ sự kiện bị thay thế trước khi giữ lại sự kiện từ chuỗi chuẩn được tự động gửi lại. [Phát lại Push](https://docs.blockvectra.com/vi/guides/webhook-push/#delivery-retries-and-replay) gửi lại các sự kiện khớp đã lưu giữ, không phải dữ liệu trước khi thêm địa chỉ hoặc chuỗi, hay khi đăng ký ở trạng thái offline. Xem [quy tắc tính phí](https://docs.blockvectra.com/en/guides/billing-rules/) và [tham chiếu lỗi](https://docs.blockvectra.com/vi/errors/) khi triển khai khôi phục.

## Chuỗi khả dụng

Bạn có thể kiểm tra đăng ký WebSocket có hoạt động trên một mạng hay không bằng cách đọc `ws` (boolean) và `subscriptions` (mảng loại đăng ký được hỗ trợ) trong `GET /v1/chains`.

Bảng dưới đây phản ánh các mạng đã bật hỗ trợ WebSocket:

| Chuỗi | Endpoint WebSocket (Key trên đường dẫn) |
| --- | --- |
| Robinhood Chain | `wss://api.blockvectra.com/v1/robinhood_mainnet/{api_key}` |
| Robinhood Chain Testnet | `wss://api.blockvectra.com/v1/robinhood_testnet/{api_key}` |

## Kết nối và xác thực

Client thiết lập kết nối WebSocket TLS bảo mật (`wss://`). API key có thể được cung cấp theo hai cách:

* **API key trong đường dẫn**: `wss://api.blockvectra.com/v1/{chain}/{api_key}`
* **API key trong header**: `wss://api.blockvectra.com/v1/{chain}` với header `x-api-key: {api_key}` hoặc `Authorization: Bearer {api_key}` trong handshake HTTP Upgrade.

Khi có API key trong đường dẫn, API key đó được dùng và cả hai header xác thực bị bỏ qua. Khi không có API key trong đường dẫn, `x-api-key` không rỗng được ưu tiên hơn `Authorization: Bearer`. WebSocket API của trình duyệt không thể đặt các header này; dùng URL có API key trong đường dẫn.

### Kiểm tra chấp nhận handshake

Handshake có thể thất bại với:

* **Xác thực**: Thiếu API key trả HTTP 401 ([`missing_api_key`](https://docs.blockvectra.com/vi/errors/#missing_api_key)); API key không xác định, bị vô hiệu hóa hoặc thu hồi trả HTTP 401 ([`invalid_api_key`](https://docs.blockvectra.com/vi/errors/#invalid_api_key)); nếu xác thực tạm thời không khả dụng, phản hồi là HTTP 503 ([`auth_unavailable`](https://docs.blockvectra.com/vi/errors/#auth_unavailable)).
* **Số dư tài khoản**: Tài khoản có số dư trả trước bằng không hoặc âm trả HTTP 402 ([`balance_exhausted`](https://docs.blockvectra.com/vi/errors/#balance_exhausted)); nếu không thể xác nhận trạng thái tính phí, phản hồi là HTTP 503 ([`billing_unavailable`](https://docs.blockvectra.com/vi/errors/#billing_unavailable)).
* **Giới hạn kết nối**: Vượt giới hạn mỗi API key (20 kết nối) hoặc mỗi tài khoản (50 kết nối) trả HTTP 429 ([`ws_connection_limit`](https://docs.blockvectra.com/vi/errors/#ws_connection_limit)).
* **Tính khả dụng của chuỗi**: Yêu cầu chuỗi không xác định hoặc không được phục vụ trả HTTP 404 ([`unknown_chain`](https://docs.blockvectra.com/vi/errors/#unknown_chain)).
* **Dung lượng máy chủ**: Khi máy chủ bận hoặc quá tải, handshake trả HTTP 503 ([`overloaded`](https://docs.blockvectra.com/vi/errors/#overloaded)) với header `Retry-After`.

Sau khi kết nối, client có thể gửi yêu cầu JSON-RPC 2.0 tiêu chuẩn (như `eth_blockNumber` hoặc `eth_call`) và phương thức điều khiển đăng ký dưới dạng frame văn bản UTF-8.

## Quy tắc tính phí

* Thiết lập kết nối, giữ kết nối nhàn rỗi và heartbeat ping/pong không bị tính phí.
* Lệnh gọi `eth_subscribe` và `eth_unsubscribe` thành công bị tính phí, kể cả unsubscribe trả `false`; lệnh gọi thất bại không bị tính phí. Lệnh gọi JSON-RPC thông thường tuân theo [quy tắc tính phí JSON-RPC](https://docs.blockvectra.com/en/guides/billing-rules/).
* Thông báo `newHeads` được tính một lần cho mỗi hash khối trên mỗi kết nối, bất kể kết nối có bao nhiêu đăng ký `newHeads`.
* Thông báo `logs` được tính một lần cho mỗi đăng ký, mỗi hash khối và giai đoạn có log khớp; khối không có log khớp không bị tính phí. Nhiều log khớp trong cùng khối và giai đoạn không làm tăng số lần tính phí. Các đăng ký riêng được tính riêng, kể cả khi bộ lọc chồng lấn. Log tái tổ chức (`removed: true`) tạo thành đơn vị riêng; khối thay thế ở cùng độ cao có hash khác và là đơn vị khác.
* Thông báo chỉ bị tính phí sau khi được flush thành công vào bộ đệm gửi socket; thông báo đang xếp hàng hoặc bị bỏ mà chưa flush không bị tính phí. Thông báo xếp hàng trước phản hồi `eth_unsubscribe` được tính nếu đã flush. Thông điệp WebSocket không có HTTP header tính phí; xem mức sử dụng tài khoản để biết CU đã đo.

## Phương thức đăng ký

API triển khai giao diện pub/sub Ethereum tiêu chuẩn: `eth_subscribe` và `eth_unsubscribe`.

### `newHeads`

Phát đối tượng header khối mới mỗi khi một khối mới được thêm vào đầu chuỗi.

* **Yêu cầu đăng ký**:
  ```json
  {"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}
  ```
* **Phản hồi đăng ký**: Trả mã định danh đăng ký thập lục phân không mang ý nghĩa có thể suy diễn:
  ```json
  {"jsonrpc":"2.0","id":1,"result":"0x1"}
  ```
* **Frame thông báo Push**:
  ```json
  {"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x1","result":{"number":"0x1b4","hash":"0x..."}}}
  ```

### `logs`

Phát log sự kiện khớp tiêu chí lọc đã chỉ định.

* **Yêu cầu bộ lọc**: Mọi bộ lọc đăng ký `logs` **phải** chỉ định `address` (địa chỉ hợp đồng hoặc mảng địa chỉ) hoặc `topic0` (vị trí topic đầu tiên, không null). Bộ lọc không chỉ định cả hai (như `{}` hoặc `{"topics":[null,"0x..."]}`) bị từ chối với mã lỗi `-32602` ([`logs_filter_required`](https://docs.blockvectra.com/vi/errors/#logs_filter_required)).

* **Giới hạn bộ lọc**: Tối đa 100 địa chỉ; tối đa 4 vị trí topic với tối đa 16 hash ứng viên mỗi vị trí.

* **Dung lượng bộ lọc**: Nếu bộ lọc log đang hoạt động đạt giới hạn dung lượng, đăng ký trả mã lỗi `-32022` ([`ws_filter_capacity`](https://docs.blockvectra.com/vi/errors/#ws_filter_capacity)).

* **Tái tổ chức chuỗi**: Nếu khối bị loại bỏ do tái tổ chức chuỗi, thông báo log cho log bị loại bỏ có `"removed": true`.

* **Yêu cầu đăng ký**:
  ```json
  {"jsonrpc":"2.0","id":2,"method":"eth_subscribe","params":["logs",{"address":"0x1234567890123456789012345678901234567890"}]}
  ```

### `eth_unsubscribe`

Chấm dứt đăng ký đang hoạt động bằng mã định danh đăng ký.

* **Yêu cầu hủy đăng ký**:
  ```json
  {"jsonrpc":"2.0","id":3,"method":"eth_unsubscribe","params":["0x1"]}
  ```
* **Phản hồi hủy đăng ký**:
  ```json
  {"jsonrpc":"2.0","id":3,"result":true}
  ```

## Ví dụ có thể chạy

**viem v2 (TypeScript)**

Kết nối bằng [viem](https://viem.sh) v2 qua `createPublicClient` và transport `webSocket`. Thay `{chain}` bằng mã định danh chuỗi đích và `{api_key}` bằng API key của bạn:

```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)**

Kết nối bằng công cụ dòng lệnh như `websocat` hoặc `wscat` và gửi frame JSON-RPC thô:

```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}"
```

Gửi lệnh đăng ký vào phiên tương tác:

```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"]}
```


## Mã đóng kết nối và hành động của client

Khi máy chủ chấm dứt phiên WebSocket, nó gửi frame Close với mã đóng cụ thể và reason ngắn. Bảng dưới đây liệt kê mã đóng máy chủ phát ra và hành động được khuyến nghị:

|                  Mã đóng | Chuỗi reason                     | Mô tả                                                                                                                                  | Có thể thử lại | Hành động của client                                                                                                                                                                                                         |
| -----------------------: | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | :------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [1001](https://docs.blockvectra.com/vi/errors/#1001) | `idle`                           | Kết nối không hoạt động, không có đăng ký hay thông điệp trong 3600 giây (1 giờ)                                                       |       Có       | Kết nối lại khi cần.                                                                                                                                                                                                         |
| [1003](https://docs.blockvectra.com/vi/errors/#1003) | `binary frames are not accepted` | Nhận frame WebSocket nhị phân; chỉ chấp nhận frame văn bản UTF-8                                                                       |      Không     | Không tự động kết nối lại. Cập nhật client để gửi frame văn bản.                                                                                                                                                             |
| [1009](https://docs.blockvectra.com/vi/errors/#1009) | `message too large`              | Payload nhận vào vượt 1 MiB                                                                                                            |      Không     | Không tự động kết nối lại. Chia yêu cầu lớn hoặc giảm kích thước payload.                                                                                                                                                    |
| [1012](https://docs.blockvectra.com/vi/errors/#1012) | `service restart`                | Máy chủ khởi động lại, hoặc phiên đạt thời gian tồn tại tối đa (24 giờ)                                                                |       Có       | Kết nối lại với backoff có ngẫu nhiên hóa, thiết lập lại đăng ký và truy xuất bổ sung dữ liệu bị bỏ lỡ.                                                                                                                      |
| [1013](https://docs.blockvectra.com/vi/errors/#1013) | `chain unavailable`              | Chuỗi không khả dụng                                                                                                                   |       Có       | Kết nối lại với backoff cấp số nhân có full jitter, thiết lập lại đăng ký và truy xuất bổ sung dữ liệu bị bỏ lỡ.                                                                                                             |
| [1013](https://docs.blockvectra.com/vi/errors/#1013) | `overloaded`                     | Máy chủ tạm thời quá tải                                                                                                               |       Có       | Kết nối lại với backoff cấp số nhân có full jitter, thiết lập lại đăng ký và truy xuất bổ sung dữ liệu bị bỏ lỡ.                                                                                                             |
| [4402](https://docs.blockvectra.com/vi/errors/#4402) | `insufficient balance`           | Số dư tài khoản cạn                                                                                                                    |      Không     | Không tự động kết nối lại. [Nạp thêm số dư rồi kết nối lại](https://docs.blockvectra.com/en/guides/billing-rules/).                                                                                                                                      |
| [4404](https://docs.blockvectra.com/vi/errors/#4404) | `invalid api key`                | API key không xác định, bị vô hiệu hóa hoặc thu hồi                                                                                    |      Không     | Không tự động kết nối lại. Xác minh hoặc xoay vòng API key trong bảng điều khiển trước khi kết nối lại.                                                                                                                      |
| [4408](https://docs.blockvectra.com/vi/errors/#4408) | `slow consumer`                  | Máy chủ đóng phiên khi hàng đợi Push vượt 512 KiB và bỏ thông báo đang chờ; client có thể không nhận frame đóng (trình duyệt báo 1006) |       Có       | Xử lý ngắt kết nối bất ngờ (không nhận frame đóng, trình duyệt báo 1006) như 4408: kết nối lại với backoff, thiết lập lại đăng ký và truy xuất bổ sung dữ liệu bị bỏ bằng `eth_getLogs`; giảm số đăng ký hoặc đọc nhanh hơn. |
| [4429](https://docs.blockvectra.com/vi/errors/#4429) | `push rate exceeded`             | Tốc độ thông báo vượt 1,000 lượt Push/giây                                                                                             |       Có       | Giảm số đăng ký hoặc thu hẹp bộ lọc; kết nối lại với backoff, đăng ký lại và truy xuất bổ sung.                                                                                                                              |
| [4503](https://docs.blockvectra.com/vi/errors/#4503) | `billing unavailable`            | Tính phí tạm thời không khả dụng                                                                                                       |       Có       | Trạng thái tạm thời; kết nối lại với backoff cấp số nhân có full jitter.                                                                                                                                                     |

## Kết nối lại và backoff cấp số nhân

Để tránh nhiều client đồng loạt kết nối lại khi mất kết nối, client phải triển khai backoff cấp số nhân với full jitter:

* **Công thức backoff**: Trước lần thử kết nối lại thứ n (n = 0, 1, 2, ...), chờ một khoảng thời gian được chọn ngẫu nhiên đều:
  ```
  delay = random(0, min(20s, 0.5s * 2^n))
  ```
* **Đặt lại bộ đếm**: Chỉ đặt lại bộ đếm thử lại n về 0 sau khi duy trì kết nối ổn định, không gián đoạn ít nhất `60 seconds`.
* **Mã đóng 1012**: Thêm thời gian chờ ban đầu ngẫu nhiên trước lần thử kết nối lại đầu tiên để tránh tăng đột biến do kết nối lại đồng loạt.
* **Mã không thể thử lại**: Không tự động kết nối lại với [4402](https://docs.blockvectra.com/vi/errors/#4402), [4404](https://docs.blockvectra.com/vi/errors/#4404), [1003](https://docs.blockvectra.com/vi/errors/#1003) hoặc [1009](https://docs.blockvectra.com/vi/errors/#1009).

### Truy xuất bổ sung dữ liệu bị bỏ lỡ sau khi kết nối lại

Đăng ký WebSocket không tồn tại qua các kết nối; thông báo phát trong lúc ngắt kết nối không được lưu giữ trên máy chủ. Sau khi kết nối lại, client nên thực hiện chiến lược bắt kịp dữ liệu:

1. **Truy xuất bổ sung log bằng `eth_getLogs`**:
   * Lưu bền vững số khối cao nhất đã xử lý thành công (`last_processed_block`).
   * Gọi ngay `eth_subscribe("logs", ...)` khi kết nối lại để nhận sự kiện trực tiếp.
   * Truy vấn khối bị bỏ lỡ qua `eth_getLogs` với `fromBlock: last_processed_block + 1` và `toBlock: "latest"` (hoặc khối đầu tiên nhận từ luồng trực tiếp).
   * Nếu khoảng ngắt kết nối vượt `max_logs_block_range` của mạng (từ `GET /v1/chains`), chia truy vấn thành các phần không vượt giới hạn đó.
   * Loại trùng mục log ở ranh giới truy vấn bằng bộ giá trị duy nhất `(blockHash, transactionHash, logIndex)`.
2. **Truy xuất bổ sung header khối bằng `eth_getBlockByNumber`**:
   * Ghi lại số khối và hash mới nhất nhận trước khi ngắt kết nối.
   * Đăng ký lại `newHeads`.
   * Truy vấn `eth_getBlockByNumber("latest", false)` và lấy tuần tự các khối trung gian bị thiếu. Xác minh tính liên tục của chuỗi qua `parentHash` để phát hiện tái tổ chức.

## Giới hạn

| Giới hạn                                 | Giá trị                                                                | Kết quả khi vượt                                                    |
| ---------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------- |
| Đăng ký mỗi kết nối WebSocket            | 100                                                                    | `-32022` [`subscription_limit`](https://docs.blockvectra.com/vi/errors/#subscription_limit)     |
| Đăng ký `newHeads` mỗi kết nối WebSocket | 4                                                                      | `-32022` [`subscription_limit`](https://docs.blockvectra.com/vi/errors/#subscription_limit)     |
| Yêu cầu bộ lọc đăng ký `logs`            | Phải chỉ định `address` hoặc `topic0` (vị trí đầu tiên trong `topics`) | `-32602` [`logs_filter_required`](https://docs.blockvectra.com/vi/errors/#logs_filter_required) |

## Các bước tiếp theo

* [Duyệt danh mục bộ dữ liệu](https://blockvectra.com/vi/data/) để xem mọi bộ dữ liệu BlockVectra lập chỉ mục.
* [Xem gói miễn phí và bảng giá](https://blockvectra.com/vi/pricing/#free) để kiểm tra những gì tài khoản của bạn bao gồm.
* [Đăng nhập bảng điều khiển](https://console.blockvectra.com/login/?next=%2Fkeys%2F) để tạo API key.
