Anchor
Anchor para Iniciantes

Anchor para Iniciantes

Desenvolvimento Client Side

A maioria das dApps usa TypeScript para interagir com programas Solana implantados. Entender como integrar seu program no lado do cliente é essencial para construir aplicações funcionais.

Anchor Client SDK

O Anchor simplifica a interação do cliente com programas Solana através de um arquivo Interface Description Language (IDL) que espelha a estrutura do seu program.

Quando combinado com a biblioteca TypeScript do Anchor (@coral-xyz/anchor), o IDL fornece uma abordagem simplificada para construir instructions e transações.

Configuração

O pacote @coral-xyz/anchor é instalado automaticamente ao criar um programa Anchor. Após executar o anchor build, o Anchor gera:

  • Um IDL em target/idl/<program-name>.json

  • Um SDK TypeScript em target/types/<program-name>.ts

Esses arquivos abstraem grande parte da complexidade subjacente. Transfira-os para seu cliente TypeScript usando esta estrutura:

text
src
├── anchor
│     ├── <program-name>.json
│     └── <program-name>.ts
└── integration.ts

O arquivo integration.ts contém a lógica de interação com o program. O arquivo <program-name>.json é o IDL, e <program-name>.ts contém os tipos TypeScript gerados.

Para usar o wallet adapter com o SDK TypeScript do Anchor, crie um objeto Provider que combina a Connection (localhost, devnet ou mainnet) e a Wallet (o endereço que paga e assina as transações).

Configure a Wallet e a Connection:

ts
import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react";

const { connection } = useConnection();
const wallet = useAnchorWallet();

O hook useWallet do @solana/wallet-adapter-react é incompatível com o objeto Wallet que o Provider do Anchor espera. É por isso que usamos o hook useAnchorWallet.

Crie o objeto Provider e defina-o como padrão:

text
import { AnchorProvider, setProvider } from "@coral-xyz/anchor";

const provider = new AnchorProvider(connection, wallet, {
  commitment: "confirmed",
});

setProvider(provider);

Program

O objeto Program do Anchor cria uma API personalizada para interagir com programas Solana. Esta API serve como a interface central para toda a comunicação com o program onchain:

  • Enviar transações,

  • Buscar accounts desserializadas,

  • Decodificar dados de instruction,

  • Inscrever-se para mudanças em accounts,

  • Ouvir eventos

Crie o objeto Program importando os tipos e o IDL:

ts
import <program-name> from "./<program-name>.json";
import type { <Program-Type> } from "./<program-name>.ts";
import { Program, Idl } from "@coral-xyz/anchor";

const program = new Program(<program-name> as <Program-Type>);

Se você não definiu um provider padrão, especifique-o explicitamente:

ts
const program = new Program(<program-name> as <Program-Type>, provider);

Uma vez configurado, use o Anchor Methods Builder para construir instructions e transações. O MethodsBuilder usa o IDL para fornecer um formato simplificado para construir transações que invocam instructions do program.

O padrão básico do MethodsBuilder:

ts
await program.methods
  .instructionName(instructionDataInputs)
  .accounts({})
  .signers([])
  .rpc();

A API usa nomenclatura camelCase em vez da convenção snake_case do Rust. Chame as instructions usando sintaxe de ponto com o nome da instruction, passando argumentos como valores separados por vírgula.

Passe signers adicionais além do provider usando .signers().

Accounts

Use sintaxe de ponto para chamar .accounts no MethodsBuilder, passando um objeto com cada account que a instruction espera com base no IDL.

A partir do Anchor 0.30.0, accounts que podem ser resolvidas automaticamente (como PDAs ou endereços explícitos) são incluídas no IDL e não são necessárias na chamada .accounts (.accountPartial() torna-se o padrão). Para passar todas as accounts manualmente, use .accountsStrict().

Transações

O método padrão para enviar transações através do Anchor é .rpc(), que envia a transação diretamente para a blockchain.

Para cenários que exigem assinatura no backend (como criar uma transação no frontend com a wallet do usuário e então assinar de forma segura com um keypair do backend), use .transaction():

ts
const transaction = await program.methods
  .instructionName(instructionDataInputs)
  .accounts({})
  .transaction();

//... Sign the transaction in the backend

// Send the transaction to the chain
await sendTransaction(transaction, connection);

Para agrupar múltiplas instructions do Anchor, use .instruction() para obter objetos de instruction:

ts
// Create first instruction
const instructionOne = await program.methods
  .instructionOneName(instructionOneDataInputs)
  .accounts({})
  .instruction();

// Create second instruction
const instructionTwo = await program.methods
  .instructionTwoName(instructionTwoDataInputs)
  .accounts({})
  .instruction();

// Add both instructions to one transaction
const transaction = new Transaction().add(instructionOne, instructionTwo);

// Send transaction
await sendTransaction(transaction, connection);

Fetch and Filter Accounts

Quando seu program cria centenas de accounts, rastreá-las torna-se desafiador. O objeto Program fornece métodos para buscar e filtrar accounts do program de forma eficiente.

