> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Cách sử dụng preprocessedSubscribe

> Truyền phát các giao dịch Solana trước khi thực thi qua WebSocket bằng phương thức preprocessedSubscribe — đăng ký, lọc theo tài khoản và giải mã payload nhị phân.

<Note>
  **Beta công khai.** `preprocessedSubscribe` có trên **tất cả các gói trả phí**
  và được tính ở mức **0,1 credit cho mỗi thông báo** (một thông báo cho mỗi giao dịch
  được chuyển đến).
</Note>

## `preprocessedSubscribe` là gì?

`preprocessedSubscribe` là một phương thức WebSocket của Helius dùng để truyền phát các giao dịch đã được xử lý trước — các giao dịch Solana trước khi thực thi được chuyển đến **trước khi đạt mức cam kết `processed`**. Helius tổng hợp nhiều nguồn trước khi thực thi — chủ yếu là các shred được giải mã trực tiếp khi đến trình xác thực, bổ sung thêm các tín hiệu [xác nhận trước](/docs/vi/pre-confirmations/overview) — rồi chuyển chúng thành một luồng duy nhất gồm các thông báo nhị phân nhỏ gọn đã loại bỏ trùng lặp mà không cần hạ tầng khôi phục từ shred ở phía bạn.

Các giao dịch lấy từ tín hiệu xác nhận trước xuất hiện trên luồng này muộn hơn so với sản phẩm [Preconfirmations](/docs/vi/pre-confirmations/overview) chuyên dụng, vốn vẫn cung cấp quyền truy cập sớm nhất vào các giao dịch đó.

Đây là sản phẩm kế nhiệm sản phẩm LaserStream (gRPC) được xử lý trước trước đây. Nếu hiện đang sử dụng giao dịch được xử lý trước qua gRPC, hãy chuyển sang phương thức này — phương thức này cung cấp cùng loại dữ liệu qua kết nối WebSocket thuần túy với độ trễ thấp hơn, còn hình thức phân phối qua gRPC sẽ bị ngừng hỗ trợ.

| Luồng                                                                             | Thời điểm tương đối                            | Phạm vi bao phủ                                   | Dữ liệu                                                                                                            |
| --------------------------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| [Preconfirmations](/docs/vi/pre-confirmations/overview)                                | Sớm nhất                                       | Giao dịch do các trình xác thực tham gia lên lịch | Giao dịch và trạng thái thực thi (chỉ xác nhận trước của Helius; xác nhận trước của BAM báo cáo là không xác định) |
| `preprocessedSubscribe`                                                           | Thường sau Preconfirmations, trước `processed` | Phạm vi bao phủ rộng đối với giao dịch Solana     | Giao dịch đã ký trước khi thực thi                                                                                 |
| [`transactionSubscribe`](/docs/vi/rpc/websocket/transaction-subscribe) tại `processed` | Sau khi thực thi                               | Giao dịch đã xử lý                                | Giao dịch kèm siêu dữ liệu thực thi                                                                                |

<Warning>
  `preprocessedSubscribe` là **tín hiệu trước khi thực thi được cung cấp trên cơ sở nỗ lực tối đa**, không phải
  mức cam kết. Một giao dịch được truyền phát có thể thất bại, bị loại bỏ hoặc được ghi vào
  một nhánh khác. Hãy đối chiếu với luồng đã xử lý hoặc đã xác nhận trước khi
  coi giao dịch là cuối cùng.
</Warning>

## Điểm cuối

`preprocessedSubscribe` được cung cấp từ `wss://beta.helius-rpc.com` — điểm cuối Helius Gatekeeper — thay vì `mainnet.helius-rpc.com`. Xác thực bằng khóa API dưới dạng tham số truy vấn:

```
wss://beta.helius-rpc.com/?api-key=<API_KEY>
```

Mỗi khóa API được giới hạn ở **10 kết nối/lượt đăng ký đồng thời**.

## Đăng ký

