Go

Version 2.0.0 ist das erste stabile Release der Go-Module, daher gibt es nichts zu migrieren. Der 2.0-Migrationsleitfaden behandelt dieselbe Aufteilung der Fähigkeiten für Rust und TypeScript.

Installation

Jedes Backend ist ein eigenes Go-Modul, sodass Ihr Build und Ihr Modulgraph nur die Backends enthalten, die Sie importieren:

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

Module werden mit go/<module>/v2.0.0 getaggt (zum Beispiel go/signers/memory/v2.0.0). Jedes Modul erfordert Go 1.25.0, mit Ausnahme von gcpkms, das Go 1.25.8 erfordert, wegen der Untergrenze, die von google.golang.org/api vorgegeben wird. Ledger gibt es nur für Rust und hat kein Go-Modul.

solana-go v2 und v1-Transaktionen

Die Module basieren auf github.com/solana-foundation/solana-go/v2 v2.0.0 und akzeptieren dessen *solana.Transaction. Legacy-, v0- und v1-Transaktionen (solana.MessageVersionV1) werden unterstützt, außer von Fireblocks mit UseProgramCall: true, das nur Legacy und v0 akzeptiert.

Grundlegende Verwendung

Jeder Signer implementiert das Basis- core.SolanaSigner-Interface (Pubkey, SignMessage, IsAvailable). Methoden erhalten einen context.Context und blockieren. Außerdem implementiert jedes Backend genau ein Capability-Interface:

  • core.TransactionSigner: SignTransaction signiert Ihre Transaktion unverändert.
  • core.ModifyingSigner: ModifyAndSignTransaction lässt den Anbieter die Transaktion umschreiben und signiert sie anschließend.
  • core.SendingSigner: SignAndSendTransaction lässt den Anbieter die Transaktion signieren und senden.

Jedes Backend-Paket stellt eine Config-Struktur und einen New-Konstruktor bereit:

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 und ModifyAndSignTransaction geben eine core.SignedTransaction zurück (EncodedTransaction, Signature, Completeness). Remote-HTTP-Backends akzeptieren optional HTTPClient und HTTPClientConfig (Timeouts). Der Standard-Client erzwingt HTTPS; wenn Sie einen eigenen verwenden, wird dies umgangen, und Sie sind selbst für dessen Sicherheit verantwortlich.

Signer-Fähigkeiten

Ein Backend, das keine Transaktion signieren kann, hat überhaupt keine SignTransaction-Methode, sodass die Fähigkeit für konkrete Typen zur Kompilierzeit feststeht und bei einem core.SolanaSigner per Type Assertion geprüft wird:

BackendFähigkeitSignMessage
memory, vault, privy, turnkey, awskms, gcpkms, fireblocks, dfns, para, openfortTransactionSignerJa
cdpTransactionSignerNur UTF-8-Payloads
utilaTransactionSignerSchlägt fehl mit CodeSigningFailed
fordefi Black Box (fordefi.BlackBoxSigner)TransactionSignerJa
fordefi nativ manuell (fordefi.NativeManualSigner)ModifyingSignerJa
fordefi nativ automatisch (fordefi.NativeAutoSigner)SendingSignerJa
crossmintSendingSignerSchlägt fehl mit 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)
}

Initialisierung

Es gibt keinen separaten Init-Schritt. Privy, Fireblocks, Dfns, Crossmint, Para, Openfort und Utila ermitteln die Wallet-Adresse innerhalb von New, weshalb ihre Konstruktoren einen context.Context entgegennehmen. Erstellen Sie Signer immer über New: Ein Crossmint-, Para- oder Utila-Signer mit Nullwert schlägt mit CodeNotInitialized fehl, anstatt für die Nulladresse zu signieren.

Signieren und Senden mit jedem Signer

core.SignAndSendTransaction bringt eine Transaktion on-chain, unabhängig von der Form des Signers. Ein SendingSigner sendet über seinen Anbieter und ignoriert die Send- Funktion; bei allen anderen Formen wird signiert, und Ihre Funktion sendet das Base64- Ergebnis, das bei einem ModifyingSigner die vom Anbieter umgeschriebene Transaktion ist. Core hat keine RPC-Abhängigkeit:

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

Batch-Signierung

Die Batch-Signierung wird als freie Hilfsfunktionen bereitgestellt, nebenläufig und mit einem optionalen Versatz pro Anfrage für ratenbegrenzte APIs:

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 erwartet einen core.TransactionSigner, daher können die von Broadcast verwalteten Signer (Crossmint, Fordefi nativ automatisch) und Fordefi nativ manuell nicht darüber gebündelt werden; rufen Sie sie jeweils für eine Transaktion einzeln auf. Ein Signer, dessen Aufrufe anbieterseitige Arbeit erzeugen (Fireblocks mit UseProgramCall), meldet dies über core.BatchServerSideEffects, und der Batch läuft dann sequenziell.

Backend-Konfiguration

Memory

Geben Sie genau eines von PrivateKey (Rohbytes), PrivateKeyString (Base58 oder ein [1,2,...]-Byte-Array) oder PrivateKeyPath (Solana-CLI-keypair-Datei) an:

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

Go bietet keine zuverlässige Möglichkeit, Speicher zu nullen: Der Garbage Collector kann Schlüsselbytes kopieren und bis zur Bereinigung behalten (Memory-keypairs sowie die Schlüssel, die Crossmint und Openfort lokal ableiten). Behandeln Sie den gesamten Prozessspeicher als sensibel, wenn Sie Backends mit lokalen Schlüsseln verwenden.

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 von CDP akzeptiert nur UTF-8-Payloads.

Crossmint

