Go

Το 2.0.0 είναι η πρώτη σταθερή έκδοση των modules Go, επομένως δεν υπάρχει τίποτα προς μετάβαση. Ο οδηγός μετάβασης στην έκδοση 2.0 καλύπτει τον ίδιο διαχωρισμό δυνατοτήτων για Rust και TypeScript.

Εγκατάσταση

Κάθε backend είναι το δικό του module Go, οπότε το build και το γράφημα των modules σας περιέχουν μόνο τα backends που εισάγετε:

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

Τα modules φέρουν ετικέτα go/<module>/v2.0.0 (για παράδειγμα go/signers/memory/v2.0.0). Κάθε module απαιτεί Go 1.25.0, εκτός από το gcpkms, που απαιτεί Go 1.25.8 λόγω του κατώτατου ορίου που ορίζει το google.golang.org/api. Το Ledger υποστηρίζεται μόνο σε Rust και δεν έχει module Go.

solana-go v2 και συναλλαγές v1

Τα modules βασίζονται στο github.com/solana-foundation/solana-go/v2 v2.0.0 και δέχονται το *solana.Transaction του. Υποστηρίζονται συναλλαγές legacy, v0 και v1 (solana.MessageVersionV1), εκτός από το Fireblocks με UseProgramCall: true, το οποίο δέχεται μόνο legacy και v0.

Βασική χρήση

Κάθε signer υλοποιεί τη βασική διεπαφή core.SolanaSigner (Pubkey, SignMessage, IsAvailable). Οι μέθοδοι δέχονται ένα context.Context και μπλοκάρουν. Κάθε backend υλοποιεί επίσης ακριβώς μία διεπαφή δυνατοτήτων:

  • core.TransactionSigner: το SignTransaction υπογράφει τη συναλλαγή σας ως έχει.
  • core.ModifyingSigner: το ModifyAndSignTransaction επιτρέπει στον πάροχο να ξαναγράψει τη συναλλαγή και στη συνέχεια την υπογράφει.
  • core.SendingSigner: το SignAndSendTransaction αναθέτει στον πάροχο να την υπογράψει και να την μεταδώσει.

Κάθε πακέτο backend εκθέτει μια δομή Config και έναν constructor 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 και ModifyAndSignTransaction επιστρέφουν ένα core.SignedTransaction (EncodedTransaction, Signature, Completeness). Τα απομακρυσμένα backends HTTP δέχονται προαιρετικά HTTPClient και HTTPClientConfig (χρονικά όρια). Ο προεπιλεγμένος client επιβάλλει HTTPS· αν δώσετε δικό σας, αυτό παρακάμπτεται και αναλαμβάνετε εσείς την ευθύνη για την ασφάλειά του.

Δυνατότητες signer

Ένα backend που δεν μπορεί να υπογράψει συναλλαγή δεν διαθέτει καθόλου μέθοδο SignTransaction, οπότε η δυνατότητα είναι γεγονός χρόνου μεταγλώττισης για τους συγκεκριμένους τύπους και έλεγχος τύπου (type assertion) για ένα core.SolanaSigner:

BackendΔυνατότηταSignMessage
memory, vault, privy, turnkey, awskms, gcpkms, fireblocks, dfns, para, openfortTransactionSignerΝαι
cdpTransactionSignerΜόνο payloads UTF-8
utilaTransactionSignerΑποτυγχάνει με CodeSigningFailed
fordefi black box (fordefi.BlackBoxSigner)TransactionSignerΝαι
fordefi native manual (fordefi.NativeManualSigner)ModifyingSignerΝαι
fordefi native auto (fordefi.NativeAutoSigner)SendingSignerΝαι
crossmintSendingSignerΑποτυγχάνει με 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)
}

Αρχικοποίηση

Δεν υπάρχει ξεχωριστό βήμα Init. Τα Privy, Fireblocks, Dfns, Crossmint, Para, Openfort και Utila επιλύουν τη διεύθυνση του πορτοφολιού μέσα στο New, γι' αυτό και οι constructors τους δέχονται ένα context.Context. Δημιουργείτε πάντα signers μέσω του New: ένας signer Crossmint, Para ή Utila με μηδενική τιμή αποτυγχάνει με CodeNotInitialized αντί να υπογράψει για τη μηδενική διεύθυνση.

Υπογραφή και αποστολή με οποιονδήποτε signer

