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

# 通过WebSocket进行代币账户（ATA）筛选

> 使用transactionSubscribe上的tokenAccounts筛选器在LaserStream WebSocket流中捕获钱包的SPL代币转账，该筛选器基于所有者匹配，而不只是简单的accountInclude筛选器。

在[`transactionSubscribe`](/docs/zh/rpc/websocket/transaction-subscribe) WebSocket方法上的`tokenAccounts`筛选器允许订阅匹配\*\*钱包拥有的关联代币账户（ATA）\*\*的活动，而不仅仅是钱包的公钥直接出现的交易。gRPC上也提供相同的筛选器 —— 参见[代币账户（ATA）筛选](/docs/zh/laserstream/token-account-filtering)的gRPC版本。

## 问题：普通账户筛选器错过了传入的代币转账

当你使用`accountInclude: [wallet]`监视一个钱包时，你只能匹配到该钱包公钥出现在交易账号密钥中的交易。一种常见情况被忽略：当有人向钱包发送SPL代币（例如USDC）时，转账接触到的是钱包的**关联代币账户（ATA）** —— 一个单独的程序派生地址 —— 而不是钱包的公钥本身。

因此，普通的`accountInclude: [wallet]`订阅从未看到传入的代币转账。你必须预先列举出钱包拥有的每个ATA并将其添加到筛选器中 —— 但ATA是在需求时创建的（每个铸币一个），所以你无法提前知道完整的集合。

## `tokenAccounts`扩展如何工作

在订阅上设置`tokenAccounts`以扩展匹配，使得`accountInclude`钱包**也**匹配到涉及其拥有的代币账户的交易。匹配是**基于所有者**的：LaserStream在匹配时解析由你`accountInclude`地址拥有的代币账户，因此可以捕捉到钱包拥有的任何代币账户 —— 包括非规范的 —— 不仅仅是派生的ATA地址。你永远不必自己列举ATAs。

省略`tokenAccounts`的订阅行为完全如前，因此可以安全地添加到现有筛选器中。

## 扩展模式

`tokenAccounts`接受三个字符串值之一：

| 值                  | 匹配                      | 体积           | 用于                             |
| ------------------ | ----------------------- | ------------ | ------------------------------ |
| `"balanceChanged"` | 代币余额实际变动（或其代币账户被关闭）的交易  | 较低 —— 推荐的默认值 | "告诉我何时资金实际移动" —— 存款、提款、钱包的结算交换 |
| `"all"`            | 任何引用钱包拥有的代币账户的交易，即使余额未变 | 较高           | 充分了解涉及钱包代币账户的任何情况              |
| `"none"`           | 无扩展 —— 与省略字段相同          | —            | 默认值                            |

从`"balanceChanged"`开始。它在捕获实际资金移动时的体积仅为`"all"`的一小部分。

## 在`transactionSubscribe`中使用它

`tokenAccounts`是Helius对标准Solana WebSocket API的扩展。无效的值会返回JSON-RPC错误`-32602`：`Invalid tokenAccounts value '<x>', expected one of: none, balanceChanged, all`。

```javascript theme={"system"}
const ws = new WebSocket('wss://mainnet.helius-rpc.com/?api-key=<API_KEY>');

ws.on('open', () => {
  ws.send(JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'transactionSubscribe',
    params: [
      {
        accountInclude: ['<WALLET_PUBKEY>'],
        tokenAccounts: 'balanceChanged' // also match the wallet's ATAs
      },
      { commitment: 'confirmed', encoding: 'jsonParsed', maxSupportedTransactionVersion: 0 }
    ]
  }));
  setInterval(() => ws.ping(), 30_000);
});

ws.on('message', (data) => {
  const msg = JSON.parse(data.toString());
  const result = msg.params?.result;
  if (!result) return;
  // Token balances this wallet owns that changed in the tx
  const owned = (result.transaction.meta.postTokenBalances || [])
    .filter((b) => b.owner === '<WALLET_PUBKEY>');
  console.log(result.signature, owned);
});
```

## 读取匹配内容

一旦交易通过ATA扩展匹配，钱包的代币移动存在于交易的`meta.postTokenBalances`和`meta.preTokenBalances`中。通过`owner`筛选这些条目以隔离你的钱包实际拥有的余额，然后对比相同`accountIndex`上的`preTokenBalances`和`postTokenBalances`以查看每个铸币移动了多少。上面的示例显示了筛选步骤。

## 相关

<CardGroup cols={2}>
  <Card title="transactionSubscribe" icon="bolt" href="/docs/zh/rpc/websocket/transaction-subscribe">
    每个`transactionSubscribe`筛选器和选项，包括`tokenAccounts`。
  </Card>

  <Card title="代币账户筛选（gRPC）" icon="coins" href="/docs/zh/laserstream/token-account-filtering">
    LaserStream gRPC交易筛选中的相同`tokenAccounts`扩展。
  </Card>

  <Card title="notifyOn筛选" icon="filter" href="/docs/zh/rpc/websocket/notify-on-filtering">
    跳过`accountSubscribe`和`programSubscribe`上的无操作账户更新。
  </Card>

  <Card title="WebSocket快速入门" icon="rocket" href="/docs/zh/rpc/websocket/quickstart">
    连接到LaserStream WebSocket并流式传输你的第一个事件。
  </Card>
</CardGroup>
