TypeScript

¿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 packages
pnpm add @solana/keychain-core # Core interfaces (required for custom signers)
pnpm add @solana/keychain-memory # Local keypair signer
pnpm add @solana/keychain-vault # HashiCorp Vault
pnpm add @solana/keychain-aws-kms # AWS KMS
pnpm add @solana/keychain-gcp-kms # GCP KMS
pnpm add @solana/keychain-privy # Privy
pnpm add @solana/keychain-turnkey # Turnkey
pnpm add @solana/keychain-fireblocks # Fireblocks
pnpm add @solana/keychain-cdp # Coinbase Developer Platform
pnpm add @solana/keychain-crossmint # Crossmint
pnpm add @solana/keychain-dfns # Dfns
pnpm add @solana/keychain-openfort # Openfort
pnpm add @solana/keychain-para # Para
pnpm add @solana/keychain-utila # Utila
pnpm 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.1
const signer = await createKeychainSigner({
backend: "privy",
appId: "your-app-id",
appSecret: "your-app-secret",
walletId: "your-wallet-id"
});
// Sign an already-compiled transaction
const 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 builder
const transaction = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(signer, tx)
// ... set a lifetime and add instructions
);
// Sign with the standard signers API
const 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 TransactionPartialSigner
client.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:

ModoConfiguraciónTipoMétodo
Caja negrachain sin definirFordefiBlackBoxSignersignTransactions()
Automático nativochain definido, pushMode omitido o "auto"FordefiNativeSignersignAndSendTransactions()
Manual nativochain definido, pushMode: "manual"FordefiNativeManualSignermodifyAndSignTransactions()

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 (TransactionPartialSigner de Kit): devuelve firmas para una transacción que te pertenece.
  • SolanaModifyingSigner (TransactionModifyingSigner de Kit): reescribe la transacción, la firma y devuelve la nueva transacción sin transmitirla.
  • SolanaSendingSigner (TransactionSendingSigner de Kit): firma y transmite a través de su proveedor.
  • SolanaMessageSigner (MessagePartialSigner de 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.

BackendForma
memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (caja negra)SolanaTransactionSigner & SolanaMessageSigner
utilaSolanaTransactionSigner (sin firma de mensajes)
fordefi (manual nativo)SolanaModifyingSigner & SolanaMessageSigner
fordefi (automático nativo)SolanaSendingSigner & SolanaMessageSigner
crossmintSolanaSendingSigner (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?