Το core.SignAndSendTransaction φέρνει μια συναλλαγή στην αλυσίδα όποια κι αν είναι η μορφή του signer. Ένας SendingSigner μεταδίδει μέσω του παρόχου του και αγνοεί τη συνάρτηση αποστολής· κάθε άλλη μορφή υπογράφει και η δική σας συνάρτηση μεταδίδει το αποτέλεσμα σε base64, το οποίο για έναν ModifyingSigner είναι η συναλλαγή που ξαναέγραψε ο πάροχός του. Ο πυρήνας (core) δεν έχει εξάρτηση από 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)
})

Υπογραφή σε παρτίδες

Η υπογραφή σε παρτίδες παρέχεται ως ανεξάρτητες βοηθητικές συναρτήσεις, ταυτόχρονα, με προαιρετική κλιμακωτή καθυστέρηση ανά αίτημα για 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 δέχεται ένα core.TransactionSigner, επομένως οι signers που διαχειρίζονται οι ίδιοι τη μετάδοση (Crossmint, Fordefi native auto) και ο Fordefi native manual δεν μπορούν να υπογράψουν σε παρτίδες μέσω αυτού· καλέστε τους μία συναλλαγή τη φορά. Ένας signer του οποίου οι κλήσεις δημιουργούν εργασία στην πλευρά του παρόχου (Fireblocks με UseProgramCall) το αναφέρει μέσω του core.BatchServerSideEffects, και τότε η παρτίδα εκτελείται σειριακά.

Διαμόρφωση backend

Memory

Δώστε ακριβώς ένα από τα PrivateKey (ακατέργαστα bytes), PrivateKeyString (base58 ή πίνακας bytes της μορφής [1,2,...]) ή PrivateKeyPath (αρχείο keypair του 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 δεν προσφέρει αξιόπιστο τρόπο μηδενισμού της μνήμης: ο garbage collector μπορεί να αντιγράψει και να διατηρήσει τα bytes των κλειδιών (τα keypair του memory και τα κλειδιά που παράγουν τοπικά τα Crossmint και Openfort) μέχρι τη συλλογή. Αντιμετωπίστε ολόκληρη τη μνήμη της διεργασίας ως ευαίσθητη όταν χρησιμοποιείτε backends τοπικών κλειδιών.

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 του CDP δέχεται μόνο payloads UTF-8.

Crossmint

Το Crossmint είναι μόνο για αποστολή: το API του εκτελεί κάθε εγκεκριμένη συναλλαγή στην πλευρά του διακομιστή, οπότε το *crossmint.Signer έχει SignAndSendTransaction και όχι SignTransaction. Η συναλλαγή σας δεν τροποποιείται ποτέ· η υπογραφή που επιστρέφεται ταυτοποιεί τη συναλλαγή που κατέγραψε το Crossmint, η οποία μπορεί να διαφέρει από τη δική σας αν το Crossmint χρηματοδοτεί το 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 υπογράφει μόνο συναλλαγές· το SignMessage αποτυγχάνει και τη υπογεγραμμένη συναλλαγή τη μεταδίδετε εσείς.

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 διατίθεται με τρεις τύπους signer, που καθορίζονται κατά την κατασκευή από τα Config.Chain και Config.PushMode. Και οι τρεις υπογράφουν μηνύματα:

ConfigΤύποςΣημείο εισόδου
Chain μη ορισμένο*fordefi.BlackBoxSignerSignTransaction
Chain ορισμένο, PushMode μη ορισμένο ή PushModeAuto*fordefi.NativeAutoSignerSignAndSendTransaction
Chain ορισμένο, PushMode: PushModeManual*fordefi.NativeManualSignerModifyAndSignTransaction

Το fordefi.New επιλέγει με βάση το config και επιστρέφει ένα core.SolanaSigner· τα fordefi.NewBlackBox, fordefi.NewNativeAuto και fordefi.NewNativeManual επιστρέφουν τον συγκεκριμένο τύπο και απορρίπτουν τα config των άλλων τρόπων λειτουργίας. Τα αιτήματα API authenticate με υπογραφή αιτήματος ECDSA P-256: δώστε ακριβώς ένα από τα PrivateKeyPEM ή έναν RequestSigner για να διατηρείτε αυτό το κλειδί σε KMS/HSM.

Λειτουργία native auto: το Fordefi μπορεί να αντικαταστήσει το blockhash και τις προμήθειες και στη συνέχεια υπογράφει και μεταδίδει. Δέχεται μόνο μη υπογεγραμμένη συναλλαγή της οποίας ο μοναδικός απαιτούμενος signer είναι το 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)

