Go

2.0.0 là bản phát hành ổn định đầu tiên của các module Go, nên không có gì cần migrate. Hướng dẫn migrate lên 2.0 đề cập đến cách phân tách khả năng (capability) tương tự dành cho Rust và TypeScript.

Cài đặt

Mỗi backend là một module Go riêng, nên bản build và đồ thị module của bạn chỉ chứa những backend mà bạn import:

go get github.com/solana-foundation/solana-keychain/go/core/v2 # Interfaces, errors, helpers
go get github.com/solana-foundation/solana-keychain/go/signers/memory/v2 # Local keypair signer
go get github.com/solana-foundation/solana-keychain/go/signers/vault/v2 # HashiCorp Vault
go get github.com/solana-foundation/solana-keychain/go/signers/awskms/v2 # AWS KMS
go get github.com/solana-foundation/solana-keychain/go/signers/gcpkms/v2 # GCP KMS
go get github.com/solana-foundation/solana-keychain/go/signers/privy/v2 # Privy
go get github.com/solana-foundation/solana-keychain/go/signers/turnkey/v2 # Turnkey
go get github.com/solana-foundation/solana-keychain/go/signers/fireblocks/v2 # Fireblocks
go get github.com/solana-foundation/solana-keychain/go/signers/cdp/v2 # Coinbase Developer Platform
go get github.com/solana-foundation/solana-keychain/go/signers/crossmint/v2 # Crossmint
go get github.com/solana-foundation/solana-keychain/go/signers/dfns/v2 # Dfns
go get github.com/solana-foundation/solana-keychain/go/signers/openfort/v2 # Openfort
go get github.com/solana-foundation/solana-keychain/go/signers/para/v2 # Para
go get github.com/solana-foundation/solana-keychain/go/signers/utila/v2 # Utila
go get github.com/solana-foundation/solana-keychain/go/signers/fordefi/v2 # Fordefi

Các module được gắn tag go/<module>/v2.0.0 (ví dụ go/signers/memory/v2.0.0). Mọi module đều yêu cầu Go 1.25.0, ngoại trừ gcpkms, yêu cầu Go 1.25.8 do mức tối thiểu được đặt bởi google.golang.org/api. Ledger chỉ dành cho Rust và không có module Go.

Giao dịch solana-go v2 và v1

Các module được xây dựng trên github.com/solana-foundation/solana-go/v2 v2.0.0 và nhận *solana.Transaction của thư viện này. Hỗ trợ các giao dịch legacy, v0 và v1 (solana.MessageVersionV1), ngoại trừ Fireblocks với UseProgramCall: true, chỉ chấp nhận legacy và v0.

Cách sử dụng cơ bản

Mọi signer đều triển khai interface core.SolanaSigner cơ sở (Pubkey, SignMessage, IsAvailable). Các phương thức nhận một context.Context và chạy theo kiểu blocking. Mỗi backend cũng triển khai đúng một interface khả năng:

  • core.TransactionSigner: SignTransaction ký giao dịch của bạn đúng như được cung cấp.
  • core.ModifyingSigner: ModifyAndSignTransaction cho phép nhà cung cấp viết lại giao dịch, rồi ký giao dịch đó.
  • core.SendingSigner: SignAndSendTransaction để nhà cung cấp ký và broadcast giao dịch.

Mỗi package backend cung cấp một struct Config và một hàm khởi tạo 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 transaction
return nil
}

SignTransaction và ModifyAndSignTransaction trả về một core.SignedTransaction (EncodedTransaction, Signature, Completeness). Các backend HTTP từ xa chấp nhận HTTPClient và HTTPClientConfig (timeout) tùy chọn. Client mặc định bắt buộc dùng HTTPS; nếu tự cung cấp client thì sẽ bỏ qua điều này, và bạn tự chịu trách nhiệm về mức độ bảo mật của client đó.

Khả năng của Signer

Một backend không thể ký giao dịch sẽ hoàn toàn không có phương thức SignTransaction, nên khả năng là điều được xác định tại thời điểm biên dịch đối với các kiểu cụ thể và là một type assertion đối với core.SolanaSigner:

BackendKhả năngSignMessage
memory, vault, privy, turnkey, awskms, gcpkms, fireblocks, dfns, para, openfortTransactionSignerCó
cdpTransactionSignerChỉ hỗ trợ payload UTF-8
utilaTransactionSignerThất bại với CodeSigningFailed
fordefi black box (fordefi.BlackBoxSigner)TransactionSignerCó
fordefi native manual (fordefi.NativeManualSigner)ModifyingSignerCó
fordefi native auto (fordefi.NativeAutoSigner)SendingSignerCó
crossmintSendingSignerThất bại với 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)
}

