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, 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
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:SignTransactionfirma la transazione così com'è.core.ModifyingSigner:ModifyAndSignTransactionconsente al provider di riscrivere la transazione e poi la firma.core.SendingSigner:SignAndSendTransactionfa 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 transactionreturn 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:
| Backend | Capacità | SignMessage |
|---|---|---|
| memory, vault, privy, turnkey, awskms, gcpkms, fireblocks, dfns, para, openfort | TransactionSigner | Sì |
| cdp | TransactionSigner | Solo payload UTF-8 |
| utila | TransactionSigner | Fallisce con CodeSigningFailed |
fordefi black box (fordefi.BlackBoxSigner) | TransactionSigner | Sì |
fordefi native manual (fordefi.NativeManualSigner) | ModifyingSigner | Sì |
fordefi native auto (fordefi.NativeAutoSigner) | SendingSigner | Sì |
| crossmint | SendingSigner | Fallisce 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 = unboundedRequestDelay: 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 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 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:
| Config | Tipo | Punto di ingresso |
|---|---|---|
Chain non impostato | *fordefi.BlackBoxSigner | SignTransaction |
Chain impostato, PushMode non impostato o PushModeAuto | *fordefi.NativeAutoSigner | SignAndSendTransaction |
Chain impostato, PushMode: PushModeManual | *fordefi.NativeManualSigner | ModifyAndSignTransaction |
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.ChainSolanaMainnetFee: &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.SignerErrorif 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?