TypeScript

Vous migrez depuis la version 1.x ? Consultez le guide de migration vers la 2.0.

Installation

Installez le package global ou des signataires individuels selon vos besoins :

# 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

Nécessite @solana/kit 8.1 ou une version ultérieure. Les transactions v1 (createTransactionMessage({ version: 1 })) sont prises en charge, sauf par Fireblocks avec useProgramCall: true, qui n'accepte que les transactions legacy et v0.

Utilisation de base

Factory unifiée (Recommandée pour les applications souhaitant prendre en charge plusieurs signataires)

Utilisez createKeychainSigner avec une configuration discriminée pour créer n'importe quel 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
);

Ou installez un package de signataire individuel pour réduire l'empreinte des dépendances :

import { createPrivySigner } from "@solana/keychain-privy";
const signer = await createPrivySigner({
appId: "your-app-id",
appSecret: "your-app-secret",
walletId: "your-wallet-id"
});

Utilisation avec les messages de transaction

Chaque backend, à l'exception de Crossmint et du mode natif de Fordefi, renvoie un SolanaTransactionSigner, qui est un TransactionPartialSigner de Kit et fonctionne avec les utilitaires de signataire partiel de @solana/signers ci-dessous. Les backends à diffusion gérée sont décrits dans Capacités des signataires.

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;
}

Signer et envoyer avec n'importe quel signataire

signAndSendTransaction de @solana/keychain-core publie une transaction sur la chaîne, quelle que soit la forme du signataire. Les signataires d'envoi diffusent via leur fournisseur ; toutes les autres formes utilisent la fonction sendTransaction que vous transmettez (core n'a aucune dépendance RPC) :

import { signAndSendTransaction } from "@solana/keychain-core";
const signature = await signAndSendTransaction(signer, transaction, {
sendTransaction: (tx) =>
sendAndConfirmTransaction(tx, { commitment: "confirmed" })
});

Utilisation avec un client Kit (plugin keychainSigner)

@solana/keychain-kit-plugin installe un signataire de trousseau directement sur un client Kit, en le configurant comme payer et identity, ce qui vous évite la configuration manuelle de pipe décrite ci-dessus :

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

Le plugin accepte la même configuration étiquetée par backend que createKeychainSigner, sauf pour Crossmint et le mode natif de Fordefi (chain défini). Ces backends réécrivent la transaction côté serveur ; ils ne peuvent donc pas jouer le rôle de payer ou d'identity côté client, et le plugin les rejette à la compilation comme à l'exécution. Seul le backend vers lequel il redirige est inclus dans le bundle (les packages de backend sont chargés via import() dynamique).

Utilisez keychainPayer ou keychainIdentity pour définir un seul rôle, et combinez des backends sur un même 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 }));

Vous avez déjà un SolanaTransactionSigner ? Il s'agit d'un TransactionPartialSigner de Kit valide ; vous pouvez donc l'installer avec @solana/kit-plugin-signer à la place (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));

Annulation et diffusions non confirmées

signTransactions, modifyAndSignTransactions, signAndSendTransactions et signMessages acceptent tous { abortSignal } comme second argument. L'annulation arrête l'attente ; elle n'annule pas le travail qu'un fournisseur a déjà accepté.

Avec Crossmint et le mode automatique natif de Fordefi, un échec ne signifie pas que rien n'a été enregistré. Lorsque le fournisseur a pu accepter la transaction, le code d'erreur est SignerErrorCode.BROADCAST_UNCONFIRMED ("SIGNER_BROADCAST_UNCONFIRMED"). context.providerTransactionId est défini lorsque la création a été acceptée, et context.idempotencyKey contient la clé dérivée du message. Renvoyer la transaction strictement identique octet pour octet est sans risque ; une transaction reconstruite (par exemple avec un nouveau blockhash) constitue un nouveau transfert. providerMayHaveAccepted(error) et providerStatus(error) de @solana/keychain-core aident à classer l'erreur. Rapprochez l'état avec le fournisseur avant de réessayer.

Configuration du backend

Mémoire

Fournissez exactement l'un des éléments suivants : 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"
});

Crossmint est réservé à l'envoi. Son API exécute toujours une transaction approuvée côté serveur et prend en charge les frais de gas ; le signataire n'expose donc que signAndSendTransactions() : il n'a ni signTransactions() ni signMessages(), et il ne peut pas être installé avec @solana/keychain-kit-plugin. La signature renvoyée identifie la transaction enregistrée par Crossmint.

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 signe uniquement les transactions ; il n'a pas de 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 propose trois modes de signature, déterminés à la construction par chain et pushMode. Chaque mode expose exactement une méthode de transaction, et les trois signent les messages :

