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

# Abonnement et mises à jour du compte

> Apprenez à vous abonner aux mises à jour de compte et à suivre efficacement les changements d'état sur la chaîne en utilisant Laserstream.

Lorsque vous développez des applications qui doivent réagir à des changements sur la chaîne, interroger les points de terminaison RPC pour les mises à jour de compte est à la fois inefficace et lent. Les abonnements de compte résolvent ce problème en fournissant des mises à jour en temps réel sur les changements d'état de compte directement à votre application.

Ce guide couvre tout ce que vous devez savoir sur les abonnements de compte : ce qu'ils sont, comment ils fonctionnent et comment les optimiser pour votre cas d'utilisation spécifique.

***

## Le contexte du modèle de compte

<Info>
  Passez cette section si vous êtes familier avec les comptes Solana et leur structure.
</Info>

Solana utilise un modèle basé sur les comptes où chaque donnée vit dans un compte - un conteneur qui contient à la fois des données et des métadonnées. Chaque compte a :

* **Données** : Les octets réels stockant l'état du programme, les soldes des jetons ou d'autres informations
* **Propriétaire** : Le programme qui contrôle ce compte et peut modifier ses données
* **Lamports** : Le solde SOL du compte pour l'exonération de loyer
* **Exécutable** : Si ce compte contient du code de programme

Les programmes sont sans état - ils ne stockent pas de données en interne. Au lieu de cela, ils créent et gèrent des comptes séparés pour stocker leur état. Lorsque vous interagissez avec un programme, vous transmettez les comptes qu'il doit lire ou écrire.

Cette conception rend les abonnements de compte puissants : vous pouvez surveiller les changements de comptes spécifiques, tous les comptes appartenant à un programme ou des comptes correspondant à certains critères.

***

## Abonnement de compte de base

Commençons par un exemple simple qui s'abonne aux changements dans les comptes de jetons. Ce script vous notifiera chaque fois que les soldes des jetons changent :

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

// Utility function to recursively convert Buffer objects to base58 strings
function convertBuffersToBase58(obj: any): any {
  if (obj === null || obj === undefined) {
    return obj;
  }
  
  if (Buffer.isBuffer(obj)) {
    return bs58.encode(obj);
  }
  
  if (Array.isArray(obj)) {
    return obj.map(convertBuffersToBase58);
  }
  
  if (typeof obj === 'object') {
    const result: any = {};
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        result[key] = convertBuffersToBase58(obj[key]);
      }
    }
    return result;
  }
  
  return obj;
}

async function main() {
  console.log('🏦 Basic Account Subscription Example');

  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY', // from https://dashboard.helius.dev/
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // pick the closest region
  };

  const request = {
    accounts: {
      "token-accounts": {
        account: [], // Specific account pubkeys (empty = all)
        owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"], // Token program
        filters: [
          {
            // Only token accounts (165 bytes)
            datasize: 165
          }
        ]
      }
    },
    commitment: CommitmentLevel.CONFIRMED,
    transactions: {}, slots: {}, transactionsStatus: {}, blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: []
  };

  const stream = await subscribe(
    config,
    request,
    async (update: SubscribeUpdate) => {
      const readableUpdate = convertBuffersToBase58(update);
      console.log('🏦 Account Update:', JSON.stringify(readableUpdate, null, 2));
    },
    async (err) => console.error('❌ Stream error:', err)
  );

  console.log(`✅ Account subscription started (id: ${stream.id})`);

  process.on('SIGINT', () => {
    console.log('\n🛑 Cancelling stream...');
    stream.cancel();
    process.exit(0);
  });
}

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

Lorsque vous exécutez cet abonnement de base, vous verrez des mises à jour de compte en temps réel diffusées sur votre console :

