TypeScript packages for building custom Solana signers compatible with @solana/kit and @solana/signers
import { createKeychainSigner } from '@solana/keychain';
import { signTransactionWithSigners } from '@solana/signers'; // requires @solana/signers ≥ 8.1
// Create any signer via the unified factory
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);Or install an individual signer package for a smaller dependency footprint:
import { createPrivySigner } from '@solana/keychain-privy';
import { signTransactionWithSigners } from '@solana/signers';
const signer = await createPrivySigner({
appId: 'your-app-id',
appSecret: 'your-app-secret',
walletId: 'your-wallet-id',
});
const signedTx = await signTransactionWithSigners([signer], compiledTransaction);@solana/keychain-core defines one capability interface per Kit signer shape, each adding isAvailable(): Promise<boolean> to the corresponding @solana/signers interface:
SolanaTransactionSigner— Kit'sTransactionPartialSigner: returns signatures for a caller-owned transaction.SolanaModifyingSigner— Kit'sTransactionModifyingSigner: may rewrite the transaction before signing it. Fordefi native manual mode (FordefiNativeManualSigner) has this shape: continue from the transaction it returns, never the one you submitted.SolanaSendingSigner— Kit'sTransactionSendingSigner: signs and broadcasts through its provider (managed broadcast).SolanaMessageSigner— Kit'sMessagePartialSigner: signs off-chain messages. Orthogonal to the transaction shapes, exactly as Kit separatesMessageSignerfromTransactionSigner.
SolanaSigner is the union of the three transaction shapes — any keychain signer. Which shape a given signer has is not knowable from the union, so narrow with isSolanaTransactionSigner() / isSolanaModifyingSigner() / isSolanaSendingSigner() before calling a signing method. Most backends are SolanaTransactionSigner & SolanaMessageSigner, and every factory's return type states its backend's exact capabilities.
See the @solana/keychain README for more usage patterns.
@solana/keychain-kit-plugin installs a keychain signer on a Kit client as the payer and/or identity:
import { createClient } from '@solana/kit';
import { keychainSigner } from '@solana/keychain-kit-plugin';
const client = await createClient().use(
keychainSigner({ backend: 'privy', appId, appSecret, walletId }),
);
client.payer; // SolanaTransactionSigner — also a Kit TransactionPartialSignerMost backends sign a transaction you own and hand back signatures (SolanaTransactionSigner). Managed-broadcast backends rewrite and broadcast the transaction themselves, so they expose only signAndSendTransactions (SolanaSendingSigner) — Kit classifies signers by method presence, so they deliberately do not expose signTransactions. Message signing (SolanaMessageSigner) is an orthogonal capability: a backend that does not sign messages exposes no signMessages method at all.
| Backend | Shape | signTransactions |
signAndSendTransactions |
signMessages |
|---|---|---|---|---|
| memory, vault, privy, turnkey, aws-kms, fireblocks, gcp-kms, dfns, para, openfort | SolanaTransactionSigner & SolanaMessageSigner |
yes | no | yes |
| cdp | SolanaTransactionSigner & SolanaMessageSigner |
yes | no | yes (UTF-8 payloads only) |
| utila | SolanaTransactionSigner |
yes | no | no |
| crossmint | SolanaSendingSigner |
no | yes | no |
| fordefi (black-box mode) | FordefiBlackBoxSigner (SolanaTransactionSigner & SolanaMessageSigner) |
yes | no | yes |
| fordefi (native auto mode) | FordefiNativeSigner (SolanaSendingSigner & SolanaMessageSigner) |
no | yes | yes |
| fordefi (native manual mode) | FordefiNativeManualSigner (SolanaModifyingSigner & SolanaMessageSigner), signs through modifyAndSignTransactions() |
no | no | yes |
signAndSendTransaction() from @solana/keychain-core gets a transaction on chain through any shape, routing by capability. Signers that cannot broadcast use the send function you inject — core has no RPC dependency:
import { signAndSendTransaction } from '@solana/keychain-core';
const signature = await signAndSendTransaction(signer, transaction, {
sendTransaction: tx => sendAndConfirmTransaction(tx, { commitment: 'confirmed' }),
});Every signing method — signMessages, signTransactions, signAndSendTransactions — takes Kit's optional config as its second argument, so { abortSignal } cancels an in-flight signing request on any backend.
Aborting stops waiting; it does not undo work a provider already accepted. On a backend that broadcasts (Crossmint, Fordefi native auto mode), an abort after the create call has been accepted is reported as BROADCAST_UNCONFIRMED with the provider transaction id and the abort reason kept as cause, because the transaction may already be on chain. Kit also re-checks the signal after a signer returns, so a signal that fires late can make the call site throw over a signature that was produced and, on a sending backend, a transaction that landed. Reconcile with the provider before retrying.
Use signerCapabilities(signer) to inspect a signer at runtime — it returns { canModifyTransactions, canSignAndSend, canSignMessages, canSignTransactions }.
| Package | Description |
|---|---|
| @solana/keychain-core | Core interfaces, types, and utilities for building custom signers |
| @solana/keychain-memory | In-memory keypair signer (local Ed25519 signing) |
| @solana/keychain-privy | Privy wallet signer implementation |
| @solana/keychain-turnkey | Turnkey wallet signer implementation |
| @solana/keychain-vault | HashiCorp Vault signer implementation |
| @solana/keychain-aws-kms | AWS KMS signer implementation |
| @solana/keychain-dfns | Dfns wallet signer implementation |
| @solana/keychain-fireblocks | Fireblocks signer implementation |
| @solana/keychain-gcp-kms | Google Cloud KMS signer implementation |
| @solana/keychain-cdp | Coinbase Developer Platform (CDP) signer implementation |
| @solana/keychain-crossmint | Crossmint wallet signer implementation |
| @solana/keychain-openfort | Openfort backend wallet signer implementation |
| @solana/keychain-para | Para MPC signer implementation |
| @solana/keychain-utila | Utila wallet signer implementation |
| @solana/keychain-kit-plugin | Kit client plugins (keychainSigner/keychainPayer/keychainIdentity) |
| @solana/keychain-fordefi | Fordefi MPC signer implementation |
# Install the umbrella package (includes all signers)
pnpm add @solana/keychain
# Or install individual packages as needed
pnpm add @solana/keychain-core # Core interfaces (required for custom signers)
pnpm add @solana/keychain-aws-kms # AWS KMS signer
pnpm add @solana/keychain-cdp # Coinbase Developer Platform (CDP) signer
pnpm add @solana/keychain-crossmint # Crossmint signer
pnpm add @solana/keychain-dfns # Dfns signer
pnpm add @solana/keychain-fireblocks # Fireblocks signer
pnpm add @solana/keychain-gcp-kms # Google Cloud KMS signer
pnpm add @solana/keychain-memory # In-memory keypair signer (local)
pnpm add @solana/keychain-openfort # Openfort backend wallet signer
pnpm add @solana/keychain-para # Para MPC signer
pnpm add @solana/keychain-privy # Privy signer
pnpm add @solana/keychain-turnkey # Turnkey signer
pnpm add @solana/keychain-utila # Utila signer
pnpm add @solana/keychain-vault # HashiCorp Vault signer
pnpm add @solana/keychain-fordefi # Fordefi signer
# Kit client plugins
pnpm add @solana/keychain-kit-plugin # keychainSigner/keychainPayer/keychainIdentity