Instructions & Context
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.
Resumo
- Uma instrução Anchor é um handler mais um
Contextvalidado: constraints rodam primeiro, depois seu código lê ou mutactx.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,
mutausente, 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_accountssem 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. - Tópicos relacionados: Instruction Handlers, Instruction Arguments, Accessing Accounts, The System & Token Programs, Return Values & CPIs from Handlers, Instruction Ordering & Dependencies.
Fundamentos
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 retornaResult<()>, ou outro tipoResultquando 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 failsHandlers 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.
Mecânica e interações
Instruction handlers e Context<T>
use anchor_lang::prelude::*;
#[program]
pub mod vault {
use super::*;
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
require!(amount > 0, VaultError::InvalidAmount);
let vault = &mut ctx.accounts.vault;
vault.total = vault
.total
.checked_add(amount)
.ok_or(VaultError::Overflow)?;
Ok(())
}
}
#[derive(Accounts)]
pub struct Deposit<'info> {
#[account(mut, seeds = [b"vault", owner.key().as_ref()], bump = vault.bump)]
pub vault: Account<'info, VaultState>,
pub owner: Signer<'info>,
}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.
Argumentos de instrução
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):
#[derive(Accounts)]
#[instruction(id: u64)]
pub struct CreatePool<'info> {
#[account(
init,
payer = payer,
space = 8 + Pool::INIT_SPACE,
seeds = [b"pool", id.to_le_bytes().as_ref()],
bump,
)]
pub pool: Account<'info, Pool>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}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.
Acessando contas dentro do handler
&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 paraCpiContext::new_with_signersem 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.
System e Token programs no context
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.
Valores de retorno e CPIs a partir de handlers
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.
Ordenação e dependências entre instruções
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 setupEscolha 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.
Considerações avançadas e aplicações
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.
Equívocos comuns
- "Context é só um saco de AccountInfos." Constraints já rodaram. Trate
ctx.accountscomo 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 PDAinvoke_signed) provam autoridade. Args podem mentir. - "
initsignifica que posso omitir o system program." Criação ainda passa por checagens do System Program; declareProgram<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.
FAQs
O que é uma instrução Anchor em uma frase?
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.
O que devo ler em seguida?
Comece com Instruction Handlers e Instruction Arguments, depois Accessing Accounts e The System & Token Programs. Adicione Return Values & CPIs from Handlers e Instruction Ordering & Dependencies quando projetar fluxos multi-etapa ou multi-programa.
Relacionados
- Instruction Handlers - assinaturas,
Context<T>e estilo de handler fino - Instruction Arguments - args Borsh,
#[instruction]e validação - Accessing Accounts -
ctx.accounts, bumps eremaining_accounts - The System & Token Programs - contas de programa obrigatórias para init e trabalho com token
- Return Values & CPIs from Handlers - eventos, return data e CPI a partir de handlers
- Instruction Ordering & Dependencies - fluxos de cliente multi-instrução e pré-requisitos
Versões da stack: Esta página foi escrita para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, Rust 1.91.1 e @solana/kit 7.0.0.