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

> 订阅匹配过滤条件的解码 Solana 交易，包括程序、账户和指令名称。接收带有命名参数和账户的完整交易。

开始订阅。每个符合你的过滤条件的已确认交易会作为 `parsedTransactionNotification` 到达，已解码：每个带有命名参数和账户的指令，加上费用、完整的账户密钥列表、交易级别的 `summary`，以及 SOL 和代币转账。

## 端点

解析流处于封闭测试阶段。Helius 团队会将你的项目 ID 加入白名单，并与你分享连接端点：

* `wss://<ENDPOINT>/?api-key=<API_KEY>`

## 授权

<ParamField query="api-key" type="string" required>
  你的 Helius API 密钥，作为 `api-key` 查询参数或 `x-api-key` 头传递。缺少、无效或未列入白名单的密钥将被拒绝，并返回 HTTP 401。
</ParamField>

## 请求体

<ParamField body="params" type="array" required>
  <Expandable title="Filter" defaultOpen>
    至少需要其中之一：`programs` 或 `accounts.include`。你设置的字段组合为 **AND**：指令必须满足所有条件才能匹配。

    <ParamField body="programs" type="string[]">
      要匹配的程序 ID（base58 地址，不是名称）。如果指令的程序在此列表中，则匹配。列表内为 OR。
    </ParamField>

    <ParamField body="instructionNames" type="string[]">
      解码后的指令名称，例如 `route`。首先精确匹配，然后不区分大小写和分隔符匹配，因此 `sharedAccountsRoute` 还匹配 wire 名称 `shared_accounts_route`。列表内为 OR。只有目录可以识别名称的指令才能匹配，因此从 [describeProgram](/docs/zh/api-reference/parsed-streams/describeprogram) 中获取名称。
    </ParamField>

    <ParamField body="accounts.include" type="string[]">
      账户地址。如果这些中的任何一个出现在指令的账户列表中，则匹配。列表内为 OR。适用于每个指令，无论是否解码。程序 ID 本身不作为账户计算在内。
    </ParamField>

    <ParamField body="accounts.roles" type="object">
      解码后账户角色名称到地址的映射，例如 `{ "user_transfer_authority": "<pubkey>" }`。每个条目必须成立（条目间为 AND），指令必须解码才能适用。角色名称需精确匹配，不区分大小写，所以从 [describeProgram](/docs/zh/api-reference/parsed-streams/describeprogram) 复制名称而不是猜测。
    </ParamField>

    <ParamField body="includeFailed" type="boolean" default="false">
      包括失败交易的指令。
    </ParamField>

    <ParamField body="includeCpi" type="boolean" default="true">
      内部（CPI）指令有资格匹配。设置 `false` 仅匹配顶级指令。
    </ParamField>
  </Expandable>

  <Expandable title="Options">
    第二个参数是可选的。

    <ParamField body="commitment" type="string" default="confirmed">
      仅支持 `confirmed`。
    </ParamField>

    <ParamField body="details" type="string" default="full">
      每个通知携带的内容。`full`：整个交易，每个指令，加上指向过滤匹配项的 `matchedIndexes`。`matched`：只有匹配的指令，没有索引列表。`raw`：仅匹配的指令，每个指令缩减到其位置、`programId` 和 base58 的 `data` blob，没有解码字段和 `accountKeys` 数组。在线带宽优先于上下文时使用 `matched`（完整负载大小约为其三倍），当自行解码指令数据时仅需字节时使用 `raw`。
    </ParamField>
  </Expandable>
</ParamField>

过滤器或选项中的任何未知字段将被 `-32602` 拒绝，而不是被悄悄忽略，因此拼写错误将导致失败而不是没有匹配。

## 响应

<ResponseField name="result" type="integer">
  订阅 ID（取消订阅时需要）
</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://<ENDPOINT>/?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://<ENDPOINT>/?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": [
            {
              "topIndex": 4,
              "innerIndex": 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>

## 通知

每个符合条件的交易每个订阅一个通知。在 `params.result.value` 中：

* **`transaction`** — 完整上下文：签名、槽位、费用（lamports）、完整的 `accountKeys` 列表、`status`/`error`、交易级别的 `summary`，以及提取的 `nativeTransfers` 和 `tokenTransfers`。
* **`instructions`** — 按执行顺序的每个指令，按 `topIndex`、`innerIndex` 和 `stackHeight` 定位。解码指令携带命名的 `decoded.args` 和 `decoded.accounts`（snake\_case，u64 值为字符串）；未解码的携带 `rawData` 和 `rawAccounts`。
* **`matchedIndexes`** — 指向 `instructions` 的索引，告诉你哪些是你的过滤器实际命中。使用 `details: "matched"` 数组仅包含命中项且无 `matchedIndexes`；使用 `details: "raw"` 每个匹配指令缩减到其位置、`programId` 和 base58 `data` blob。

有关通知有效负载的字段逐项解读，请参阅 [快速入门协议参考](/docs/zh/parsed-streams/quickstart#notifications)。

## 管理订阅

从订阅响应中获得的 `result` 与从该订阅中每个通知的 `params.subscription` 中出现的数字相同。存储它 — 你需要它来[取消订阅](/docs/zh/api-reference/parsed-streams/parsedtransactionunsubscribe)。

一个项目最多可以持有 **100 个并发连接**，在其所有 API 密钥中共享，每个连接最多可有 **25 个订阅**。查看 [概览](/docs/zh/api-reference/parsed-streams/overview#limits) 了解所有限制。
