Stai passando da 1.x? Consulta la guida alla migrazione a 2.0.
Installazione
Installa il pacchetto principale o i singoli signer secondo necessità:
# 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
Richiede @solana/kit 8.1 o versioni successive. Le transazioni v1
(createTransactionMessage({ version: 1 })) sono supportate, tranne da Fireblocks
con useProgramCall: true, che accetta solo legacy e v0.
Utilizzo di base
Factory unificata (Consigliata per le app che desiderano supportare più signer)
Utilizza createKeychainSigner con una configurazione discriminata per creare
qualsiasi 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);
Oppure installa un singolo pacchetto signer per un footprint di dipendenze ridotto:
import { createPrivySigner } from "@solana/keychain-privy";const signer = await createPrivySigner({appId: "your-app-id",appSecret: "your-app-secret",walletId: "your-wallet-id"});
Utilizzo con i messaggi di transazione
Ogni backend, eccetto Crossmint e la modalità nativa di Fordefi, restituisce un
SolanaTransactionSigner, ovvero un TransactionPartialSigner di Kit che funziona
con gli helper per partial signer di @solana/signers riportati di seguito. I backend con broadcast gestito
sono trattati in Funzionalità dei 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;}
Firmare e inviare con qualsiasi signer
signAndSendTransaction di @solana/keychain-core porta una transazione on-chain
qualunque sia la forma del signer. I signer di invio eseguono il broadcast tramite il
proprio provider; ogni altra forma usa il sendTransaction che fornisci tu (core non ha
dipendenze RPC):
import { signAndSendTransaction } from "@solana/keychain-core";const signature = await signAndSendTransaction(signer, transaction, {sendTransaction: (tx) =>sendAndConfirmTransaction(tx, { commitment: "confirmed" })});
Utilizzo con un Kit Client (plugin keychainSigner)
@solana/keychain-kit-plugin installa un keychain signer direttamente su un
Kit client, collegandolo come payer e
identity così da evitare la configurazione manuale di pipe mostrata in
precedenza:
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
Il plugin accetta la stessa configurazione con tag di backend di createKeychainSigner,
tranne Crossmint e la modalità nativa di Fordefi (con chain impostato). Questi backend riscrivono
la transazione lato server, quindi non possono fungere da payer o
identity del client; il plugin li rifiuta sia in fase di compilazione sia in fase di esecuzione. Viene incluso nel bundle solo il
backend a cui viene delegata l'operazione (i pacchetti dei backend vengono caricati tramite import()
dinamico).
Usa keychainPayer o keychainIdentity per impostare un solo ruolo e combinare
backend su un singolo 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 }));
Hai già un SolanaTransactionSigner? È un TransactionPartialSigner di Kit
valido, quindi puoi installarlo con
@solana/kit-plugin-signer
invece (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));
Annullamento e broadcast non confermati
signTransactions, modifyAndSignTransactions, signAndSendTransactions e
signMessages accettano tutti { abortSignal } come secondo argomento. L'interruzione
fa smettere di attendere, ma non annulla il lavoro che un provider ha già accettato.
Con Crossmint e la modalità auto nativa di Fordefi, un errore non significa che non
sia stato registrato nulla. Quando il provider potrebbe aver accettato la transazione, il codice di errore è
SignerErrorCode.BROADCAST_UNCONFIRMED ("SIGNER_BROADCAST_UNCONFIRMED").
context.providerTransactionId viene impostato quando la creazione è stata accettata, e
context.idempotencyKey contiene la chiave derivata dal messaggio. Reinviare la
transazione identica byte per byte è sicuro; una ricostruita (ad esempio con un nuovo
blockhash) è un nuovo trasferimento. providerMayHaveAccepted(error) e
providerStatus(error) di @solana/keychain-core aiutano a classificare l'errore.
Esegui la riconciliazione con il provider prima di riprovare.
Configurazione del Backend
Memory
Fornisci esattamente uno tra 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 supporta solo l'invio. La sua API esegue sempre una transazione approvata
lato server e sponsorizza il gas, quindi il signer espone solo
signAndSendTransactions(): non ha signTransactions() né signMessages(),
e non può essere installato con @solana/keychain-kit-plugin. La firma
restituita identifica la transazione registrata da 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"});
Paragrafo
import { createParaSigner } from "@solana/keychain-para";const signer = await createParaSigner({apiKey: "api_key",walletId: "wallet_id"});
Utila
Utila firma solo le transazioni; non ha 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 ha tre modalità di firma, stabilite in fase di costruzione tramite chain e
pushMode. Ogni modalità espone esattamente un metodo per le transazioni e tutte e tre firmano
messaggi:
| Modalità | Configurazione | Tipo | Metodo |
|---|---|---|---|
| Black box | chain non impostato | FordefiBlackBoxSigner | signTransactions() |
| Nativa auto | chain impostato, pushMode omesso o "auto" | FordefiNativeSigner | signAndSendTransactions() |
| Nativa manuale | chain impostato, pushMode: "manual" | FordefiNativeManualSigner | modifyAndSignTransactions() |
Le richieste API vengono autenticate con una firma della richiesta ECDSA P-256: fornisci la
chiave di firma come privateKeyPem, oppure conservala in un KMS/HSM passando un
requestSigner.
Modalità auto nativa (consigliata per l'uso on-chain): imposta chain e Fordefi
firma e trasmette la transazione autonomamente. Poiché Fordefi può sostituire il
blockhash o le commissioni prima della firma, il signer è un TransactionSendingSigner
di Kit: usalo con
signAndSendTransactionMessageWithSigners, non con il flusso partial-signer mostrato
sopra. La modalità auto nativa accetta solo una transazione non firmata il cui unico signer
richiesto è il vault e non può essere usata 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);
Modalità manuale nativa: imposta chain e pushMode: "manual". Fordefi riscrive
il messaggio (blockhash recente, istruzioni di commissione del Compute Budget) e lo firma,
ma non esegue il broadcast. modifyAndSignTransactions() restituisce la transazione
firmata da Fordefi, che sostituisce quella che hai inviato: continua sempre dalla
transazione restituita. Fordefi deve essere il fee payer e deve firmare prima di qualsiasi
altro signer richiesto. La riscrittura non viene confrontata con l'originale (nessun diff), quindi controlla il risultato prima di
eseguire il 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]);
Modalità black box: ometti chain. Fordefi firma i byte grezzi e non trasmette la transazione; sei tu ad assemblare e inviare la transazione. Utilizzala con un vault black box di 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")});
Funzionalità dei signer
@solana/keychain-core definisce un'interfaccia per ogni forma di signer di Kit. Ognuna aggiunge
isAvailable(): Promise<boolean> all'interfaccia corrispondente di @solana/signers:
SolanaTransactionSigner(TransactionPartialSignerdi Kit): restituisce le firme per una transazione di tua proprietà.SolanaModifyingSigner(TransactionModifyingSignerdi Kit): riscrive la transazione, la firma e restituisce la nuova transazione senza eseguirne il broadcast.SolanaSendingSigner(TransactionSendingSignerdi Kit): firma ed esegue il broadcast tramite il proprio provider.SolanaMessageSigner(MessagePartialSignerdi Kit): firma messaggi off-chain. È distinto dalle forme per le transazioni.
SolanaSigner è l'unione delle tre forme per le transazioni. Il tipo restituito da ogni factory
indica la forma esatta del proprio backend. Quando la configurazione è nota solo in
fase di esecuzione, restringi con isSolanaTransactionSigner, isSolanaModifyingSigner,
isSolanaSendingSigner o isSolanaMessageSigner. signerCapabilities(signer)
restituisce tutte e quattro le informazioni in una volta.
| Backend | Forma |
|---|---|
| memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (black box) | SolanaTransactionSigner & SolanaMessageSigner |
| utila | SolanaTransactionSigner (senza firma dei messaggi) |
| fordefi (nativa manuale) | SolanaModifyingSigner & SolanaMessageSigner |
| fordefi (nativa auto) | SolanaSendingSigner & SolanaMessageSigner |
| crossmint | SolanaSendingSigner (senza firma dei messaggi) |
import {isSolanaSendingSigner,isSolanaTransactionSigner} from "@solana/keychain-core";if (isSolanaSendingSigner(signer)) {await signer.signAndSendTransactions([transaction]);} else if (isSolanaTransactionSigner(signer)) {await signer.signTransactions([transaction]);}
Creazione di Signer Personalizzati
Implementa le interfacce di funzionalità supportate dal tuo 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 ciò che il tuo backend supporta. Kit classifica i signer in base ai metodi esistenti, quindi non aggiungere mai un metodo che si limita a generare un errore.
Consulta la guida all'aggiunta di Signer per integrare servizi aggiuntivi di gestione delle chiavi.
Is this page helpful?