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

# Filtrado de cuentas a gran escala con filtros comprimidos

> Suscríbete a cientos de miles de cuentas de Solana en un solo stream gRPC de LaserStream con filtros de cuco comprimidos: solicitudes de suscripción aproximadamente 8 veces más pequeñas.

## Descripción general

LaserStream admite el **filtrado comprimido de cuentas mediante filtros de cuco**. En lugar de enviar una lista explícita de claves públicas en tu solicitud de suscripción (32 bytes por cuenta), envías un filtro probabilístico compacto que ocupa aproximadamente **3–4 bytes por cuenta** durante la transmisión.

Esto permite suscribirte a **cientos de miles de cuentas en un solo stream**, sin fragmentar entre conexiones ni enviar solicitudes de suscripción demasiado grandes.

Por ejemplo, un filtro que sigue 500,000 cuentas se serializa en aproximadamente 2.1 MB, frente a los 16 MB de una lista sin procesar de claves públicas; es decir, es aproximadamente **7.6 veces más pequeño**. El ahorro exacto depende de qué tan lleno esté el filtro: cuanto más cerca esté de su capacidad, menos bytes se usarán por cuenta.

### Disponibilidad

| Cliente                                                           | Versión mínima | Compatibilidad con filtros de cuco |
| ----------------------------------------------------------------- | -------------- | ---------------------------------- |
| SDK de LaserStream — Rust (`helius-laserstream`)                  | 0.2.0          | ✅                                  |
| SDK de LaserStream — JavaScript/TypeScript (`helius-laserstream`) | 0.4.0          | ✅                                  |
| SDK de LaserStream — Go                                           | —              | ❌ Aún no                           |
| Yellowstone gRPC — Rust (`yellowstone-grpc-client`)               | 13.1.0         | ✅                                  |

## Cuándo usar filtros de cuco

| Cuentas seguidas | Enfoque recomendado                                                        |
| ---------------- | -------------------------------------------------------------------------- |
| Hasta \~10,000   | Listas explícitas de claves públicas (`account: [...]`): simples y exactas |
| \~10,000 o más   | Filtro de cuco mediante `CompressedAccountFilterSet`                       |

Casos de uso habituales: supervisar a todos los titulares de un token, seguir todas las posiciones de un protocolo de préstamos o vigilar grandes conjuntos de billeteras para un sistema de trading o análisis.

## Cómo funciona

1. **Crea el filtro en el cliente.** Inserta cada clave pública seguida en un `CompressedAccountFilterSet`. La semilla de hash se genera aleatoriamente para cada filtro y se serializa junto con él, por lo que el servidor calcula el hash de las cuentas entrantes con la misma semilla que usó tu cliente.
2. **Adjúntalo a tu solicitud de suscripción.** `insert_into_subscribe_request()` coloca el filtro serializado en el stream de cuentas de un `SubscribeRequest` estándar.
3. **El servidor busca coincidencias de forma probabilística.** Como el filtro es probabilístico, el servidor puede entregar actualizaciones de cuentas que no sigues; los falsos positivos se mantienen **por debajo del 1 % con carga completa**. **Nunca hay falsos negativos**: se entrega cada actualización de una cuenta seguida.
4. **Vuelve a comprobar localmente cada actualización; este paso es obligatorio.** Llama a `set.contains(pubkey)` para cada cuenta entrante antes de procesarla. Esta comprobación es exacta (está respaldada por un conjunto hash interno), por lo que después del filtrado local no verás ningún falso positivo.

## Inicio rápido (Rust)

Agrega el SDK a tu proyecto:

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

Crea un filtro, adjúntalo a una suscripción y elimina localmente los falsos positivos:

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

