2.0.0 是 Go 模块的首个稳定版本,因此无需迁移。 2.0 迁移指南 介绍了 Rust 和 TypeScript 中同样的能力划分。
安装
每个后端都是独立的 Go 模块,因此你的构建产物和模块依赖图中只包含你导入的后端:
go get github.com/solana-foundation/solana-keychain/go/core/v2 # Interfaces, errors, helpersgo get github.com/solana-foundation/solana-keychain/go/signers/memory/v2 # Local keypair signergo get github.com/solana-foundation/solana-keychain/go/signers/vault/v2 # HashiCorp Vaultgo get github.com/solana-foundation/solana-keychain/go/signers/awskms/v2 # AWS KMSgo get github.com/solana-foundation/solana-keychain/go/signers/gcpkms/v2 # GCP KMSgo get github.com/solana-foundation/solana-keychain/go/signers/privy/v2 # Privygo get github.com/solana-foundation/solana-keychain/go/signers/turnkey/v2 # Turnkeygo get github.com/solana-foundation/solana-keychain/go/signers/fireblocks/v2 # Fireblocksgo get github.com/solana-foundation/solana-keychain/go/signers/cdp/v2 # Coinbase Developer Platformgo get github.com/solana-foundation/solana-keychain/go/signers/crossmint/v2 # Crossmintgo get github.com/solana-foundation/solana-keychain/go/signers/dfns/v2 # Dfnsgo get github.com/solana-foundation/solana-keychain/go/signers/openfort/v2 # Openfortgo get github.com/solana-foundation/solana-keychain/go/signers/para/v2 # Parago get github.com/solana-foundation/solana-keychain/go/signers/utila/v2 # Utilago get github.com/solana-foundation/solana-keychain/go/signers/fordefi/v2 # Fordefi
模块的标签格式为 go/<module>/v2.0.0(例如 go/signers/memory/v2.0.0)。所有模块都要求 Go 1.25.0,唯独 gcpkms 要求 Go 1.25.8,这是由 google.golang.org/api 设定的最低版本所致。Ledger 仅支持 Rust,没有 Go 模块。
solana-go v2 与 v1 交易
这些模块基于
github.com/solana-foundation/solana-go/v2
v2.0.0 构建,并接受其 *solana.Transaction。支持 Legacy、v0 和 v1(solana.MessageVersionV1)交易,但启用 UseProgramCall: true 的 Fireblocks 除外,它只接受 Legacy 和 v0。
基本用法
所有签名器都实现了基础的
core.SolanaSigner 接口
(Pubkey、SignMessage、IsAvailable)。这些方法接收 context.Context 并会阻塞。每个后端还会恰好实现一个能力接口:
core.TransactionSigner:SignTransaction按原样对你的交易进行签名。core.ModifyingSigner:ModifyAndSignTransaction允许服务提供方改写交易,然后再签名。core.SendingSigner:SignAndSendTransaction由服务提供方负责签名并广播交易。
每个后端包都会暴露一个 Config 结构体和一个 New 构造函数:
import ("context""fmt""github.com/solana-foundation/solana-go/v2""github.com/solana-foundation/solana-keychain/go/core/v2""github.com/solana-foundation/solana-keychain/go/signers/memory/v2")func signWithKeychain(ctx context.Context, tx *solana.Transaction) error {signer, err := memory.New(memory.Config{PrivateKeyPath: "/path/to/id.json"})if err != nil {return err}if !signer.IsAvailable(ctx) {return core.NewSignerError(core.CodeNotAvailable, "signer offline")}result, err := signer.SignTransaction(ctx, tx)if err != nil {return err}fmt.Println("signature:", result.Signature)fmt.Println("complete:", result.IsComplete())// result.EncodedTransaction is the base64 wire transactionreturn nil}
SignTransaction 和 ModifyAndSignTransaction 返回 core.SignedTransaction(EncodedTransaction、Signature、Completeness)。远程 HTTP 后端接受可选的 HTTPClient 和 HTTPClientConfig(超时设置)。默认客户端强制使用 HTTPS;如果传入自定义客户端,则会绕过该限制,其安全性由你自行负责。
签名器能力
无法对交易签名的后端根本不会提供 SignTransaction 方法,因此对于具体类型,能力在编译期即可确定;对于 core.SolanaSigner,则通过类型断言来判断:
| 后端 | 能力 | SignMessage |
|---|---|---|
| memory, vault, privy, turnkey, awskms, gcpkms, fireblocks, dfns, para, openfort | TransactionSigner | 支持 |
| cdp | TransactionSigner | 仅支持 UTF-8 载荷 |
| utila | TransactionSigner | 失败,返回 CodeSigningFailed |
fordefi black box(fordefi.BlackBoxSigner) | TransactionSigner | 支持 |
fordefi native manual(fordefi.NativeManualSigner) | ModifyingSigner | 支持 |
fordefi native auto(fordefi.NativeAutoSigner) | SendingSigner | 支持 |
| crossmint | SendingSigner | 失败,返回 CodeSigningFailed |
switch s := signer.(type) {case core.SendingSigner:sig, err := s.SignAndSendTransaction(ctx, tx)case core.ModifyingSigner:signed, err := s.ModifyAndSignTransaction(ctx, tx)case core.TransactionSigner:signed, err := s.SignTransaction(ctx, tx)}
初始化
没有单独的 Init 步骤。Privy、Fireblocks、Dfns、Crossmint、Para、Openfort 和 Utila 会在 New 内部解析钱包地址,因此它们的构造函数需要传入 context.Context。请始终通过 New 构建签名器:零值的 Crossmint、Para 或 Utila 签名器会返回 CodeNotInitialized 错误,而不是用零地址进行签名。
使用任意签名器签名并发送
无论签名器属于哪种形态,core.SignAndSendTransaction 都能将交易提交上链。SendingSigner 通过其服务提供方广播,并忽略发送函数;其他形态则只负责签名,由你的函数广播 base64 结果,对于 ModifyingSigner 而言,该结果就是其服务提供方改写后的交易。Core 不依赖任何 RPC:
import "github.com/solana-foundation/solana-go/v2/rpc"client := rpc.New("https://api.devnet.solana.com")sig, err := core.SignAndSendTransaction(ctx, signer, tx,func(ctx context.Context, encoded string) (solana.Signature, error) {return client.SendEncodedTransaction(ctx, encoded)})
批量签名
批量签名以独立的辅助函数形式提供,支持并发执行,并可为受速率限制的 API 设置可选的逐请求错峰间隔:
sigs, err := core.SignMessages(ctx, signer, [][]byte{msg1, msg2}, core.BatchOptions{MaxConcurrency: 4, // 0 = unboundedRequestDelay: 50 * time.Millisecond,})signed, err := core.SignTransactions(ctx, signer, txs, core.BatchOptions{})
core.SignTransactions 接收的是 core.TransactionSigner,因此由服务端管理广播的签名器(Crossmint、Fordefi native auto)以及 Fordefi native manual 无法通过它进行批量处理;请逐笔调用它们。如果某个签名器的调用会在服务提供方产生额外工作(启用 UseProgramCall 的 Fireblocks),它会通过 core.BatchServerSideEffects 上报,此时批量任务将改为顺序执行。
后端配置
Memory
PrivateKey(原始字节)、PrivateKeyString(base58 或 [1,2,...] 字节数组)和 PrivateKeyPath(Solana CLI keypair 文件)三者中,请恰好提供一个:
import "github.com/solana-foundation/solana-keychain/go/signers/memory/v2"signer, err := memory.New(memory.Config{PrivateKeyPath: "/path/to/id.json",})
Go 没有可靠的内存清零方式:垃圾回收器可能会复制并保留密钥字节(memory keypair,以及 Crossmint 和 Openfort 在本地派生的密钥),直到下一次回收。使用本地密钥后端时,请将整个进程内存视为敏感数据。
HashiCorp Vault
import "github.com/solana-foundation/solana-keychain/go/signers/vault/v2"signer, err := vault.New(vault.Config{VaultAddr: "https://vault.example.com:8200",Token: "hvs.xxxxx",KeyName: "my-solana-key",Pubkey: "base58_public_key",})
AWS KMS
import "github.com/solana-foundation/solana-keychain/go/signers/awskms/v2"signer, err := awskms.New(ctx, awskms.Config{KeyID: "alias/my-solana-key",PublicKey: "base58_public_key",Region: "us-east-1", // optional})
GCP KMS
import "github.com/solana-foundation/solana-keychain/go/signers/gcpkms/v2"signer, err := gcpkms.New(ctx, gcpkms.Config{KeyName: "projects/my-project/locations/us-east1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1",PublicKey: "base58_public_key",})if err != nil {return err}defer signer.Close()
Privy
import "github.com/solana-foundation/solana-keychain/go/signers/privy/v2"signer, err := privy.New(ctx, privy.Config{AppID: "app_id",AppSecret: "app_secret",WalletID: "wallet_id",})
Turnkey
import "github.com/solana-foundation/solana-keychain/go/signers/turnkey/v2"signer, err := turnkey.New(turnkey.Config{APIPublicKey: "api_public_key",APIPrivateKey: "api_private_key",OrganizationID: "org_id",PrivateKeyID: "private_key_id",PublicKey: "base58_public_key",})
Fireblocks
import "github.com/solana-foundation/solana-keychain/go/signers/fireblocks/v2"signer, err := fireblocks.New(ctx, fireblocks.Config{APIKey: "api_key",PrivateKeyPEM: "-----BEGIN RSA PRIVATE KEY-----\n...",VaultAccountID: "0",AssetID: "SOL", // or "SOL_TEST" for devnetUseProgramCall: false, // true signs via sign-only PROGRAM_CALL (legacy/v0, hot wallet)})
CDP(Coinbase Developer Platform)
import "github.com/solana-foundation/solana-keychain/go/signers/cdp/v2"signer, err := cdp.New(cdp.Config{APIKeyID: "api_key_id",APIKeySecret: "api_key_secret", // base64 Ed25519WalletSecret: "wallet_secret", // base64 PKCS#8 DERAddress: "base58_address",Network: cdp.NetworkMainnet, // or cdp.NetworkDevnet; required for address lookup tables})
CDP 的 SignMessage 仅接受 UTF-8 载荷。
Crossmint
Crossmint 仅支持发送:其 API 在服务端执行每一笔获批的交易,因此 *crossmint.Signer 只有 SignAndSendTransaction,没有 SignTransaction。你的交易不会被修改;返回的签名标识的是 Crossmint 实际上链的那笔交易,如果 Crossmint 代付 gas,它可能与你的交易不同。
import "github.com/solana-foundation/solana-keychain/go/signers/crossmint/v2"signer, err := crossmint.New(ctx, crossmint.Config{APIKey: "api_key",WalletLocator: "wallet_locator",SignerSecret: "", // optional xmsk1_ secret: auto-approves what Crossmint presents})sig, err := signer.SignAndSendTransaction(ctx, tx)
Dfns
import "github.com/solana-foundation/solana-keychain/go/signers/dfns/v2"signer, err := dfns.New(ctx, dfns.Config{AuthToken: "auth_token",CredID: "cred_id",PrivateKeyPEM: "-----BEGIN EC PRIVATE KEY-----\n...",WalletID: "wallet_id",})
Openfort
import "github.com/solana-foundation/solana-keychain/go/signers/openfort/v2"signer, err := openfort.New(ctx, openfort.Config{SecretKey: "sk_live_...",AccountID: "acc_...",WalletSecret: "wallet_secret", // base64 PKCS#8 DER or PEM})
Para
import "github.com/solana-foundation/solana-keychain/go/signers/para/v2"signer, err := para.New(ctx, para.Config{APIKey: "sk_...",WalletID: "wallet_id",})
Utila
Utila 仅对交易签名;SignMessage 会失败,已签名的交易需由你自行广播。
import "github.com/solana-foundation/solana-keychain/go/signers/utila/v2"signer, err := utila.New(ctx, utila.Config{ServiceAccountEmail: "sa@example.com",ServiceAccountPrivateKeyPEM: "-----BEGIN PRIVATE KEY-----\n...",VaultID: "vault_id",WalletID: "wallet_id",Network: "networks/solana-devnet",})
Fordefi
Fordefi 提供三种签名器类型,在构造时由 Config.Chain 和 Config.PushMode 决定。三者均支持消息签名:
| Config | 类型 | 入口方法 |
|---|---|---|
未设置 Chain | *fordefi.BlackBoxSigner | SignTransaction |
已设置 Chain,PushMode 未设置或为 PushModeAuto | *fordefi.NativeAutoSigner | SignAndSendTransaction |
已设置 Chain,PushMode: PushModeManual | *fordefi.NativeManualSigner | ModifyAndSignTransaction |
fordefi.New 根据配置分派并返回 core.SolanaSigner;fordefi.NewBlackBox、fordefi.NewNativeAuto 和 fordefi.NewNativeManual 返回具体类型,并拒绝其他模式的配置。API 请求使用 ECDSA P-256 请求签名进行身份验证:请在 PrivateKeyPEM 与 RequestSigner 中恰好提供一个,后者可将该密钥保留在 KMS/HSM 中。
Native auto 模式:Fordefi 可能会替换 blockhash 和手续费,然后签名并广播。它只接受唯一必需签名者为该 vault 的未签名交易。
import "github.com/solana-foundation/solana-keychain/go/signers/fordefi/v2"signer, err := fordefi.NewNativeAuto(ctx, fordefi.Config{AccessToken: os.Getenv("FORDEFI_ACCESS_TOKEN"),VaultID: os.Getenv("FORDEFI_VAULT_ID"),PublicKey: os.Getenv("FORDEFI_PUBLIC_KEY"), // Solana vault address (base58)PrivateKeyPEM: string(pemBytes),Chain: fordefi.ChainSolanaDevnet, // or fordefi.ChainSolanaMainnetFee: &fordefi.Fee{Type: fordefi.FeeTypePriority,PriorityLevel: fordefi.PriorityMedium,},})sig, err := signer.SignAndSendTransaction(ctx, tx)
Native manual 模式:设置 PushMode: fordefi.PushModeManual。Fordefi 会改写消息(最近的 blockhash、Compute Budget 手续费指令)并签名,但不广播。ModifyAndSignTransaction 会用 Fordefi 签名后的交易替换 tx:请务必基于它继续后续操作。Fordefi 必须是手续费支付方,并且必须先于任何其他必需签名者签名,因为改写会使已有签名失效。改写内容不会生成差异对比,因此广播前请自行检查结果。
signer, err := fordefi.NewNativeManual(ctx, fordefi.Config{AccessToken: os.Getenv("FORDEFI_ACCESS_TOKEN"),VaultID: os.Getenv("FORDEFI_VAULT_ID"),PublicKey: os.Getenv("FORDEFI_PUBLIC_KEY"),PrivateKeyPEM: string(pemBytes),Chain: fordefi.ChainSolanaDevnet,PushMode: fordefi.PushModeManual,})signed, err := signer.ModifyAndSignTransaction(ctx, tx)
Black box 模式:不设置 Chain。Fordefi 会对你提供的消息字节原样签名,并且不会广播。请将其与 Fordefi black box vault 配合使用。
signer, err := fordefi.NewBlackBox(ctx, fordefi.Config{AccessToken: os.Getenv("FORDEFI_ACCESS_TOKEN"),VaultID: os.Getenv("FORDEFI_BB_VAULT_ID"),PublicKey: os.Getenv("FORDEFI_BB_PUBLIC_KEY"),PrivateKeyPEM: string(pemBytes),})
为什么没有统一入口包
Rust 有 Signer 枚举,TypeScript 有 createKeychainSigner;Go 则有意两者皆无。Go 不会跨运行时分派的 switch 消除无用代码,因此统一入口包会把每个后端的供应商 SDK(AWS、GCP 等)都拉进每个使用者的构建中。导入你所需的后端包,本身就是选择器。如需在运行时选择后端,请在你自己的代码中仅针对所支持的后端编写 switch,并将结果保存为 core.SolanaSigner。
错误处理
每个错误都是带有稳定 Code 的 *core.SignerError(CodeConfigError、CodeHTTPError、CodeRemoteAPIError、CodeSigningFailed、CodeNotInitialized、CodeBroadcastUnconfirmed 等)。Error() 只会按错误码返回固定的消息,绝不包含密钥材料或服务提供方的原始响应。可使用 errors.As、errors.Is(err, &core.SignerError{Code: ...}) 或 core.CodeOf(err) 进行匹配。
在 Crossmint 和 Fordefi native auto 模式下,失败并不意味着交易一定没有上链。当服务提供方可能已接受该交易时,错误码为 core.CodeBroadcastUnconfirmed("SIGNER_BROADCAST_UNCONFIRMED")。创建请求被接受时会设置 ProviderTxID,IdempotencyKey 则携带由消息派生的密钥。重新发送字节完全相同的交易是安全的;而重新构建的交易(例如使用了新的 blockhash)则会成为一笔新的转账。当你的发送函数失败时,core.SignAndSendTransaction 也会报告此错误码,并在 TransactionSignature 中给出手续费支付方的签名。Fireblocks PROGRAM_CALL 仅签名且不发送密钥;只有在轮询显示 Fireblocks 仍然广播了交易时,才会报告此错误码。
sig, err := core.SignAndSendTransaction(ctx, signer, tx, send)var se *core.SignerErrorif errors.As(err, &se) && se.Code == core.CodeBroadcastUnconfirmed {// Reconcile se.ProviderTxID, se.IdempotencyKey or se.TransactionSignature// with the provider before retrying.}
请参阅 安全模型 了解各种签名形态提供的保证,参阅 Go README 查看完整的模块列表,并参阅 pkg.go.dev 获取 API 参考。
添加自定义签名器
请实现 core.SolanaSigner 以及恰好一个能力接口,并且不要添加后端无法支持的方法:调用方通过类型断言来检测能力。请参阅添加签名器指南,以集成更多密钥管理服务。
Is this page helpful?