ModeConfigurationTypeMéthode
Boîte noirechain non définiFordefiBlackBoxSignersignTransactions()
Natif automatiquechain défini, pushMode omis ou "auto"FordefiNativeSignersignAndSendTransactions()
Natif manuelchain défini, pushMode: "manual"FordefiNativeManualSignermodifyAndSignTransactions()

Les requêtes API sont authentifiées par une signature de requête ECDSA P-256 : fournissez la clé de signature via privateKeyPem, ou conservez-la dans un KMS/HSM en transmettant un requestSigner.

Mode automatique natif (recommandé pour une utilisation on-chain) : définissez chain, et Fordefi signe et diffuse lui-même la transaction. Comme Fordefi peut remplacer le blockhash ou les frais avant la signature, le signataire est un TransactionSendingSigner de Kit : utilisez-le avec signAndSendTransactionMessageWithSigners, et non avec le flux de signataire partiel présenté ci-dessus. Le mode automatique natif n'accepte qu'une transaction non signée dont le seul signataire requis est le coffre, et il ne peut pas être utilisé avec @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);

Mode manuel natif : définissez chain et pushMode: "manual". Fordefi réécrit le message (blockhash récent, instructions de frais Compute Budget) et le signe, mais ne le diffuse pas. modifyAndSignTransactions() renvoie la transaction que Fordefi a signée, laquelle remplace celle que vous avez soumise : repartez toujours de la transaction renvoyée. Fordefi doit être le payeur des frais et doit signer avant tout autre signataire requis. La réécriture n'est pas comparée à l'original ; inspectez donc le résultat avant la diffusion.

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
]);

Mode boîte noire : omettez chain. Fordefi signe les octets bruts et ne diffuse pas la transaction ; vous assemblez et soumettez la transaction vous-même. Utilisez ce mode avec un coffre Fordefi en mode boîte noire.

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")
});

Capacités des signataires

@solana/keychain-core définit une interface par forme de signataire Kit. Chacune ajoute isAvailable(): Promise<boolean> à l'interface correspondante de @solana/signers :

  • SolanaTransactionSigner (TransactionPartialSigner de Kit) : renvoie les signatures d'une transaction dont vous êtes propriétaire.
  • SolanaModifyingSigner (TransactionModifyingSigner de Kit) : réécrit la transaction, la signe et renvoie la nouvelle transaction sans la diffuser.
  • SolanaSendingSigner (TransactionSendingSigner de Kit) : signe et diffuse via son fournisseur.
  • SolanaMessageSigner (MessagePartialSigner de Kit) : signe des messages hors chaîne. Cette forme est distincte des formes de transaction.

SolanaSigner est l'union des trois formes de transaction. Le type de retour de chaque factory indique la forme exacte de son backend. Lorsque la configuration n'est connue qu'à l'exécution, restreignez le type avec isSolanaTransactionSigner, isSolanaModifyingSigner, isSolanaSendingSigner ou isSolanaMessageSigner. signerCapabilities(signer) renvoie les quatre d'un coup.

BackendForme
memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (boîte noire)SolanaTransactionSigner & SolanaMessageSigner
utilaSolanaTransactionSigner (sans signature de message)
fordefi (natif manuel)SolanaModifyingSigner & SolanaMessageSigner
fordefi (natif automatique)SolanaSendingSigner & SolanaMessageSigner
crossmintSolanaSendingSigner (sans signature de message)
import {
isSolanaSendingSigner,
isSolanaTransactionSigner
} from "@solana/keychain-core";
if (isSolanaSendingSigner(signer)) {
await signer.signAndSendTransactions([transaction]);
} else if (isSolanaTransactionSigner(signer)) {
await signer.signTransactions([transaction]);
}

Création de signataires personnalisés

Implémentez les interfaces de capacités prises en charge par votre 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);
}
}

N'implémentez que ce que votre backend prend en charge. Kit classe les signataires selon les méthodes qui existent ; n'ajoutez donc jamais une méthode qui se contente de lever une erreur.

Consultez le guide d'ajout de signataires pour intégrer des services de gestion de clés supplémentaires.

Is this page helpful?

© 2026 Fondation Solana. Tous droits réservés.