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

# Inscrição e Atualizações de Conta

> Aprenda a se inscrever em atualizações de conta e a rastrear eficientemente as alterações de estado on-chain usando o Laserstream.

Ao desenvolver aplicativos que precisam responder a alterações on-chain, sondar endpoints RPC para atualizações de conta é ineficiente e lento. As inscrições de contas resolvem isso fornecendo atualizações em tempo real sobre alterações de estado de conta diretamente para o seu aplicativo.

Este guia cobre tudo o que você precisa saber sobre inscrições de contas: o que são, como funcionam e como otimizá-las para seu caso de uso específico.

***

## Contexto do modelo de conta

<Info>
  Pule esta seção se você estiver familiarizado com contas Solana e sua estrutura.
</Info>

Solana usa um modelo baseado em contas onde cada pedaço de dados vive em uma conta - um contêiner que armazena tanto dados quanto metadados. Cada conta possui:

* **Dados**: Os bytes reais que armazenam o estado do programa, saldos de tokens ou outras informações
* **Proprietário**: O programa que controla essa conta e pode modificar seus dados
* **Lamports**: O saldo SOL da conta para isenção de aluguel
* **Executável**: Se essa conta contém código de programa

Programas são sem estado - eles não armazenam dados internamente. Em vez disso, criam e gerenciam contas separadas para armazenar seu estado. Quando você interage com um programa, você passa as contas das quais ele deve ler ou para as quais deve escrever.

Este design torna as inscrições de contas poderosas: você pode observar alterações em contas específicas, todas as contas pertencentes a um programa ou contas que correspondem a certos critérios.

***

## Assinatura básica de conta

Vamos começar com um exemplo simples que assina alterações em contas de tokens. Este script notificará você sempre que os saldos de tokens mudarem:

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

