Está a atualizar da versão 1.x? Consulte o guia de migração para a 2.0.
Instalação
Instale o pacote umbrella ou signatários individuais conforme necessário:
# Umbrella package (includes all signers)pnpm add @solana/keychain# Or install individual packagespnpm add @solana/keychain-core # Core interfaces (required for custom signers)pnpm add @solana/keychain-memory # Local keypair signerpnpm add @solana/keychain-vault # HashiCorp Vaultpnpm add @solana/keychain-aws-kms # AWS KMSpnpm add @solana/keychain-gcp-kms # GCP KMSpnpm add @solana/keychain-privy # Privypnpm add @solana/keychain-turnkey # Turnkeypnpm add @solana/keychain-fireblocks # Fireblockspnpm add @solana/keychain-cdp # Coinbase Developer Platformpnpm add @solana/keychain-crossmint # Crossmintpnpm add @solana/keychain-dfns # Dfnspnpm add @solana/keychain-openfort # Openfortpnpm add @solana/keychain-para # Parapnpm add @solana/keychain-utila # Utilapnpm add @solana/keychain-fordefi # Fordefi# Kit client plugins (keychainSigner / keychainPayer / keychainIdentity)pnpm add @solana/keychain-kit-plugin
Requer @solana/kit 8.1 ou posterior. As transações v1
(createTransactionMessage({ version: 1 })) são suportadas, exceto pela Fireblocks
com useProgramCall: true, que aceita apenas legacy e v0.
Uso Básico
Factory Unificada (Recomendada para aplicações que desejam suportar múltiplos signatários)
Use createKeychainSigner com uma configuração discriminada para criar qualquer
backend:
import { createKeychainSigner } from "@solana/keychain";import { signTransactionWithSigners } from "@solana/signers"; // requires @solana/signers >= 8.1const signer = await createKeychainSigner({backend: "privy",appId: "your-app-id",appSecret: "your-app-secret",walletId: "your-wallet-id"});// Sign an already-compiled transactionconst signedTx = await signTransactionWithSigners([signer],compiledTransaction);
Ou instale um pacote de signatário individual para uma pegada de dependências menor:
import { createPrivySigner } from "@solana/keychain-privy";const signer = await createPrivySigner({appId: "your-app-id",appSecret: "your-app-secret",walletId: "your-wallet-id"});
Usando com Mensagens de Transação
Todos os backends, exceto Crossmint e o modo nativo da Fordefi, devolvem um
SolanaTransactionSigner, que é um TransactionPartialSigner do Kit e funciona
com os auxiliares de partial-signer de @solana/signers apresentados abaixo. Os backends com broadcast gerido
são abordados em Capacidades do Signer.
import type { SolanaTransactionSigner } from "@solana/keychain-core";import { signTransactionMessageWithSigners } from "@solana/signers";import {createTransactionMessage,setTransactionMessageFeePayerSigner,pipe} from "@solana/kit";async function signWithKeychain(signer: SolanaTransactionSigner) {// Check availability (useful for remote signers)if (!(await signer.isAvailable())) {throw new Error("Signer offline");}// Use with @solana/kit transaction builderconst transaction = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(signer, tx)// ... set a lifetime and add instructions);// Sign with the standard signers APIconst signedTx = await signTransactionMessageWithSigners(transaction);return signedTx;}
Assinar e enviar com qualquer Signer
signAndSendTransaction de @solana/keychain-core coloca uma transação na
blockchain, independentemente do formato do signer. Os signers de envio fazem o broadcast através do respetivo
provedor; qualquer outro formato utiliza o sendTransaction que fornecer (o core não tem
dependência de RPC):
import { signAndSendTransaction } from "@solana/keychain-core";const signature = await signAndSendTransaction(signer, transaction, {sendTransaction: (tx) =>sendAndConfirmTransaction(tx, { commitment: "confirmed" })});
Usando com um Kit Client (plugin keychainSigner)
@solana/keychain-kit-plugin instala um keychain signer diretamente em um
Kit client, configurando-o como payer e
identity para que você pule a configuração manual de pipe mostrada acima:
import { createClient } from "@solana/kit";import { keychainSigner } from "@solana/keychain-kit-plugin";const client = await createClient().use(keychainSigner({backend: "privy",appId: process.env.PRIVY_APP_ID!,appSecret: process.env.PRIVY_APP_SECRET!,walletId: process.env.PRIVY_WALLET_ID!}));client.payer; // SolanaTransactionSigner, also a Kit TransactionPartialSignerclient.identity; // same signer instance
O plugin aceita a mesma configuração com etiqueta de backend que createKeychainSigner,
exceto Crossmint e o modo nativo da Fordefi (com chain definido). Esses backends reescrevem
a transação no lado do servidor, pelo que não podem atuar como payer ou
identity do cliente; o plugin rejeita-os em tempo de compilação e em tempo de execução. Apenas o
backend para o qual encaminha é incluído no pacote (os pacotes de backend são carregados através de
import() dinâmico).
Use keychainPayer ou keychainIdentity para definir apenas um papel e
misturar backends em um único client:
import { createClient } from "@solana/kit";import { keychainIdentity, keychainPayer } from "@solana/keychain-kit-plugin";const client = await createClient().use(keychainPayer({backend: "memory",privateKeyPath: `${process.env.HOME}/.config/solana/id.json`})).use(keychainIdentity({ backend: "turnkey", ...turnkeyConfig }));
Já tem um SolanaTransactionSigner? É um TransactionPartialSigner do Kit
válido, pelo que pode instalá-lo com
@solana/kit-plugin-signer
em vez disso (pnpm add @solana/kit-plugin-signer):
import { createClient } from "@solana/kit";import { signer } from "@solana/kit-plugin-signer";import { createKeychainSigner } from "@solana/keychain";const mySigner = await createKeychainSigner({backend: "vault",...vaultConfig});const client = createClient().use(signer(mySigner));
Cancelamento e broadcasts não confirmados
signTransactions, modifyAndSignTransactions, signAndSendTransactions e
signMessages aceitam todos { abortSignal } como segundo argumento. Abortar
interrompe a espera; não desfaz o trabalho que um provedor já aceitou.
No modo nativo automático da Crossmint e da Fordefi, uma falha não significa que nada
tenha sido processado. Quando o provedor pode ter aceitado a transação, o código de erro é
SignerErrorCode.BROADCAST_UNCONFIRMED ("SIGNER_BROADCAST_UNCONFIRMED").
context.providerTransactionId é definido quando a criação foi aceite, e
context.idempotencyKey contém a chave derivada da mensagem. Reenviar a
transação com bytes idênticos é seguro; uma transação reconstruída (por exemplo, com um novo
blockhash) é uma nova transferência. providerMayHaveAccepted(error) e
providerStatus(error) de @solana/keychain-core ajudam a classificar o erro.
Concilie com o provedor antes de tentar novamente.
Configuração de Backend
Memória
Indique exatamente um de keyPair, privateKey, privateKeyString ou
privateKeyPath:
import { createMemorySigner } from "@solana/keychain-memory";const signer = await createMemorySigner({privateKeyPath: "/path/to/id.json"});
HashiCorp Vault
import { createVaultSigner } from "@solana/keychain-vault";const signer = createVaultSigner({vaultAddr: "https://vault.example.com:8200",vaultToken: "hvs.xxxxx",keyName: "my-solana-key",publicKey: "base58_public_key"});
AWS KMS
import { createAwsKmsSigner } from "@solana/keychain-aws-kms";const signer = createAwsKmsSigner({keyId: "alias/my-solana-key",publicKey: "base58_public_key",region: "us-east-1" // optional});
GCP KMS
import { createGcpKmsSigner } from "@solana/keychain-gcp-kms";const signer = createGcpKmsSigner({keyName:"projects/my-project/locations/us-east1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1",publicKey: "base58_public_key"});
Privy
import { createPrivySigner } from "@solana/keychain-privy";const signer = await createPrivySigner({appId: "app_id",appSecret: "app_secret",walletId: "wallet_id"});
Turnkey
import { createTurnkeySigner } from "@solana/keychain-turnkey";const signer = createTurnkeySigner({apiPublicKey: "api_public_key",apiPrivateKey: "api_private_key",organizationId: "org_id",privateKeyId: "private_key_id",publicKey: "base58_public_key"});
Fireblocks
import { createFireblocksSigner } from "@solana/keychain-fireblocks";const signer = await createFireblocksSigner({apiKey: "api_key",privateKeyPem: "-----BEGIN RSA PRIVATE KEY-----\n...",vaultAccountId: "0",assetId: "SOL" // or "SOL_TEST" for devnet});
CDP (Coinbase Developer Platform)
import { createCdpSigner } from "@solana/keychain-cdp";const signer = await createCdpSigner({cdpApiKeyId: "api_key_id",cdpApiKeySecret: "api_key_secret",cdpWalletSecret: "wallet_secret",address: "base58_address",network: "solana" // or "solana-devnet"; required for address lookup tables});
Crossmint
import { createCrossmintSigner } from "@solana/keychain-crossmint";const signer = await createCrossmintSigner({apiKey: "api_key",walletLocator: "wallet_locator"});
A Crossmint é apenas de envio. A sua API executa sempre uma transação aprovada
no lado do servidor e patrocina o gas, pelo que o signer expõe apenas
signAndSendTransactions(): não tem signTransactions() nem signMessages(),
e não pode ser instalado com @solana/keychain-kit-plugin. A assinatura devolvida
identifica a transação que a Crossmint processou.
const [signature] = await signer.signAndSendTransactions([transaction]);// or: await signAndSendTransactionMessageWithSigners(transactionMessage);
Dfns
import { createDfnsSigner } from "@solana/keychain-dfns";const signer = await createDfnsSigner({authToken: "auth_token",credId: "cred_id",privateKeyPem: "-----BEGIN EC PRIVATE KEY-----\n...",walletId: "wallet_id"});
Openfort
import { createOpenfortSigner } from "@solana/keychain-openfort";const signer = await createOpenfortSigner({secretKey: "sk_...",walletSecret: "wallet_secret",accountId: "account_id"});
Para
import { createParaSigner } from "@solana/keychain-para";const signer = await createParaSigner({apiKey: "api_key",walletId: "wallet_id"});
Utila
A Utila apenas assina transações; não tem signMessages().
import { createUtilaSigner } from "@solana/keychain-utila";const signer = await createUtilaSigner({serviceAccountEmail: "sa@example.com",serviceAccountPrivateKeyPem: "-----BEGIN PRIVATE KEY-----\n...",vaultId: "vault_id",walletId: "wallet_id",network: "networks/solana-devnet"});
Fordefi
A Fordefi tem três modos de assinatura, definidos na construção por chain e
pushMode. Cada modo expõe exatamente um método de transação, e os três assinam
mensagens:
| Modo | Configuração | Tipo | Método |
|---|---|---|---|
| Black box | chain não definido | FordefiBlackBoxSigner | signTransactions() |
| Nativo automático | chain definido, pushMode omitido ou "auto" | FordefiNativeSigner | signAndSendTransactions() |
| Nativo manual | chain definido, pushMode: "manual" | FordefiNativeManualSigner | modifyAndSignTransactions() |
Os pedidos à API são autenticados com uma assinatura de pedido ECDSA P-256: forneça a
chave de assinatura como privateKeyPem, ou mantenha-a num KMS/HSM passando um
requestSigner.
Modo nativo automático (recomendado para uso on-chain): defina chain e a Fordefi
assina e faz o broadcast da própria transação. Como a Fordefi pode substituir o
blockhash ou as taxas antes de assinar, o signer é um TransactionSendingSigner
do Kit: utilize-o com
signAndSendTransactionMessageWithSigners, não com o fluxo de partial-signer apresentado
acima. O modo nativo automático aceita apenas uma transação não assinada cujo único signatário
necessário seja o vault, e não pode ser utilizado com @solana/keychain-kit-plugin.
import { createFordefiSigner } from "@solana/keychain-fordefi";import { signAndSendTransactionMessageWithSigners } from "@solana/signers";import { readFileSync } from "node:fs";const signer = await createFordefiSigner({accessToken: process.env.FORDEFI_ACCESS_TOKEN!,vaultId: process.env.FORDEFI_VAULT_ID!,publicKey: process.env.FORDEFI_PUBLIC_KEY!, // Solana vault address (base58)privateKeyPem: readFileSync("./secret/private.pem", "utf8"),chain: "solana_devnet", // or "solana_mainnet"fee: { type: "custom", priority_fee: "1000" } // optional});const signature =await signAndSendTransactionMessageWithSigners(transactionMessage);
Modo nativo manual: defina chain e pushMode: "manual". A Fordefi reescreve
a mensagem (blockhash recente, instruções de taxa do Compute Budget) e assina-a,
mas não faz o broadcast. modifyAndSignTransactions() devolve a transação que
a Fordefi assinou, a qual substitui a que submeteu: continue sempre a partir da
transação devolvida. A Fordefi tem de ser o pagador das taxas e tem de assinar antes de qualquer
outro signatário necessário. A reescrita não é comparada com o original, por isso inspecione o resultado antes de
fazer o broadcast.
import { createFordefiSigner } from "@solana/keychain-fordefi";import { readFileSync } from "node:fs";const signer = await createFordefiSigner({accessToken: process.env.FORDEFI_ACCESS_TOKEN!,vaultId: process.env.FORDEFI_VAULT_ID!,publicKey: process.env.FORDEFI_PUBLIC_KEY!,privateKeyPem: readFileSync("./secret/private.pem", "utf8"),chain: "solana_devnet",pushMode: "manual"});const [signedTransaction] = await signer.modifyAndSignTransactions([transaction]);
Modo caixa-preta: omita chain. O Fordefi assina bytes brutos e não transmite a transação; você mesmo monta e envia a transação. Use este modo com um cofre caixa-preta do Fordefi.
import { createFordefiSigner } from "@solana/keychain-fordefi";import { readFileSync } from "node:fs";const signer = await createFordefiSigner({accessToken: process.env.FORDEFI_ACCESS_TOKEN!,vaultId: process.env.FORDEFI_BB_VAULT_ID!,publicKey: process.env.FORDEFI_BB_PUBLIC_KEY!,privateKeyPem: readFileSync("./secret/private.pem", "utf8")});
Capacidades do Signer
@solana/keychain-core define uma interface por cada formato de signer do Kit. Cada uma acrescenta
isAvailable(): Promise<boolean> à interface correspondente de @solana/signers:
SolanaTransactionSigner(TransactionPartialSignerdo Kit): devolve assinaturas para uma transação que lhe pertence.SolanaModifyingSigner(TransactionModifyingSignerdo Kit): reescreve a transação, assina-a e devolve a nova transação sem fazer broadcast.SolanaSendingSigner(TransactionSendingSignerdo Kit): assina e faz broadcast através do respetivo provedor.SolanaMessageSigner(MessagePartialSignerdo Kit): assina mensagens off-chain. É independente dos formatos de transação.
SolanaSigner é a união dos três formatos de transação. O tipo de retorno de cada factory
indica o formato exato do respetivo backend. Quando a configuração só é conhecida em
tempo de execução, restrinja o tipo com isSolanaTransactionSigner, isSolanaModifyingSigner,
isSolanaSendingSigner ou isSolanaMessageSigner. signerCapabilities(signer)
indica as quatro capacidades de uma só vez.
| Backend | Formato |
|---|---|
| memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (black box) | SolanaTransactionSigner & SolanaMessageSigner |
| utila | SolanaTransactionSigner (sem assinatura de mensagens) |
| fordefi (nativo manual) | SolanaModifyingSigner & SolanaMessageSigner |
| fordefi (nativo automático) | SolanaSendingSigner & SolanaMessageSigner |
| crossmint | SolanaSendingSigner (sem assinatura de mensagens) |
import {isSolanaSendingSigner,isSolanaTransactionSigner} from "@solana/keychain-core";if (isSolanaSendingSigner(signer)) {await signer.signAndSendTransactions([transaction]);} else if (isSolanaTransactionSigner(signer)) {await signer.signTransactions([transaction]);}
Criando Signers Personalizados
Implemente as interfaces de capacidades que o seu backend suporta:
import type {SolanaMessageSigner,SolanaTransactionSigner} from "@solana/keychain-core";import type { Address } from "@solana/addresses";import type {MessagePartialSignerConfig,SignableMessage,TransactionPartialSignerConfig} from "@solana/signers";import type { Transaction } from "@solana/transactions";class MyCustomSigner implements SolanaTransactionSigner, SolanaMessageSigner {constructor(readonly address: Address) {}async isAvailable(): Promise<boolean> {return await myBackend.healthCheck();}async signMessages(messages: readonly SignableMessage[],config?: MessagePartialSignerConfig) {return await myBackend.signMessages(messages, config?.abortSignal);}async signTransactions(transactions: readonly Transaction[],config?: TransactionPartialSignerConfig) {return await myBackend.signTransactions(transactions, config?.abortSignal);}}
Implemente apenas o que o seu backend suporta. O Kit classifica os signers pelos métodos que existem, por isso nunca adicione um método que apenas lança uma exceção.
Consulte o guia de Adição de Signers para integrar serviços adicionais de gerenciamento de chaves.
Is this page helpful?