Khởi tạo

Không có bước Init riêng. Privy, Fireblocks, Dfns, Crossmint, Para, Openfort và Utila xác định địa chỉ ví bên trong New, đó là lý do các hàm khởi tạo của chúng nhận một context.Context. Hãy luôn tạo signer thông qua New: một signer Crossmint, Para hoặc Utila ở giá trị zero sẽ thất bại với CodeNotInitialized thay vì ký cho địa chỉ zero.

Ký và gửi với bất kỳ Signer nào

core.SignAndSendTransaction đưa một giao dịch lên chain bất kể signer có dạng nào. Một SendingSigner broadcast thông qua nhà cung cấp của nó và bỏ qua hàm gửi; mọi dạng khác sẽ ký và hàm của bạn broadcast kết quả base64, với ModifyingSigner thì đó là giao dịch mà nhà cung cấp đã viết lại. Core không phụ thuộc vào 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)
})

Ký hàng loạt

Việc ký hàng loạt được cung cấp dưới dạng các hàm trợ giúp độc lập, chạy đồng thời với độ trễ tùy chọn giữa các yêu cầu dành cho các API bị giới hạn tần suất:

sigs, err := core.SignMessages(ctx, signer, [][]byte{msg1, msg2}, core.BatchOptions{
MaxConcurrency: 4, // 0 = unbounded
RequestDelay: 50 * time.Millisecond,
})
signed, err := core.SignTransactions(ctx, signer, txs, core.BatchOptions{})

core.SignTransactions nhận một core.TransactionSigner, nên các signer do broadcast quản lý (Crossmint, Fordefi native auto) và Fordefi native manual không thể được xử lý hàng loạt qua hàm này; hãy gọi từng giao dịch một. Một signer mà các lệnh gọi tạo ra công việc phía nhà cung cấp (Fireblocks với UseProgramCall) sẽ báo điều đó thông qua core.BatchServerSideEffects, và khi đó batch sẽ chạy tuần tự.

Cấu hình Backend

Memory

Cung cấp đúng một trong các tùy chọn sau: PrivateKey (byte thô), PrivateKeyString (base58 hoặc một mảng byte [1,2,...]) hoặc PrivateKeyPath (tệp keypair của Solana CLI):

import "github.com/solana-foundation/solana-keychain/go/signers/memory/v2"
signer, err := memory.New(memory.Config{
PrivateKeyPath: "/path/to/id.json",
})

Go không có cách đáng tin cậy để xóa sạch bộ nhớ: bộ thu gom rác có thể sao chép và giữ lại các byte của khóa (keypair trong memory, và các khóa mà Crossmint và Openfort tạo ra cục bộ) cho đến khi được thu gom. Hãy coi toàn bộ bộ nhớ của tiến trình là nhạy cảm khi sử dụng các backend dùng khóa cục bộ.

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 devnet
UseProgramCall: 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 Ed25519
WalletSecret: "wallet_secret", // base64 PKCS#8 DER
Address: "base58_address",
Network: cdp.NetworkMainnet, // or cdp.NetworkDevnet; required for address lookup tables
})

SignMessage của CDP chỉ chấp nhận payload UTF-8.

Crossmint

Crossmint chỉ hỗ trợ gửi: API của nó thực thi mọi giao dịch đã được phê duyệt ở phía máy chủ, nên *crossmint.Signer có SignAndSendTransaction mà không có SignTransaction. Giao dịch của bạn không bao giờ bị sửa đổi; chữ ký được trả về xác định giao dịch mà Crossmint đã đưa lên chain, có thể khác với giao dịch của bạn nếu Crossmint tài trợ phí 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 chỉ ký giao dịch; SignMessage sẽ thất bại, và bạn tự broadcast giao dịch đã ký.

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 có ba loại signer, được cố định khi khởi tạo bởi Config.Chain và Config.PushMode. Cả ba đều có thể ký message:

ConfigKiểuĐiểm vào
Chain không được đặt*fordefi.BlackBoxSignerSignTransaction
Chain được đặt, PushMode không được đặt hoặc là PushModeAuto*fordefi.NativeAutoSignerSignAndSendTransaction
Chain được đặt, PushMode: PushModeManual*fordefi.NativeManualSignerModifyAndSignTransaction

