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 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
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.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 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 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;}
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 TransactionPartialSignerclient.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 :
| Mode | Configuration | Type | Méthode |
|---|---|---|---|
| Boîte noire | chain non défini | FordefiBlackBoxSigner | signTransactions() |
| Natif automatique | chain défini, pushMode omis ou "auto" | FordefiNativeSigner | signAndSendTransactions() |
| Natif manuel | chain défini, pushMode: "manual" | FordefiNativeManualSigner | modifyAndSignTransactions() |
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(TransactionPartialSignerde Kit) : renvoie les signatures d'une transaction dont vous êtes propriétaire.SolanaModifyingSigner(TransactionModifyingSignerde Kit) : réécrit la transaction, la signe et renvoie la nouvelle transaction sans la diffuser.SolanaSendingSigner(TransactionSendingSignerde Kit) : signe et diffuse via son fournisseur.SolanaMessageSigner(MessagePartialSignerde 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.
| Backend | Forme |
|---|---|
| memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (boîte noire) | SolanaTransactionSigner & SolanaMessageSigner |
| utila | SolanaTransactionSigner (sans signature de message) |
| fordefi (natif manuel) | SolanaModifyingSigner & SolanaMessageSigner |
| fordefi (natif automatique) | SolanaSendingSigner & SolanaMessageSigner |
| crossmint | SolanaSendingSigner (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?