```
🏦 Basic Account Subscription Example
✅ Account subscription started (id: xyz789)

🏦 Account Update: {
  "filters": ["token-accounts"],
  "account": {
    "account": {
      "pubkey": "BKMHWYLAX4un3HUbR7a3u9jPmzCiLNa4mSj1RiX11eWF",
      "lamports": "2039280",
      "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
      "rentEpoch": "18446744073709551615",
      "data": "2NUx6Xw9QkmgJCyYUP3d8TPsjJhUpSM7hcy9Fi1juGc6g9DrpPFyGyvBZzu9qiAjFtyEDbNiLHYFJsq1dD5Wxr4LPcF9Dqs4AJa15L1N92pfinnoKVfCsVCcybhV1iwkCCTMeMyxTRA4tqJm6MrLwgKG3HmmwVdhsEuXjSsGJFXGzgfgPHucVzBEgAqcpH9JPpoaQyis2MFwRJLjenxzkE8xJzWHv1Zk2T",
      "writeVersion": "2697618495",
      "txnSignature": "5C9Hr5nG2j8eQz6inxPmfyjbYdmXddzUDyR1iQgEnjYQ3RNvuP4Zzc8t1enLNy7Rk8KNCtQPEQztENYWxkt9GaVD"
    },
    "slot": "352366983"
  },
  "createdAt": "2025-07-10T11:56:22.027Z"
}
```

**Que s'est-il passé ?** Notre abonnement a parfaitement fonctionné ! Nous avons demandé à Laserstream de nous notifier des changements de compte de jetons, et il a fourni une mise à jour sur le compte `BKMHWYLAX4un3HUbR7a3u9jPmzCiLNa4mSj1RiX11eWF`.

Ce compte a :

