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

# Pemfilteran Akun Skala Besar dengan Filter Terkompresi

> Berlangganan ratusan ribu akun Solana dalam satu stream gRPC LaserStream dengan filter cuckoo terkompresi — permintaan berlangganan ~8x lebih kecil.

## Gambaran umum

LaserStream mendukung **pemfilteran akun terkompresi melalui filter cuckoo**. Alih-alih mengirim daftar pubkey eksplisit dalam permintaan berlangganan Anda (32 byte per akun), Anda mengirim filter probabilistik ringkas yang hanya memerlukan sekitar **3–4 byte per akun** saat ditransmisikan.

Dengan demikian, Anda dapat berlangganan ke **ratusan ribu akun dalam satu stream** — tanpa membagi akun ke beberapa koneksi dan tanpa permintaan berlangganan berukuran terlalu besar.

Sebagai contoh, filter yang melacak 500.000 akun memiliki ukuran serialisasi sekitar 2,1 MB, dibandingkan dengan 16 MB jika menggunakan daftar pubkey mentah — sekitar **7,6x lebih kecil**. Penghematan sebenarnya bergantung pada tingkat keterisian filter: makin mendekati kapasitas, makin sedikit byte yang diperlukan per akun.

### Ketersediaan

| Klien                                                          | Versi minimum | Dukungan cuckoo  |
| -------------------------------------------------------------- | ------------- | ---------------- |
| LaserStream SDK — Rust (`helius-laserstream`)                  | 0.2.0         | ✅                |
| LaserStream SDK — JavaScript/TypeScript (`helius-laserstream`) | 0.4.0         | ✅                |
| LaserStream SDK — Go                                           | —             | ❌ Belum tersedia |
| Yellowstone gRPC — Rust (`yellowstone-grpc-client`)            | 13.1.0        | ✅                |

## Kapan harus menggunakan filter cuckoo

| Akun yang dilacak | Pendekatan yang direkomendasikan                                  |
| ----------------- | ----------------------------------------------------------------- |
| Hingga \~10.000   | Daftar pubkey eksplisit (`account: [...]`) — sederhana dan akurat |
| \~10.000 ke atas  | Filter cuckoo melalui `CompressedAccountFilterSet`                |

Kasus penggunaan umum meliputi pemantauan setiap pemegang token, pelacakan semua posisi dalam protokol pinjaman, atau pemantauan kumpulan dompet besar untuk sistem perdagangan atau analitik.

## Cara kerjanya

1. **Buat filter di sisi klien.** Masukkan setiap pubkey yang dilacak ke dalam `CompressedAccountFilterSet`. Seed hash diacak untuk setiap filter dan diserialisasi bersamanya, sehingga server melakukan hash pada akun masuk dengan seed yang sama seperti yang digunakan klien Anda.
2. **Lampirkan filter ke permintaan berlangganan Anda.** `insert_into_subscribe_request()` menempatkan filter yang telah diserialisasi ke dalam stream akun dari `SubscribeRequest` standar.
3. **Server melakukan pencocokan secara probabilistik.** Karena filter bersifat probabilistik, server mungkin mengirim pembaruan untuk akun yang tidak Anda lacak — positif palsu dibatasi hingga **di bawah 1% saat terisi penuh**. Tidak pernah ada **negatif palsu**: setiap pembaruan untuk akun yang dilacak akan dikirim.
4. **Periksa ulang setiap pembaruan secara lokal — langkah ini wajib dilakukan.** Panggil `set.contains(pubkey)` pada setiap akun masuk sebelum memprosesnya. Pemeriksaan ini bersifat akurat (didukung oleh hash set internal), sehingga setelah pemfilteran lokal tidak ada positif palsu.

## Mulai cepat (Rust)

Tambahkan SDK ke proyek Anda:

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

Buat filter, lampirkan ke langganan, lalu singkirkan positif palsu secara lokal:

```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(())
}
```

