> ## 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.

# parsedTransactionSubscribe

> Đăng ký nhận các giao dịch Solana đã giải mã khớp với bộ lọc theo chương trình, tài khoản và tên lệnh. Nhận toàn bộ giao dịch cùng các đối số và tài khoản có tên.

Bắt đầu một đăng ký. Mỗi giao dịch đã xác nhận khớp với bộ lọc sẽ được gửi dưới dạng `parsedTransactionNotification` và đã được giải mã: mọi lệnh đều có đối số và tài khoản được đặt tên, cùng với phí, danh sách đầy đủ các khóa tài khoản, `summary` ở cấp giao dịch, cũng như các giao dịch chuyển SOL và token.

## Điểm cuối

Parsed Streams có sẵn trên tất cả các gói và được cung cấp qua điểm cuối [Gatekeeper](/docs/vi/gatekeeper/overview), cùng máy chủ với lưu lượng RPC và WebSocket của Helius:

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

## Xác thực

<ParamField query="api-key" type="string" required>
  Khóa API Helius của bạn, được truyền dưới dạng tham số truy vấn `api-key` hoặc tiêu đề `x-api-key`. Khóa bị thiếu hoặc không hợp lệ sẽ bị từ chối với HTTP 401.
</ParamField>

## Nội dung yêu cầu

<ParamField body="params" type="array" required>
  <Expandable title="Filter" defaultOpen>
    Cần có ít nhất một trong `programs` hoặc `accounts.include`. Các trường bạn thiết lập được kết hợp bằng **AND**: một lệnh phải thỏa mãn tất cả các trường đó thì mới khớp.

    <ParamField body="programs" type="string[]">
      Các ID chương trình cần khớp (địa chỉ base58, không phải tên). Một lệnh khớp nếu chương trình của lệnh nằm trong danh sách này. Các phần tử trong danh sách được kết hợp bằng OR.
    </ParamField>

    <ParamField body="instructionNames" type="string[]">
      Tên lệnh đã giải mã, chẳng hạn như `route`. Trước tiên, tên được so khớp chính xác; sau đó dùng phương án dự phòng không phân biệt chữ hoa chữ thường và dấu phân tách, vì vậy `sharedAccountsRoute` cũng khớp với tên trên đường truyền `shared_accounts_route`. Các phần tử trong danh sách được kết hợp bằng OR. Chỉ những lệnh có tên mà danh mục xác định được mới có thể khớp, vì vậy hãy lấy tên từ [describeProgram](/docs/vi/api-reference/parsed-streams/describeprogram).
    </ParamField>

    <ParamField body="accounts.include" type="string[]">
      Địa chỉ tài khoản. Một lệnh khớp nếu bất kỳ địa chỉ nào trong số này xuất hiện trong danh sách tài khoản của lệnh. Các phần tử trong danh sách được kết hợp bằng OR. Hoạt động với mọi lệnh, dù đã giải mã hay chưa. ID chương trình không được tính là tài khoản ở đây.
    </ParamField>

    <ParamField body="accounts.roles" type="object">
      Ánh xạ từ tên vai trò tài khoản đã giải mã đến địa chỉ, chẳng hạn như `{ "user_transfer_authority": "<pubkey>" }`. Mọi mục đều phải thỏa mãn (AND giữa các mục) và lệnh phải được giải mã để áp dụng điều kiện này. Tên vai trò được so khớp **chính xác**, không chuyển đổi chữ hoa chữ thường, vì vậy hãy sao chép chúng từ [describeProgram](/docs/vi/api-reference/parsed-streams/describeprogram) thay vì phỏng đoán.
    </ParamField>

    <ParamField body="includeFailed" type="boolean" default="false">
      Bao gồm các lệnh từ giao dịch thất bại.
    </ParamField>

    <ParamField body="includeCpi" type="boolean" default="true">
      Các lệnh nội bộ (CPI) có thể được xét khớp. Đặt `false` để chỉ khớp các lệnh cấp cao nhất.
    </ParamField>
  </Expandable>

  <Expandable title="Options">
    Tham số thứ hai là tùy chọn.

    <ParamField body="commitment" type="string" default="confirmed">
      Chỉ hỗ trợ `confirmed`.
    </ParamField>

    <ParamField body="details" type="string" default="full">
      Nội dung của mỗi thông báo. `full`: toàn bộ giao dịch, mọi lệnh, cùng với `matchedIndexes` trỏ đến các kết quả khớp bộ lọc. `matched`: chỉ các lệnh đã khớp, không có danh sách chỉ mục. `raw`: chỉ các lệnh đã khớp, mỗi lệnh được rút gọn thành vị trí, `programId` và blob `data` dạng base58, không có trường đã giải mã và không có mảng `accountKeys`. Dùng `matched` khi băng thông quan trọng hơn ngữ cảnh (payload đầy đủ có kích thước trung bình lớn gấp khoảng ba lần), và dùng `raw` khi bạn tự giải mã dữ liệu lệnh và chỉ cần các byte.
    </ParamField>
  </Expandable>
