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

# Large-Scale Account Filtering with Compressed Filters

> Subscribe to hundreds of thousands of Solana accounts in a single LaserStream gRPC stream using compressed cuckoo filters — ~8x smaller subscribe requests with exact local verification.

## Overview

LaserStream supports **compressed account filtering via cuckoo filters**. Instead of sending an explicit pubkey list in your subscribe request (32 bytes per account), you send a compact probabilistic filter that costs roughly **3–4 bytes per account** on the wire.

This makes it practical to subscribe to **hundreds of thousands of accounts in a single stream** — no sharding across connections, no oversized subscribe requests.

For example, a filter tracking 500,000 accounts serializes to about 2.1 MB, versus 16 MB as a raw pubkey list — roughly **7.6x smaller**. The exact savings depend on how full the filter is: the closer it is to capacity, the fewer bytes per account.

### Availability

| Client                                                         | Minimum version | Cuckoo support |
| -------------------------------------------------------------- | --------------- | -------------- |
| LaserStream SDK — Rust (`helius-laserstream`)                  | 0.2.0           | ✅              |
| LaserStream SDK — JavaScript/TypeScript (`helius-laserstream`) | 0.4.0           | ✅              |
| LaserStream SDK — Go                                           | —               | ❌ Not yet      |
| Yellowstone gRPC — Rust (`yellowstone-grpc-client`)            | 13.1.0          | ✅              |

## When to use cuckoo filters

| Tracked accounts   | Recommended approach                                        |
| ------------------ | ----------------------------------------------------------- |
| Up to \~10,000     | Explicit pubkey lists (`account: [...]`) — simple and exact |
| \~10,000 and above | Cuckoo filter via `CompressedAccountFilterSet`              |

Typical use cases: monitoring every holder of a token, tracking all positions in a lending protocol, or watching large wallet sets for a trading or analytics system.

## How it works

1. **Build the filter client-side.** Insert each tracked pubkey into a `CompressedAccountFilterSet`. The hash seed is randomized per filter and serialized alongside it, so the server hashes incoming accounts with the same seed your client used.
2. **Attach it to your subscribe request.** `insert_into_subscribe_request()` places the serialized filter into the accounts stream of a standard `SubscribeRequest`.
3. **The server matches probabilistically.** Because the filter is probabilistic, the server may deliver updates for accounts you didn't track — false positives are bounded at **under 1% at full load**. There are **never false negatives**: every update for a tracked account is delivered.
4. **Re-check each update locally — this step is required.** Call `set.contains(pubkey)` on every incoming account before processing it. This check is exact (backed by an internal hash set), so after local filtering you see zero false positives.

## Quickstart (Rust)

Add the SDK to your project:

```toml Cargo.toml theme={"system"}
[dependencies]
helius-laserstream = "0.2"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
```

Build a filter, attach it to a subscription, and scrub false positives locally:

