Go

2.0.0 è la prima release stabile dei moduli Go, quindi non c'è nulla da migrare. La guida alla migrazione a 2.0 copre la stessa suddivisione delle capacità per Rust e TypeScript.

Installazione

Ogni backend è un modulo Go a sé stante, quindi la tua build e il tuo grafo dei moduli contengono solo i backend che importi:

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

I moduli sono taggati go/<module>/v2.0.0 (ad esempio go/signers/memory/v2.0.0). Ogni modulo richiede Go 1.25.0, tranne gcpkms, che richiede Go 1.25.8 a causa del requisito minimo imposto da google.golang.org/api. Ledger è solo Rust e non ha un modulo Go.

Transazioni solana-go v2 e v1

I moduli sono basati su github.com/solana-foundation/solana-go/v2 v2.0.0 e accettano il relativo *solana.Transaction. Sono supportate le transazioni legacy, v0 e v1 (solana.MessageVersionV1), tranne che da Fireblocks con UseProgramCall: true, che accetta solo legacy e v0.

Utilizzo di base

Ogni signer implementa l'interfaccia base core.SolanaSigner (Pubkey, SignMessage, IsAvailable). I metodi accettano un context.Context e sono bloccanti. Ogni backend implementa inoltre esattamente un'interfaccia di capacità:

  • core.TransactionSigner: SignTransaction firma la transazione così com'è.
  • core.ModifyingSigner: ModifyAndSignTransaction consente al provider di riscrivere la transazione e poi la firma.
  • core.SendingSigner: SignAndSendTransaction fa firmare e trasmettere la transazione dal provider.

Ogni pacchetto backend espone una struct Config e un costruttore 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 e ModifyAndSignTransaction restituiscono un core.SignedTransaction (EncodedTransaction, Signature, Completeness). I backend HTTP remoti accettano HTTPClient e HTTPClientConfig opzionali (timeout). Il client predefinito impone HTTPS; fornendone uno proprio si aggira questo vincolo e ci si assume la responsabilità della relativa sicurezza.

Capacità dei signer

Un backend che non può firmare una transazione non ha alcun metodo SignTransaction, quindi la capacità è un dato noto in fase di compilazione per i tipi concreti e una type assertion per un core.SolanaSigner:

BackendCapacitàSignMessage
memory, vault, privy, turnkey, awskms, gcpkms, fireblocks, dfns, para, openfortTransactionSignerSì
cdpTransactionSignerSolo payload UTF-8
utilaTransactionSignerFallisce con CodeSigningFailed
fordefi black box (fordefi.BlackBoxSigner)TransactionSignerSì
fordefi native manual (fordefi.NativeManualSigner)ModifyingSignerSì
fordefi native auto (fordefi.NativeAutoSigner)SendingSignerSì
crossmintSendingSignerFallisce con 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)
}

Inizializzazione

Non esiste un passaggio Init separato. Privy, Fireblocks, Dfns, Crossmint, Para, Openfort e Utila risolvono l'indirizzo del wallet all'interno di New, motivo per cui i loro costruttori accettano un context.Context. Costruisci sempre i signer tramite New: un signer Crossmint, Para o Utila a valore zero fallisce con CodeNotInitialized invece di firmare per l'indirizzo zero.

Firmare e inviare con qualsiasi signer

core.SignAndSendTransaction porta una transazione on-chain qualunque sia la forma del signer. Un SendingSigner trasmette tramite il proprio provider e ignora la funzione di invio; ogni altra forma firma e la tua funzione trasmette il risultato in base64, che per un ModifyingSigner è la transazione riscritta dal suo provider. Core non ha dipendenze 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)
})

Firma in batch

La firma in batch è fornita come helper indipendenti, eseguiti in modo concorrente, con un intervallo facoltativo per richiesta per le API soggette a limiti di frequenza:

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 accetta un core.TransactionSigner, quindi i signer con broadcast gestito (Crossmint, Fordefi native auto) e Fordefi native manual non possono essere processati in batch con questo helper; chiamali una transazione alla volta. Un signer le cui chiamate generano lavoro lato provider (Fireblocks con UseProgramCall) lo segnala tramite core.BatchServerSideEffects, e il batch viene quindi eseguito in modo sequenziale.

Configurazione dei backend

Memory