Quando você executa essa assinatura básica, verá atualizações de conta em tempo real no seu 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"
}
```

**O que acabou de acontecer?** Nossa assinatura funcionou perfeitamente! Pedimos ao Laserstream para nos notificar sobre alterações em contas de tokens, e ele forneceu uma atualização sobre a conta `BKMHWYLAX4un3HUbR7a3u9jPmzCiLNa4mSj1RiX11eWF`.

Esta conta possui:

* **2.039.280 lamports** (\~0,002 SOL de saldo - esta é a quantia isenta de aluguel para esta conta de token)
* **Programa proprietário** `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` (este é o programa SPL Token)
* **Assinatura de transação** `5C9Hr5nG2j8eQz6inxPmfyjbYdmXddzUDyR1iQgEnjYQ3RNvuP4Zzc8t1enLNy7Rk8KNCtQPEQztENYWxkt9GaVD` mostrando qual transação específica causou essa mudança de conta
* **Slot 352366983** indicando quando essa atualização ocorreu na blockchain
* **Campo de dados** contendo 165 bytes de dados de conta codificados como base58

### Compreendendo a filtragem de contas com datasize

O campo de dados é crucial - ele contém a estrutura real da conta de token. Vamos usar esse entendimento para **filtragem inteligente de contas**.

#### Por que usar filtragem por datasize?

Para entender por que precisamos de filtragem, primeiro vamos entender o que são realmente as contas de token. **Para cada token que uma carteira possui, há uma conta separada on-chain.** Se sua carteira possui 3 tokens diferentes (USDC, BONK e SOL), você na verdade tem 1 conta de carteira (sua conta principal de SOL) mais 3 contas de token (uma para cada tipo de token). Cada conta de token tem exatamente 165 bytes e armazena: qual token ela contém (endereço de mint), quem a possui (endereço da sua carteira) e quanto desse token ela contém (quantidade).

O Programa de Token possui **milhões de contas** na Solana, mas nem todas são o que consideramos "contas de token" com saldos de usuário. Aqui está o que acontece com e sem filtragem:

**Sem filtragem - A inundação:**

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

Isso se inscreve em TODAS as contas pertencentes ao Programa de Token, o que inclui:

* **Contas de token** (165 bytes) - Saldos de usuários: milhões de contas
* **Contas de mint** (82 bytes) - Definições de tokens: centenas de milhares de contas
* **Contas multisig** (355 bytes) - Controles de carteiras compartilhadas: dezenas de milhares de contas
* **Contas do Programa de Token Associado** (vários tamanhos) - milhões de contas

<Warning>
  **Resultado:** Seu aplicativo recebe milhões de atualizações de conta constantemente, a maioria das quais você não se importa.
</Warning>

**Com filtragem inteligente - Precisão cirúrgica:**

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

Isso filtra apenas as contas de 165 bytes, que são especificamente as contas de saldo de token de usuário - exatamente o que você quer para acompanhar transferências de tokens, mudanças de saldo e atualizações de portfólio.

**A diferença:**

* **Sem filtragem:** Milhões de atualizações de contas (criações de mint, mudanças multisig, etc.)
* **Com filtragem por datasize:** Apenas mudanças de saldo de token

Isso é uma redução significativa no ruído, focando apenas nas contas que realmente representam posses de tokens de usuário.

#### De onde vêm os 165 bytes?

Isso não é mágica - vem da [estrutura de conta do programa SPL Token](https://github.com/solana-program/token/blob/d05d10807fe8cf157f6e1f024c708274c30c953a/program/src/state.rs#L87). Olhando para o código-fonte, podemos ver que a struct `Account` define exatamente 165 bytes:

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

Esse tamanho fixo nos permite filtrar precisamente por contas de token padrão e excluir:

* Contas de mint (82 bytes)
* Contas multisig (355 bytes)
* Contas de programa de token associado
* Outras contas relacionadas a tokens com tamanhos diferentes

Para calcular os tamanhos de conta em outros programas, confira a [Referência de Espaço do Anchor](https://www.anchor-lang.com/docs/references/space) - ela mostra quanto espaço diferentes tipos de dados ocupam (Pubkey = 32 bytes, u64 = 8 bytes, etc.).

#### Decodificando a estrutura da conta

Agora que entendemos por que filtramos por 165 bytes, vamos decodificar o que há dentro da nossa conta de exemplo:

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

Os 165 bytes dividem-se em:

* **Bytes 0-31:** Endereço de mint (qual token esta conta contém)
* **Bytes 32-63:** Endereço do proprietário (quem possui esta conta de token)
* **Bytes 64-71:** Quantidade de tokens (quantos tokens estão na conta)
* **Bytes 72-164:** Metadados adicionais (delegado, estado, autoridade de fechamento, etc.)

Essa abordagem estruturada nos dá precisão cirúrgica: recebemos apenas atualizações para contas de token padrão, não o ruído de outros tipos de conta.

### Combinando filtros: datasize + memcmp para precisão laser

Agora que sabemos que o endereço de mint está nos bytes 0-31, podemos ser ainda mais específicos. Digamos que queremos monitorar apenas contas de token USDC. Podemos combinar nosso filtro `datasize` com um filtro `memcmp` para direcionar o endereço de mint exato:

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

**Estratégia de filtragem progressiva:**

1. **Filtro de proprietário:** "Dê-me contas pertencentes ao Programa de Token" (milhões de contas)
2. **Filtro de datasize:** "Mas apenas contas de token padrão de 165 bytes" (centenas de milhares)
3. **Filtro de memcmp:** "E apenas aquelas que contém USDC" (milhares)

Essa progressão do amplo ao específico é a chave para o monitoramento eficiente de contas. Cada filtro reduz o conjunto de resultados, para que você receba apenas as atualizações exatas que lhe interessam.

**Importante:** Todos os filtros usam lógica AND - todas as condições devem ser atendidas para que uma atualização de conta seja acionada.

### Lendo atualizações de conta USDC: Quem, Quanto, Onde?

Agora vamos ver o que essas atualizações filtradas realmente contêm. Vamos criar um monitor específico para USDC que responde às perguntas-chave quando uma conta de token muda:

* **Quem** possui esta conta de token?
* **Quanto** USDC ela contém agora?
* **Onde** (qual conta específica) mudou?
* **Quando** essa mudança aconteceu?
* **Que transação** causou a mudança?

As atualizações brutas de conta contêm dados binários que precisamos decodificar. Como Solana usa codificação base58 para endereços e assinaturas, usamos a função `bs58.encode()` para converter objetos Buffer binários em strings legíveis.

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

Quando você executa este monitor USDC, verá uma saída limpa e estruturada como esta:

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

Cada bloco representa uma conta USDC que mudou de estado. A primeira conta agora possui 1.500 USDC, enquanto a segunda conta foi esvaziada para 0 USDC. Você obtém o saldo atual imediatamente após cada transação, junto com qual conta específica mudou e quando.

As inscrições de conta mostram o resultado final do que aconteceu com cada conta, não os detalhes da transação. Se você precisar entender o contexto completo da transação (quem enviou para quem, taxas, etc.), precisaria buscar a transação completa usando a assinatura mostrada.

## Referência completa de filtragem

Além dos filtros básicos `owner`, `datasize` e `memcmp` que usamos, as inscrições de conta suportam opções de filtragem adicionais para restringir ainda mais seus resultados:

### Filtragem de contas específicas

Monitore contas exatas por suas chaves públicas:

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

Esta abordagem funciona bem quando você sabe exatamente quais contas são importantes para seu aplicativo - como monitorar as contas do tesouro de seu aplicativo ou contas específicas de usuários.

Para conjuntos de contas muito grandes, listas explícitas de pubkey se tornam caras — 32 bytes por conta no pedido de inscrição. Para mais de \~10.000 contas, use um [filtro cuckoo](/docs/pt-BR/laserstream/cuckoo-filters) comprimido (\~3–4 bytes por conta) para rastrear centenas de milhares de contas em um único stream. Disponível nos SDKs Rust e JavaScript.

### Estratégias de filtragem combinadas

O poder vem da combinação de vários tipos de filtro. Aqui está o modelo mental:

1. **Lance uma rede ampla** com `owner` - "Dê-me todas as contas geridas por este programa"
2. **Filtre por estrutura** com `datasize` - "Mas apenas contas deste tipo específico"
3. **Mire em dados específicos** com `memcmp` - "E apenas aquelas contendo esta informação específica"
4. **Monitore contas conhecidas** com `account` - "Ou apenas observe estas contas exatas que me importam"

Por exemplo, monitorando contas de USDC de alto valor:

```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
    ]
  }
}
```

O principal insight é que cada filtro reduz o volume de atualizações que você recebe. Sem filtragem, você pode receber quantidades esmagadoras de atualizações de conta. Com filtragem inteligente, você recebe apenas as atualizações que importam para seu caso de uso específico.

### Compreendendo o quadro geral

Pense nas inscrições de contas como assistir a um feed ao vivo de alterações de banco de dados. O estado da Solana é essencialmente um enorme armazenamento de chave-valor onde cada conta é uma entrada. Quando programas são executados, eles modificam essas contas. Sua inscrição permite que você veja entradas específicas mudando em tempo real.

O sistema de filtragem funciona como índices de banco de dados - você não está apenas assistindo "todas as mudanças", mas sim "mudanças nas contas que correspondem a esses critérios". Isso torna possível construir aplicativos responsivos que reagem imediatamente a eventos on-chain relevantes sem sobrecarregar seu sistema com dados irrelevantes.

## Aplicando este padrão a outros programas

A abordagem que aprendemos funciona para qualquer programa Solana. Aqui está o padrão geral:

1. **Pesquise a estrutura da conta** - Verifique o código-fonte ou a documentação do programa
2. **Comece com filtragem de proprietário** - Direcione o programa que gerencia as contas
3. **Aplique filtros estruturais** - Use tamanho da conta, padrões de dados ou outras características para reduzir a tipos específicos de conta
4. **Adicione filtros direcionados** - Foque em contas específicas, estados ou valores de dados que importam para seu aplicativo