Gửi yêu cầu JSON-RPC bằng phương thức `preprocessedSubscribe`. `params` chứa các bộ lọc tài khoản và là trường bắt buộc — `accountInclude` và `accountRequired` phải chỉ định tổng cộng ít nhất một tài khoản (xem [Lọc](#lọc)):

```json theme={"system"}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "preprocessedSubscribe",
  "params": {
    "accountInclude": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
    "accountExclude": [],
    "accountRequired": []
  }
}
```

Máy chủ xác nhận lượt đăng ký bằng một khung văn bản JSON chứa ID đăng ký:

```json theme={"system"}
{
  "jsonrpc": "2.0",
  "result": 1,
  "id": 1
}
```

Sau xác nhận này, các bản cập nhật giao dịch sẽ đến dưới dạng khung WebSocket **nhị phân** — xem [Payload thông báo](#payload-thông-báo).

## Lọc

Mỗi lượt đăng ký được giới hạn phạm vi bằng các bộ lọc tài khoản trong `params`. Quá trình lọc diễn ra ở phía máy chủ, vì vậy bạn chỉ nhận được những giao dịch mình quan tâm:

```json theme={"system"}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "preprocessedSubscribe",
  "params": {
    "accountInclude": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
    "accountExclude": ["Vote111111111111111111111111111111111111111"],
    "accountRequired": []
  }
}
```

| Bộ lọc            | Cách đối sánh                                                                            |
| ----------------- | ---------------------------------------------------------------------------------------- |
| `accountInclude`  | Khớp khi giao dịch tham chiếu đến **bất kỳ** tài khoản nào trong danh sách.              |
| `accountExclude`  | Loại bỏ giao dịch nếu giao dịch tham chiếu đến **bất kỳ** tài khoản nào trong danh sách. |
| `accountRequired` | Chỉ khớp khi giao dịch tham chiếu đến **tất cả** tài khoản trong danh sách.              |

Quy tắc lọc:

* Ba bộ lọc được kết hợp bằng logic AND.
* `accountInclude` và `accountRequired` phải chỉ định tổng cộng **ít nhất một tài khoản** — không có luồng đầy đủ không được lọc.
* Tài khoản là các khóa công khai được mã hóa base58. Mỗi danh sách chấp nhận tối đa **5.000** địa chỉ.

### Phân giải bảng tra cứu địa chỉ (ALT)

Các bộ lọc tài khoản không chỉ đối sánh với khóa tài khoản tĩnh của giao dịch — Helius phân giải [bảng tra cứu địa chỉ](/docs/vi/glossary#bảng-tra-cứu-địa-chỉ-alt) ở phía máy chủ, vì vậy `accountInclude`, `accountExclude` và `accountRequired` cũng đối sánh với các tài khoản mà giao dịch tải qua ALT. Chỉ cần truyền khóa công khai của tài khoản; bạn không cần tự duy trì ánh xạ ALT hoặc phân giải bảng.

## Payload thông báo

Thông báo được chuyển đến dưới dạng khung WebSocket **nhị phân** (không phải JSON). Mỗi khung chứa một giao dịch duy nhất theo bố cục byte được đóng gói:

| Byte | Trường        | Kiểu                  | Mô tả                                                                                       |
| ---- | ------------- | --------------------- | ------------------------------------------------------------------------------------------- |
| 0    | `version`     | `u8`                  | Phiên bản lược đồ payload. Hiện là `1`.                                                     |
| 1–8  | `slot`        | `u64` (little-endian) | Slot nơi giao dịch được quan sát.                                                           |
| 9–72 | `signature`   | 64 byte               | Chữ ký đầu tiên của giao dịch ở dạng nhị phân.                                              |
| 73+  | `transaction` | `bytes`               | Giao dịch đã ký ở định dạng truyền dẫn Solana. Xem [Giải mã giao dịch](#giải-mã-giao-dịch). |

Đọc lần lượt tiền tố cố định dài 73 byte, sau đó giải mã các byte còn lại để đọc chỉ thị, tài khoản và thông tin tra cứu bảng địa chỉ. Chữ ký được đưa vào tiền tố để bạn có thể xác định và loại bỏ giao dịch trùng lặp mà không cần giải mã toàn bộ phần thân giao dịch.

Luôn đọc và kiểm tra byte `version` trước tiên. Nếu Helius cần cập nhật định dạng payload, phiên bản sẽ tăng — hãy phân nhánh theo phiên bản để bộ giải mã tiếp tục hoạt động khi lược đồ thay đổi.

### Giải mã giao dịch

Các byte giao dịch được chuyển tiếp chính xác như khi quan sát trên mạng, theo kiểu mã hóa truyền dẫn tiêu chuẩn cho phiên bản giao dịch tương ứng. Giao dịch cũ và v0 sử dụng bố cục chữ ký trước do `bincode` tạo ra. Giao dịch v1 ([SIMD-0385](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0385-transaction-v1.md)) sử dụng bố cục thông điệp trước với chữ ký ở cuối, vì vậy `bincode` không xử lý được payload v1. Hãy sử dụng bộ giải mã hỗ trợ mọi phiên bản:

* **Rust:** [`agave-transaction-view`](https://docs.rs/agave-transaction-view) phân tích tại chỗ các giao dịch cũ, v0 và v1 mà không cần bản sao trung gian. Đây là lựa chọn được khuyến nghị. [`wincode`](https://docs.rs/wincode), bộ tuần tự hóa tương thích với bincode được các SDK Solana hiện tại sử dụng, cũng giải mã v1 thành `VersionedTransaction`.
* **JavaScript / TypeScript:** hãy đảm bảo phiên bản thư viện của bạn hỗ trợ giao dịch v1. Các cách triển khai `VersionedTransaction.deserialize` cũ hơn chỉ xử lý giao dịch cũ và v0. Sử dụng `@solana/kit` 8.0+ hoặc `@solana/web3.js` v3. Xem [Hỗ trợ giao dịch v1](/docs/vi/rpc/transaction-v1).

```rust theme={"system"}
use agave_transaction_view::transaction_view::TransactionView;

// `frame` is the full binary WebSocket message
let tx_bytes = &frame[73..];
let tx = TransactionView::try_new_unsanitized(tx_bytes)?;

println!("version: {:?}", tx.version()); // Legacy, V0, or V1
for ix in tx.instructions_iter() {
    println!("program index {}: {} bytes", ix.program_id_index, ix.data.len());
}
```

## Ví dụ

```javascript theme={"system"}
const WebSocket = require('ws');
const bs58module = require('bs58');
const bs58 = bs58module.default ?? bs58module;

const ws = new WebSocket('wss://beta.helius-rpc.com/?api-key=<API_KEY>');

ws.on('open', () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'preprocessedSubscribe',
    // Only Jupiter v6 transactions — accountInclude/accountRequired must
    // specify at least one account between them.
    params: {
      accountInclude: ['JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4'],
      accountExclude: [],
      accountRequired: []
    }
  }));

  // Keep the connection alive
  setInterval(() => ws.ping(), 30_000);
});

ws.on('message', (data, isBinary) => {
  // The subscribe acknowledgement arrives as a JSON text frame
  if (!isBinary) {
    const msg = JSON.parse(data.toString());
    if (msg.id === 1) console.log('Subscribed, ID:', msg.result);
    return;
  }

  // Notifications arrive as binary frames:
  // version (u8) | slot (u64 LE) | signature ([u8; 64]) | transaction bytes
  const buf = Buffer.from(data);
  const version = buf.readUInt8(0); // currently 1 — branch on this if it changes
  if (version !== 1) return; // unknown schema version; update your decoder
  const slot = buf.readBigUInt64LE(1);
  const signature = bs58.encode(buf.subarray(9, 73));
  const txBytes = buf.subarray(73); // transaction in Solana wire format (legacy, v0, or v1)

  console.log('Preprocessed transaction:', { slot, signature, bytes: txBytes.length });
  // Decode txBytes with a decoder that supports transaction v1 (see "Decoding the transaction")
});

ws.on('error', console.error);
ws.on('close', () => process.exit(1));
```

## Có những dữ liệu nào?

Mỗi thông báo chứa giao dịch đã ký, chữ ký đầu tiên và slot của giao dịch. Vì quá trình chuyển đến diễn ra trước khi thực thi, luồng **không** bao gồm:

* Trạng thái thực thi hoặc lỗi
* Số dư trước/sau hoặc thay đổi số dư token
* Thông báo nhật ký hoặc chỉ thị nội bộ
* Số đơn vị tính toán đã tiêu thụ

Có thể hình dung đây là việc nhận được "đề xuất" mà không có "kết quả" — bạn thấy người gửi đã cố thực hiện điều gì, nhưng không biết điều gì thực sự xảy ra. Các bản cập nhật trạng thái tài khoản và chương trình cũng chưa tồn tại ở giai đoạn này; nếu cần trạng thái tài khoản theo thời gian thực, hãy sử dụng [LaserStream gRPC](/docs/vi/laserstream) ở mức cam kết `processed`.

## Áp lực ngược

Luồng không lưu vào bộ đệm vô thời hạn cho các bên tiêu thụ chậm. Nếu máy khách đọc quá chậm và có hơn **4.000 thông báo** tồn đọng ở phía máy chủ, Helius sẽ đóng kết nối — bạn sẽ nhận được một khung đóng WebSocket hợp lệ. Hãy xử lý hết các khung nhanh hơn tốc độ chúng đến: tách các tác vụ nặng như giải mã giao dịch và logic chiến lược khỏi vòng lặp nhận, đồng thời kết nối lại và đăng ký lại sau khi mất kết nối.

## Bảo đảm chuyển giao

Việc chuyển giao được thực hiện trên cơ sở nỗ lực tối đa, không được bảo đảm và không có tính năng phát lại lịch sử. Máy khách nên:

1. Kết nối lại và đăng ký lại sau khi kết nối đóng.
2. Loại bỏ trùng lặp theo chữ ký giao dịch.
3. Coi slot là một quan sát, không phải trạng thái cuối cùng.
4. Đối chiếu với luồng đã xử lý hoặc đã xác nhận khi kết quả thực thi là quan trọng.

## Giá

`preprocessedSubscribe` có trên **tất cả các gói trả phí** và được tính ở mức **0,1 credit cho mỗi thông báo** — một thông báo cho mỗi giao dịch được chuyển đến, được tính vào gói của bạn. Xem [Credit](/docs/vi/billing/credits) để biết chi tiết.

## Nội dung liên quan

<CardGroup cols={2}>
  <Card title="Preconfirmations" icon="bolt" href="/docs/vi/pre-confirmations/overview">
    Các giao dịch được truyền phát trước khi trở thành shred — tín hiệu giao dịch sớm nhất.
  </Card>

  <Card title="Raw Shreds (UDP)" icon="network-wired" href="/docs/vi/shred-delivery/raw-shreds">
    Các gói shred chưa xử lý qua UDP. Bạn tự triển khai quá trình khôi phục từ shred.
  </Card>

  <Card title="transactionSubscribe" icon="tower-broadcast" href="/docs/vi/rpc/websocket/transaction-subscribe">
    Các giao dịch sau khi thực thi với khả năng lọc phong phú và siêu dữ liệu thực thi.
  </Card>
</CardGroup>