Busque todos os endereços de um tipo específico de account:

ts
const accounts = await program.account.counter.all();

Filtre accounts específicas usando a flag memcmp:

ts
const accounts = await program.account.counter.all([
  {
    memcmp: {
      offset: 8,
      bytes: bs58.encode(new BN(0, "le").toArray()),
    },
  },
]);

Isso busca todas as accounts Counter onde o primeiro campo é igual a 0.

Para verificar se os dados da account mudaram, busque dados desserializados de uma account específica usando fetch:

ts
const account = await program.account.counter.fetch(ACCOUNT_ADDRESS);

Busque múltiplas accounts simultaneamente:

ts
const accounts = await program.account.counter.fetchMultiple([
  ACCOUNT_ADDRESS_ONE,
  ACCOUNT_ADDRESS_TWO,
]);

Events and Webhooks

Em vez de buscar dados onchain toda vez que os usuários conectam suas wallets, configure sistemas que ouvem a blockchain e armazenam dados relevantes em um banco de dados.

Existem duas abordagens principais para ouvir eventos onchain:

  • Polling: O cliente verifica repetidamente por novos dados em intervalos. O servidor responde com os dados mais recentes independentemente de mudanças, potencialmente retornando informações duplicadas.

  • Streaming: O servidor envia dados ao cliente apenas quando ocorrem atualizações. Isso proporciona transferência de dados mais eficiente e em tempo real, pois apenas mudanças relevantes são transmitidas.

Para fazer streaming de instructions do Anchor, use webhooks que ouvem eventos e os enviam para seu servidor quando ocorrem. Por exemplo, atualize uma entrada no banco de dados sempre que uma venda de NFT acontecer no seu marketplace.

Para aplicações com latência extremamente baixa onde diferenças de 5ms importam, webhooks podem não fornecer velocidade suficiente.

O Anchor fornece duas macros para emitir eventos:

  • emit!(): Emite eventos diretamente para os logs do program usando a syscall sol_log_data(), codificando dados do evento como strings base64 prefixadas com "Program Data"

  • emit_cpi!(): Emite eventos através de Cross Program Invocations (CPIs). Os dados do evento são codificados e incluídos nos dados da instruction da CPI em vez dos logs do program

Macro emit!()

Implementação no program:

rust
use anchor_lang::prelude::*;
 
declare_id!("8T7MsCZyzxboviPJg5Rc7d8iqEcDReYR2pkQKrmbg7dy");
 
#[program]
pub mod event {
    use super::*;
 
    pub fn emit_event(_ctx: Context<EmitEvent>, input: String) -> Result<()> {
        emit!(CustomEvent { message: input });
        Ok(())
    }
}
 
#[derive(Accounts)]
pub struct EmitEvent {}
 
#[event]
pub struct CustomEvent {
    pub message: String,
}

Escuta de eventos no lado do cliente com helpers do SDK do Anchor para decodificação base64:

ts
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Event } from "../target/types/event";
 
describe("event", () => {
  // Configure the client to use the local cluster.
  anchor.setProvider(anchor.AnchorProvider.env());
 
  const program = anchor.workspace.Event as Program<Event>;
 
  it("Emits custom event", async () => {
    // Set up listener before sending transaction
    const listenerId = program.addEventListener("customEvent", event => {
      // Process the event data
      console.log("Event Data:", event);
    });
  });
});

Macro emit_cpi!()

Implementação no program:

rust
use anchor_lang::prelude::*;
 
declare_id!("2cDQ2LxKwQ8fnFUz4LLrZ157QzBnhPNeQrTSmWcpVin1");
 
#[program]
pub mod event_cpi {
    use super::*;
 
    pub fn emit_event(ctx: Context<EmitEvent>, input: String) -> Result<()> {
        emit_cpi!(CustomEvent { message: input });
        Ok(())
    }
}
 
#[event_cpi]
#[derive(Accounts)]
pub struct EmitEvent {}
 
#[event]
pub struct CustomEvent {
    pub message: String,
}

Decodificação de eventos no lado do cliente:

ts
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { EventCpi } from "../target/types/event_cpi";
 
describe("event-cpi", () => {
  // Configure the client to use the local cluster.
  anchor.setProvider(anchor.AnchorProvider.env());
  const program = anchor.workspace.EventCpi as Program<EventCpi>;
 
  it("Emits custom event", async () => { 
    // Fetch the transaction data
    const transactionData = await program.provider.connection.getTransaction(
      transactionSignature,
      { commitment: "confirmed" },
    );
 
    // Decode the event data from the CPI instruction data
    const eventIx = transactionData.meta.innerInstructions[0].instructions[0];
    const rawData = anchor.utils.bytes.bs58.decode(eventIx.data);
    const base64Data = anchor.utils.bytes.base64.encode(rawData.subarray(8));
    const event = program.coder.events.decode(base64Data);

    console.log(event);
  });
});
Blueshift © 2026Commit: 1b88646