```rust main.rs [expandable] theme={"system"}
use {
    futures::StreamExt,
    helius_laserstream::{
        cuckoo::{CompressedAccountFilterSet, Pubkey},
        grpc::{subscribe_update::UpdateOneof, SubscribeRequest},
        subscribe, LaserstreamConfig,
    },
    std::str::FromStr,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // The exact set of accounts you care about. In production this is
    // typically loaded from your database — hundreds of thousands of keys.
    let tracked: Vec<Pubkey> = [
        "So11111111111111111111111111111111111111112", // Wrapped SOL
        "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
        "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", // USDT
    ]
    .iter()
    .map(|s| Pubkey::from_str(s).unwrap())
    .collect();

    // Build the cuckoo filter. Size it for your peak tracked-set size.
    let mut set = CompressedAccountFilterSet::with_capacity(500_000)?;
    for pk in &tracked {
        set.insert(*pk)?;
    }
    println!(
        "Tracking {} accounts via cuckoo filter ({} bytes on the wire)",
        set.len(),
        set.to_proto().data.len()
    );

    // Attach the compressed filter to the accounts stream.
    let mut request = SubscribeRequest::default();
    set.insert_into_subscribe_request(&mut request, "tracked_accounts");

    let config = LaserstreamConfig::new(
        "https://laserstream-mainnet-ewr.helius-rpc.com".to_string(), // Choose your closest region
        "YOUR_API_KEY".to_string(), // Replace with your key from https://dashboard.helius.dev/
    );

    let (stream, _handle) = subscribe(config, request);
    tokio::pin!(stream);
    while let Some(message) = stream.next().await {
        match message {
            Ok(update) => {
                if let Some(UpdateOneof::Account(account_update)) = update.update_oneof {
                    if let Some(info) = account_update.account {
                        let pk = Pubkey::try_from(info.pubkey.as_slice()).ok();
                        // Re-check locally: drop server-side false positives.
                        match pk {
                            Some(pk) if set.contains(pk) => {
                                println!(
                                    "tracked account update: {pk} (slot {})",
                                    account_update.slot
                                );
                            }
                            Some(pk) => {
                                println!("(false positive, ignored): {pk}");
                            }
                            None => {}
                        }
                    }
                }
            }
            Err(e) => eprintln!("stream error: {e}"),
        }
    }

    Ok(())
}
```