El SDK incluye una versión completa y ejecutable: [`rust/examples/cuckoo_account_filter.rs`](https://github.com/helius-labs/laserstream-sdk/blob/main/rust/examples/cuckoo_account_filter.rs).

## Inicio rápido (JavaScript/TypeScript)

Instala el SDK (la compatibilidad con filtros de cuco requiere `helius-laserstream` 0.4.0+):

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

Crea el filtro, adjúntalo y vuelve a comprobar localmente cada actualización:

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

El SDK incluye una versión completa y ejecutable: [`javascript/examples/cuckoo-account-sub.ts`](https://github.com/helius-labs/laserstream-sdk/blob/main/javascript/examples/cuckoo-account-sub.ts).

## Referencia de la API

`CompressedAccountFilterSet` encapsula el filtro de cuco sin procesar junto con un conjunto hash exacto, por lo que las mutaciones y las comprobaciones de pertenencia siempre son seguras y exactas:

| Método                                                 | Comportamiento                                                                                                                              |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `with_capacity(n)`                                     | Crea un filtro con capacidad para `n` cuentas seguidas. Dimensiona el filtro según el tamaño **máximo** de tu conjunto de cuentas seguidas. |
| `insert(pubkey)`                                       | Devuelve `Ok(true)` si es nueva, `Ok(false)` si está duplicada o `Err(TableFullError)` si el filtro alcanzó su capacidad.                   |
| `remove(pubkey)`                                       | Elimina la cuenta. Es seguro y exacto.                                                                                                      |
| `contains(pubkey)`                                     | Comprobación exacta de pertenencia; úsala para eliminar los falsos positivos del servidor.                                                  |
| `insert_into_subscribe_request(&mut request, "label")` | Adjunta el filtro al stream de cuentas de un `SubscribeRequest`.                                                                            |
| `to_account_filter()` / `to_proto()`                   | Conversiones de nivel inferior para crear solicitudes personalizadas.                                                                       |
| `is_dirty()` / `take_dirty()`                          | Indican si el conjunto cambió desde la última vez que se incluyó en una solicitud; son útiles para los ciclos de resuscripción.             |

Los nombres de métodos anteriores siguen las convenciones de Rust. El SDK de JavaScript/TypeScript ofrece la misma interfaz en camelCase: `new CompressedAccountFilterSet(capacity)` en lugar de `with_capacity`, `insertIntoSubscribeRequest`, `isDirty`, `takeDirty`, `toProto`, etc. En JavaScript, `insert` devuelve un valor booleano (`true` si acaba de agregarse) y genera `TableFullError` cuando el filtro está saturado. Puedes pasar una clave pública como cadena base58, 32 bytes sin procesar o cualquier objeto con un método `toBytes()`.

Usa siempre `CompressedAccountFilterSet` en lugar del `CuckooFilter` sin procesar que encapsula. El método `remove()` del filtro sin procesar puede eliminar silenciosamente el elemento equivocado, un riesgo conocido y documentado de los filtros de cuco. El contenedor combina el filtro con un conjunto hash exacto, por lo que las operaciones de inserción, eliminación y comprobación de contenido siempre son correctas.

## Dimensionamiento de la capacidad

* Dimensiona el filtro según la cantidad **máxima** de cuentas que esperas seguir mediante `with_capacity(n)`.
* Si intentas insertar más elementos de los que admite la capacidad, la operación falla de forma controlada con un `TableFullError`; el filtro nunca se corrompe. En la práctica, la tabla tolera una ligera sobrecarga antes de rechazar inserciones, pero no dependas de ese margen.
* El tamaño serializado depende de la capacidad, no de cuántas cuentas hayas insertado. Por lo tanto, un filtro sobredimensionado desperdicia bytes durante la transmisión. Elige una capacidad cercana a tu máximo real.

## Actualización del conjunto seguido

Cuando cambie tu conjunto de cuentas seguidas (cuentas nuevas que quieras seguir o antiguas que quieras eliminar):

1. Llama a `insert()` / `remove()` en el `CompressedAccountFilterSet`.
2. Comprueba `is_dirty()` (o consume el indicador con `take_dirty()`) para saber si el filtro cambió desde la última vez que se envió.
3. Si tiene cambios pendientes, vuelve a crear la solicitud con `insert_into_subscribe_request()`. En JavaScript, puedes volver a enviarla por el mismo stream con `stream.write(request)`; en Rust, vuelve a suscribirte con la solicitud recreada.

## Preguntas frecuentes

<Accordion title="Can I miss updates for accounts in my filter?">
  No. Los filtros de cuco generan falsos positivos (actualizaciones adicionales de cuentas no seguidas), pero **nunca falsos negativos**. Se entrega cada actualización de una cuenta seguida.
</Accordion>

<Accordion title="How many extra (false-positive) updates will I receive?">
  Menos del 1 % con carga completa y, normalmente, menos cuando el filtro está por debajo de su capacidad. Una llamada local a `contains()` por actualización las filtra de manera exacta.
</Accordion>

<Accordion title="Which clients support cuckoo filters?">
  El SDK de Rust (`helius-laserstream` 0.2.0+), el SDK de JavaScript/TypeScript (`helius-laserstream` 0.4.0+) y el cliente de Yellowstone para Rust (`yellowstone-grpc-client` 13.1.0+) admiten filtros de cuco. El SDK de Go aún no los admite. Consulta la [tabla de disponibilidad](#disponibilidad) anterior.
</Accordion>

<Accordion title="Can I still use explicit pubkey lists?">
  Sí. Los filtros `account: [...]` estándar funcionan sin cambios y siguen siendo la opción adecuada para conjuntos pequeños de cuentas (hasta aproximadamente 10,000 cuentas). Consulta la [guía de suscripción a cuentas](/docs/es/laserstream/guides/account-subscription).
</Accordion>

<Accordion title="Do compressed filters work with matchMints?">
  Sí. Cuando se adjunta un filtro comprimido a una suscripción de transacciones y se establece `matchMints: true`, el servidor también compara con el filtro las direcciones de acuñación de los saldos de tokens anteriores y posteriores a la transacción, además de las claves de sus cuentas. Consulta [Filtrado por dirección de acuñación de token](/docs/es/laserstream/mint-filtering).
</Accordion>

## Contenido relacionado

<CardGroup cols={2}>
  <Card title="Account Subscriptions" icon="user" href="/docs/es/laserstream/guides/account-subscription">
    Filtrado estándar de cuentas con filtros de propietario, tamaño de datos y memcmp.
  </Card>

  <Card title="Clients & SDKs" icon="code" href="/docs/es/laserstream/clients">
    SDK de TypeScript, Rust y Go con reproducción y reconexión automáticas.
  </Card>

  <Card title="Token Mint Filtering" icon="coins" href="/docs/es/laserstream/mint-filtering">
    Busca transacciones por dirección de acuñación de token con `matchMints`, incluso dentro de filtros comprimidos.
  </Card>
</CardGroup>
