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, 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
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:SignTransactionsigniert Ihre Transaktion unverändert.core.ModifyingSigner:ModifyAndSignTransactionlässt den Anbieter die Transaktion umschreiben und signiert sie anschließend.core.SendingSigner:SignAndSendTransactionlä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 transactionreturn 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:
| Backend | Fähigkeit | SignMessage |
|---|---|---|
| memory, vault, privy, turnkey, awskms, gcpkms, fireblocks, dfns, para, openfort | TransactionSigner | Ja |
| cdp | TransactionSigner | Nur UTF-8-Payloads |
| utila | TransactionSigner | Schlägt fehl mit CodeSigningFailed |
fordefi Black Box (fordefi.BlackBoxSigner) | TransactionSigner | Ja |
fordefi nativ manuell (fordefi.NativeManualSigner) | ModifyingSigner | Ja |
fordefi nativ automatisch (fordefi.NativeAutoSigner) | SendingSigner | Ja |
| crossmint | SendingSigner | Schlä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 = unboundedRequestDelay: 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 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})
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:
| Config | Typ | Einstiegspunkt |
|---|---|---|
Chain nicht gesetzt | *fordefi.BlackBoxSigner | SignTransaction |
Chain gesetzt, PushMode nicht gesetzt oder PushModeAuto | *fordefi.NativeAutoSigner | SignAndSendTransaction |
Chain gesetzt, PushMode: PushModeManual | *fordefi.NativeManualSigner | ModifyAndSignTransaction |
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.ChainSolanaMainnetFee: &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.SignerErrorif 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?