Λειτουργία native manual: ορίστε PushMode: fordefi.PushModeManual. Το Fordefi ξαναγράφει το μήνυμα (πρόσφατο blockhash, εντολές προμήθειας Compute Budget) και το υπογράφει χωρίς να το μεταδώσει. Το ModifyAndSignTransaction αντικαθιστά το tx με τη συναλλαγή που υπέγραψε το Fordefi: συνεχίστε πάντα από αυτήν. Το Fordefi πρέπει να είναι ο πληρωτής των προμηθειών και να υπογράφει πριν από οποιονδήποτε άλλο απαιτούμενο signer, καθώς η επανεγγραφή ακυρώνει τις υπάρχουσες υπογραφές. Η επανεγγραφή δεν συγκρίνεται (diff), γι' αυτό ελέγξτε το αποτέλεσμα πριν από τη μετάδοση.

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: αφήστε το Chain μη ορισμένο. Το Fordefi υπογράφει ακριβώς τα bytes του μηνύματός σας και δεν το μεταδίδει. Χρησιμοποιήστε την με ένα vault black box του 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),
})

Γιατί δεν υπάρχει συγκεντρωτικό πακέτο

Η Rust έχει ένα enum Signer και η TypeScript το createKeychainSigner· η Go σκόπιμα δεν έχει κανένα από τα δύο. Η Go δεν αφαιρεί νεκρό κώδικα (dead-code elimination) πέρα από έναν διακλαδωτή δυναμικής επιλογής (switch) κατά το runtime, οπότε ένα συγκεντρωτικό πακέτο θα εξανάγκαζε το SDK κάθε προμηθευτή (AWS, GCP και τα υπόλοιπα) να μπει στο build κάθε καταναλωτή. Η εισαγωγή του πακέτου backend που χρειάζεστε είναι ο επιλογέας. Για να επιλέξετε backend κατά το runtime, γράψτε το switch στον δικό σας κώδικα μόνο για τα backends που υποστηρίζετε και κρατήστε το αποτέλεσμα ως core.SolanaSigner.

Διαχείριση σφαλμάτων

Κάθε σφάλμα είναι ένα *core.SignerError με σταθερό Code (CodeConfigError, CodeHTTPError, CodeRemoteAPIError, CodeSigningFailed, CodeNotInitialized, CodeBroadcastUnconfirmed και άλλα). Το Error() επιστρέφει μόνο ένα σταθερό μήνυμα ανά κωδικό, ποτέ υλικό κλειδιών ή ακατέργαστες απαντήσεις παρόχου. Κάντε αντιστοίχιση με errors.As, errors.Is(err, &core.SignerError{Code: ...}) ή core.CodeOf(err).

Στα Crossmint και Fordefi native auto, μια αποτυχία δεν σημαίνει ότι δεν καταγράφηκε τίποτα. Όταν ο πάροχος μπορεί να έχει αποδεχθεί τη συναλλαγή, ο κωδικός είναι core.CodeBroadcastUnconfirmed ("SIGNER_BROADCAST_UNCONFIRMED"). Το ProviderTxID ορίζεται όταν η δημιουργία έγινε αποδεκτή και το IdempotencyKey φέρει το κλειδί που προκύπτει από το μήνυμα. Η επαναποστολή της συναλλαγής με πανομοιότυπα bytes είναι ασφαλής· μια συναλλαγή που ξαναχτίστηκε (για παράδειγμα με νέο blockhash) είναι νέα μεταφορά. Το core.SignAndSendTransaction αναφέρει επίσης αυτόν τον κωδικό όταν η συνάρτηση αποστολής σας αποτυγχάνει, με την υπογραφή του πληρωτή προμηθειών στο TransactionSignature. Το Fireblocks PROGRAM_CALL είναι μόνο για υπογραφή και δεν στέλνει κλειδί· αναφέρει αυτόν τον κωδικό μόνο όταν ένα poll δείξει ότι το Fireblocks μετέδωσε παρ' όλα αυτά.

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

Δείτε το μοντέλο ασφαλείας για το τι εγγυάται κάθε μορφή υπογραφής, το README της Go για την πλήρη λίστα modules και το pkg.go.dev για την τεκμηρίωση του API.

Προσθήκη προσαρμοσμένων signers

Υλοποιήστε το core.SolanaSigner συν ακριβώς μία διεπαφή δυνατοτήτων και μην προσθέσετε καμία μέθοδο που το backend σας δεν μπορεί να υποστηρίξει: οι καλούντες εντοπίζουν τη δυνατότητα με type assertion. Δείτε τον οδηγό προσθήκης signers για να ενσωματώσετε πρόσθετες υπηρεσίες διαχείρισης κλειδιών.

Is this page helpful?

© 2026 Ίδρυμα Solana. Με επιφύλαξη παντός δικαιώματος.