* **2,039,280 lamports** (\~0.002 SOL de solde - c'est la somme exonérée de loyer pour ce compte de jeton)
* **Programme propriétaire** `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` (c'est le programme SPL Token)
* **Signature de transaction** `5C9Hr5nG2j8eQz6inxPmfyjbYdmXddzUDyR1iQgEnjYQ3RNvuP4Zzc8t1enLNy7Rk8KNCtQPEQztENYWxkt9GaVD` montrant quelle transaction spécifique a provoqué le changement de ce compte
* **Slot 352366983** indiquant quand cette mise à jour s'est produite sur la blockchain
* **Champ de données** contenant 165 octets de données de compte encodées en base58

### Comprendre le filtrage des comptes avec datasize

Le champ de données est crucial - il contient la structure réelle du compte de jetons. Utilisons cette compréhension pour le **filtrage intelligent des comptes**.

#### Pourquoi utiliser le filtrage par datasize ?

Pour comprendre pourquoi nous avons besoin de filtrage, comprenons d'abord ce que sont réellement les comptes de jetons. **Pour chaque jeton qu'un portefeuille détient, il y a un compte séparé sur la chaîne.** Si votre portefeuille détient 3 jetons différents (USDC, BONK, et SOL), vous avez en fait 1 compte de portefeuille (votre compte principal SOL) plus 3 comptes de jetons (un pour chaque type de jeton). Chaque compte de jeton fait exactement 165 octets et stocke : quel jeton il détient (adresse de frappe), qui le possède (adresse de votre portefeuille), et combien de ce jeton il contient (montant).

Le programme Token possède **des millions de comptes** sur Solana, mais tous ne sont pas ce que nous considérons comme des "comptes de jetons" détenant des soldes utilisateur. Voici ce qui se passe avec et sans filtrage :

**Sans filtrage - L'inondation :**

```ts theme={"system"}
accounts: {
  "all-token-program-accounts": {
    owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"] // ❌ Overwhelming!
  }
}
```

Cela s'abonne à TOUS les comptes appartenant au programme Token, ce qui inclut :

* **Comptes de jetons** (165 octets) - Soldes utilisateurs : millions de comptes
* **Comptes de frappe** (82 octets) - Définitions des jetons : centaines de milliers de comptes
* **Comptes multisig** (355 octets) - Contrôles de portefeuille partagés : dizaines de milliers de comptes
* **Comptes du programme de jetons associés** (tailles variées) - millions de comptes

<Warning>
  **Résultat:** Votre application reçoit des millions de mises à jour de compte constamment, dont la plupart ne vous intéressent pas.
</Warning>

**Avec un filtrage intelligent - Précision chirurgicale :**

```ts theme={"system"}
accounts: {
  "token-accounts-only": {
    owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
    filters: [{ datasize: 165 }] // ✅ Only standard token accounts
  }
}
```

Cela filtre uniquement les comptes de 165 octets, qui sont spécifiquement les comptes de solde de jetons utilisateur - exactement ce que vous voulez pour suivre les transferts de jetons, les changements de solde, et les mises à jour de portefeuille.

**La différence :**

* **Sans filtrage :** Millions de mises à jour de comptes (créations de frappe, changements multisig, etc.)
* **Avec filtrage par datasize :** Uniquement les changements de solde de jetons

Cela représente une réduction significative du bruit, en se concentrant uniquement sur les comptes qui représentent réellement les avoirs en jetons des utilisateurs.

#### D'où viennent les 165 octets ?

Ce n'est pas de la magie - cela provient de la [structure de compte du programme SPL Token](https://github.com/solana-program/token/blob/d05d10807fe8cf157f6e1f024c708274c30c953a/program/src/state.rs#L87). En regardant le code source, nous pouvons voir que la struct `Account` définit exactement 165 octets :

```rust theme={"system"}
pub struct Account {
    pub mint: Pubkey,                    // 32 bytes
    pub owner: Pubkey,                   // 32 bytes  
    pub amount: u64,                     // 8 bytes
    pub delegate: COption<Pubkey>,       // 4 + 32 bytes
    pub state: AccountState,             // 1 byte
    pub is_native: COption<u64>,         // 4 + 8 bytes
    pub delegated_amount: u64,           // 8 bytes
    pub close_authority: COption<Pubkey> // 4 + 32 bytes
}
// Total: 32+32+8+36+1+12+8+36 = 165 bytes
```

Cette taille fixe nous permet de filtrer précisément les comptes de jetons standard et d'exclure :

* Comptes de frappe (82 octets)
* Comptes multisig (355 octets)
* Comptes de programme de compte de jeton associé
* Autres comptes liés aux jetons de tailles différentes

Pour calculer les tailles de compte dans d'autres programmes, consultez la [référence d'espace Anchor](https://www.anchor-lang.com/docs/references/space) - elle vous montre combien d'espace prennent différents types de données (Pubkey = 32 octets, u64 = 8 octets, etc.).

#### Décoder la structure du compte

Maintenant que nous comprenons pourquoi nous avons filtré 165 octets, décodons ce qui se trouve dans notre compte d'exemple :

```
Base58 data: 2NUx6Xw9QkmgJCyYUP3d8TPsjJhUpSM7hcy9Fi1juGc6g9...
```

Les 165 octets se décomposent comme suit :

* **Octets 0-31 :** Adresse de frappe (quel jeton ce compte détient)
* **Octets 32-63 :** Adresse du propriétaire (qui possède ce compte de jeton)
* **Octets 64-71 :** Montant du jeton (combien de jetons sont dans le compte)
* **Octets 72-164 :** Métadonnées supplémentaires (délégué, état, autorité de fermeture, etc.)

Cette approche structurée nous donne une précision chirurgicale : nous ne recevons que des mises à jour pour les comptes de jetons standard, pas le bruit provenant d'autres types de comptes.

### Combiner les filtres : datasize + memcmp pour une précision laser

Maintenant que nous savons que l'adresse de frappe se situe aux octets 0-31, nous pouvons être encore plus spécifiques. Supposons que nous voulons seulement surveiller les comptes de jetons USDC. Nous pouvons combiner notre filtre `datasize` avec un filtre `memcmp` pour cibler l'adresse de frappe exacte :

```ts theme={"system"}
const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

const request = {
  accounts: {
    "usdc-only": {
      owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
      filters: [
        { datasize: 165 },                    // Standard token accounts only
        { 
          memcmp: {
            offset: 0,                         // Mint address starts at byte 0
            base58: USDC_MINT                  // Match this specific mint
          }
        }
      ]
    }
  },
  // ... other config
};
```

**Stratégie de filtrage progressive :**

1. **Filtre de propriétaire :** "Donne-moi les comptes gérés par le programme Token" (millions de comptes)
2. **Filtre de datasize :** "Mais seulement les comptes de jetons standard de 165 octets" (centaines de milliers)
3. **Filtre memcmp :** "Et seulement ceux détenant de l'USDC" (milliers)

Cette progression du large au spécifique est la clé d'une surveillance efficace des comptes. Chaque filtre réduit le jeu de résultats, de sorte que vous ne recevez que les mises à jour exactes qui vous intéressent.

**Important :** Tous les filtres utilisent la logique AND - chaque condition doit être remplie pour qu'une mise à jour de compte soit déclenchée.

### Lire les mises à jour de comptes USDC : Qui, Combien, Où ?

Voyons maintenant ce que ces mises à jour filtrées contiennent réellement. Créons un moniteur spécifique à l'USDC qui répond aux questions clés lorsqu'un compte de jeton change :

* **Qui** possède ce compte de jeton ?
* **Combien** d'USDC contient-il maintenant ?
* **Où** (quel compte spécifique) a changé ?
* **Quand** ce changement s'est-il produit ?
* **Quelle transaction** a causé le changement ?

Les mises à jour des comptes bruts contiennent des données binaires que nous devons décoder. Étant donné que Solana utilise le codage base58 pour les adresses et les signatures, nous utilisons la fonction `bs58.encode()` pour convertir les objets Buffer binaires en chaînes lisibles.

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

async function main() {
  console.log('USDC Account Monitor');

  const config: LaserstreamConfig = {
    apiKey: 'YOUR_API_KEY', // from https://dashboard.helius.dev/
    endpoint: 'https://laserstream-mainnet-ewr.helius-rpc.com', // pick the closest region
  };

  const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

  const request = {
    accounts: {
      "usdc-accounts": {
        account: [],
        owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
        filters: [
          { datasize: 165 },                           // Standard token accounts
          { memcmp: { offset: 0, base58: USDC_MINT } } // Only USDC
        ]
      }
    },
    commitment: CommitmentLevel.CONFIRMED,
    transactions: {}, slots: {}, transactionsStatus: {}, blocks: {}, blocksMeta: {}, entry: {}, accountsDataSlice: []
  };

  const stream = await subscribe(
    config,
    request,
    async (update: SubscribeUpdate) => {
      explainAccountUpdate(update);
    },
    async (err) => console.error('Stream error:', err)
  );

  console.log(`Account monitor started (id: ${stream.id})`);

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

function explainAccountUpdate(update: SubscribeUpdate) {
  if (!update.account) return;
  
  const account = update.account.account;
  
  // Decode the key addresses
  const tokenAccountAddress = bs58.encode(account.pubkey);
  const transactionSignature = account.txnSignature ? bs58.encode(account.txnSignature) : 'Unknown';
  
  // Extract and decode the token account data (165 bytes)
  const walletOwner = bs58.encode(account.data.slice(32, 64));       // Bytes 32-63: Owner
  const tokenAmount = account.data.readBigUInt64LE(64);              // Bytes 64-71: Amount
  const usdcAmount = Number(tokenAmount) / 1_000_000;                // Convert to USDC (6 decimals)
  
  console.log(`Account: ${tokenAccountAddress}`);
  console.log(`Owner: ${walletOwner}`);
  console.log(`Balance: ${usdcAmount.toLocaleString()} USDC`);
  console.log(`Slot: ${update.account.slot}`);
  console.log(`Transaction: ${transactionSignature.slice(0, 8)}...`);
  console.log('---');
}

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

Lorsque vous exécutez ce moniteur USDC, vous verrez une sortie propre et structurée comme ceci :

```
USDC Account Monitor
Account monitor started (id: abc123)

Account: 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU
Owner: 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM
Balance: 1,500 USDC
Slot: 352154103
Transaction: 5v8fy0eJ...
---
Account: BQy5rNRxLfcaK6554PMzsg4VJsFXzwGnAnayb8TZKgZX
Owner: HN7cABqLq46Es1jh92dQQisAq662SmxELLLsHHe4YWrH
Balance: 0 USDC
Slot: 352154103
Transaction: 5v8fy0eJ...
---
```

Chaque bloc représente un compte USDC qui a changé d'état. Le premier compte détient maintenant 1 500 USDC, tandis que le deuxième compte a été vidé à 0 USDC. Vous obtenez le solde actuel immédiatement après chaque transaction, ainsi que le compte spécifique qui a changé et quand.

Les abonnements aux comptes vous montrent le résultat final de ce qui est arrivé à chaque compte, pas les détails de la transaction. Si vous avez besoin de comprendre le contexte complet de la transaction (qui a envoyé à qui, les frais, etc.), vous devrez récupérer la transaction complète en utilisant la signature affichée.

## Référence complète de filtrage

Au-delà des filtres de base `owner`, `datasize`, et `memcmp` que nous avons utilisés, les abonnements de compte prennent en charge des options de filtrage supplémentaires pour affiner encore vos résultats :

### Filtrage de comptes spécifiques

Surveillez des comptes exacts par leurs clés publiques :

```ts theme={"system"}
accounts: {
  "specific-accounts": {
    account: [
      "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
      "BQy5rNRxLfcaK6554PMzsg4VJsFXzwGnAnayb8TZKgZX"
    ]
  }
}
```

Cette approche fonctionne bien lorsque vous savez exactement quels comptes comptent pour votre application - comme surveiller les comptes de trésorerie de votre application ou des comptes utilisateur spécifiques.

Pour des ensembles de comptes très larges, les listes de clés publiques explicites deviennent coûteuses — 32 octets par compte dans la demande d'abonnement. Au-delà de \~10 000 comptes, utilisez un [filtre cuckoo](/docs/fr/laserstream/cuckoo-filters) compressé (\~3–4 octets par compte) pour suivre des centaines de milliers de comptes dans un seul flux. Disponible dans les SDK Rust et JavaScript.

### Stratégies de filtrage combinées

La puissance vient de la combinaison de plusieurs types de filtres. Voici le modèle mental :

1. **Lancer un large filet** avec `owner` - "Donne-moi tous les comptes gérés par ce programme"
2. **Filtrer par structure** avec `datasize` - "Mais seulement les comptes de ce type spécifique"
3. **Cibler des données spécifiques** avec `memcmp` - "Et seulement ceux contenant ces informations spécifiques"
4. **Surveiller des comptes connus** avec `account` - "Ou regardez simplement ces comptes exacts qui m'intéressent"

Par exemple, surveiller les comptes USDC à forte valeur :

```ts theme={"system"}
accounts: {
  "high-value-usdc": {
    owner: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
    filters: [
      { datasize: 165 },                           // Token accounts only
      { memcmp: { offset: 0, base58: USDC_MINT } } // USDC only
      // Note: You'd implement balance filtering in your callback logic
    ]
  }
}
```

L'idée clé est que chaque filtre réduit le volume de mises à jour que vous recevez. Sans filtrage, vous pourriez obtenir des quantités écrasantes de mises à jour de compte. Avec un filtrage intelligent, vous obtenez uniquement les mises à jour qui comptent pour votre cas d'utilisation spécifique.

### Comprendre le tableau d'ensemble

Considérez les abonnements de compte comme la surveillance d'un flux en direct de changements de base de données. L'état de Solana est essentiellement un vaste magasin de valeurs clés où chaque compte est une entrée. Lorsque les programmes s'exécutent, ils modifient ces comptes. Votre abonnement vous permet de surveiller en temps réel les entrées spécifiques qui changent.

Le système de filtrage fonctionne comme les index de base de données - vous ne regardez pas juste "tous les changements" mais plutôt "les changements des comptes qui correspondent à ces critères". Cela rend possible la création d'applications réactives qui réagissent immédiatement aux événements pertinents sur la chaîne sans submerger votre système avec des données non pertinentes.

## Appliquer ce modèle à d'autres programmes

L'approche que nous avons apprise fonctionne pour n'importe quel programme Solana. Voici le modèle général :

1. **Recherchez la structure du compte** - Consultez le code source ou la documentation du programme
2. **Commencez par le filtrage de propriétaire** - Ciblez le programme qui gère les comptes
3. **Appliquez des filtres structurels** - Utilisez la taille du compte, les motifs de données ou d'autres caractéristiques pour réduire à des types de comptes spécifiques
4. **Ajoutez des filtres ciblés** - Concentrez-vous sur des comptes spécifiques, des états ou des valeurs de données qui comptent pour votre application