Fornisci esattamente uno tra PrivateKey (byte grezzi), PrivateKeyString (base58 o un array di byte [1,2,...]) o PrivateKeyPath (file keypair della 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 non offre un modo affidabile per azzerare la memoria: il garbage collector può copiare e conservare i byte delle chiavi (i keypair del backend memory e le chiavi che Crossmint e Openfort derivano localmente) fino alla successiva raccolta. Considera sensibile l'intera memoria del processo quando usi backend con chiavi locali.

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 di CDP accetta solo payload UTF-8.

Crossmint

Crossmint supporta solo l'invio: la sua API esegue ogni transazione approvata lato server, quindi *crossmint.Signer ha SignAndSendTransaction e non ha SignTransaction. La tua transazione non viene mai modificata; la firma restituita identifica la transazione che Crossmint ha mandato a buon fine, che può differire dalla tua se Crossmint sponsorizza il 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 firma solo le transazioni; SignMessage fallisce e sei tu a trasmettere la transazione firmata.

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 è disponibile in tre tipi di signer, stabiliti in fase di costruzione da Config.Chain e Config.PushMode. Tutti e tre firmano i messaggi:

ConfigTipoPunto di ingresso
Chain non impostato*fordefi.BlackBoxSignerSignTransaction
Chain impostato, PushMode non impostato o PushModeAuto*fordefi.NativeAutoSignerSignAndSendTransaction
Chain impostato, PushMode: PushModeManual*fordefi.NativeManualSignerModifyAndSignTransaction

fordefi.New smista in base alla config e restituisce un core.SolanaSigner; fordefi.NewBlackBox, fordefi.NewNativeAuto e fordefi.NewNativeManual restituiscono il tipo concreto e rifiutano le config delle altre modalità. Le richieste API sono autenticate con una firma della richiesta ECDSA P-256: fornisci esattamente uno tra PrivateKeyPEM o un RequestSigner per mantenere quella chiave in un KMS/HSM.

Modalità native auto: Fordefi può sostituire blockhash e commissioni, quindi firma e trasmette. Accetta solo una transazione non firmata il cui unico firmatario richiesto è il 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)

Modalità native manual: imposta PushMode: fordefi.PushModeManual. Fordefi riscrive il messaggio (recent blockhash, istruzioni di commissione Compute Budget) e lo firma senza trasmetterlo. ModifyAndSignTransaction sostituisce tx con la transazione firmata da Fordefi: prosegui sempre da quella. Fordefi deve essere il fee payer e deve firmare prima di qualsiasi altro firmatario richiesto, poiché la riscrittura invalida le firme esistenti. La riscrittura non viene confrontata con l'originale (diff), quindi ispeziona il risultato prima di trasmettere.

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)

Modalità black box: lascia Chain non impostato. Fordefi firma i byte esatti del tuo messaggio e non trasmette. Usala con un vault black box di Fordefi.

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

Perché non esiste un pacchetto ombrello

Rust ha un enum Signer e TypeScript ha createKeychainSigner; Go volutamente non ha nessuno dei due. Go non elimina il codice morto attraverso uno switch di dispatch a runtime, quindi un pacchetto ombrello imporrebbe l'inclusione dell'SDK del vendor di ogni backend (AWS, GCP e gli altri) nella build di ogni consumer. Importare il pacchetto del backend che serve è già la selezione. Per scegliere un backend a runtime, scrivi lo switch nel tuo codice solo sui backend che supporti e conserva il risultato come core.SolanaSigner.

Gestione degli errori

Ogni errore è un *core.SignerError con un Code stabile (CodeConfigError, CodeHTTPError, CodeRemoteAPIError, CodeSigningFailed, CodeNotInitialized, CodeBroadcastUnconfirmed e altri). Error() restituisce solo un messaggio fisso per ciascun codice, mai materiale delle chiavi o risposte grezze del provider. Effettua il confronto con errors.As, errors.Is(err, &core.SignerError{Code: ...}) oppure core.CodeOf(err).

Con Crossmint e Fordefi in modalità native auto, un errore non significa che non sia stato recapitato nulla. Quando il provider potrebbe aver accettato la transazione, il codice è core.CodeBroadcastUnconfirmed ("SIGNER_BROADCAST_UNCONFIRMED"). ProviderTxID viene valorizzato quando la creazione è stata accettata, e IdempotencyKey contiene la chiave derivata dal messaggio. Reinviare la transazione identica byte per byte è sicuro; una ricostruita (ad esempio con un nuovo blockhash) è un nuovo trasferimento. core.SignAndSendTransaction restituisce questo codice anche quando la tua funzione di invio fallisce, con la firma del fee payer in TransactionSignature. Fireblocks PROGRAM_CALL è solo firma e non invia alcuna chiave; restituisce questo codice solo quando un polling mostra che Fireblocks ha comunque trasmesso la transazione.

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

Consulta il modello di sicurezza per le garanzie offerte da ciascuna modalità di firma, il README di Go per l'elenco completo dei moduli e pkg.go.dev per il riferimento API.

Aggiungere signer personalizzati

Implementa core.SolanaSigner più esattamente un'interfaccia di capacità e non aggiungere alcun metodo che il tuo backend non può supportare: i chiamanti rilevano la capacità tramite type assertion. Consulta la guida all'aggiunta dei signer per integrare ulteriori servizi di gestione delle chiavi.

Is this page helpful?

© 2026 Solana Foundation. Tutti i diritti riservati.