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

# Token Mint Filtering

> Subscribe to every transaction that touches a token mint in LaserStream gRPC with matchMints. Catches SPL transfers that accountInclude misses because the mint is not in the account keys.

LaserStream's `matchMints` flag lets a gRPC transaction subscription match on the **token mints in a transaction's pre/post token balances** in addition to its account keys.

Put a mint in `accountInclude`, set `matchMints: true`, and you receive every transaction that touches that token: transfers, swaps, mint-to, burns, and account closes.

<Note>
  `matchMints` is available on LaserStream gRPC only. It is not available on LaserStream WebSocket yet.
</Note>

## The problem: plain account filters miss most token transfers

When you watch a token with `accountInclude: [mint]`, you only match transactions where the mint pubkey appears in the transaction's account keys.

A classic SPL `Transfer` instruction never references the mint. It only names the source token account, the destination token account, and the owner, so a plain account filter misses the most common operation on any token.

Only instructions that pass the mint directly match, such as `MintTo`, `Burn`, `TransferChecked`, and swaps whose program accounts include the mint. The only workaround was to stream all transactions and inspect each one's token balances yourself.

## How `matchMints` works

Set `matchMints: true` on a transaction filter and LaserStream builds a set of mints from the transaction's `preTokenBalances` and `postTokenBalances`.

Your `accountInclude`, `accountExclude`, and `accountRequired` lists are then matched against **both** the account keys and that mint set. A mint qualifies if any token account for it appears in either balance list, regardless of whether the balance changed.

The flag is opt-in, and filters that omit it behave exactly as before, so you can add it to an existing subscription without changing what that subscription already receives. SPL and Token-2022 mints work because both programs populate pre/post token balances.

## Semantics

| Predicate         | Without `matchMints`                             | With `matchMints: true`                                                                                   |
| ----------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
| `accountInclude`  | Matches if any listed key is in the account keys | Matches if any listed key is in the account keys **or** in the mint set                                   |
| `accountExclude`  | Rejects if any listed key is in the account keys | Rejects if any listed key is in the account keys **or** in the mint set                                   |
| `accountRequired` | Every listed key must be in the account keys     | Every listed key must be in the account keys **or** in the mint set (each key can be satisfied by either) |

The rest of the filter logic is unchanged:

* Predicates inside one named filter are still AND-combined (`vote`, `failed`, `signature`, and the account lists).
* Multiple named filters are still OR-combined.
* Values inside a list are OR (except `accountRequired`, where all must match).
* A transaction with no token balances falls back to keys-only matching. `matchMints` never adds transactions that have no token activity.
* `matchMints` on its own does not restrict the stream. You still need at least one key or mint in an account list (or another restricting predicate) for the filter to be accepted.

<Note>
  LaserStream matches mints by exact pubkey. There is no "balance changed only" mode for mints, unlike `tokenAccounts: "balanceChanged"`.
</Note>

## Use it in LaserStream gRPC

Add `matchMints: true` to a transaction filter in your `SubscribeRequest` and put the mint in `accountInclude`. This example streams every USDC transaction on mainnet:

<Tabs>
  <Tab title="TypeScript">
    Requires `helius-laserstream` 0.8.5 or later. The field is also accepted as `match_mints`.

    ```typescript theme={"system"}
    import { subscribe, CommitmentLevel, LaserstreamConfig, SubscribeRequest } from 'helius-laserstream';
    import bs58 from 'bs58';

    const USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';

    const subscriptionRequest: SubscribeRequest = {
      transactions: {
        'usdc-txs': {
          accountInclude: [USDC],
          accountExclude: [],
          accountRequired: [],
          vote: false,
          failed: false,
          matchMints: true, // match USDC via pre/post token-balance mints
        },
      },
      commitment: CommitmentLevel.CONFIRMED,
      accounts: {}, slots: {}, transactionsStatus: {},
      blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: [],
    };

    const config: LaserstreamConfig = {
      apiKey: 'YOUR_API_KEY',
      endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com',
    };

    await subscribe(config, subscriptionRequest, async (data) => {
      const tx = data.transaction?.transaction;
      if (!tx) return;
      // USDC balances touched by this transaction
      const usdcBalances = (tx.meta?.postTokenBalances || []).filter((b: any) => b.mint === USDC);
      console.log(bs58.encode(tx.signature), usdcBalances);
    }, async (error) => {
      console.error('Stream error:', error);
    });
    ```
  </Tab>

  <Tab title="Rust">
    Requires `helius-laserstream` 0.6.4 or later (which pulls `laserstream-core-proto` 11.3.0). The field comes straight from the proto crate.

    ```rust theme={"system"}
    use std::collections::HashMap;
    use helius_laserstream::grpc::{SubscribeRequest, SubscribeRequestFilterTransactions};

    let request = SubscribeRequest {
        transactions: HashMap::from([(
            "usdc-txs".to_string(),
            SubscribeRequestFilterTransactions {
                account_include: vec!["EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v".to_string()],
                vote: Some(false),
                failed: Some(false),
                match_mints: true,
                ..Default::default()
            },
        )]),
        ..Default::default()
    };
    ```
  </Tab>

  <Tab title="Go">
    Requires the Go module at tag `go/v0.3.0` or later.

    ```go theme={"system"}
    vote := false
    failed := false
    req := &laserstream.SubscribeRequest{
        Transactions: map[string]*laserstream.SubscribeRequestFilterTransactions{
            "usdc-txs": {
                AccountInclude: []string{"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"},
                Vote:           &vote,
                Failed:         &failed,
                MatchMints:     true,
            },
        },
        Commitment: &commitmentLevel,
    }
    ```
  </Tab>
