No Anchor, uma instrução é a unidade de trabalho on-chain: uma função pública dentro de #[program], uma struct de contas correspondente, argumentos tipados opcionais e um Result que informa ao runtime se o caminho da transação pode continuar. Clientes (incluindo os construídos com @solana/kit 7.0.0) não chamam suas funções Rust pelo nome de forma isolada. Eles enviam um program id, um discriminator de 8 bytes, args codificados com Borsh e uma lista fechada de contas. O Anchor deserializa essa mensagem, valida cada conta contra suas constraints #[derive(Accounts)] e então entra no seu handler com um Context<T> tipado.
Esta página é o guarda-chuva de Instructions & Context. Páginas irmãs aprofundam handlers, argumentos, acesso a contas, slots de System e Token program, valores de retorno e CPIs, e ordenação multi-instrução. Aqui você obtém um modelo coerente para que essas páginas funcionem como zooms, não histórias separadas.
Uma instrução Anchor é um handler mais um Context validado: constraints rodam primeiro, depois seu código lê ou muta ctx.accounts, usa args de instrução e opcionalmente faz CPI ou escreve resultados que clientes podem observar.
Por que importa: Quase todo bug de segurança e integração em programas Anchor começa aqui: ordem de conta errada, mut ausente, args confiados sem signers, contas de System/Token program omitidas, ou handlers que assumem init prévio sem impor isso.
Conceitos-chave:handler #[program], Context<T>, #[derive(Accounts)], argumentos de instrução (Borsh), ctx.accounts / ctx.bumps / remaining_accounts, Program<System> / Program<Token>, CPI a partir de handlers, ordenação de instruções, clientes orientados por IDL.
Quando usar: Projetar ou revisar qualquer superfície de programa Anchor 0.32.1: novas instruções, builders de cliente, fluxos multi-etapa do usuário ou auditorias de como contas e args interagem.
Limitações / trade-offs: Validação de Context custa compute; payloads grandes de args e remaining_accounts sem limite são superfícies de ataque; CPIs compartilham o orçamento de CU da transação e o limite de profundidade; fluxos multi-instrução são atômicos apenas dentro de uma transação.
Programas Solana são executáveis. Estado vive em contas. Um cliente monta uma transação com uma ou mais instruções; cada instrução nomeia um programa, bytes de dados e account metas (pubkey, signer, writable). O runtime carrega seu programa SBF, passa a lista de contas e dados, e mede compute units sob as regras do Agave 4.1.1.
O Anchor fica sobre esse modelo e padroniza três camadas:
Dispatch - #[program] exporta handlers nomeados; o IDL e discriminators de 8 bytes mapeiam métodos do cliente para essas funções.
Validação - Para cada handler, uma struct de contas com constraints (Signer, mut, seeds, init, checagens de token, etc.) roda antes da lógica de negócio.
Corpo do handler - Seu código Rust recebe Context<T> (e args) com contas já tipadas e verificadas, depois retorna Result<()>, ou outro tipo Result quando você intencionalmente retorna dados.
Client (Kit / Anchor TS / Codama) | v Instruction: program_id + discriminator + Borsh args Account metas in IDL order (+ optional remaining_accounts) | v Runtime loads program (.so) | v Anchor: match discriminator -> build Context (constraints fail => Err) -> deserialize args -> call handler(ctx, args...) | +--> mutate Account<'info, T> fields +--> CPI (System, Token, partner programs) +--> emit! / set_return_data (optional) | v Ok => serialize dirty accounts; Err => whole instruction fails
Handlers são funções públicas comuns. Convenção: o primeiro parâmetro é sempre Context<AccountsStruct>, depois zero ou mais args que correspondem à ordem do IDL. Mantenha-os finos: valide regras de negócio com require!, atualize estado, chame helpers ou módulos de CPI, retorne.
Context não é um saco livre de contas. Após avaliação bem-sucedida de constraints ele expõe:
Campo
Papel
ctx.accounts
Campos tipados da sua struct de contas
ctx.program_id
Pubkey deste programa (declare_id!)
ctx.bumps
Bumps canônicos para contas que usaram seeds + bump
ctx.remaining_accounts
AccountInfos extras após a lista fixa do IDL
Argumentos vs contas. Args são dados puros (amounts, enums, ids, pubkeys opcionais). Contas são identidades on-chain com owners, lamports, dados e flags signer/writable que o runtime impõe. Prefira contas quando uma chave deve assinar, manter estado ou ser constraint por seeds. Prefira args para escalares e configuração pequena que ainda não está on-chain. Nunca trate uma pubkey em arg como autoridade autenticada a menos que um Signer correspondente (ou assinatura PDA em CPI) prove isso.
System e Token no context. Criar contas, pagar rent, transferir SOL e mover saldos SPL exigem CPI para o System Program ou Token (ou Token-2022) program. O Anchor torna isso seguro exigindo contas de programa tipadas como Program<'info, System> e Program<'info, Token> (ou interfaces de token) para que um cliente não possa substituir um executável malicioso. Constraints init e associated-token também puxam System e token programs para a lista de contas automaticamente em espírito: você ainda os declara para que a transação possa passá-los e as constraints possam verificar seus ids.
Fluxo para cada chamada: constraints em Deposit rodam (owner assinou, vault PDA corresponde às seeds, vault é writable e owned pelo programa), bumps são registrados se derivados, depois deposit roda. Falhas em constraints nunca chegam à lógica de negócio. Essa ordem fail-closed é a principal vantagem de segurança do Anchor 0.32.1 sobre dispatch manual.
Após Context, parâmetros são decodificados com Borsh a partir dos dados da instrução (após o discriminator). Ordem e tipos devem corresponder ao cliente e ao IDL. Use #[instruction(...)] na struct de contas quando constraints precisam de valores de args (por exemplo seeds de PDA que incluem um id):
Valide intervalos cedo (fee_bps <= 10_000, amounts não zero). Mantenha superfícies de args pequenas: Vecs grandes explodem CU e tamanho de mensagem. Gere clientes a partir do IDL para que a ordem de serialização nunca desvie.
&ctx.accounts.field / &mut - Leia ou escreva estado tipado. Account<'info, T> resserializa no drop quando mutável e dirty.
ctx.bumps.field_name - Use o mesmo nome de campo da conta em seeds; passe para CpiContext::new_with_signer sem re-buscar bumps.
ctx.remaining_accounts - Caudas dinâmicas (rotas, oráculos opcionais). Documente ordem, valide owner, writability e discriminators você mesmo; o IDL não os descreve totalmente.
to_account_info() - Ponte wrappers tipados para listas de contas em CPI.
Menor privilégio ainda se aplica: marque contas mut apenas quando a instrução deve escrevê-las. Readonly quando possível reduz o raio de explosão se um bug ou alvo de CPI se comportar mal.
Declare contas de programa para toda instrução que cria contas, move SOL ou toca saldos SPL:
pub system_program: Program<'info, System>,pub token_program: Program<'info, Token>,// Token-2022 / dual support often uses Interface<'info, TokenInterface>
Program<T> verifica o id executável. Omiti-lo (ou usar infos brutas não verificadas) convida substituição de programa. Associated Token Program aparece quando você cria ATAs. Caminhos Token-2022 precisam do program id correto e frequentemente tipos interface para aceitar com segurança tanto Token clássico quanto Token-2022. Detalhes e tabelas estão em The System & Token Programs.
A maioria dos handlers retorna Result<()> e comunica resultados escrevendo contas e emitindo eventos (emit!). Solana também suporta return data de instrução; use com moderação para resultados síncronos pequenos a um caller de CPI. Indexers e dApps quase sempre preferem eventos ou estado de conta em vez de buffers de retorno opacos.
Quando o handler deve mover SOL ou tokens, ou chamar outro programa, construa um CPI com contas já presentes no context:
use anchor_lang::system_program::{transfer, Transfer};transfer( CpiContext::new( ctx.accounts.system_program.to_account_info(), Transfer { from: ctx.accounts.payer.to_account_info(), to: ctx.accounts.recipient.to_account_info(), }, ), lamports,)?;
Autoridades PDA usam CpiContext::new_with_signer e ctx.bumps. Erros de CPI propagam com ?. Profundidade aninhada e CU são preocupações em toda a transação (limite de profundidade 4 nos clusters atuais); achate quando possível. Veja Return Values & CPIs from Handlers.
Um handler é uma instrução. Clientes frequentemente encadeiam várias: criar PDA de usuário, criar ATA, depois depositar. Dentro de uma transação, instruções posteriores veem o estado de conta escrito por instruções anteriores. Entre transações, nada é atômico: projete etapas idempotentes ou flags de status explícitas, e retorne erros claros quando pré-requisitos estiverem ausentes (NotInitialized) em vez de panics.
Same transaction (atomic) 1. create_user -> init PDA 2. create_ata -> init token account 3. deposit -> transfer + update vault Separate transactions (partial completion possible) tx A: create_user tx B: deposit // must tolerate or detect missing setup
Escolha uma instrução robusta quando o programa deve impor invariantes intermediárias sem confiar no cliente. Escolha várias instruções menores quando limites de CU, composabilidade ou retries de UX favorecem etapas finas. Documente a ordem obrigatória em docs e comentários do IDL para que builders Kit não invertam init e uso.
Handlers de propósito único. Separe superfícies de admin, usuário e settlement. Handlers pequenos compõem melhor em transações multi-ix, mantêm orçamentos de CU previsíveis e simplificam auditorias. Lógica compartilhada pertence a módulos, não copiada entre funções #[program].
Args em seeds vs contas em seeds. Identidades estáveis (carteira do usuário, mint) geralmente vêm de campos de conta. Ids numéricos ou strings frequentemente vêm de args via #[instruction]. Misturar ambos é normal; mudar qualquer um altera a PDA. Armazene o bump no init e constraint bump = account.bump depois para evitar bumps não canônicos.
Grafos de contas dinâmicos. Roteadores e fluxos multi-hop empurram contas extras via remaining_accounts. Versione o layout esperado, limite o comprimento e valide cada entrada antes do CPI. Contas fixas na struct permanecem o padrão para autoridades de alto risco.
Geração de cliente. Após anchor build, o IDL dirige pipelines TypeScript e Kit. Ordem de contas no cliente deve corresponder à struct de contas. Remaining accounts são sempre um segundo contrato: publique-os ao lado do IDL. Prefira Codama ou codegen de cliente Anchor em vez de discriminators empacotados manualmente.
Testando o contrato de uma instrução. Testes unitários (LiteSVM) e testes de integração (anchor test contra um validador local ou Surfpool) devem cobrir falhas de constraint (signer errado, seeds ruins), validação de args, mutabilidade de contas em CPI e caminhos felizes multi-ix. Simule CU em handlers críticos com ferramentas Solana CLI 3.0.10 para que instruções de compute budget em produção sejam realistas.
Checklist de revisão de segurança para esta camada. Para cada instrução pergunte: quais contas são signers; quais são writable e por quê; quais PDAs são derivadas e onde bumps vivem; se ids de System/Token program são tipados; se args são autenticados ou apenas parâmetros; se estado é finalizado antes de CPI externo; se clientes podem fazer retry com segurança.
"Context é só um saco de AccountInfos." Constraints já rodaram. Trate ctx.accounts como validado; não reabra caminhos não verificados sem motivo.
"Se o cliente passa a pubkey certa em args, o signer está provado." Apenas contas Signer (ou PDA invoke_signed) provam autoridade. Args podem mentir.
"init significa que posso omitir o system program." Criação ainda passa por checagens do System Program; declare Program<System> e um payer mutável.
"Transferências de token podem ser feitas escrevendo a conta de token no meu programa." Apenas Token/Token-2022 podem reescrever dados de conta de token. CPI com autoridade válida.
"Valores de retorno de handler são como frontends obtêm resultados." Prefira estado de conta e eventos. Return data é nicho e limitado em tamanho.
"Ordem de instrução no cliente é cosmética." Init-antes-uso e flags de mutabilidade tornam a ordem load-bearing em transações multi-ix.
"remaining_accounts é seguro porque o Anchor tipou o resto." Extras são não verificados por padrão. Valide owners e papéis você mesmo.
"Uma instrução gigante é sempre mais segura." Pode impor atomicidade, mas também dispara CU e acopla falhas não relacionadas. Combine forma de instrução ao risco do produto.
Uma função pública #[program] cujas contas são validadas em um Context, cujos args são decodificados com Borsh a partir dos dados da instrução, e cujo Result decide sucesso ou falha para aquela etapa da transação.
O que roda primeiro: o corpo do meu handler ou as constraints de conta?
Constraints na struct de contas sempre rodam primeiro. Se falharem, o handler nunca executa.
O que o Context contém?
Contas tipadas (ctx.accounts), id deste programa, bumps de PDA a partir de constraints de seeds (ctx.bumps), e quaisquer remaining_accounts finais que o cliente acrescentou.
Como escolher entre um argumento e uma conta?
Use contas para chaves que devem assinar, manter estado ou ser constraint por seeds. Use args para escalares pequenos e configuração que ainda não está representada on-chain.
Por que System e Token aparecem em tantas structs de contas?
init, movimento de SOL e operações SPL fazem CPI para esses programas. Contas Program tipadas fixam os ids executáveis corretos para que clientes não possam trocar por um programa falso.
Quando um handler deve fazer CPI em vez de depender de outra instrução de topo?
CPI quando seu programa deve impor lógica intermediária e mudanças de estado aninhadas atomicamente sob seu próprio controle. Use múltiplas instruções de topo quando etapas são independentes, pesadas em CU ou melhor retentadas separadamente dentro de uma transação montada pelo cliente.
Como clientes sabem ordem de contas e tipos de args?
Pelo IDL Anchor produzido por anchor build, consumido por Anchor TS, Codama ou builders @solana/kit 7.0.0 escritos à mão que correspondem ao mesmo layout.
De onde vêm bumps usados em assinatura de CPI?
De ctx.bumps.<field> quando a struct de contas declarou seeds e bump, ou de um campo bump armazenado na conta após init. Não invente bumps ad hoc no handler.
Uma transação pode chamar várias das minhas instruções?
Sim. Ordene-as para que criadores rodem antes de consumidores. A transação inteira é atômica; progresso parcial entre transações separadas não é.
Handlers devem retornar dados ricos para a UI?
Geralmente não. Escreva contas de estado e emita eventos para indexers e UIs. Mantenha return data para sinais CPI-para-CPI pequenos se você realmente precisar.
Quais versões do Anchor e toolchain esta seção assume?
Anchor 0.32.1, Rust 1.91.1, Agave 4.1.1, Solana CLI 3.0.10 e clientes em @solana/kit 7.0.0, salvo indicação contrária em uma página.