¿Actualizas desde la versión 1.x? Consulta la guía de migración a 2.0.
Instalación
Instala el paquete principal o los firmantes individuales según sea necesario:
# 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
Requiere @solana/kit 8.1 o posterior. Las transacciones v1
(createTransactionMessage({ version: 1 })) son compatibles, excepto con Fireblocks
con useProgramCall: true, que solo acepta legacy y v0.
Uso Básico
Fábrica Unificada (Recomendado para aplicaciones que desean soportar múltiples firmantes)
Usa createKeychainSigner con una configuración discriminada para crear
cualquier 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);
O instala un paquete de firmante individual para reducir las dependencias:
import { createPrivySigner } from "@solana/keychain-privy";const signer = await createPrivySigner({appId: "your-app-id",appSecret: "your-app-secret",walletId: "your-wallet-id"});
Uso con Mensajes de Transacción
Todos los backends, excepto Crossmint y el modo nativo de Fordefi, devuelven un
SolanaTransactionSigner, que es un TransactionPartialSigner de Kit y funciona
con los helpers de firmante parcial de @solana/signers que se muestran abajo. Los
backends con difusión gestionada se tratan en Capacidades del firmante.
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;}
Firmar y enviar con cualquier firmante
signAndSendTransaction de @solana/keychain-core lleva una transacción a la
cadena sea cual sea la forma del firmante. Los firmantes de envío la transmiten a través de su
proveedor; cualquier otra forma usa el sendTransaction que le pases (core no tiene
dependencia de RPC):
import { signAndSendTransaction } from "@solana/keychain-core";const signature = await signAndSendTransaction(signer, transaction, {sendTransaction: (tx) =>sendAndConfirmTransaction(tx, { commitment: "confirmed" })});
Uso con un cliente Kit (plugin keychainSigner)
@solana/keychain-kit-plugin instala un firmante de cadena de claves
directamente en un cliente Kit, conectándolo
como payer y identity, por lo que omites la configuración manual de pipe
mostrada anteriormente:
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
El plugin acepta la misma configuración etiquetada por backend que createKeychainSigner,
excepto Crossmint y el modo nativo de Fordefi (con chain definido). Esos backends reescriben
la transacción en el servidor, por lo que no pueden actuar como payer o
identity del cliente; el plugin los rechaza en tiempo de compilación y de ejecución. Solo se incluye el
backend al que se despacha (los paquetes de backend se cargan mediante import() dinámico).
Usa keychainPayer o keychainIdentity para asignar un único rol, y combina
backends en un solo cliente:
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 }));
¿Ya tienes un SolanaTransactionSigner? Es un TransactionPartialSigner válido de Kit,
por lo que puedes instalarlo con
@solana/kit-plugin-signer
en su lugar (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));
Cancelación y difusiones sin confirmar
signTransactions, modifyAndSignTransactions, signAndSendTransactions y
signMessages aceptan { abortSignal } como segundo argumento. Abortar
detiene la espera; no deshace el trabajo que un proveedor ya haya aceptado.
En el modo automático nativo de Crossmint y Fordefi, un fallo no significa que no se haya
registrado nada. Cuando el proveedor pueda haber aceptado la transacción, el código de error es
SignerErrorCode.BROADCAST_UNCONFIRMED ("SIGNER_BROADCAST_UNCONFIRMED").
context.providerTransactionId se establece cuando se aceptó la creación, y
context.idempotencyKey contiene la clave derivada del mensaje. Reenviar la
transacción idéntica byte a byte es seguro; una reconstruida (por ejemplo, con un nuevo
blockhash) es una transferencia nueva. providerMayHaveAccepted(error) y
providerStatus(error) de @solana/keychain-core ayudan a clasificar el error.
Concilia con el proveedor antes de reintentar.
Configuración del backend
Memoria
Proporciona exactamente uno de keyPair, privateKey, privateKeyString o
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"});
Crossmint es solo de envío. Su API siempre ejecuta una transacción aprobada en el
servidor y patrocina el gas, por lo que el firmante expone únicamente
signAndSendTransactions(): no tiene signTransactions() ni signMessages(),
y no se puede instalar con @solana/keychain-kit-plugin. La firma devuelta
identifica la transacción que Crossmint registró en la cadena.
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
Utila solo firma transacciones; no tiene 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
Fordefi tiene tres modos de firma, fijados en la construcción mediante chain y
pushMode. Cada modo expone exactamente un método de transacción, y los tres firman
mensajes:
| Modo | Configuración | Tipo | Método |
|---|---|---|---|
| Caja negra | chain sin definir | FordefiBlackBoxSigner | signTransactions() |
| Automático nativo | chain definido, pushMode omitido o "auto" | FordefiNativeSigner | signAndSendTransactions() |
| Manual nativo | chain definido, pushMode: "manual" | FordefiNativeManualSigner | modifyAndSignTransactions() |
Las solicitudes a la API se autentican con una firma de solicitud ECDSA P-256: proporciona la
clave de firma como privateKeyPem, o guárdala en un KMS/HSM pasando un
requestSigner.
Modo automático nativo (recomendado para uso on-chain): define chain y Fordefi
firma y transmite la transacción por sí mismo. Como Fordefi puede reemplazar el
blockhash o las comisiones antes de firmar, el firmante es un
TransactionSendingSigner de Kit: úsalo con
signAndSendTransactionMessageWithSigners, no con el flujo de firmante parcial mostrado
arriba. El modo automático nativo solo acepta una transacción sin firmar cuyo único firmante
requerido sea la bóveda, y no se puede usar con @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 manual nativo: define chain y pushMode: "manual". Fordefi reescribe
el mensaje (blockhash reciente, instrucciones de comisión de Compute Budget) y lo firma,
pero no lo transmite. modifyAndSignTransactions() devuelve la transacción que
Fordefi firmó, que reemplaza a la que enviaste: continúa siempre desde la
transacción devuelta. Fordefi debe ser el pagador de comisiones y debe firmar antes que cualquier
otro firmante requerido. La reescritura no se compara con un diff, así que inspecciona el resultado antes de
transmitir.
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 caja negra: omita chain. Fordefi firma bytes sin procesar y no transmite; usted ensambla y envía la transacción por su cuenta. Úselo con una bóveda de caja negra de 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 del firmante
@solana/keychain-core define una interfaz por cada forma de firmante de Kit. Cada una añade
isAvailable(): Promise<boolean> a la interfaz correspondiente de @solana/signers:
SolanaTransactionSigner(TransactionPartialSignerde Kit): devuelve firmas para una transacción que te pertenece.SolanaModifyingSigner(TransactionModifyingSignerde Kit): reescribe la transacción, la firma y devuelve la nueva transacción sin transmitirla.SolanaSendingSigner(TransactionSendingSignerde Kit): firma y transmite a través de su proveedor.SolanaMessageSigner(MessagePartialSignerde Kit): firma mensajes fuera de la cadena. Es independiente de las formas de transacción.
SolanaSigner es la unión de las tres formas de transacción. El tipo de retorno de cada
factory indica la forma exacta de su backend. Cuando la configuración solo se conoce en tiempo de
ejecución, acota con isSolanaTransactionSigner, isSolanaModifyingSigner,
isSolanaSendingSigner o isSolanaMessageSigner. signerCapabilities(signer)
informa de las cuatro a la vez.
| Backend | Forma |
|---|---|
| memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (caja negra) | SolanaTransactionSigner & SolanaMessageSigner |
| utila | SolanaTransactionSigner (sin firma de mensajes) |
| fordefi (manual nativo) | SolanaModifyingSigner & SolanaMessageSigner |
| fordefi (automático nativo) | SolanaSendingSigner & SolanaMessageSigner |
| crossmint | SolanaSendingSigner (sin firma de mensajes) |
import {isSolanaSendingSigner,isSolanaTransactionSigner} from "@solana/keychain-core";if (isSolanaSendingSigner(signer)) {await signer.signAndSendTransactions([transaction]);} else if (isSolanaTransactionSigner(signer)) {await signer.signTransactions([transaction]);}
Creación de Firmantes Personalizados
Implementa las interfaces de capacidades que admita tu backend:
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);}}
Implementa solo lo que tu backend admita. Kit clasifica los firmantes según qué métodos existen, así que nunca añadas un método que simplemente lance un error.
Consulta la guía para agregar firmantes para integrar servicios adicionales de gestión de claves.
Is this page helpful?