Keypairs e Endereços
A identidade na Solana é construída sobre keypairs Ed25519. A chave pública é o endereço da conta; a chave secreta autoriza transações. Entender papéis de assinatura previne bugs de autorização e vazamentos de chave.
Receita
Cartão de receita de referência rápida - pronto para copiar.
solana-keygen new --outfile ./keypair.json --no-bip39-passphrase
solana-keygen pubkey ./keypair.jsonimport { createKeyPairSignerFromBytes, address } from "@solana/kit";
import { readFileSync } from "node:fs";
const secret = Uint8Array.from(JSON.parse(readFileSync("./keypair.json", "utf8")));
const signer = await createKeyPairSignerFromBytes(secret);
console.log(signer.address);Quando usar:
- Criar carteiras para desenvolvimento e testes
- Carregar signers em montadores de transação no servidor
- Verificar que um account meta está marcado
isSigner: true - Derivar PDAs (endereços sem chaves secretas) para contas com owner programa
- Validar strings de endereço base58 antes de chamadas RPC
Exemplo Prático
import {
createSolanaRpc,
createKeyPairSignerFromBytes,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
signTransactionMessageWithSigners,
getSignatureFromTransaction,
pipe,
} from "@solana/kit";
import { getTransferSolInstruction } from "@solana-program/system";
import { readFileSync } from "node:fs";
const rpc = createSolanaRpc("https://api.devnet.solana.com");
// Carregar signer do arquivo keypair
const secret = Uint8Array.from(
JSON.parse(readFileSync(process.env.HOME + "/.config/solana/id.json", "utf8")),
);
const feePayer = await createKeyPairSignerFromBytes(secret);
const recipient = await createKeyPairSignerFromBytes(
crypto.getRandomValues(new Uint8Array(64)),
);
const { value: blockhash } = await rpc.getLatestBlockhash().send();
const message = pipe(
createTransactionMessage({ version: 0 }),
(m) => setTransactionMessageFeePayer(feePayer.address, m),
(m) => setTransactionMessageLifetimeUsingBlockhash(blockhash, m),
(m) =>
appendTransactionMessageInstruction(
getTransferSolInstruction({
source: feePayer.address,
destination: recipient.address,
amount: 1_000_000n,
}),
m,
),
);
const signed = await signTransactionMessageWithSigners(message, [feePayer]);
const signature = getSignatureFromTransaction(signed);
console.log("Signature:", signature);O que isso demonstra:
- Arquivos keypair armazenam array de 64 bytes: seed de 32 bytes + chave pública de 32 bytes
- O fee payer deve assinar porque autoriza dedução de lamports
createKeyPairSignerFromBytesenvolve o keypair como signer Kit- Destinatário não assina - apenas a autoridade da conta de origem assina transferências
Aprofundamento
Como Funciona
- Ed25519 produz chave pública de 32 bytes a partir de seed de 32 bytes
- A Solana armazena a chave secreta de 64 bytes (seed + pubkey) em arquivos JSON de keypair
- Transações listam contas com flags
isSignereisWritable - O runtime verifica assinaturas Ed25519 contra signatários declarados antes da execução
Papéis de Signatário
| Papel | Assina? | Conta Típica |
|---|---|---|
| Fee payer | Sim | Primeiro signatário; paga taxa de transação |
| Authority | Sim | Owner de lamports ou conta de token |
| Program | Não | Conta executável invocada pela instrução |
| PDA | Não (programa assina via seeds) | Endereço derivado sem chave privada |
Notas Rust
use anchor_lang::prelude::*;
#[derive(Accounts)]
pub struct UpdateData<'info> {
#[account(mut, signer)]
pub authority: Signer<'info>,
#[account(mut, has_one = authority)]
pub data_account: Account<'info, MyData>,
}- Constraint
Signer<'info>verifica que a conta assinou a transação - PDAs usam
invoke_signedcom seeds em vez de chave privada
Erros Comuns
- Commit de keypair.json no git - dreno instantâneo de fundos na mainnet. Correção: use
.gitignore, variáveis de ambiente ou hardware wallets em produção. - Flag
isSignerausente - transação rejeitada em runtime. Correção: marque toda conta signatária como signer nas instruções. - Assumir que pubkey é igual a PDA - PDAs estão off-curve e não têm chave secreta. Correção: derive PDAs com
find_program_addresse deixe o programa assinar. - Keypair errado para o cluster - keypair funciona em todo lugar mas contas são específicas de cluster. Correção: arquivos de chave separados para dev e prod.
- Confusão 64 bytes vs 32 bytes - arquivos keypair Solana têm 64 bytes, seeds têm 32. Correção: use
solana-keygenou helpers Kit, não fatiamento bruto de bytes. - Fee payer não é primeiro signatário - alguns clientes exigem fee payer no índice 0. Correção: defina fee payer primeiro na lista de contas.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
solana-keygen CLI | Carteiras dev rápidas | Gestão de chaves em produção |
| Hardware wallet (Ledger) | Assinatura mainnet | Assinatura automatizada servidor em alto throughput |
Signers @solana/kit | Montagem de transação TypeScript | Lógica de programa on-chain |
| PDAs com seeds | Contas controladas pelo programa | Carteiras de usuário |
FAQs
Qual algoritmo a Solana usa para assinaturas?
Ed25519. Chaves públicas têm 32 bytes, codificadas em base58 para exibição.
O que há em um arquivo JSON de keypair Solana?
Um array JSON de 64 bytes: seed secreto de 32 bytes concatenado com chave pública de 32 bytes.
Chave pública é o mesmo que endereço?
Sim. Os termos são intercambiáveis - a chave pública Ed25519 de 32 bytes codificada em base58.
Dois keypairs diferentes podem compartilhar endereço?
Não. Cada keypair válido mapeia para exatamente uma chave pública/endereço.
O que é Program Derived Address (PDA)?
Endereço derivado de seeds e program ID. Não tem chave privada - o programa assina via invoke_signed.
Quantos signatários uma transação pode ter?
Até 16 assinaturas por transação no formato atual. O fee payer é sempre um deles.
O destinatário assina transferência de SOL?
Não. Apenas a autoridade da conta de origem (e fee payer) deve assinar.
Como validar string de endereço base58?
import { address } from "@solana/kit";
try {
address("invalid!");
} catch {
console.log("Invalid address");
}Posso recuperar keypair só da chave pública?
Não. A chave secreta não pode ser derivada da pública (função unidirecional).
Qual é o endereço do System Program?
11111111111111111111111111111111 - trata criação de conta e transferências SOL.
Apps server-side devem usar o mesmo keypair padrão da CLI?
Não. Use keypairs de serviço dedicados com saldos mínimos financiados e controles de acesso rigorosos.
Como hardware wallet assina?
A chave privada nunca sai do dispositivo. O app envia a mensagem da transação; o dispositivo retorna a assinatura.
Relacionado
- Noções Básicas de Solana - exemplo de geração de keypair
- Como uma Transação Flui - onde a assinatura se encaixa
- Carteiras e Explorers - ferramentas de carteira
Versões da stack: 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.