La versione 2.0.0 è la prima release stabile dei moduli Go, quindi non c'è nulla da migrare. La guida alla migrazione 2.0 illustra 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, eccetto gcpkms,
che richiede Go 1.25.8 a causa del requisito minimo imposto da google.golang.org/api.
Ledger è disponibile solo per Rust e non ha un modulo Go.
solana-go v2 e transazioni v1
I moduli sono basati su
github.com/solana-foundation/solana-go/v2
v2.0.0 e accettano il suo *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 un HTTPClient e un HTTPClientConfig opzionali
(timeout). Il client predefinito impone HTTPS; fornirne uno proprio aggira questo vincolo,
e la sua sicurezza resta sotto la tua responsabilità.
Capacità dei signer
Un backend che non può firmare una transazione non ha alcun metodo SignTransaction,
quindi la capacità è un fatto 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. Crea sempre i signer tramite New: un
signer Crossmint, Para o Utila a valore zero fallisce con CodeNotInitialized
anziché firmare per l'indirizzo zero.
Firmare e inviare con qualsiasi signer
core.SignAndSendTransaction porta una transazione on-chain indipendentemente dalla
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 alcuna dipendenza 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 liberi, concorrenti, con uno sfasamento opzionale per richiesta per le API soggette a rate limit:
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 usati in batch; chiamali una transazione alla volta. Un
signer le cui chiamate creano lavoro lato provider (Fireblocks con UseProgramCall)
lo segnala tramite core.BatchServerSideEffects, e il batch viene quindi eseguito
in sequenza.
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 alcun modo affidabile per azzerare la memoria: il garbage collector può copiare e conservare i byte delle chiavi (keypair in memoria e chiavi che Crossmint e Openfort derivano localmente) fino alla raccolta. Considera l'intera memoria del processo come sensibile 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 è solo per 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 effettivamente registrato, 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 la transazione firmata
devi trasmetterla tu stesso.
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 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 configurazione e restituisce un core.SolanaSigner;
fordefi.NewBlackBox, fordefi.NewNativeAuto e fordefi.NewNativeManual
restituiscono il tipo concreto e rifiutano le configurazioni delle altre modalità. Le richieste API vengono
autenticate con una firma della richiesta ECDSA P-256: fornisci esattamente uno tra
PrivateKeyPEM oppure un RequestSigner per mantenere quella chiave in un KMS/HSM.
Modalità native auto: Fordefi può sostituire il blockhash e le 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 del Compute Budget) e lo firma
senza trasmetterlo. ModifyAndSignTransaction sostituisce tx con la
transazione firmata da Fordefi: continua 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 produce un 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 esattamente i byte 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 né l'uno né l'altro. Go non elimina il codice morto attraverso un'istruzione switch di
dispatch a runtime, quindi un pacchetto ombrello imporrebbe l'SDK del fornitore di ogni backend (AWS, GCP
e gli altri) nella build di ogni consumatore. Importare il pacchetto del backend di cui
hai bisogno è il selettore. 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 codice, mai materiale delle chiavi né risposte grezze del provider.
Esegui 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 nulla
sia stato registrato. Quando il provider potrebbe aver accettato la transazione, il codice è
core.CodeBroadcastUnconfirmed ("SIGNER_BROADCAST_UNCONFIRMED").
ProviderTxID è impostato 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 segnala questo codice anche quando la tua funzione di invio
fallisce, con la firma del fee payer in TransactionSignature. Fireblocks
PROGRAM_CALL è solo di firma e non invia alcuna chiave; segnala questo codice solo quando un
polling mostra che Fireblocks ha comunque trasmesso.
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 sapere cosa garantisce ogni forma 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 possa onorare: i chiamanti rilevano la capacità tramite type assertion.
Consulta la guida Adding Signers per integrare
ulteriori servizi di gestione delle chiavi.
Is this page helpful?