从 1.x 升级?请参阅2.0 迁移指南。
安装
根据需要安装伞形包或单独的签名器:
# 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
需要 @solana/kit 8.1 或更高版本。支持 v1 交易(createTransactionMessage({ version: 1 })),但 Fireblocks 在 useProgramCall: true 时除外,此时仅接受 legacy 和 v0。
基本用法
统一工厂(推荐用于希望支持多个签名器的应用程序)
使用 createKeychainSigner 配合可辨识的配置来创建任何后端:
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);
或者安装单独的签名器包以减少依赖占用:
import { createPrivySigner } from "@solana/keychain-privy";const signer = await createPrivySigner({appId: "your-app-id",appSecret: "your-app-secret",walletId: "your-wallet-id"});
与交易消息配合使用
除 Crossmint 和 Fordefi 原生模式外,所有后端都返回 SolanaTransactionSigner,它是 Kit 的 TransactionPartialSigner,可与下面的 @solana/signers 部分签名器辅助函数配合使用。托管广播后端请参阅签名器能力。
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;}
使用任意签名器签名并发送
无论签名器是何种形态,@solana/keychain-core 中的 signAndSendTransaction 都能将交易上链。发送型签名器通过其提供商广播;其他所有形态则使用您传入的 sendTransaction(core 不依赖 RPC):
import { signAndSendTransaction } from "@solana/keychain-core";const signature = await signAndSendTransaction(signer, transaction, {sendTransaction: (tx) =>sendAndConfirmTransaction(tx, { commitment: "confirmed" })});
与 Kit 客户端配合使用(keychainSigner 插件)
@solana/keychain-kit-plugin 可直接在
Kit 客户端 上安装一个密钥链签名器,将其配置为
payer 和 identity,从而跳过上述手动 pipe 设置步骤:
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
该插件接受与 createKeychainSigner 相同的带后端标记的配置,但 Crossmint 和 Fordefi 原生模式(设置了 chain)除外。这些后端会在服务端重写交易,因此无法充当客户端的 payer 或 identity;插件会在编译时和运行时拒绝它们。只有其所分派到的后端才会被打包(后端包通过动态 import() 加载)。
使用 keychainPayer 或 keychainIdentity
仅设置单一角色,并在同一客户端上混用多个后端:
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 }));
已经有 SolanaTransactionSigner?它是有效的 Kit TransactionPartialSigner,因此您可以改用 @solana/kit-plugin-signer 来安装它(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));
取消与未确认的广播
signTransactions、modifyAndSignTransactions、signAndSendTransactions 和 signMessages 都接受 { abortSignal } 作为第二个参数。中止只会停止等待,不会撤销提供商已接受的工作。
在 Crossmint 和 Fordefi 原生自动模式下,失败并不意味着交易没有上链。当提供商可能已接受该交易时,错误码为 SignerErrorCode.BROADCAST_UNCONFIRMED("SIGNER_BROADCAST_UNCONFIRMED")。如果创建请求已被接受,则会设置 context.providerTransactionId,而 context.idempotencyKey 携带由消息派生的密钥。重新发送字节完全相同的交易是安全的;重新构建的交易(例如使用了新的 blockhash)则是一笔新的转账。@solana/keychain-core 中的 providerMayHaveAccepted(error) 和 providerStatus(error) 有助于对错误进行分类。重试前请先与提供商核对。
后端配置
内存
请从 keyPair、privateKey、privateKeyString 或 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 开发者平台)
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 仅支持发送。其 API 始终在服务端执行已批准的交易并代付 gas,因此该签名器只暴露 signAndSendTransactions():它没有 signTransactions() 或 signMessages(),也无法通过 @solana/keychain-kit-plugin 安装。返回的签名用于标识 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"});
参数
import { createParaSigner } from "@solana/keychain-para";const signer = await createParaSigner({apiKey: "api_key",walletId: "wallet_id"});
Utila
Utila 仅对交易签名;它没有 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 有三种签名模式,由 chain 和 pushMode 在构造时确定。每种模式恰好暴露一个交易方法,且三种模式都能对消息签名:
| 模式 | 配置 | 类型 | 方法 |
|---|---|---|---|
| 黑盒 | 未设置 chain | FordefiBlackBoxSigner | signTransactions() |
| 原生自动 | 已设置 chain,pushMode 省略或为 "auto" | FordefiNativeSigner | signAndSendTransactions() |
| 原生手动 | 已设置 chain,pushMode: "manual" | FordefiNativeManualSigner | modifyAndSignTransactions() |
API 请求通过 ECDSA P-256 请求签名进行身份验证:将签名密钥作为 privateKeyPem 提供,或通过传入 requestSigner 将其保存在 KMS/HSM 中。
原生自动模式(推荐用于链上场景):设置 chain,Fordefi 会自行签名并广播交易。由于 Fordefi 可能在签名前替换 blockhash 或费用,该签名器是 Kit 的 TransactionSendingSigner:请将其与 signAndSendTransactionMessageWithSigners 配合使用,而非上文所示的部分签名器流程。原生自动模式仅接受唯一必需签名者为该保险库的未签名交易,并且不能与 @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);
原生手动模式:设置 chain 和 pushMode: "manual"。Fordefi 会重写消息(最近的 blockhash、Compute Budget 费用指令)并签名,但不会广播。modifyAndSignTransactions() 返回 Fordefi 签名后的交易,它会取代您提交的交易:请始终基于返回的交易继续操作。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_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]);
黑盒模式:省略 chain。Fordefi 对原始字节进行签名,不负责广播;您需自行组装并提交交易。请将此模式与 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")});
签名器能力
@solana/keychain-core 为每种 Kit 签名器形态定义了一个接口。每个接口都在对应的 @solana/signers 接口基础上增加了 isAvailable(): Promise<boolean>:
SolanaTransactionSigner(Kit 的TransactionPartialSigner):为您持有的交易返回签名。SolanaModifyingSigner(Kit 的TransactionModifyingSigner):重写交易、对其签名,并返回新交易,但不广播。SolanaSendingSigner(Kit 的TransactionSendingSigner):通过其提供商签名并广播。SolanaMessageSigner(Kit 的MessagePartialSigner):对链下消息签名。这与交易形态相互独立。
SolanaSigner 是三种交易形态的联合类型。每个工厂函数的返回类型都会指明其后端的确切形态。当配置仅在运行时才可知时,请使用 isSolanaTransactionSigner、isSolanaModifyingSigner、isSolanaSendingSigner 或 isSolanaMessageSigner 进行类型收窄。signerCapabilities(signer) 会一次性报告全部四种能力。
| 后端 | 形态 |
|---|---|
| memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi(黑盒) | SolanaTransactionSigner & SolanaMessageSigner |
| utila | SolanaTransactionSigner(不支持消息签名) |
| fordefi(原生手动) | SolanaModifyingSigner & SolanaMessageSigner |
| fordefi(原生自动) | SolanaSendingSigner & SolanaMessageSigner |
| crossmint | SolanaSendingSigner(不支持消息签名) |
import {isSolanaSendingSigner,isSolanaTransactionSigner} from "@solana/keychain-core";if (isSolanaSendingSigner(signer)) {await signer.signAndSendTransactions([transaction]);} else if (isSolanaTransactionSigner(signer)) {await signer.signTransactions([transaction]);}
构建自定义签名器
实现您的后端所支持的能力接口:
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);}}
只实现您的后端所支持的部分。Kit 根据签名器具有哪些方法来对其分类,因此切勿添加仅抛出错误的方法。
请参阅添加签名器指南,以集成额外的密钥管理服务。
Is this page helpful?