A complete runnable version ships with the SDK: [`rust/examples/cuckoo_account_filter.rs`](https://github.com/helius-labs/laserstream-sdk/blob/main/rust/examples/cuckoo_account_filter.rs).

## Quickstart (JavaScript/TypeScript)

Install the SDK (cuckoo support requires `helius-laserstream` 0.4.0+):

```bash theme={"system"}
npm install helius-laserstream
```

Build the filter, attach it, and re-check each update locally:

```typescript [expandable] theme={"system"}
import {
  subscribe,
  CommitmentLevel,
  CompressedAccountFilterSet,
  SubscribeUpdate,
  LaserstreamConfig,
} from 'helius-laserstream';

async function main() {
  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY', // Replace with your key from https://dashboard.helius.dev/
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // Choose your closest region
  };

  // The accounts you want to track. In production this is typically loaded
  // from your database — hundreds of thousands of keys.
  const addresses = [
    'So11111111111111111111111111111111111111112', // Wrapped SOL
    'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
    'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB', // USDT
  ];

  // Build a compact cuckoo filter instead of sending the full pubkey list.
  // Size capacity for your peak tracked-set size.
  const tracked = new CompressedAccountFilterSet(500_000);
  for (const address of addresses) {
    tracked.insert(address);
  }

  // Attach the filter to the request (no explicit account list needed).
  const request: any = { accounts: {}, commitment: CommitmentLevel.CONFIRMED };
  tracked.insertIntoSubscribeRequest(request, 'tracked-accounts');

  const stream = await subscribe(
    config,
    request,
    async (update: SubscribeUpdate) => {
      const pubkey = update.account?.account?.pubkey;
      if (!pubkey) return;
      // Re-check locally: drop server-side false positives. This is exact.
      if (tracked.contains(pubkey)) {
        console.log('tracked account update:', update.account);
      }
    },
    (error: Error) => {
      console.error('Stream error:', error);
    }
  );

  process.on('SIGINT', () => {
    stream.cancel();
    process.exit(0);
  });
}

main().catch(console.error);
```

A complete runnable version ships with the SDK: [`javascript/examples/cuckoo-account-sub.ts`](https://github.com/helius-labs/laserstream-sdk/blob/main/javascript/examples/cuckoo-account-sub.ts).

## API reference

`CompressedAccountFilterSet` wraps the raw cuckoo filter together with an exact hash set, so mutations and membership checks are always safe and exact:

| Method                                                 | Behavior                                                                                                   |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| `with_capacity(n)`                                     | Create a filter sized for `n` tracked accounts. Size for your **peak** tracked-set size.                   |
| `insert(pubkey)`                                       | Returns `Ok(true)` if new, `Ok(false)` if a duplicate, `Err(TableFullError)` if the filter is at capacity. |
| `remove(pubkey)`                                       | Removes the account. Safe and exact.                                                                       |
| `contains(pubkey)`                                     | Exact membership check — use this to scrub server-side false positives.                                    |
| `insert_into_subscribe_request(&mut request, "label")` | Attach the filter to the accounts stream of a `SubscribeRequest`.                                          |
| `to_account_filter()` / `to_proto()`                   | Lower-level conversions for custom request assembly.                                                       |
| `is_dirty()` / `take_dirty()`                          | Report whether the set changed since it was last placed into a request — useful for resubscribe cycles.    |

Method names above use Rust conventions. The JavaScript/TypeScript SDK exposes the same surface in camelCase — `new CompressedAccountFilterSet(capacity)` instead of `with_capacity`, `insertIntoSubscribeRequest`, `isDirty`, `takeDirty`, `toProto`, and so on. In JavaScript `insert` returns a boolean (`true` if newly added) and throws `TableFullError` when the filter is saturated. A pubkey can be passed as a base58 string, raw 32 bytes, or any object with a `toBytes()` method.

Always use `CompressedAccountFilterSet` rather than the raw `CuckooFilter` it wraps. The raw filter's `remove()` can silently remove the wrong item — a documented footgun of cuckoo filters. The wrapper pairs the filter with an exact hash set, so insert, remove, and contains are always correct.

## Capacity sizing

* Size the filter for the **peak** number of accounts you expect to track via `with_capacity(n)`.
* Inserting beyond capacity fails gracefully with a `TableFullError` — the filter is never corrupted. In practice the table tolerates slight overfill before rejecting inserts, but don't rely on that headroom.
* The serialized size is determined by capacity, not by how many accounts you've inserted — so an oversized filter wastes wire bytes. Pick a capacity close to your real peak.

## Updating the tracked set

When your tracked set changes (new accounts to follow, old ones to drop):

1. Call `insert()` / `remove()` on the `CompressedAccountFilterSet`.
2. Check `is_dirty()` (or consume the flag with `take_dirty()`) to see whether the filter changed since it was last sent.
3. If dirty, rebuild the request with `insert_into_subscribe_request()`. In JavaScript you can re-send it on the same stream with `stream.write(request)`; in Rust, resubscribe with the rebuilt request.

## FAQ

<Accordion title="Can I miss updates for accounts in my filter?">
  No. Cuckoo filters produce false positives (extra updates for untracked accounts) but **never false negatives**. Every update for a tracked account is delivered.
</Accordion>

<Accordion title="How many extra (false-positive) updates will I receive?">
  Under 1% at full load, and typically less when the filter is below capacity. One local `contains()` call per update filters them out exactly.
</Accordion>

<Accordion title="Which clients support cuckoo filters?">
  The Rust SDK (`helius-laserstream` 0.2.0+), the JavaScript/TypeScript SDK (`helius-laserstream` 0.4.0+), and the Yellowstone Rust client (`yellowstone-grpc-client` 13.1.0+). The Go SDK does not support it yet. See the [availability table](#availability) above.
</Accordion>

<Accordion title="Can I still use explicit pubkey lists?">
  Yes. Standard `account: [...]` filters work unchanged and remain the right choice for small account sets (up to roughly 10,000 accounts). See the [account subscription guide](/laserstream/guides/account-subscription).
</Accordion>

## Related

<CardGroup cols={2}>
  <Card title="Account Subscriptions" icon="user" href="/laserstream/guides/account-subscription">
    Standard account filtering with owner, datasize, and memcmp filters.
  </Card>

  <Card title="Clients & SDKs" icon="code" href="/laserstream/clients">
    TypeScript, Rust, and Go SDKs with automatic replay and reconnects.
  </Card>
</CardGroup>
