TypeScript

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

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

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

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 TransactionPartialSigner
client.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àConfigurazioneTipoMetodo
Black boxchain non impostatoFordefiBlackBoxSignersignTransactions()
Nativa autochain impostato, pushMode omesso o "auto"FordefiNativeSignersignAndSendTransactions()
Nativa manualechain impostato, pushMode: "manual"FordefiNativeManualSignermodifyAndSignTransactions()

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 (TransactionPartialSigner di Kit): restituisce le firme per una transazione di tua proprietà.
  • SolanaModifyingSigner (TransactionModifyingSigner di Kit): riscrive la transazione, la firma e restituisce la nuova transazione senza eseguirne il broadcast.
  • SolanaSendingSigner (TransactionSendingSigner di Kit): firma ed esegue il broadcast tramite il proprio provider.
  • SolanaMessageSigner (MessagePartialSigner di 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.

BackendForma
memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (black box)SolanaTransactionSigner & SolanaMessageSigner
utilaSolanaTransactionSigner (senza firma dei messaggi)
fordefi (nativa manuale)SolanaModifyingSigner & SolanaMessageSigner
fordefi (nativa auto)SolanaSendingSigner & SolanaMessageSigner
crossmintSolanaSendingSigner (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?

© 2026 Solana Foundation. Tutti i diritti riservati.