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

# Filtrage à grande échelle des comptes avec filtres compressés

> Abonnez-vous à des centaines de milliers de comptes Solana dans un flux gRPC LaserStream avec des filtres de coucou compressés — demandes d'abonnement environ 8x plus petites.

## Aperçu

LaserStream prend en charge **le filtrage de comptes compressés via des filtres de coucou**. Au lieu d'envoyer une liste explicite de clés publiques dans votre demande d'abonnement (32 octets par compte), vous envoyez un filtre probabiliste compact qui coûte environ **3 à 4 octets par compte** sur le réseau.

Cela rend possible l'abonnement à **des centaines de milliers de comptes dans un seul flux** — sans partitionnement sur plusieurs connexions, sans demandes d'abonnement surdimensionnées.

Par exemple, un filtre suivant 500 000 comptes se sérialise à environ 2,1 Mo, contre 16 Mo en tant que liste brute de clés publiques — environ **7,6x plus petit**. Les économies exactes dépendent de la saturation du filtre : plus il est proche de sa capacité, moins il y a d'octets par compte.

### Disponibilité

| Client                                                         | Version minimale | Support Cuckoo |
| -------------------------------------------------------------- | ---------------- | -------------- |
| LaserStream SDK — Rust (`helius-laserstream`)                  | 0.2.0            | ✅              |
| LaserStream SDK — JavaScript/TypeScript (`helius-laserstream`) | 0.4.0            | ✅              |
| LaserStream SDK — Go                                           | —                | ❌ Pas encore   |
| Yellowstone gRPC — Rust (`yellowstone-grpc-client`)            | 13.1.0           | ✅              |

## Quand utiliser des filtres de coucou

| Comptes suivis   | Approche recommandée                                                      |
| ---------------- | ------------------------------------------------------------------------- |
| Jusqu'à \~10,000 | Listes de clés publiques explicites (`account: [...]`) — simple et exacte |
| \~10,000 et plus | Filtre de coucou via `CompressedAccountFilterSet`                         |

Cas d'utilisation typiques : surveiller chaque détenteur d'un token, suivre toutes les positions dans un protocole de prêt, ou observer de grands ensembles de portefeuilles pour un système de trading ou d'analyse.

## Comment ça fonctionne

1. **Construisez le filtre côté client.** Insérez chaque clé publique suivie dans un `CompressedAccountFilterSet`. La graine de hachage est randomisée pour chaque filtre et sérialisée à côté, de sorte que le serveur hache les comptes entrants avec la même graine utilisée par votre client.
2. **Attachez-le à votre demande d'abonnement.** `insert_into_subscribe_request()` place le filtre sérialisé dans le flux de comptes d'un standard `SubscribeRequest`.
3. **Le serveur fait correspondre de manière probabiliste.** Étant donné que le filtre est probabiliste, le serveur peut fournir des mises à jour pour des comptes que vous n'avez pas suivis — les faux positifs sont limités à **moins de 1 % à pleine charge**. Il n'y a **jamais de faux négatifs** : chaque mise à jour pour un compte suivi est livrée.
4. **Re-vérifiez chaque mise à jour localement — cette étape est requise.** Appelez `set.contains(pubkey)` sur chaque compte entrant avant de le traiter. Cette vérification est exacte (soutenue par un ensemble de hachage interne), donc après le filtrage local, vous ne voyez aucun faux positif.

## Démarrage rapide (Rust)

Ajoutez le SDK à votre projet :

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

Construisez un filtre, attachez-le à un abonnement, et éliminez localement les faux positifs :

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

