> For the complete documentation index, see [llms.txt](https://docs.triton.one/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.triton.one/solana/real-time-streaming/preconfirmations-grpc/filters.md).

# Filters

Preconfirmations gRPC filters: account\_include, signer\_include, instructions, signature and execution\_results, and how address lookup tables affect matching.

A subscribe request carries one or more named filters. A transaction is delivered when it matches at least one, and every delivered transaction names the filters it matched.

A transaction matches a filter when it satisfies every condition the filter sets:

| condition                                  | matches when the transaction                                         |
| ------------------------------------------ | -------------------------------------------------------------------- |
| `account_include`                          | references any of these accounts                                     |
| `account_required`                         | references all of these accounts                                     |
| `account_exclude`                          | references none of these accounts                                    |
| `signer_include`                           | is signed by any of these accounts                                   |
| `signer_exclude`                           | is signed by none of these accounts                                  |
| `instructions`                             | has a top-level instruction passing any of these instruction filters |
| `signature` (Rust client: `.signatures()`) | has one of these first signatures                                    |
| `execution_results`                        | has one of these outcomes, as its feed reported it                   |

Every filter must select something with `account_include`, `account_required`, `signer_include`, `instructions` or `signature`; the full feed cannot be subscribed. `account_exclude`, `signer_exclude` and `execution_results` only narrow a selection, so a filter made of them alone is refused.

"References" means the account is a static account key of the transaction message. Accounts loaded through address lookup tables do not count, for includes and for excludes alike, see [Address lookup tables](#address-lookup-tables).

`execution_results` works on both feeds. A transaction whose outcome was not reported, such as one from a BAM node that does not send it, never matches it, and BAM never reports fees only.

The number of filters and instruction filters per stream, accounts per list and signatures per filter is limited; see [Limits](/solana/real-time-streaming/preconfirmations-grpc/coverage-billing-and-limits.md#limits).

## Excluding accounts

`account_exclude` drops a transaction that references any of the listed accounts, after the rest of the filter matched. It is the "give me A, skip anything that also touches B" filter from Yellowstone gRPC and LaserStream: follow a pool without the bot that trades it all day, or a program without a spam account.

```rust
Filter::new().accounts([pool]).exclude_accounts([spammer])
```

An account a v0 transaction loads through a lookup table is not seen by the exclusion either, so a spammer reaching the account that way still gets through.

## Signers

The signers of a transaction are the first `num_required_signatures` static account keys of its message: the fee payer first, then every other account that signed, cosigners included. `signer_include` matches a transaction signed by any of the listed accounts and `signer_exclude` drops one signed by any of them. An account that is only referenced, for example the recipient of a transfer, is not a signer and does not match.

```rust
Filter::new().signers([wallet]);                          // everything the wallet signed
Filter::new().signers([wallet]).accounts([program]);      // its transactions on one program
Filter::new().instructions([swap]).exclude_signers([me]); // everyone's swaps except your own
```

## Instructions

An instruction filter names a program and, optionally, conditions on the instruction's data, the same shape as the `getProgramAccounts` filters applied to instruction data:

* `program_id`: the program the instruction invokes, required
* `memcmp`: bytes the data must hold at an offset, up to four, all must hold
* `data_size`: the exact length of the data

A transaction matches when one of its top-level instructions passes every condition of any of the listed instruction filters. Only top-level instructions exist before execution: an instruction a program invokes through CPI is not part of the transaction bytes and is not seen. Data shorter than a memcmp's end does not match.

Anchor programs start the data with an 8 byte discriminator, the first 8 bytes of `sha256("global:<instruction name>")`. Every Meteora Dynamic Bonding Curve `create_config`, from any creator and without the swaps on the same program:

```rust
let dbc: Pubkey = "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN".parse()?;
Filter::new().instructions([
    InstructionFilter::new(dbc).memcmp(0, [201, 207, 243, 114, 75, 111, 47, 189]),
])
```

SPL Token instructions start with a one byte tag, and `data_size` pins the layout. `TransferChecked` is tag 12, an 8 byte amount and a 1 byte decimals:

```rust
Filter::new().instructions([InstructionFilter::new(token_program).memcmp(0, [12]).data_size(10)])
```

`InstructionFilter::new(program)` with no conditions matches transactions that invoke the program. `account_include` with the program id also matches transactions that only pass it as an account.

## In the client

```rust
use triton_preconfs_client::proto::preconfs::ExecutionResult;
use triton_preconfs_client::{Filter, Filters, InstructionFilter};

let create_config = [201, 207, 243, 114, 75, 111, 47, 189];
let filters = Filters::new()
    .with("token", Filter::new().accounts([token_program]))
    .with("mine", Filter::new().accounts([token_program]).require([my_account]))
    .with("clean", Filter::new().accounts([pool]).exclude_accounts([spammer]))
    .with("wallet", Filter::new().signers([wallet]))
    .with("configs", Filter::new().instructions([InstructionFilter::new(dbc).memcmp(0, create_config)]))
    .with("landed", Filter::new().accounts([my_account]).execution_results([ExecutionResult::Success]));
```

`Filters::single(filter)` names a lone filter `default`. The [client README](https://github.com/rpcpool/preconfs-client#recipes) has more recipes.

## Address lookup tables

One transaction message format hides accounts:

| format                                                                                                                                          | accounts                                                                                                                                              | lookup tables |
| ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| legacy                                                                                                                                          | every account written in the message                                                                                                                  | no            |
| v0                                                                                                                                              | static accounts in the message, plus indexes into on-chain [address lookup tables](https://solana.com/developers/cookbook/transactions/lookup-tables) | yes           |
| v1 ([SIMD-0385](https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0385-transaction-v1.md), the 4 KB format) | every account written in the message, up to 64                                                                                                        | no            |

For legacy and v1 transactions the bytes name every account, so a filter sees all of them. The gap is v0: its bytes carry only the table address and an index for each looked-up account, and turning those into addresses means reading the table's current state on chain.

Preconfirmations are the raw transaction bytes, emitted before the block exists. Neither Harmonic nor BAM sends resolved lookup-table addresses, and Triton does not resolve them. So for v0 transactions, filters match the static keys only. The client parses all three formats; `parse::parse_static_parts` returns exactly the keys a filter sees.

What this means in practice:

* The program id of a top-level instruction is always a static key, in every format. A filter on the SPL token program, a DEX program or pump.fun matches every transaction that invokes it at top level.
* Pools, positions, vaults and user token accounts that a v0 transaction loads through a table are not matched: a filter on such an account misses that transaction. Legacy and v1 transactions touching the same account match normally.
* The same v0 transaction seen through Deshred or Dragon's Mouth (Geyser) has its tables resolved. That is why a side by side comparison of "transactions touching account X" shows more on those streams than on the preconf stream.

If your accounts are mostly behind tables, filter on the program and on the static accounts that always accompany your transactions, and select the rest on your side. v1 transactions have no tables, so filters match all of their accounts.

***

<i class="fa-life-ring">:life-ring:</i> Contact support by clicking the chat icon in your [customer dashboard](https://customers.triton.one)\ <i class="fa-briefcase">:briefcase:</i> Sales questions? [Contact us](https://triton.one/contact)\ <i class="fa-sparkles">:sparkles:</i> AI agent? Read [llms.txt](https://docs.triton.one/llms.txt)\ <i class="fa-rss">:rss:</i> Follow updates: [Blog](https://blog.triton.one) · [X](https://x.com/triton_one) · [YouTube](https://www.youtube.com/@triton_one_ltd) · [Telegram](https://t.me/tritonone) · [GitHub](https://github.com/rpcpool)