</ParamField>

Các trường không xác định ở bất kỳ đâu trong bộ lọc hoặc tùy chọn đều bị từ chối với `-32602` thay vì bị bỏ qua âm thầm, vì vậy lỗi chính tả sẽ được báo rõ thay vì không khớp với dữ liệu nào.

## Phản hồi

<ResponseField name="result" type="integer">
  ID đăng ký (cần thiết để hủy đăng ký)
</ResponseField>

<RequestExample>
  ```json Request theme={"system"}
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "parsedTransactionSubscribe",
    "params": [
      {
        "programs": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
        "instructionNames": ["route", "shared_accounts_route"],
        "accounts": {
          "include": ["So11111111111111111111111111111111111111112"],
          "roles": { "user_transfer_authority": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin" }
        },
        "includeFailed": false,
        "includeCpi": true
      },
      { "commitment": "confirmed", "details": "full" }
    ]
  }
  ```

  ```typescript TypeScript theme={"system"}
  import WebSocket from "ws";

  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: "parsedTransactionSubscribe",
      params: [{ programs: ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"] }],
    }));
  });

  ws.on("message", (data) => {
    const msg = JSON.parse(data.toString());
    if (msg.method === "parsedTransactionNotification") {
      const { transaction, instructions, matchedIndexes } = msg.params.result.value;
      for (const i of matchedIndexes ?? instructions.keys()) {
        const ix = instructions[i];
        console.log(transaction.signature, ix.programName, ix.instructionName, ix.decoded?.args);
      }
    }
  });
  ```

  ```python Python theme={"system"}
  import asyncio, json, websockets

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

  async def main():
      async with websockets.connect(URL) as ws:
          await ws.send(json.dumps({
              "jsonrpc": "2.0", "id": 1, "method": "parsedTransactionSubscribe",
              "params": [{"programs": ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"]}],
          }))
          async for raw in ws:
              msg = json.loads(raw)
              if msg.get("method") == "parsedTransactionNotification":
                  value = msg["params"]["result"]["value"]
                  for i in value.get("matchedIndexes") or range(len(value["instructions"])):
                      ix = value["instructions"][i]
                      print(ix.get("programName"), ix.get("instructionName"), (ix.get("decoded") or {}).get("args"))

  asyncio.run(main())
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={"system"}
  { "jsonrpc": "2.0", "id": 1, "result": 23 }
  ```

  ```json Notification theme={"system"}
  {
    "jsonrpc": "2.0",
    "method": "parsedTransactionNotification",
    "params": {
      "subscription": 23,
      "result": {
        "context": { "slot": 430172053 },
        "value": {
          "transaction": {
            "signature": "3riSYL4HTRxgQjLayt6L2JPaDR3oaEQg1H4v3fnjUxNU...",
            "slot": 430172053,
            "blockTime": null,
            "feePayer": "6jduWNCTQzG91JGBchfGGxd55Vi5FxJCCJEV18RkXzJX",
            "fee": 5000,
            "accountKeys": ["6jduWNCT...", "..."],
            "status": "ok",
            "error": null,
            "summary": {
              "type": "swap",
              "description": "6jduWNCTQzG91JGBchfGGxd55Vi5FxJCCJEV18RkXzJX swapped 0.001 SOL for 0.183985 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v via Jupiter",
              "parsedData": {
                "type": "swap",
                "protocol": "jupiter",
                "kind": "swap",
                "in_amount": "1000000",
                "actual_out_amount": "183985",
                "input_mint": "So11111111111111111111111111111111111111112",
                "output_mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
              }
            },
            "nativeTransfers": [
              { "fromUserAccount": "6jduWNCT...", "toUserAccount": "DfXygSm4...", "amount": 1000000 }
            ],
            "tokenTransfers": [
              {
                "fromUserAccount": "6jduWNCT...",
                "toUserAccount": "AeUfFU6L...",
                "fromTokenAccount": "HLaEoW1s...",
                "toTokenAccount": "G13P9kSY...",
                "rawTokenAmount": 183985,
                "decimals": 6,
                "tokenStandard": "Fungible",
                "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
              }
            ]
          },
          "instructions": [
            {
              "instructionIndex": 4,
              "innerInstructionIndex": null,
              "stackHeight": 1,
              "programId": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
              "programName": "jupiter",
              "instructionName": "route",
              "summary": {
                "type": "swap",
                "description": "6jduWNCTQzG91JGBchfGGxd55Vi5FxJCCJEV18RkXzJX swapped 0.001 SOL for 0.183985 EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v via Jupiter",
                "parsedData": {
                  "type": "swap",
                  "protocol": "jupiter",
                  "kind": "swap",
                  "in_amount": "1000000",
                  "actual_out_amount": "183985",
                  "input_mint": "So11111111111111111111111111111111111111112",
                  "output_mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
                }
              },
              "decoded": {
                "args": { "in_amount": "1000000", "slippage_bps": 50 },
                "accounts": [
                  { "name": "user_transfer_authority", "pubkey": "9xQe...", "isSigner": true, "isWritable": false }
                ]
              }
            }
          ],
          "matchedIndexes": [8, 13]
        }
      }
    }
  }
  ```
