tokenAccounts filter lets a transaction subscription match activity on the associated token accounts (ATAs) a wallet owns, not just transactions where the wallet’s pubkey appears directly. It works the same way in both LaserStream gRPC and the LaserStream WSS.
The problem: plain account filters miss incoming token transfers
When you watch a wallet withaccountInclude: [wallet], you only match transactions where that wallet pubkey appears in the transaction’s account keys. A common case slips through: when someone sends the wallet an SPL token (USDC, for example), the transfer touches the wallet’s associated token account (ATA) — a separate program-derived address — not the wallet pubkey itself.
So a plain accountInclude: [wallet] subscription never sees incoming token transfers. You would have to enumerate every ATA the wallet owns up front and add each one to the filter — but ATAs are created on demand (one per mint), so you can’t know the full set in advance.
How tokenAccounts expansion works
Set tokenAccounts on a transaction filter to expand matching so an accountInclude wallet also matches transactions that touch a token account it owns. Matching is owner-based: LaserStream resolves the token accounts owned by your accountInclude addresses at match time, so it catches any token account the wallet owns — including non-canonical ones — not just the derived ATA address. You never have to list the ATAs yourself.
Subscriptions that omit tokenAccounts behave exactly as before, so it’s safe to add to an existing filter.
Expansion modes
tokenAccounts takes one of three string values:
| Value | Matches | Volume | Use it for |
|---|---|---|---|
"balanceChanged" | Transactions where an owned token balance actually changed (or its token account was closed) | Lower — the recommended default | ”Tell me when money actually moved” — deposits, withdrawals, swaps that settle to the wallet |
"all" | Any transaction that references a token account the wallet owns, even if the balance didn’t change | Higher | Full visibility into anything that so much as touches the wallet’s token accounts |
"none" | No expansion — identical to omitting the field | — | The default |
"balanceChanged". It captures real fund movement at a fraction of the volume of "all".
Use it in LaserStream gRPC
AddtokenAccounts to a transaction filter in your SubscribeRequest. The Helius LaserStream SDK converts the string to the wire-level TokenAccountExpansionControlFlag enum for you (part of yellowstone-grpc-proto 12.5.0+).
Use it in LaserStream WebSocket
The same field is available on thetransactionSubscribe WebSocket method — a Helius extension to the standard Solana WebSocket API. An invalid value returns JSON-RPC error -32602: Invalid tokenAccounts value '<x>', expected one of: none, balanceChanged, all.
Reading what matched
Once a transaction matches via ATA expansion, the wallet’s token movement lives in the transaction’smeta.postTokenBalances and meta.preTokenBalances. Filter those entries by owner to isolate the balances your wallet actually owns, then diff preTokenBalances against postTokenBalances on the same accountIndex to see how much each mint moved. The examples above show the filtering step; the Transaction Monitoring guide shows the full diff.
Related
Transaction Monitoring
Full filtering strategies and a runnable wallet-watch example over gRPC.
transactionSubscribe (WebSocket)
The same
tokenAccounts field on the WebSocket transactionSubscribe method.Subscribe Request Reference
Every transaction filter field, including
tokenAccounts.Compressed Filters
Track hundreds of thousands of accounts in one stream.