2.0.0 — первый стабильный релиз Go-модулей, поэтому мигрировать нечего. В руководстве по миграции на 2.0 описано то же разделение возможностей для Rust и TypeScript.
Установка
Каждый бэкенд — это отдельный Go-модуль, поэтому в вашей сборке и графе модулей присутствуют только те бэкенды, которые вы импортируете:
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
Модули помечаются тегами go/<module>/v2.0.0 (например,
go/signers/memory/v2.0.0). Каждый модуль требует Go 1.25.0, за исключением gcpkms,
которому нужен Go 1.25.8 из-за минимальной версии, заданной в google.golang.org/api.
Ledger поддерживается только в Rust, Go-модуля для него нет.
Транзакции solana-go v2 и v1
Модули построены на
github.com/solana-foundation/solana-go/v2
v2.0.0 и принимают его *solana.Transaction. Поддерживаются транзакции legacy, v0 и v1
(solana.MessageVersionV1), кроме Fireblocks
с UseProgramCall: true, который принимает только legacy и v0.
Базовое использование
Каждый подписант реализует базовый
интерфейс core.SolanaSigner
(Pubkey, SignMessage, IsAvailable). Методы принимают context.Context и
являются блокирующими. Кроме того, каждый бэкенд реализует ровно один интерфейс возможностей:
core.TransactionSigner:SignTransactionподписывает вашу транзакцию как есть.core.ModifyingSigner:ModifyAndSignTransactionпозволяет провайдеру изменить транзакцию, после чего подписывает её.core.SendingSigner:SignAndSendTransactionпоручает провайдеру подписать и отправить её в сеть.
Каждый пакет бэкенда предоставляет структуру Config и конструктор 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 и ModifyAndSignTransaction возвращают
core.SignedTransaction (EncodedTransaction, Signature, Completeness).
Удалённые HTTP-бэкенды принимают необязательные HTTPClient и HTTPClientConfig
(тайм-ауты). Клиент по умолчанию требует HTTPS; если передать собственный клиент, это требование обходится,
и ответственность за его безопасность лежит на вас.
Возможности подписантов
Бэкенд, не способный подписывать транзакцию, вообще не имеет метода SignTransaction,
поэтому наличие возможности для конкретных типов определяется на этапе компиляции, а для core.SolanaSigner — приведением типа:
| Бэкенд | Возможность | SignMessage |
|---|---|---|
| memory, vault, privy, turnkey, awskms, gcpkms, fireblocks, dfns, para, openfort | TransactionSigner | Да |
| cdp | TransactionSigner | Только данные в UTF-8 |
| utila | TransactionSigner | Завершается ошибкой CodeSigningFailed |
fordefi black box (fordefi.BlackBoxSigner) | TransactionSigner | Да |
fordefi native manual (fordefi.NativeManualSigner) | ModifyingSigner | Да |
fordefi native auto (fordefi.NativeAutoSigner) | SendingSigner | Да |
| crossmint | SendingSigner | Завершается ошибкой 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, поэтому их
конструкторы принимают context.Context. Всегда создавайте подписантов через New: подписант
Crossmint, Para или Utila с нулевым значением завершается ошибкой CodeNotInitialized,
а не подписывает от имени нулевого адреса.
Подпись и отправка с любым подписантом
core.SignAndSendTransaction отправляет транзакцию в сеть независимо от
типа подписанта. 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)})
Пакетная подпись
Пакетная подпись реализована в виде свободных вспомогательных функций, работающих параллельно, с необязательной задержкой между запросами для API с ограничением частоты:
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 принимает core.TransactionSigner, поэтому
подписантов с управляемой рассылкой (Crossmint, Fordefi native auto) и Fordefi native
manual нельзя использовать в пакетном режиме; вызывайте их по одной транзакции. Подписант,
вызовы которого создают работу на стороне провайдера (Fireblocks с UseProgramCall),
сообщает об этом через core.BatchServerSideEffects, и тогда пакет выполняется
последовательно.
Конфигурация бэкендов
Memory
Укажите ровно одно из: PrivateKey (сырые байты), PrivateKeyString (base58 или массив байтов вида
[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 нет надёжного способа обнулить память: сборщик мусора может копировать и сохранять байты ключей (memory keypair, а также ключи, которые Crossmint и Openfort выводят локально) до момента сборки. При использовании бэкендов с локальными ключами считайте всю память процесса конфиденциальной.
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 в CDP принимает только данные в UTF-8.
Crossmint
Crossmint работает только на отправку: его API выполняет каждую одобренную транзакцию
на стороне сервера, поэтому у *crossmint.Signer есть SignAndSendTransaction, но нет
SignTransaction. Ваша транзакция никогда не изменяется; возвращаемая подпись
идентифицирует транзакцию, которую провёл Crossmint, и она может отличаться от вашей, если
Crossmint спонсирует оплату газа.
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 представлен тремя типами подписантов, которые фиксируются при создании через Config.Chain и
Config.PushMode. Все три подписывают сообщения:
| Конфигурация | Тип | Точка входа |
|---|---|---|
Chain не задан | *fordefi.BlackBoxSigner | SignTransaction |
Chain задан, PushMode не задан или PushModeAuto | *fordefi.NativeAutoSigner | SignAndSendTransaction |
Chain задан, PushMode: PushModeManual | *fordefi.NativeManualSigner | ModifyAndSignTransaction |
fordefi.New выбирает тип по конфигурации и возвращает core.SolanaSigner;
fordefi.NewBlackBox, fordefi.NewNativeAuto и fordefi.NewNativeManual
возвращают конкретный тип и отклоняют конфигурации других режимов. API-запросы
аутентифицируются подписью запроса ECDSA P-256: укажите ровно одно из
PrivateKeyPEM или RequestSigner, чтобы хранить этот ключ в KMS/HSM.
Режим native auto: Fordefi может заменить blockhash и комиссии, затем подписывает и рассылает транзакцию. Принимается только неподписанная транзакция, единственным обязательным подписантом которой является хранилище (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)
Режим native manual: задайте PushMode: fordefi.PushModeManual. Fordefi переписывает
сообщение (recent blockhash, инструкции комиссии Compute Budget) и подписывает его
без рассылки. ModifyAndSignTransaction заменяет tx на
транзакцию, подписанную Fordefi: всегда продолжайте работу с ней. Fordefi должен быть плательщиком комиссии
и подписывать раньше любого другого обязательного подписанта, поскольку перезапись аннулирует
существующие подписи. Изменения не сравниваются (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 подписывает точные байты вашего сообщения
и не рассылает его. Используйте этот режим с black box vault в 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 не удаляет неиспользуемый код при диспетчеризации
через switch во время выполнения, поэтому универсальный пакет заставил бы включать SDK вендоров всех бэкендов (AWS, GCP
и остальных) в сборку каждого потребителя. Селектором служит импорт нужного вам пакета бэкенда.
Чтобы выбирать бэкенд во время выполнения, напишите switch в собственном коде только для поддерживаемых вами бэкендов
и храните результат как 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 содержит
ключ, выведенный из сообщения. Повторная отправка побайтово идентичной транзакции
безопасна; пересобранная (например, с новым blockhash) — это уже новый перевод.
core.SignAndSendTransaction также возвращает этот код, если ваша функция отправки
завершается сбоем, при этом подпись плательщика комиссии указана в TransactionSignature. Fireblocks
PROGRAM_CALL только подписывает и не передаёт ключ; этот код он возвращает, только если опрос показывает,
что Fireblocks всё же разослал транзакцию.
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.}
О гарантиях каждого вида подписи см. в модели безопасности, полный список модулей — в README для Go, а справочник по API — на pkg.go.dev.
Добавление собственных подписантов
Реализуйте core.SolanaSigner и ровно один интерфейс возможностей и не добавляйте
методов, которые ваш бэкенд не может поддерживать: вызывающий код определяет возможности приведением типа.
См. руководство по добавлению подписантов, чтобы интегрировать
дополнительные сервисы управления ключами.
Is this page helpful?