Versi lengkap yang dapat dijalankan disertakan bersama SDK: [`rust/examples/cuckoo_account_filter.rs`](https://github.com/helius-labs/laserstream-sdk/blob/main/rust/examples/cuckoo_account_filter.rs).

## Mulai cepat (JavaScript/TypeScript)

Instal SDK (dukungan cuckoo memerlukan `helius-laserstream` 0.4.0+):

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

Buat filter, lampirkan, lalu periksa ulang setiap pembaruan secara lokal:

```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);
```

Versi lengkap yang dapat dijalankan disertakan bersama SDK: [`javascript/examples/cuckoo-account-sub.ts`](https://github.com/helius-labs/laserstream-sdk/blob/main/javascript/examples/cuckoo-account-sub.ts).

## Referensi API

`CompressedAccountFilterSet` membungkus filter cuckoo mentah bersama hash set yang akurat, sehingga mutasi dan pemeriksaan keanggotaan selalu aman dan akurat:

| Metode                                                 | Perilaku                                                                                                                               |
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `with_capacity(n)`                                     | Buat filter dengan ukuran yang sesuai untuk `n` akun yang dilacak. Tentukan ukuran berdasarkan jumlah **puncak** akun yang dilacak.    |
| `insert(pubkey)`                                       | Mengembalikan `Ok(true)` jika baru, `Ok(false)` jika duplikat, atau `Err(TableFullError)` jika filter telah mencapai kapasitas.        |
| `remove(pubkey)`                                       | Menghapus akun. Aman dan akurat.                                                                                                       |
| `contains(pubkey)`                                     | Pemeriksaan keanggotaan yang akurat — gunakan ini untuk menyingkirkan positif palsu dari sisi server.                                  |
| `insert_into_subscribe_request(&mut request, "label")` | Lampirkan filter ke stream akun dari `SubscribeRequest`.                                                                               |
| `to_account_filter()` / `to_proto()`                   | Konversi tingkat rendah untuk menyusun permintaan khusus.                                                                              |
| `is_dirty()` / `take_dirty()`                          | Melaporkan apakah kumpulan telah berubah sejak terakhir kali dimasukkan ke dalam permintaan — berguna untuk siklus berlangganan ulang. |

Nama metode di atas menggunakan konvensi Rust. SDK JavaScript/TypeScript menyediakan antarmuka yang sama dalam camelCase — `new CompressedAccountFilterSet(capacity)`, bukan `with_capacity`, `insertIntoSubscribeRequest`, `isDirty`, `takeDirty`, `toProto`, dan seterusnya. Di JavaScript, `insert` mengembalikan boolean (`true` jika baru ditambahkan) dan melempar `TableFullError` ketika filter telah jenuh. Pubkey dapat diteruskan sebagai string base58, 32 byte mentah, atau objek apa pun yang memiliki metode `toBytes()`.

Selalu gunakan `CompressedAccountFilterSet`, bukan `CuckooFilter` mentah yang dibungkusnya. `remove()` milik filter mentah dapat menghapus item yang salah tanpa pemberitahuan — ini merupakan kekeliruan umum pada filter cuckoo yang telah didokumentasikan. Wrapper memasangkan filter dengan hash set yang akurat, sehingga operasi penyisipan, penghapusan, dan pemeriksaan keberadaan selalu benar.

## Penentuan ukuran kapasitas

* Tentukan ukuran filter berdasarkan jumlah **puncak** akun yang diperkirakan akan Anda lacak melalui `with_capacity(n)`.
* Penyisipan yang melampaui kapasitas akan gagal dengan aman dan menghasilkan `TableFullError` — filter tidak pernah rusak. Dalam praktiknya, tabel dapat menoleransi sedikit kelebihan muatan sebelum menolak penyisipan, tetapi jangan mengandalkan kapasitas tambahan tersebut.
* Ukuran serialisasi ditentukan oleh kapasitas, bukan oleh jumlah akun yang telah Anda masukkan — jadi filter yang terlalu besar memboroskan byte saat transmisi. Pilih kapasitas yang mendekati jumlah puncak sebenarnya.

## Memperbarui kumpulan yang dilacak

Saat kumpulan yang Anda lacak berubah (akun baru yang perlu diikuti atau akun lama yang perlu dihapus):

1. Panggil `insert()` / `remove()` pada `CompressedAccountFilterSet`.
2. Periksa `is_dirty()` (atau gunakan flag dengan `take_dirty()`) untuk mengetahui apakah filter telah berubah sejak terakhir dikirim.
3. Jika berstatus dirty, buat ulang permintaan dengan `insert_into_subscribe_request()`. Di JavaScript, Anda dapat mengirimkannya kembali melalui stream yang sama dengan `stream.write(request)`; di Rust, lakukan langganan ulang dengan permintaan yang telah dibuat ulang.

## FAQ

<Accordion title="Can I miss updates for accounts in my filter?">
  Tidak. Filter cuckoo menghasilkan positif palsu (pembaruan tambahan untuk akun yang tidak dilacak), tetapi **tidak pernah menghasilkan negatif palsu**. Setiap pembaruan untuk akun yang dilacak akan dikirim.
</Accordion>

<Accordion title="How many extra (false-positive) updates will I receive?">
  Di bawah 1% saat terisi penuh, dan biasanya lebih sedikit ketika filter berada di bawah kapasitas. Satu panggilan `contains()` lokal per pembaruan akan memfilternya secara akurat.
</Accordion>

<Accordion title="Which clients support cuckoo filters?">
  SDK Rust (`helius-laserstream` 0.2.0+), SDK JavaScript/TypeScript (`helius-laserstream` 0.4.0+), dan klien Yellowstone Rust (`yellowstone-grpc-client` 13.1.0+) mendukung filter cuckoo. SDK Go belum mendukungnya. Lihat [tabel ketersediaan](#ketersediaan) di atas.
</Accordion>

<Accordion title="Can I still use explicit pubkey lists?">
  Ya. Filter `account: [...]` standar tetap berfungsi tanpa perubahan dan masih menjadi pilihan yang tepat untuk kumpulan akun kecil (hingga sekitar 10.000 akun). Lihat [panduan langganan akun](/docs/id/laserstream/guides/account-subscription).
</Accordion>

<Accordion title="Do compressed filters work with matchMints?">
  Ya. Ketika filter terkompresi dilampirkan ke langganan transaksi dan `matchMints: true` ditetapkan, server juga menguji mint saldo token sebelum/sesudah transaksi terhadap filter, selain kunci akunnya. Lihat [Pemfilteran Mint Token](/docs/id/laserstream/mint-filtering).
</Accordion>

## Terkait

<CardGroup cols={2}>
  <Card title="Account Subscriptions" icon="user" href="/docs/id/laserstream/guides/account-subscription">
    Pemfilteran akun standar dengan filter pemilik, ukuran data, dan memcmp.
  </Card>

  <Card title="Clients & SDKs" icon="code" href="/docs/id/laserstream/clients">
    SDK TypeScript, Rust, dan Go dengan pemutaran ulang serta koneksi ulang otomatis.
  </Card>

  <Card title="Token Mint Filtering" icon="coins" href="/docs/id/laserstream/mint-filtering">
    Cocokkan transaksi berdasarkan mint token dengan `matchMints`, termasuk di dalam filter terkompresi.
  </Card>
</CardGroup>