Crossmint kann nur senden: Die API führt jede genehmigte Transaktion serverseitig aus, daher hat *crossmint.Signer SignAndSendTransaction und kein SignTransaction. Ihre Transaktion wird nie verändert; die zurückgegebene Signatur identifiziert die Transaktion, die Crossmint on-chain gebracht hat, und kann sich von Ihrer unterscheiden, wenn Crossmint die Gas-Kosten übernimmt.

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 signiert nur Transaktionen; SignMessage schlägt fehl, und Sie senden die signierte Transaktion selbst.

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 gibt es in drei Signer-Typen, die bei der Erstellung durch Config.Chain und Config.PushMode festgelegt werden. Alle drei signieren Nachrichten:

ConfigTypEinstiegspunkt
Chain nicht gesetzt*fordefi.BlackBoxSignerSignTransaction
Chain gesetzt, PushMode nicht gesetzt oder PushModeAuto*fordefi.NativeAutoSignerSignAndSendTransaction
Chain gesetzt, PushMode: PushModeManual*fordefi.NativeManualSignerModifyAndSignTransaction

fordefi.New verzweigt anhand der Config und gibt einen core.SolanaSigner zurück; fordefi.NewBlackBox, fordefi.NewNativeAuto und fordefi.NewNativeManual geben den konkreten Typ zurück und lehnen die Configs der anderen Modi ab. API-Anfragen werden mit einer ECDSA-P-256-Anfragesignatur authentifiziert: Geben Sie genau eines von PrivateKeyPEM oder einen RequestSigner an, um diesen Schlüssel in einem KMS/HSM zu behalten.

Nativer Auto-Modus: Fordefi kann den Blockhash und die Fee ersetzen, signiert dann und sendet. Akzeptiert wird nur eine unsignierte Transaktion, deren einziger erforderlicher Signer der Vault ist.

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)

Nativer manueller Modus: Setzen Sie PushMode: fordefi.PushModeManual. Fordefi schreibt die Nachricht um (aktueller Blockhash, Compute-Budget-Fee-Anweisungen) und signiert sie ohne zu senden. ModifyAndSignTransaction ersetzt tx durch die von Fordefi signierte Transaktion: Arbeiten Sie immer mit dieser weiter. Fordefi muss der Fee-Payer sein und vor jedem anderen erforderlichen Signer signieren, da das Umschreiben bestehende Signaturen ungültig macht. Das Umschreiben wird nicht per Diff dargestellt, prüfen Sie das Ergebnis daher vor dem Senden.

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-Modus: Lassen Sie Chain ungesetzt. Fordefi signiert Ihre exakten Nachrichtenbytes und sendet nicht. Verwenden Sie diesen Modus mit einem 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),
})

Warum es kein Sammelpaket gibt

Rust hat ein Signer-Enum und TypeScript hat createKeychainSigner; Go hat absichtlich keines von beiden. Go eliminiert über einen Laufzeit- Dispatch-Switch hinweg keinen toten Code, daher würde ein Sammelpaket die Vendor-SDKs aller Backends (AWS, GCP und die übrigen) in den Build jedes Nutzers zwingen. Das Importieren des benötigten Backend-Pakets ist die Auswahl. Um ein Backend zur Laufzeit auszuwählen, schreiben Sie den Switch in Ihrem eigenen Code nur über die Backends, die Sie unterstützen, und halten Sie das Ergebnis als core.SolanaSigner.

Fehlerbehandlung

Jeder Fehler ist ein *core.SignerError mit einem stabilen Code (CodeConfigError, CodeHTTPError, CodeRemoteAPIError, CodeSigningFailed, CodeNotInitialized, CodeBroadcastUnconfirmed und weitere). Error() gibt nur eine feste Meldung pro Code zurück, niemals Schlüsselmaterial oder rohe Anbieterantworten. Prüfen Sie mit errors.As, errors.Is(err, &core.SignerError{Code: ...}) oder core.CodeOf(err).

Bei Crossmint und Fordefi im nativen Auto-Modus bedeutet ein Fehler nicht, dass nichts übermittelt wurde. Wenn der Anbieter die Transaktion möglicherweise akzeptiert hat, lautet der Code core.CodeBroadcastUnconfirmed ("SIGNER_BROADCAST_UNCONFIRMED"). ProviderTxID wird gesetzt, wenn die Erstellung akzeptiert wurde, und IdempotencyKey enthält den aus der Nachricht abgeleiteten Schlüssel. Das erneute Senden der byte-identischen Transaktion ist sicher; eine neu erstellte (zum Beispiel mit einem neuen Blockhash) ist eine neue Überweisung. core.SignAndSendTransaction meldet diesen Code ebenfalls, wenn Ihre Send-Funktion fehlschlägt, mit der Fee-Payer-Signatur in TransactionSignature. Fireblocks PROGRAM_CALL signiert nur und sendet nichts; er meldet diesen Code nur, wenn eine Abfrage zeigt, dass Fireblocks trotzdem gesendet hat.

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

Siehe das Sicherheitsmodell für die Garantien der einzelnen Signierformen, die Go-README für die vollständige Modulliste und pkg.go.dev für die API-Referenz.

Eigene Signer hinzufügen

Implementieren Sie core.SolanaSigner plus genau ein Capability-Interface und fügen Sie keine Methode hinzu, die Ihr Backend nicht erfüllen kann: Aufrufer erkennen Fähigkeiten per Type Assertion. Siehe den Leitfaden zum Hinzufügen von Signern, um weitere Key-Management-Dienste zu integrieren.

Is this page helpful?

© 2026 Solana Foundation. Alle Rechte vorbehalten.