Une version complète exécutable est livrée avec le SDK : [`rust/examples/cuckoo_account_filter.rs`](https://github.com/helius-labs/laserstream-sdk/blob/main/rust/examples/cuckoo_account_filter.rs).

## Démarrage rapide (JavaScript/TypeScript)

Installez le SDK (le support de coucou nécessite `helius-laserstream` 0.4.0+):

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

Construisez le filtre, attachez-le, et re-vérifiez chaque mise à jour localement :

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

Une version complète exécutable est livrée avec le SDK : [`javascript/examples/cuckoo-account-sub.ts`](https://github.com/helius-labs/laserstream-sdk/blob/main/javascript/examples/cuckoo-account-sub.ts).

## Référence de l'API

`CompressedAccountFilterSet` enveloppe le filtre de coucou brut avec un ensemble de hachage exact, de sorte que les mutations et vérifications de l'appartenance sont toujours sûres et exactes :

| Méthode                                                | Comportement                                                                                                          |
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| `with_capacity(n)`                                     | Créez un filtre dimensionné pour `n` comptes suivis. Dimensionnez pour votre taille **maximale** de l'ensemble suivi. |
| `insert(pubkey)`                                       | Renvoie `Ok(true)` si nouveau, `Ok(false)` si un doublon, `Err(TableFullError)` si le filtre est à pleine capacité.   |
| `remove(pubkey)`                                       | Supprime le compte. Sûr et exact.                                                                                     |
| `contains(pubkey)`                                     | Vérification exacte de l'appartenance — utilisez ceci pour éliminer les faux positifs côté serveur.                   |
| `insert_into_subscribe_request(&mut request, "label")` | Attachez le filtre au flux de comptes d'un `SubscribeRequest`.                                                        |
| `to_account_filter()` / `to_proto()`                   | Conversions de niveau inférieur pour l'assemblage de requêtes personnalisé.                                           |
| `is_dirty()` / `take_dirty()`                          | Indique si l'ensemble a changé depuis qu'il a été placé dans une requête — utile pour les cycles de réabonnement.     |

Les noms des méthodes ci-dessus suivent les conventions Rust. Le SDK JavaScript/TypeScript expose la même interface en camelCase — `new CompressedAccountFilterSet(capacity)` au lieu de `with_capacity`, `insertIntoSubscribeRequest`, `isDirty`, `takeDirty`, `toProto`, et ainsi de suite. En JavaScript, `insert` renvoie un booléen (`true` si nouvellement ajouté) et lance `TableFullError` lorsque le filtre est saturé. Une clé publique peut être passée en tant que chaîne base58, raw 32 octets, ou tout objet avec une méthode `toBytes()`.

Utilisez toujours `CompressedAccountFilterSet` plutôt que le raw `CuckooFilter` qu'il enveloppe. La méthode `remove()` du filtre brut peut silencieusement supprimer le mauvais élément — une erreur documentée des filtres de coucou. Le wrapper associe le filtre à un ensemble de hachage exact, donc l'insertion, la suppression et la vérification d'appartenance sont toujours correctes.

## Dimensionnement de la capacité

* Dimensionnez le filtre pour le **nombre maximal** de comptes que vous prévoyez de suivre via `with_capacity(n)`.
* Insérer au-delà de la capacité échoue gracieusement avec un `TableFullError` — le filtre n'est jamais corrompu. En pratique, la table tolère un léger sur-remplissage avant de rejeter les insertions, mais ne comptez pas sur cette marge.
* La taille sérialisée est déterminée par la capacité, et non par le nombre de comptes que vous avez insérés — donc un filtre surdimensionné gaspille des octets réseau. Choisissez une capacité proche de votre véritable pic.

## Mise à jour de l'ensemble suivi

Lorsque votre ensemble suivi change (nouveaux comptes à suivre, anciens à supprimer) :

1. Appelez `insert()` / `remove()` sur le `CompressedAccountFilterSet`.
2. Vérifiez `is_dirty()` (ou consommez le drapeau avec `take_dirty()`) pour voir si le filtre a changé depuis qu'il a été envoyé pour la dernière fois.
3. Si sale, reconstruisez la demande avec `insert_into_subscribe_request()`. En JavaScript, vous pouvez le renvoyer sur le même flux avec `stream.write(request)`; en Rust, réabonnez-vous avec la demande reconstruite.

## FAQ

<Accordion title="Puis-je manquer des mises à jour pour les comptes dans mon filtre ?">
  Non. Les filtres de coucou produisent des faux positifs (des mises à jour supplémentaires pour des comptes non suivis) mais **jamais de faux négatifs**. Chaque mise à jour pour un compte suivi est livrée.
</Accordion>

<Accordion title="Combien de mises à jour (faux positifs) supplémentaires vais-je recevoir ?">
  Moins de 1 % à pleine charge, et généralement moins lorsque le filtre est en dessous de sa capacité. Un appel local `contains()` par mise à jour les filtre exactement.
</Accordion>

<Accordion title="Quels clients prennent en charge les filtres de coucou ?">
  Le SDK Rust (`helius-laserstream` 0.2.0+), le SDK JavaScript/TypeScript (`helius-laserstream` 0.4.0+), et le client Rust Yellowstone (`yellowstone-grpc-client` 13.1.0+). Le SDK Go ne le prend pas encore en charge. Voir le [tableau de disponibilité](#disponibilité) ci-dessus.
</Accordion>

<Accordion title="Puis-je encore utiliser des listes de clés publiques explicites ?">
  Oui. Les filtres standard `account: [...]` fonctionnent sans changement et restent le bon choix pour les petits ensembles de comptes (jusqu'à environ 10,000 comptes). Voir le [guide d'abonnement aux comptes](/docs/fr/laserstream/guides/account-subscription).
</Accordion>

## Connexe

<CardGroup cols={2}>
  <Card title="Abonnements aux comptes" icon="user" href="/docs/fr/laserstream/guides/account-subscription">
    Filtrage standard des comptes avec propriétaire, taille de données et filtres memcmp.
  </Card>

  <Card title="Clients et SDKs" icon="code" href="/docs/fr/laserstream/clients">
    SDKs TypeScript, Rust, et Go avec relecture automatique et reconnexions.
  </Card>
</CardGroup>
