Pontos-Chave Avançados do Anchor
Os padrões do Anchor (contas Borsh, listas fixas #[derive(Accounts)], InitSpace) fazem a maioria dos programas funcionar. Sistemas de produção atingem os limites: grandes livros, rotas de salto variáveis, crescimento de esquema, chamados externos e tetos de CU e tamanho de implantação.
Esta página é o mapa conceitual para Anchor Avançado - zero-copy, remaining_accounts, realloc, tipos de conta personalizados, flags de recursos e cfg, CU e tamanho binário, e interoperação com programas nativos ou Pinocchio no Agave 4.1.1.
Resumo
- O Anchor Avançado mantém o mesmo contrato de tempo de execução (contas declaradas, propriedade, aluguel, CU), mas opta por conveniências que consomem computação, congelam o layout ou ocultam metadados variáveis, substituindo-os por carregadores, caudas, redimensionamentos e pontes de CPI.
- Por que Importa: Layout zero-copy incorreto, contas restantes não validadas, realloc descuidado ou deriva de IDL com flags de recursos transformam a velocidade do framework em corrupção, problemas de aluguel ou quebra de cliente sob carga de rede principal.
- Conceitos-Chave:
#[account(zero_copy)]/AccountLoader,ctx.remaining_accounts,realloc+realloc::payer/realloc::zero, traits de conta manuais, recursos Cargo /#[cfg], unidades de computação e tamanho.so, CPI nativa / Pinocchio (invoke,declare_program!). - Quando Usar Este Modelo: Estado fixo grande, roteadores e caminhos de múltiplos oráculos, esquemas em crescimento, DeFi de framework misto, pressão de CU ou tamanho de implantação, ou instruções apenas de desenvolvimento que nunca devem ser enviadas.
- Limitações / Trade-offs: Mais poder significa mais validação manual; zero-copy e realloc lutam contra a mudança de esquema; contas restantes deixam o IDL; cfg pode bifurcar o IDL e o layout; a interoperação confia nas regras do chamado, não nas restrições do Anchor do outro lado.
- Tópicos Relacionados: Contas Zero-Copy, realloc no Anchor, remaining_accounts, Tipos de Conta Personalizados, Otimização de CU e Tamanho, Flags de Recursos e cfg, Interagindo com Programas Nativos/Pinocchio.
Fundamentos
O Anchor fica sobre o modelo de conta da Solana: programas são executáveis sem estado; o estado durável vive em contas de dados de propriedade do programa; toda conta que uma instrução precisa deve aparecer na transação com flags corretas de signatário e gravável.
O caminho usual é: nomear contas em #[derive(Accounts)] com restrições; decodificar corpos com Borsh (Account<'info, T>); fixar espaço em init com space = / InitSpace; expor um IDL estável para clientes (@solana/kit 7.0.0, Codama, Anchor TS).
O Anchor Avançado começa quando esse caminho está errado para a carga de trabalho:
| Pressão | Padrão que falha | Resposta Avançada |
|---|---|---|
| Tabelas fixas grandes (livros, ticks, bitmaps) | Desserialização/serialização Borsh completa a cada ix | Zero-copy + AccountLoader |
| Contagem variável de contas (rotas, hooks, oráculos) | Campos nomeados fixos apenas | ctx.remaining_accounts |
| Crescimento de esquema ou coleção após init | SPACE fixo para sempre | realloc (ou migrar para uma nova conta) |
| Layouts estrangeiros ou exóticos | Account<T> + #[account] apenas | Tipos personalizados ou UncheckedAccount + verificações manuais |
| Diagnósticos de desenvolvimento / ix apenas para cluster | Mesmo binário em todos os lugares | Recursos + cfg (ou crates separados) |
| Teto de CU ou tamanho de implantação | Logs verbosos, redescobrir PDAs, dependências pesadas | Medir, então cortar custo do caminho quente e peso binário |
| Compor com programas não-Anchor | Auxiliares de CPI apenas do Anchor | Interoperação bruta invoke / declare_program! |
Nada aqui muda os bloqueios do Sealevel ou a propriedade: você ainda não pode inventar contas em tempo de execução, escrever dados estrangeiros sem CPI para o proprietário, ou gastar CU ilimitado. Você apenas muda como o Anchor mapeia essas regras em tipos e macros.
Mecânicas e Interações
Contas Zero-copy
Para configurações pequenas, Account<'info, T> é bom: o Anchor verifica proprietário e discriminador, decodifica Borsh e escreve de volta ao sair quando mutável. Quando T é grande e majoritariamente fixo (livros de ordens, arrays de ticks, slabs), a análise/serialização completa consome CU com o tamanho. Zero-copy mantém os bytes no local e muta através de uma visualização tipada.
No Anchor 0.32.1 a forma usual é:
#[account(zero_copy)]
#[repr(C)]
pub struct Market {
pub head: u64,
pub bids: [u64; 1024],
}
#[derive(Accounts)]
pub struct Touch<'info> {
#[account(mut)]
pub market: AccountLoader<'info, Market>,
}
pub fn touch(ctx: Context<Touch>) -> Result<()> {
let mut market = ctx.accounts.market.load_mut()?;
market.head = market.head.saturating_add(1);
Ok(())
}Prefira #[repr(C)], campos de tamanho fixo e layout compatível com bytemuck/Pod (sem String/Vec ingênuos). Discriminadores ainda se aplicam. Use AccountLoader, não Account, e não misture Borsh e zero-copy na mesma conta ativa. Planeje a capacidade antecipadamente; marque mut para load_mut. Perfis primeiro: em contas minúsculas, zero-copy geralmente custa mais do que economiza.
remaining_accounts
A struct Accounts tipada é um prefixo da lista de contas da instrução. Tudo após os campos nomeados é ctx.remaining_accounts: AccountInfo brutos sem verificações automáticas de proprietário, signatário ou discriminador.
Usos típicos: roteadores multi-salto, conjuntos de oráculos opcionais ou hooks, e encaminhamento de metadados abertos para CPI.
pub fn route(ctx: Context<Route>, hops: u8) -> Result<()> {
require!(
ctx.remaining_accounts.len() == hops as usize * 2,
RouterError::BadMetas
);
require!(ctx.remaining_accounts.len() <= MAX_REMAINING, RouterError::TooMany);
for (i, acc) in ctx.remaining_accounts.iter().enumerate() {
if i % 2 == 0 {
require_keys_eq!(*acc.owner, token_program::ID, RouterError::BadOwner);
}
}
Ok(())
}As contas restantes não são nomeadas no IDL; documente o protocolo para clientes e indexadores, ou envie instruções tipadas por rota comum. Clientes anexam metadados na ordem acordada com flags corretas de is_signer / is_writable. Limite o comprimento. Valide cada entrada antes de ler, escrever ou fazer CPI.
realloc no Anchor
O Init congela data_len e o mínimo isento de aluguel para esse tamanho. Quando esquemas ou coleções crescem, realloc muda o buffer sob a propriedade do programa dentro de uma instrução:
#[account(
mut,
realloc = 8 + Config::INIT_SPACE + extra,
realloc::payer = payer,
realloc::zero = false,
)]
pub config: Account<'info, Config>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,realloc = N:data_lenalvo (inclua o discriminador de 8 bytes ao usar padrõesINIT_SPACE).realloc::payer: financia o delta de aluguel no crescimento; deve ser mutável.realloc::zero: se os novos bytes são zerados. Zerar quando novos bytes se tornam novos campos; bytes obsoletos são uma classe de bug se você os deixar sujos.- Programa de sistema deve estar presente para transferências de aluguel.
Prefira crescimento para frente e migre instruções para grandes redesenhos. Não faça realloc em todos os caminhos quentes: cresça em blocos ou pré-aloque quando o teto for conhecido. Zero-copy mais realloc frequente é uma má combinação.
Tipos de conta personalizados
A maioria dos programas deve permanecer em Account<'info, T>, wrappers de token InterfaceAccount / e AccountLoader, e UncheckedAccount / SystemAccount / Signer restritos.
Tipos de conta personalizados implementam traits do Anchor manualmente (AccountDeserialize, verificações de propriedade) quando layouts estrangeiros ou validações especializadas não se mapeiam para derives. Eles aumentam a superfície de auditoria: tempos de vida, uso indevido de try_deserialize_unchecked, falta de verificações de proprietário. Prefira envolver built-ins ou um IDL estrangeiro, com fixtures LiteSVM se você precisar ir para o custom.
Flags de recursos e cfg
Recursos Cargo e #[cfg(feature = "...")] compilam código para dentro ou para fora:
[features]
default = []
debug-log = []
devnet-only = []#[cfg(feature = "debug-log")]
msg!("debug path");Bons usos: diagnósticos opcionais, asserts de teste mais pesados, constantes de cluster de scripts de build. Usos perigosos: dividir #[program] para que o IDL principal seja diferente dos clientes; sinalizar layouts de campo de conta entre compilações do mesmo ID de programa; deixar instruções devnet-only na versão de lançamento. Prefira padrões vazios, CI que proíba combinações perigosas de recursos principais, portões de administração em tempo de execução, ou um crate de desenvolvimento separado quando as instruções nunca devem existir na rede principal.
CU e tamanho binário
Unidades de computação medem o trabalho dentro de um orçamento de transação finito (planeje em torno de um padrão na ordem de ~1.400.000 CU a menos que instruções de Orçamento de Computação aumentem o limite dentro dos limites do protocolo). Deserialização, restrições, logs, derivação de PDA e CPI consomem do mesmo pool.
Hábitos de alta alavancagem: armazene bumps canônicos; use zero-copy apenas onde Borsh domina em estado grande; corte msg! e prefira eventos; menos contas e CPI mais rasa; dimensionamento correto dos limites de CU do cliente a partir da simulação com margem; perfis de lançamento e divisão de superfícies de administração raras podem encolher o .so. Nunca remova a validação para economizar CU. Meça primeiro; quando a taxa do framework ainda domina, considere um caminho quente Pinocchio ou nativo via CPI.
Interoperação com nativo e Pinocchio
O Anchor pode chamar programas não-Anchor na mesma transação:
declare_program!quando o chamado publica um IDL compatível (auxiliares de CPI tipados em tempo de compilação).invoke/invoke_signedbrutos com dados deInstructionconstruídos manualmente e listas deAccountMetaquando não há IDL ou o layout é empacotado manualmente.
invoke(
&Instruction {
program_id: native_program.key(),
accounts: metas,
data: ix_data, // discriminador + argumentos por especificação do chamado
},
account_infos,
)?;As restrições do Anchor se aplicam apenas à sua struct Accounts. Valide IDs de programa, ordem das contas, flags de gravável/signatário e estado pós-CPI. Clientes com @solana/kit 7.0.0 devem codificar os mesmos discriminadores e metadados que o chamado espera.
Considerações e Aplicações Avançadas
Trate essas ferramentas como uma pilha de saídas de emergência, não como uma lista de verificação para habilitar todas de uma vez.
| Ferramenta | Ganhos | Custos | Adequado para |
|---|---|---|---|
| Zero-copy | Baixa CU em estado fixo grande | Layout rígido, migrações mais difíceis | Livros, slabs, arrays de ticks |
| remaining_accounts | Composição variável | Sem nomes de IDL; validação manual completa | Roteadores, hooks, multi-oráculos |
| realloc | Paga aluguel quando necessário | Pagador, zeramento, CU, limites máximos | Crescimento aditivo, listas limitadas |
| Nova conta + migrar | Layout V2 limpo | UX e período de leitura dupla | Redesenhos que quebram |
| Tipos de conta personalizados | Ajuste de layout estrangeiro | Auditoria e carga de trait | Último recurso de interoperação |
| Recursos cfg | Binários de lançamento menores ou mais seguros | Risco de bifurcação de IDL/layout | Logs de depuração, ix de desenvolvimento que nunca envia |
| Ajuste de CU/tamanho | Aterrissagem sob orçamento, ajuste de implantação | Complexidade se não medida | Caminhos quentes perfilados |
| CPI Nativa/Pinocchio | Desempenho especializado ou protocolo compartilhado | Risco de contrato byte a byte | Pilhas DeFi mistas |
Migração: prefira campos Borsh anexáveis e um byte de versão quando possível; realloc para crescimento limitado; migração dedicada quando o layout quebra; leitura dupla durante o lançamento.
Clientes e indexadores: toda saída que deixa o IDL (contas restantes, discriminadores manuais, instruções com flags cfg) precisa de um protocolo publicado: constantes, documentação de rota, codecs Codama/kit e eventos de migração.
Revisão de segurança: autenticação de contas restantes, griefing de aluguel de realloc e tamanho máximo, alinhamento zero-copy e confusão de tipos, desserialização personalizada não verificada, superfície de ataque de flags de recursos, falsificação de programa de CPI.
Em uma pilha alinhada com Agave 4.1.1 (CLI 3.0.10, Anchor 0.32.1, Rust 1.91.1, LiteSVM 0.6.x, Surfpool 0.12.0), exercite o crescimento, contas restantes adversárias e perfis de CU antes que o tráfego da rede principal o faça.
Equívocos Comuns
- "Zero-copy é sempre mais rápido, então toda conta deve usá-lo." Contas pequenas raramente justificam a rigidez do Pod. Perfis o custo do Borsh primeiro.
- "remaining_accounts já são validadas pelo Anchor." Apenas a struct tipada recebe macros de restrição. A cauda é bruta até que você a verifique.
- "realloc zera e inicializa novos campos para mim." Você escolhe a política de zeramento e deve inicializar campos semânticos.
- "Posso encolher e crescer casualmente como um heap." O tamanho da conta é uma operação de aluguel e proprietário; projete o crescimento para frente e migre para grandes mudanças.
- "Tipos de conta personalizados são uma escolha de estilo normal." Eles são um último recurso. Prefira built-ins mais restrições.
- "Flags de recursos me permitem manter um único codebase com diferentes IDLs principais." Clientes e compilações verificáveis precisam de um único produto de lançamento.
- "Cortar restrições é uma otimização de CU válida." Regressões de segurança não são CU grátis. Otimize layout, logs, bumps e CPI em vez disso.
- "Se a CPI do Anchor for bem-sucedida, o programa nativo fez o que eu quis." Sucesso significa que o chamado retornou
Ok; ainda verifique valores e autoridades. - "A completude do IDL é opcional se o programa estiver correto." Contas restantes e bytes de CPI manuais são invisíveis para clientes ingênuos.
- "O tamanho binário não importa depois que a implantação é bem-sucedida." Programas maiores aumentam o custo de implantação e o atrito operacional.
FAQs
O que conta como "Anchor Avançado" versus Anchor do dia a dia?
Anchor do dia a dia: Account<T> Borsh, structs de conta fixas, init com espaço conhecido, auxiliares de CPI padrão. Anchor Avançado: carregadores zero-copy, caudas de contas restantes, crescimento realloc, tipos de conta criados manualmente, divisões de recursos em tempo de compilação, trabalho agressivo de CU/tamanho e interoperação nativa/Pinocchio.
Quando devo usar contas zero-copy?
Quando os dados da conta são grandes e majoritariamente fixos, e a profilagem mostra que a desserialização/serialização Borsh domina a CU. Use #[account(zero_copy)] com #[repr(C)] e AccountLoader. Veja Contas Zero-Copy.
Zero-copy ainda usa um discriminador?
Sim. Tags de tipo ainda protegem contra a leitura de um layout como outro. Caminhos de init e load devem escrever e verificar o prefixo esperado.
O que é ctx.remaining_accounts?
Contas listadas na instrução após os campos da sua struct #[derive(Accounts)]. Elas não são nomeadas no IDL e não recebem verificações automáticas de restrição. Veja remaining_accounts.
Quando preciso de realloc no Anchor?
Quando uma conta existente de propriedade do programa precisa crescer para novos campos ou mais entradas (ou encolher cuidadosamente). Use realloc, realloc::payer, realloc::zero e o programa de sistema. Veja realloc no Anchor.
realloc é um substituto para versionamento de conta?
Não. Realloc muda o comprimento dos bytes; o versionamento decide como você analisa layouts antigos e novos. Combine um discriminador estável, um campo de versão, leitura dupla e migre ou realloc conforme necessário.
Devo implementar tipos de conta personalizados?
Apenas quando os built-ins não podem expressar o layout ou a validação. Prefira built-ins mais restrições. Veja Tipos de Conta Personalizados.
Como as flags de recursos interagem com o IDL?
Qualquer coisa que mude instruções ou formas de conta entre compilações produz IDLs diferentes. Mantenha um único perfil de lançamento principal. Veja Flags de Recursos & cfg.
Como reduzir a CU sem enfraquecer a segurança?
Armazene bumps, corte logs verbosos, prefira zero-copy apenas em estado quente grande, minimize contas e profundidade de CPI, e defina limites de CU do cliente a partir da simulação. Nunca remova verificações de propriedade ou signatário para CU. Veja Otimização de CU e Tamanho.
Como medir a CU e o tamanho binário?
Use o consumo de unidade de computação em testes LiteSVM/Surfpool e simulação RPC na devnet; inspecione o tamanho do .so de lançamento após anchor build com recursos de produção.
Um programa Anchor pode fazer CPI para um programa Pinocchio ou nativo?
Sim. Prefira declare_program! quando um IDL existir; caso contrário, construa dados de instrução e metadados e invoke / invoke_signed. Valide o ID do programa e cada conta. Veja Interagindo com Programas Nativos/Pinocchio.
O que declare_program! precisa?
Um IDL JSON do chamado disponível em tempo de compilação para que o Anchor possa gerar auxiliares de CPI tipados. Sem um IDL, use a construção de instrução bruta.
Como os clientes @solana/kit devem tratar esses padrões avançados?
Gere ou escreva manualmente codecs que correspondam a discriminadores, contas fixas, protocolos de contas restantes e quaisquer bytes de CPI não-Anchor. Tenha como alvo @solana/kit 7.0.0 alinhado com esta pilha.
Quando devo deixar o Anchor para Pinocchio em um caminho quente?
Quando a CU medida ou o tamanho binário ainda falham nas metas do produto após otimizações do lado do Anchor, e você pode isolar uma pequena superfície com um limite de CPI claro.
Relacionados
- Contas Zero-Copy -
AccountLoader, layout Pod, estado grande - realloc no Anchor - crescer espaço, pagador, política de zeramento
- remaining_accounts - caudas de contas variáveis e validação
- Tipos de Conta Personalizados - traits de conta manuais e layouts estrangeiros
- Otimização de CU e Tamanho - unidades de computação e tamanho de implantação
- Flags de Recursos & cfg - compilação condicional e estabilidade de IDL
- Interagindo com Programas Nativos/Pinocchio - CPI de framework misto
- Melhores Práticas Avançadas do Anchor - lista de verificação para limites de produção
Versões da pilha: Esta página foi escrita para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, e LiteSVM 0.6.x.