fordefi.New điều phối dựa trên config và trả về một core.SolanaSigner; fordefi.NewBlackBox, fordefi.NewNativeAuto và fordefi.NewNativeManual trả về kiểu cụ thể và từ chối config của các chế độ khác. Các yêu cầu API được xác thực bằng chữ ký yêu cầu ECDSA P-256: hãy cung cấp đúng một trong PrivateKeyPEM, hoặc một RequestSigner để giữ khóa đó trong KMS/HSM.

Chế độ native auto: Fordefi có thể thay thế blockhash và phí, sau đó ký và broadcast. Chế độ này chỉ chấp nhận giao dịch chưa ký mà signer bắt buộc duy nhất là 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.ChainSolanaMainnet
Fee: &fordefi.Fee{
Type: fordefi.FeeTypePriority,
PriorityLevel: fordefi.PriorityMedium,
},
})
sig, err := signer.SignAndSendTransaction(ctx, tx)

Chế độ native manual: đặt PushMode: fordefi.PushModeManual. Fordefi viết lại message (recent blockhash, các instruction phí Compute Budget) và ký mà không broadcast. ModifyAndSignTransaction thay tx bằng giao dịch mà Fordefi đã ký: hãy luôn tiếp tục từ giao dịch này. Fordefi phải là bên trả phí và phải ký trước mọi signer bắt buộc khác, vì việc viết lại sẽ vô hiệu hóa các chữ ký hiện có. Phần viết lại không có diff, vì vậy hãy kiểm tra kết quả trước khi broadcast.

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)

Chế độ black box: để Chain không được đặt. Fordefi ký chính xác các byte message của bạn và không broadcast. Hãy dùng chế độ này với vault Fordefi black box.

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),
})

Vì sao không có package tổng hợp

Rust có enum Signer và TypeScript có createKeychainSigner; Go cố ý không có cả hai. Go không loại bỏ dead code xuyên qua một câu lệnh switch điều phối lúc runtime, nên một package tổng hợp sẽ buộc SDK nhà cung cấp của mọi backend (AWS, GCP và các nhà cung cấp còn lại) phải có mặt trong bản build của mọi người dùng. Việc import package backend mà bạn cần chính là cách chọn backend. Để chọn backend lúc runtime, hãy tự viết câu lệnh switch trong mã của bạn chỉ với các backend bạn hỗ trợ, và giữ kết quả dưới dạng một core.SolanaSigner.

Xử lý lỗi

Mọi lỗi đều là một *core.SignerError với Code ổn định (CodeConfigError, CodeHTTPError, CodeRemoteAPIError, CodeSigningFailed, CodeNotInitialized, CodeBroadcastUnconfirmed và các mã khác). Error() chỉ trả về một thông báo cố định cho mỗi mã, không bao giờ chứa dữ liệu khóa hay phản hồi thô của nhà cung cấp. Hãy so khớp bằng errors.As, errors.Is(err, &core.SignerError{Code: ...}) hoặc core.CodeOf(err).

Với Crossmint và chế độ Fordefi native auto, một lỗi không có nghĩa là không có gì được đưa lên chain. Khi nhà cung cấp có thể đã chấp nhận giao dịch, mã lỗi là core.CodeBroadcastUnconfirmed ("SIGNER_BROADCAST_UNCONFIRMED"). ProviderTxID được đặt khi yêu cầu tạo đã được chấp nhận, và IdempotencyKey chứa khóa được suy ra từ message. Gửi lại giao dịch giống hệt từng byte là an toàn; một giao dịch được dựng lại (ví dụ với blockhash mới) là một lần chuyển mới. core.SignAndSendTransaction cũng báo mã này khi hàm gửi của bạn thất bại, kèm chữ ký của bên trả phí trong TransactionSignature. Fireblocks PROGRAM_CALL chỉ ký và không gửi khóa; nó chỉ báo mã này khi một lần poll cho thấy Fireblocks vẫn đã broadcast.

sig, err := core.SignAndSendTransaction(ctx, signer, tx, send)
var se *core.SignerError
if errors.As(err, &se) && se.Code == core.CodeBroadcastUnconfirmed {
// Reconcile se.ProviderTxID, se.IdempotencyKey or se.TransactionSignature
// with the provider before retrying.
}

Xem mô hình bảo mật để biết mỗi dạng ký đảm bảo điều gì, Go README để xem danh sách module đầy đủ, và pkg.go.dev để xem tài liệu tham khảo API.

Thêm Signer tùy chỉnh

Hãy triển khai core.SolanaSigner cùng đúng một interface khả năng, và không thêm phương thức nào mà backend của bạn không thể đáp ứng: bên gọi phát hiện khả năng bằng type assertion. Xem hướng dẫn Thêm Signer để tích hợp các dịch vụ quản lý khóa bổ sung.

Is this page helpful?