Blueprint de Padrões On-Chain
Padrões de programa on-chain são designs reutilizáveis de como um programa Solana cria estado, decide quem pode alterá-lo, move valor, avança o ciclo de vida e reage ao tempo. Não são guias de estilo opcionais. Na Solana, todo campo durável vive em uma conta de propriedade do programa que você deve passar explicitamente, então segurança e regras de produto aparecem como layouts de conta, verificações de signatário, guardas de init e tabelas de transição em vez de middleware oculto no servidor.
Noções Básicas de Padrões de Programa é a entrada prática; Controle de Acesso e Autoridades, Padrões de Inicialização de Conta, Padrões de Taxa e Tesouraria, Máquinas de Estado On-Chain e Tempo e Clock aprofundam cada mecanismo. Esta página fica abaixo: como esses padrões formam um blueprint que você pode aplicar ao projetar ou revisar qualquer programa.
Resumo
- Programas Solana seguros combinam controle de acesso, inicialização segura, roteamento de valor (taxas e tesourarias), estado de ciclo de vida explícito e regras de tempo respaldadas por Clock para que toda mutação tenha um ator conhecido, uma forma de conta conhecida e um próximo passo legal conhecido.
- Por que Importa: O runtime aplica propriedade e locks; ele não aplica seu produto. Padrões codificam "quem, quando, quanto e em qual ordem" para que clientes não inventem transições, reinicializem contas ou dreinem tesourarias por verificações ausentes.
- Conceitos Chave: autoridade vs owner, verificações de signatário e papel, guardas de init / reinit, discriminadores, PDAs de tesouraria e fee bps, enums de status e tabelas de transição, sysvar Clock, slots vs timestamps unix.
- Quando Usar: Projetar um novo programa, revisar superfícies de instrução, codificar fluxos de escrow ou ordem, adicionar taxas de protocolo, rotacionar admins ou explicar por que "apenas um bool" não é um ciclo de vida.
- Limitações / Trade-offs: Padrões adicionam campos de conta, instruções e CU; admins excessivamente centralizados trocam flexibilidade por confiança; regras de tempo precisam de carência para deriva de relógio; taxas e máquinas de estado devem permanecer simples o suficiente para auditar.
- Tópicos Relacionados: controle de acesso, init de conta, taxas e tesourarias, máquinas de estado, tempo e clock, noções básicas de padrões de programa.
Fundamentos
Um programa Solana é um executável sem estado. Regras duráveis vivem em contas: config, registros de usuário, cofres, ordens e tesourarias. Padrões respondem cinco perguntas recorrentes que toda instrução deve resolver antes de mutar qualquer coisa:
- Quem tem permissão? Controle de acesso e autoridades.
- Esta conta já significa algo? Inicialização e proteção contra reinit.
- Para onde vai o valor? Taxas, tesourarias e caminhos de custódia.
- Em qual fase está este objeto? Máquinas de estado e transições legais.
- É cedo ou tarde demais? Clock, slots e prazos.
Essas perguntas se compõem. Uma instrução de fill pode exigir maker ou taker como signatário, uma conta de ordem inicializada com o discriminador correto, status Open, prazo não expirado do Clock e divisão de taxa em uma ATA de tesouraria - tudo antes de qualquer CPI de token.
Instruction arrives
|
v
+------------------+
| Access control | <- signer? role? PDA authority?
+--------+---------+
|
v
+------------------+
| Account shape | <- initialized? right disc? right owner?
+--------+---------+
|
v
+------------------+
| Lifecycle / time | <- legal status? deadline? pause flag?
+--------+---------+
|
v
+------------------+
| Value movement | <- fee math, treasury, vault CPI
+--------+---------+
|
v
Commit new bytes (and events)Owner nos metadados da conta é sempre um ID de programa: somente esse programa pode reescrever data. Autoridade é geralmente uma pubkey de aplicação (ou PDA) armazenada dentro de data que seu programa compara a um signatário da transação (ou autoriza via invoke_signed). Confundir os dois é a raiz de muitos bugs de controle de acesso: o runtime não sabe que seu campo admin existe até você verificá-lo.
Inicialização é o momento em que um buffer vazio (ou recém-alocado) se torna estado tipado. Até que um discriminador e campos sejam escritos sob uma guarda de reinit, nada que vem depois (depósitos, papéis, taxas) é confiável. Taxas e tesourarias são apenas padrões de roteamento de valor: matemática de basis points verificada e uma conta de destino cujo mint e owner você verifica. Máquinas de estado substituem booleans ad hoc por um enum e tabela de transição para que objetos concluídos ou cancelados não possam ser reutilizados. Tempo vem do sysvar Clock (slot, epoch, unix_timestamp), não de um argumento arbitrário do cliente que você nunca revalida on-chain.
Mecânica e Interações
Controle de acesso e autoridades
Todo caminho privilegiado precisa de uma verificação concreta: a pubkey esperada está presente, é signatário quando uma carteira deve aprovar, e corresponde ao campo de papel armazenado (admin, operador, owner). Separação de papéis mantém poderes de pause-and-config distintos de direitos de depósito do usuário. Design de admin em produção favorece multisig ou governança como pubkey de admin, transferência de admin em duas etapas (define pending, depois accept), e a menor superfície de admin possível para que uma chave comprometida não esvazie cofres de usuário.
Autoridades controladas pelo programa usam PDAs e invoke_signed: o programa prova sementes, não uma chave privada. Custódia de usuário de estado pessoal é geralmente "campo owner deve igualar signatário". Custódia de protocolo de cofres é "PDA deve assinar a transferência de token após regras de negócio passarem".
Padrões de inicialização de conta
Init é um portão de mão única. Fluxo típico: System Program (ou init do Anchor) aloca espaço e atribui seu programa como owner; sua lógica escreve um discriminador e campos padrão; instruções posteriores rejeitam contas que já mostram esse discriminador ou que falham guardas de "já inicializado". Prefira create-then-init explícito quando a UX permitir; trate init_if_needed como alto risco a menos que a verificação de inicializado seja à prova de falhas.
A ordem importa para segurança: inicialize estrutura antes da conta conter valor significativo. Um ataque de reinit reescreve autoridade ou ponteiros de cofre após fundos chegarem. Discriminadores únicos por tipo de conta previnem confusão de tipo (passar config onde se espera cofre).
Padrões de taxa e tesouraria
Taxas de protocolo retiram basis points (bps) de um valor, depois direcionam a taxa para uma tesouraria (geralmente ATA de propriedade PDA por mint) e o restante para o destinatário. Toda aritmética deve usar operações checked; limite fee_bps configurável em set-config (por exemplo nunca acima de um máximo documentado); documente arredondamento (floor em direção ao protocolo ou usuário). Verifique mint e owner da tesouraria em todo caminho de taxa para que taxas não caiam em ATA errada.
amount
|
+-- fee = amount * bps / 10_000 --> treasury
|
+-- net = amount - fee --> user / counterpartyCobre taxas em ações econômicas bem-sucedidas (ou em entradas claramente definidas), não em meios-caminhos falhos que deixam usuários cobrados sem a mudança de estado prometida.
Máquinas de estado on-chain
Objetos de ciclo de vida (ordens, escrows, leilões, propostas) armazenam um enum de status. Cada instrução implementa uma transição ou consulta um mapa de transição compartilhado: somente Open -> Filled ou Open -> Cancelled, nunca Filled -> Open a menos que você projete intencionalmente um caminho raro de admin. Estados terminais aceitam close (ou limpeza similar) e rejeitam mutações de negócio. Emita eventos nas transições para que indexadores e clientes não adivinhem a partir de campos parciais.
Centralize verificações de status em helpers para que uma instrução esquecida não possa contornar a máquina. Mantenha o enum pequeno; reserve valores ou campos version se esperar estados futuros.
Tempo e clock
Leia Clock::get() (ou a conta sysvar Clock) para tempo vinculante. Use unix_timestamp para prazos humanos (cliffs de vesting, expiração de escrow); use slot quando se importar com progresso da chain relativo ao cronograma do líder; use epoch para regras no estilo período de stake. Timestamps de validadores têm deriva limitada, então designs de produção usam períodos de carência em vez de precisão de um slot para UX de relógio de parede. Nunca confie apenas em um "agora" fornecido pelo cliente; armazene prazos no init e compare-os ao Clock em cada instrução com portão.
Como os padrões se empilham em um fluxo
Liberação de escrow é uma instância completa do blueprint:
- Init criou escrow com disc, partes, valor, prazo, status
Funded. - Controle de acesso exige vendedor ou comprador (ou regras PDA) como signatário dependendo de release vs cancel.
- Máquina de estado permite somente
Funded -> ReleasedouFunded -> Cancelled/Expired. - Clock rejeita release após prazo se cancel-on-expiry é a regra (ou o inverso para time locks).
- Taxas (se houver) dividem no release; tesouraria e contas de token verificadas; PDA de cofre assina a transferência.
Pule qualquer camada e o resto é teatro: uma fórmula de taxa perfeita não ajuda se reinit redefine o campo de autoridade.
Considerações Avançadas e Aplicações
Padrões escalam de um programa contador a um venue DeFi; a diferença é o quão estritamente você os compõe e como troca poder de admin por imutabilidade.
| Cluster de padrão | Força | Fraqueza | Melhor encaixe |
|---|---|---|---|
| Controle de acesso / autoridades | Papéis claros; admin rotacionável; custódia PDA | Admin excessivamente amplo é sumidouro de confiança | Config, pause, cofres, autoridade de mint |
| Guardas de init / reinit | Estado tipado estável; impede substituição de conta | Instruções extras e disciplina de rent | Qualquer conta de propriedade do programa que mantém valor ou papéis |
| Taxa / tesouraria | Receita de protocolo transparente; trilha de auditoria on-chain | Bugs de matemática e bps ilimitados prejudicam usuários | Mercados, mints com taxas, skims de saque |
| Máquinas de estado | Ciclo de vida auditável; bloqueia replay de trabalho concluído | Estados excessivamente finos incham lógica | Escrow, ordens, leilões, governança |
| Tempo / clock | Prazos aplicáveis sem cron off-chain | Deriva e variância de slot precisam de carência | Vesting, expiração, cooldowns, epochs |
Flags de pause são uma máquina de estado estreita sobre todo o protocolo: admin define paused, instruções de usuário falham enquanto true, resume é um privilégio separado. Combine pause com separação de papéis para que uma chave de operador quente não possa também mudar destinatários de taxa.
Design seguro para upgrade interage com esses padrões: campos version e espaço reservado permitem adicionar papéis ou estados sem quebrar discriminadores; veja design de dados orientado a upgrade nesta seção quando planejar contas de longa duração. Cofre e escrow são especializações de taxa + autoridade PDA + máquina de estado + clock aplicadas à custódia.
Para revisões de design, liste cada instrução e preencha: signatários e papéis obrigatórios, suposições de init, status antes/depois, predicados de clock e sumidouros de valor. Lacunas nessa matriz são a superfície de exploit usual.
Equívocos Comuns
- "O runtime aplica meu papel de admin." Ele aplica propriedade do programa sobre dados. Papéis de admin e operador existem somente se seu programa verifica pubkeys armazenadas e signatários em cada instrução privilegiada.
- "Init uma vez no deploy é suficiente para todas as contas." Deploy inicializa o binário do programa, não cofres ou ordens de usuário. Cada conta de propriedade do programa precisa de seu próprio caminho create/init e guarda de reinit.
- "Taxas podem usar matemática inteira comum." Multiply/divide não verificados e bps ilimitados são vetores clássicos de dreno. Use matemática checked e valide limites de taxa quando config mudar.
- "Alguns booleans são o mesmo que uma máquina de estado." Flags independentes permitem combinações ilegais (
filledecancelledambos true). Enum mais verificações de transição tornam estados ilegais inrepresentáveis ou rejeitados. - "Timestamps do cliente são ok se a UI for honesta." Qualquer um pode construir uma transação. Prazos e cooldowns devem comparar com Clock (ou slot) on-chain.
- "Uma chave de admin global é mais simples e portanto mais segura." É mais simples até perda ou comprometimento de chave. Multisig, transferência em duas etapas e privilégios mínimos reduzem raio de explosão.
FAQs
O que é um "padrão de programa on-chain" nesta seção?
Uma forma reutilizável de estruturar contas e instruções para que um programa Solana resolva repetidamente controle de acesso, inicialização, roteamento de valor, ciclo de vida ou tempo de forma revisável e segura.
Como owner e autoridade diferem?
Owner é o ID de programa nos metadados da conta (quem pode escrever data). Autoridade é geralmente uma pubkey ou PDA de nível de app armazenada em data que seu programa verifica contra um signatário ou usa com invoke_signed.
Por que a inicialização é tratada como fronteira de segurança?
Antes do init, o buffer é não tipado ou com forma de atacante. Sem discriminadores e guardas de reinit, um atacante pode redefinir autoridades ou confundir tipos de conta após valor ter sido depositado.
Quando devo usar init_if_needed?
Somente quando a verificação de inicializado é estrita (discriminador ou equivalente) e reinit é impossível. Prefira init simples ou create separado quando a conta deve existir exatamente uma vez sob sementes conhecidas.
Como taxas de protocolo devem ser implementadas?
Calcule taxa com matemática checked a partir de bps limitado, verifique a conta de tesouraria (mint, owner, sementes PDA), transfira taxa depois net (ou documente ordem), e nunca permita que config defina taxas ilimitadas.
Onde uma tesouraria deve ficar?
Tipicamente uma conta de token controlada por PDA dedicada (ou PDA SOL) por mint ou classe de ativo, separada de cofres de usuário, para que saques e acúmulo de taxas permaneçam auditáveis e com portão de papel.
O que pertence a uma máquina de estado on-chain?
Qualquer ciclo de vida multi-etapa onde algumas ações são ilegais após outras: open/fill/cancel, fund/release/refund, propose/vote/execute. Codifique status, valide transições, trate terminais como fechados para operações de negócio.
Cada transição deve ser sua própria instrução?
Frequentemente sim para clareza e menor privilégio (fill vs cancel como handlers separados). Helpers compartilhados ainda podem centralizar a correspondência de status para que nenhum caminho pule a tabela.
Para que o sysvar Clock é usado?
Tempo vinculante on-chain: unix_timestamp para prazos de relógio de parede, slot para progresso relativo à chain, epoch para regras no escopo de epoch. Leia no programa; não confie em "agora" apenas do cliente.
Slots ou timestamps unix para expiração de escrow?
Prefira timestamps unix quando usuários raciocinam em tempo de calendário; use slots quando se importar com progresso discreto da chain. Permita pequena carência onde deriva de relógio de validador importa para UX.
Como PDAs se encaixam nesses padrões?
PDAs dão aos programas endereços determinísticos e assinatura sem chaves privadas. Elas alimentam autoridades de cofre, contas de config, tesourarias de taxa e sementes de estado por usuário que clientes podem derivar offline.
O que é transferência de admin em duas etapas?
Admin atual propõe um pending_admin; a nova chave deve assinar uma instrução accept. Isso previne que um typo entregue permanentemente o controle a um endereço inutilizável.
Posso pular máquinas de estado para um cofre simples?
Se o cofre só mantém saldo e nunca tem fases, saldo mais autoridade pode bastar. Assim que você tem caminhos fund/release/cancel/expire, um status explícito previne ações contraditórias.
Como esses padrões se relacionam com constraints do Anchor?
Anchor codifica muitos deles: constraints has_one e signatário para controle de acesso, init / seeds / space para inicialização, e tipos de conta para propriedade. Você ainda projeta as regras de produto; macros implementam verificações que você especifica.
O que um checklist de revisão de design deve incluir?
Para cada instrução: signatários e papéis obrigatórios, suposições de init, transições de status, predicados de clock, destinos de taxa e tesouraria, e quais contas devem ser PDAs com sementes verificadas.
Relacionados
- Noções Básicas de Padrões de Programa - exemplos práticos de padrões e blocos de construção
- Controle de Acesso e Autoridades - papéis, verificações de signatário, rotação de admin
- Padrões de Inicialização de Conta - init, guardas de reinit, discriminadores
- Padrões de Taxa e Tesouraria - taxas bps, tesourarias, matemática verificada
- Máquinas de Estado On-Chain - enums de status e transições legais
- Tempo e Clock - sysvar Clock, slots, prazos, cooldowns
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.