</Tabs>

If you use a raw gRPC or Yellowstone client instead of the SDK, regenerate from the Helius proto (`laserstream-core-proto` 11.3.0 or later, or the `.proto` bundled in the SDK repo).

`match_mints` is field 32 of `SubscribeRequestFilterTransactions`. Clients generated from the upstream Triton proto silently drop the unknown field, so the flag has no effect until you regenerate.

See the [Subscribe Request reference](/docs/laserstream/grpc#subscribe-request) for every transaction filter field.

## Combine with `tokenAccounts` to watch one token for one wallet

`matchMints` composes with [`tokenAccounts` expansion](/docs/laserstream/token-account-filtering), so one filter can match on wallet owners and mints at the same time.

This example streams every change to one wallet's USDC balance:

```typescript theme={"system"}
transactions: {
  'wallet-usdc': {
    accountInclude: [WALLET],
    accountRequired: [USDC],
    accountExclude: [],
    tokenAccounts: 'balanceChanged', // wallet matched via its token accounts
    matchMints: true,                // USDC matched via balance mints
    vote: false,
    failed: false,
  },
},
```

`accountInclude` plus `tokenAccounts` finds transactions where the wallet's token balances moved. `accountRequired` plus `matchMints` narrows those to the ones involving USDC.

## Reading what matched

Once a transaction matches via a mint, look for the mint in `meta.preTokenBalances[].mint` and `meta.postTokenBalances[].mint`. For plain transfers, the mint is usually absent from the account keys, so do not look for it there.

Diff `preTokenBalances` against `postTokenBalances` on the same `accountIndex` to see how much of the token moved and between which owners.

The [Transaction Monitoring guide](/docs/laserstream/guides/transaction-monitoring#transaction-data-structure) covers the transaction structure in detail.

## Limits and notes

* Mints go in the same `accountInclude`, `accountExclude`, and `accountRequired` lists as account keys, so they count toward the same per-list plan limits. There is no separate mint limit.
* Matching cost does not scale with the number of mints you list. 100 mints and 100,000 mints perform the same, and subscribers that do not set the flag pay nothing.
* [Historical replay](/docs/laserstream/historical-replay) honors `matchMints`, so a replay subscription returns the same transactions the live stream would have returned.
* If you attach a [compressed (cuckoo) filter](/docs/laserstream/cuckoo-filters) to a transaction subscription, `matchMints` probes the mint set against it as well as the account keys.
* `matchMints` is live on all LaserStream gRPC regions, mainnet and devnet. It is not available on LaserStream WebSocket at this time.
* Minimum SDK versions: JavaScript/TypeScript `helius-laserstream` 0.8.5, Rust `helius-laserstream` 0.6.4, Go `go/v0.3.0`.

## Related

<CardGroup cols={2}>
  <Card title="Token Account (ATA) Filtering" icon="coins" href="/docs/laserstream/token-account-filtering">
    Match transactions that touch the token accounts a wallet owns
  </Card>

  <Card title="Transaction Monitoring" icon="receipt" href="/docs/laserstream/guides/transaction-monitoring">
    Full filtering strategies and runnable examples over gRPC
  </Card>

  <Card title="Subscribe Request Reference" icon="filter" href="/docs/laserstream/grpc">
    Every transaction filter field, including `matchMints`
  </Card>

  <Card title="Historical Replay" icon="clock-rotate-left" href="/docs/laserstream/historical-replay">
    Backfill up to 24 hours of token activity with the same filter
  </Card>
</CardGroup>
