Erros e Eventos do Anchor
Erros, eventos e logs são como um programa Anchor comunica resultados que o estado da conta sozinho não captura: falhar com um código estável, publicar um registro de sucesso tipado para indexadores ou deixar um rastro humano curto para engenheiros. O Anchor 0.32.1 integra esses canais ao IDL para que clientes e indexadores compartilhem um contrato com o binário on-chain.
#[error_code] & require! é a entrada prática; Mensagens de Erro Personalizadas, Eventos com emit!, emit_cpi! & Eventos Self-CPI, Logging & Custo de CU e Decodificação de Erros no Lado do Cliente aprofundam em cada superfície. Esta página é o mapa por baixo.
Resumo
- O Anchor divide a observabilidade em erros tipados (
#[error_code],require!,error!), eventos estruturados (#[event],emit!,emit_cpi!) e logs com orçamento (msg!), todos exportáveis através do IDL para consumidores off-chain. - Por que Importa: Usuários nunca veem sua cadeia
?do Rust. Eles veem falhas de simulação, linhas de log do explorador e o que quer que seu dApp mapeie deCustom(u32). Projete essas superfícies propositalmente ou você enviará UX opaca de "Transação falhou" e desperdício de CU. - Conceitos Chave:
#[error_code],require!/error!,#[msg], constraint@ Error,#[event]/emit!/emit_cpi!, custo de CU de log, tabelas de erro do IDL, decodificação via simulação primeiro. - Quando Usar: Projetando modos de falha para cofres, mercados ou caminhos administrativos; instrumentando sucesso para análise; integrando clientes @solana/kit 7.0.0 ou Anchor para decodificar falhas; explicando a observabilidade do programa para equipes de frontend e indexadores.
- Limitações / Trade-offs: Cada log e evento consome unidades de computação; cada código de erro é uma API pública que você deve versionar; instruções falhas revertem o estado; logs e eventos são dados públicos da cadeia (sem segredos).
- Tópicos Relacionados: códigos de erro e require, mensagens personalizadas, emit e emit_cpi, custo de CU de log, decodificação do lado do cliente.
Fundamentos
Um manipulador de instrução Anchor retorna Result<()>. Sucesso comete mutações de conta; falha aborta a instrução (e a transação) e reverte essas mutações. Não há commit parcial de "campos de erro". A falha em si é o sinal.
Esse design empurra a observabilidade para metadados de execução e o IDL, não para contas last_error duráveis (a menos que você escreva tal estado em um caminho de sucesso). Três canais preenchem a lacuna:
| Canal | Consumidor principal | Semântica de falha | Superfície Anchor |
|---|---|---|---|
| Erro | Runtime, carteiras, decodificadores de dApp | Instrução falha; estado é revertido | #[error_code], require!, error!, constraint @ |
Log (msg!) | Engenheiros, exploradores, suporte | Visível para passos executados até a falha | msg!, debug com recurso habilitado |
| Evento | Indexadores, análise, webhooks | Emitido em caminhos que rodam o suficiente para logar | #[event], emit!, emit_cpi! |
Manipulador Anchor (Result<()>)
|
+-- require! / error! / ? --> Err(code) --> falha tx + logs até agora
|
+-- msg!("...") --> custo CU --> logMessages
|
+-- emit! / emit_cpi! --> #[event] --> indexadores / consumidores IDL
|
+-- Ok(()) --> comita escritas de contaErros são o contrato de fluxo de controle. Prefira variantes tipadas para regras voltadas ao usuário e mantenha as mensagens seguras para o produto. Logs são um canal de debug curto, prefixado e com recurso habilitado. Eventos são o canal estruturado para sistemas off-chain que não devem raspar texto.
Todos os três compartilham uma propriedade: logs de transação são públicos. Sementes, chaves privadas, PII e internos prontos para exploração não pertencem lá.
A vantagem do Anchor sobre o ProgramError::Custom bruto é a integração com o IDL: anchor build exporta nomes de erro, códigos, strings opcionais #[msg] e esquemas de evento para target/idl/*.json. Clientes fixam esse artefato no programa implantado.
Mecânicas e Interações
#[error_code], require!, e error!
Defina falhas de domínio como um enum. O Anchor atribui códigos numéricos estáveis (convencionalmente começando em 6000) e os inclui no IDL:
#[error_code]
pub enum VaultError {
#[msg("Vault is paused")]
Paused,
#[msg("Insufficient balance")]
InsufficientFunds,
#[msg("Math overflow")]
Overflow,
}
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> Result<()> {
require!(!ctx.accounts.vault.paused, VaultError::Paused);
require!(
ctx.accounts.vault.balance >= amount,
VaultError::InsufficientFunds
);
ctx.accounts.vault.balance = ctx.accounts.vault.balance
.checked_sub(amount)
.ok_or(VaultError::Overflow)?;
Ok(())
}| Macro / forma | Papel |
|---|---|
require!(cond, Error::Variant) | Asserção booleana; falha com a variante |
require_eq! / require_keys_eq! | Verificações de igualdade / pubkey |
error!(Error::Variant) / err! | Retorno antecipado sem guarda booleana |
? em Result | Propaga ou mapeia erros de CPI e auxiliares |
Use constraints para regras de tempo de conta para que contas inválidas nunca cheguem à lógica do corpo do manipulador:
#[account(constraint = !pool.disabled @ SwapError::PoolDisabled)]
pub pool: Account<'info, Pool>,Variantes de adição apenas para que os códigos nunca sejam remapeados silenciosamente. Reordenar renumeram os códigos e quebram todos os mapas de clientes. Trate o enum como uma API pública versionada.
Aprofundamento: #[error_code] & require!.
Mensagens personalizadas com #[msg]
#[msg("...")] anexa uma string padrão em inglês a cada variante. Essa string vai para o IDL para exploradores, AnchorError e tabelas geradas. Não é um sistema de localização e não é um lugar para valores dinâmicos. Mantenha as strings curtas, seguras para o usuário e estáveis.
Off-chain, mapeie códigos para cópias de produto, chaves i18n e dicas de retentativa. O inglês on-chain é um fallback de ferramenta, não a única superfície de UX. Detalhes: Mensagens de Erro Personalizadas.
Eventos com emit!
Quando dashboards ou plugins Geyser precisam de campos confiáveis, analisar msg! de texto livre se torna um problema. Marque uma struct amigável a Borsh com #[event] e publique-a em caminhos de sucesso:
#[event]
pub struct PositionOpened {
pub owner: Pubkey,
pub market: Pubkey,
pub size: u64,
pub timestamp: i64,
}
emit!(PositionOpened {
owner: ctx.accounts.owner.key(),
market: ctx.accounts.market.key(),
size,
timestamp: Clock::get()?.unix_timestamp,
});Eventos aparecem no IDL. Campos devem ser públicos e serializáveis. Prefira chaves e quantias a grandes blobs. O estado da conta permanece autoritativo. Instruções falhas não comitam estado, então projete a UX de falha em torno de códigos de erro, não contas de "erro duráveis". Aprofundamento: Eventos com emit!.
emit_cpi! e entrega de eventos self-CPI
O emit! clássico registra dados de evento em um formato que muitas ferramentas entendem, muitas vezes via um padrão interno de self-CPI. emit_cpi! torna o caminho CPI explícito para que indexadores que dependem da observação de CPI capturem eventos de forma mais confiável:
emit_cpi!(LiquidityAdded {
provider: ctx.accounts.provider.key(),
amount,
});| Macro | Custo de CU | Ajuste típico |
|---|---|---|
emit! | Menor | Testes, pipelines simples, telemetria de menor risco |
emit_cpi! | Maior | Indexadores de produção com requisitos de captura rigorosos |
Padronize um estilo por programa para que os parsers permaneçam simples. Orce CU após mudar para emit_cpi! em caminhos quentes. Detalhes: emit_cpi! & Eventos Self-CPI.
Logging e unidades de computação
msg! invoca a syscall de log. O custo escala com a contagem de chamadas e o tamanho da carga útil. Em um orçamento apertado de CU, um loop de logs formatados pode levar uma transação a exceder o limite de computação.
Regras práticas:
- Prefira uma linha curta por decisão em vez de dumps de várias linhas.
- Controle logs verbosos com recursos Cargo em builds mainnet.
- Use prefixos estáveis (
VAULT:deposit_ok) para operações de grep. - Prefira eventos para observabilidade de produção; reserve
msg!para dev, Surfpool, LiteSVM e incidentes curtos. - Nunca registre segredos (sementes, metadados privados, apenas dados de billboard públicos).
Perfure com sol_log_compute_units e defina um orçamento de computação do cliente quando as instruções se aproximarem dos limites. Veja Logging & CU Cost.
Decodificação do lado do cliente
Carteiras frequentemente mostram uma falha genérica. Seu dApp deve fazer melhor:
- Execute
simulateTransaction(ou confirme e busque meta) com as mesmas contas e dados. - Extraia o ID do programa falho e o código personalizado (decimal ou hexadecimal, por exemplo,
0x1770= 6000). - Mapeie através dos
errorsdo IDL viaAnchorErrorde@coral-xyz/anchor, tabelas geradas por Codama, ou um lookup fixado para @solana/kit 7.0.0. - Mostre uma mensagem de produto, string i18n opcional e uma dica de retentativa específica do código.
Simular mensagem --> InstructionError + logs
|
v
program_id + code --> IDL / tabela gerada
|
v
String da UI + dica de retentativaFixe o artefato decodificador no hash do programa implantado. IDLs desatualizados causam nomes de erro incorretos em tickets de suporte. Sempre decodifique com (program_id, code) em transações multi-programa. Padrões: Decodificação de Erros no Lado do Cliente.
Mapa de observabilidade para equipes Anchor
| Pergunta | Canal | Ferramenta on-chain | Ferramenta off-chain |
|---|---|---|---|
| Esta ix deve falhar? | Erro | require! / error! / @ Variant | Decodificar código; bloquear envio em falha de simulação |
| Qual passo rodou? | Log | msg! com recurso habilitado | Explorador / sim logMessages |
| O que os indexadores devem armazenar? | Evento | emit! / emit_cpi! | Plugin Geyser, indexador personalizado |
| O estado está correto após o sucesso? | Dados da conta | Escritas sob regras do proprietário | RPC getAccount / assinaturas |
Erros decidem o fluxo de controle. Logs explicam engenheiros. Eventos alimentam máquinas. O estado da conta resolve disputas.
Considerações Avançadas e Aplicações
Constraints vs. erros do manipulador. Coloque regras de identidade e forma de conta em #[derive(Accounts)] com @ YourError::Variant. Coloque regras de negócio que precisam de valores computados ou resultados de CPI em require! dentro do manipulador.
Limites de CPI. Erros do chamado podem aparecer nos logs externos. Retransmita, envolva em seu próprio código de UX ou deixe o código do chamado honesto. Decodifique com (program_id, code), não apenas com o código.
Segurança e UX. Erros granulares ajudam usuários; excessivamente granulares podem ensinar atacantes qual verificação falhou. Prefira categorias estáveis. Coloque detalhes de caça a exploits em logs não-mainnet controlados, não em strings #[msg] permanentes.
Orçamento de computação. Spam de logs ou eventos pode causar falhas apenas sob carga. Perfure CU após atualizações (este site fixa Agave 4.1.1) e trate a remoção de logs como uma mudança real de performance.
Versionamento e testes. Conecte o binário do programa, o IDL e a tabela de erros. Arquive artefatos decodificadores por deploy. Afirme códigos específicos em testes Anchor/LiteSVM para que a renumeram silenciosa falhe o CI.
| Abordagem | Força | Fraqueza | Melhor ajuste |
|---|---|---|---|
| Apenas erros de constraint embutidos | Estrutura rápida | UX de produto fraca | Protótipos |
#[error_code] + IDL | UX estável, pronto para i18n | Deve ser versionado cuidadosamente | Protocolos voltados ao usuário |
Uso intensivo de msg! | Debug local rápido | CU, ruído, dados públicos | Builds de dev, incidentes curtos |
emit! / emit_cpi! | Esquema amigável para indexadores | Manutenção de CU + esquema | Análise, telemetria de protocolo |
| Simulação + decodificação em dApp | Melhor mensagem ao usuário | Requer fixação de IDL mantida | Carteiras, UIs de negociação, suporte |
Equívocos Comuns
- "Vou retornar uma string livre e a carteira a mostrará." Clientes veem principalmente códigos e linhas de log. Projete variantes
#[error_code]estáveis e decodifique-as você mesmo. - "
msg!é depuração gratuita." Cada chamada consome unidades de computação e pode falhar orçamentos apertados. - "Eventos são armazenamento durável." São cargas úteis de log estruturadas; o estado da conta é a fonte da verdade, e instruções falhas não comitam estado.
- "Posso registrar sementes; apenas minha equipe lê os logs." Logs de transação são dados públicos da cadeia.
- "Erro personalizado 6001 sempre significa a mesma coisa no Solana." Códigos são por programa; sempre combine com o ID do programa.
- "Reordenar variantes de enum é um refator puro." Renumeram códigos e quebram mapas de clientes; adicione apenas.
- "
emit!eemit_cpi!são intercambiáveis para todos os indexadores." Confiabilidade de captura e custo de CU diferem; escolha um estilo e valide em devnet. - "O
#[msg]on-chain é suficiente para internacionalização." Mapeie códigos para strings de local off-chain; não confie no#[msg]em inglês on-chain como o único texto do usuário.
FAQs
Qual é a diferença entre um erro, um log e um evento no Anchor?
Um erro falha a instrução com um código de #[error_code] e reverte o estado. Um log é uma linha msg! de texto livre para humanos. Um evento é uma carga útil #[event] tipada para que indexadores possam decodificar campos via IDL.
Como o Anchor atribui códigos de erro?
Enums #[error_code] são exportados no IDL com códigos numéricos convencionalmente começando em 6000, mais strings opcionais #[msg] para ferramentas e clientes.
Quando devo usar require! vs constraint @ Error?
Use constraints de conta para validação disponível no tempo de construção do contexto. Use require! ou error! para regras de negócio em tempo de manipulador, valores computados e verificações pós-CPI.
Qual é a diferença entre error! e require!?
require!(condition, Error::V) afirma um booleano e retorna o erro quando falso. error!(Error::V) (ou err!) retorna esse erro imediatamente sem uma condição. Ambos se tornam a mesma classe de falha mapeada pelo IDL.
Por que msg! custa unidades de computação?
Logging é uma syscall de runtime. Mais chamadas e strings maiores consomem mais do orçamento de computação da transação.
Programas de produção devem manter chamadas msg! verbosas?
Geralmente não. Prefira logs verbosos com recursos habilitados, apenas breadcrumbs curtos permanentes onde o valor de operações é alto e eventos estruturados para necessidades de indexadores.
emit! vs emit_cpi! qual devo escolher?
emit! é mais barato e serve para muitos aplicativos e testes. emit_cpi! custa mais CU, mas melhora a confiabilidade de captura para indexadores dependentes de CPI. Padronize por programa e valide em devnet.
Eventos são visíveis se a transação falhar?
Instruções falhas não comitam estado do programa. Projete a UX de falha em torno de códigos de erro e logs até o ponto da falha, não contas de evento de erro duráveis.
Como clientes @solana/kit decodificam erros Anchor?
Combine o código personalizado da simulação ou meta confirmada contra tabelas geradas por IDL ou Codama, fixadas na versão do programa com a qual seu aplicativo fala. Sempre inclua o ID do programa em transações multi-instrução.
Constraints de conta podem retornar erros personalizados?
Sim. Use constraint = ... @ MyError::Variant para que falhas de validação mapeiem para sua tabela IDL quando você precisar de UX específica do produto.
Como transações multi-programa devem ser decodificadas?
Percorra a pilha de invocação de log, identifique qual ID de programa falhou e, em seguida, mapeie a tabela de códigos desse programa. Nunca assuma que um inteiro nu é seu.
Eventos substituem assinaturas de conta para indexadores?
Eles os complementam. Eventos fornecem fatos explícitos do domínio; assinaturas de conta fornecem diferenças de estado. Muitos pipelines de produção usam ambos.
Como manter tabelas de erro sincronizadas com os deploys?
Publique o IDL no CI junto com o artefato do programa, versionando-o com o hash de deploy e recuse lançamentos de clientes que fixam a tabela errada.
Onde o i18n para erros pertence?
Off-chain. Mapeie códigos estáveis para strings de local no cliente; não confie no #[msg] em inglês on-chain como a única cópia do usuário.
Como isso se mapeia para o Solana nativo sem Anchor?
Os canais de runtime são os mesmos: ProgramError, syscalls de log e dados de log binários opcionais. Sem Anchor, você é dono de mais da história de publicação de tabelas e perde as macros de conveniência.
Relacionados
- #[error_code] & require! - Declarando e afirmando erros tipados
- Mensagens de Erro Personalizadas - Strings
#[msg]e cópias de UX estáveis - Eventos com emit! - Cargas úteis
#[event]estruturadas para indexadores - emit_cpi! & Eventos Self-CPI - Captura confiável via CPI explícita
- Logging & CU Cost - Orçamento de
msg!e observabilidade de produção - Decodificação de Erros no Lado do Cliente - Mapeie códigos IDL em clientes TypeScript
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.