</ResponseExample>

## Thông báo

Mỗi giao dịch khớp sẽ tạo một thông báo cho mỗi đăng ký. Bên trong `params.result.value`:

* **`transaction`** — ngữ cảnh đầy đủ: chữ ký, slot, phí (lamport), danh sách `accountKeys` đầy đủ, `status`/`error`, `summary` ở cấp giao dịch, cùng `nativeTransfers` và `tokenTransfers` đã trích xuất.
* **`instructions`** — mọi lệnh theo thứ tự thực thi, được xác định vị trí bằng `instructionIndex`, `innerInstructionIndex` và `stackHeight`. Các lệnh đã giải mã chứa `decoded.args` và `decoded.accounts` có tên (snake\_case, giá trị u64 ở dạng chuỗi); các lệnh chưa giải mã chứa `rawData` và `rawAccounts` thay thế.
* **`matchedIndexes`** — các chỉ mục trỏ vào `instructions`, cho biết những lệnh nào thực sự khớp với bộ lọc. Với `details: "matched"`, mảng chỉ chứa các kết quả khớp và không có `matchedIndexes`; với `details: "raw"`, mỗi lệnh đã khớp được rút gọn thành vị trí, `programId` và blob `data` dạng base58.

Để xem diễn giải từng trường trong payload thông báo, hãy tham khảo [tài liệu tham chiếu giao thức bắt đầu nhanh](/docs/vi/parsed-streams/quickstart#thông-báo).

## Quản lý đăng ký

`result` từ phản hồi đăng ký là cùng một số xuất hiện trong `params.subscription` trên mọi thông báo từ đăng ký đó. Hãy lưu số này — bạn cần nó để [hủy đăng ký](/docs/vi/api-reference/parsed-streams/parsedtransactionunsubscribe).

Một dự án có thể duy trì tối đa **5 kết nối đồng thời** ở gói Free, **10** ở gói Developer và **50** ở các gói Business và Professional. Giới hạn này được dùng chung cho tất cả các khóa API của dự án, với tối đa **25 đăng ký trên mỗi kết nối**. Xem phần [tổng quan](/docs/vi/api-reference/parsed-streams/overview#giới-hạn) để biết tất cả các giới hạn.
