Go

2.0.0 — це перший стабільний випуск модулів Go, тож мігрувати нічого не потрібно. Посібник із міграції на 2.0 описує той самий поділ можливостей для Rust і TypeScript.

Встановлення

Кожен бекенд — це окремий модуль Go, тому ваша збірка та граф модулів містять лише ті бекенди, які ви імпортуєте:

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

Модулі позначаються тегами 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 transaction
return nil
}

SignTransaction і ModifyAndSignTransaction повертають core.SignedTransaction (EncodedTransaction, Signature, Completeness). Віддалені HTTP-бекенди приймають необов’язкові HTTPClient і HTTPClientConfig (тайм-аути). Типовий клієнт вимагає HTTPS; якщо ви передаєте власний, ця вимога обходиться, і відповідальність за його безпеку лежить на вас.

Можливості підписувачів

Бекенд, який не може підписувати транзакції, взагалі не має методу SignTransaction, тож можливість — це факт часу компіляції для конкретних типів і перевірка типу (type assertion) для core.SolanaSigner:

БекендМожливістьSignMessage
memory, vault, privy, turnkey, awskms, gcpkms, fireblocks, dfns, para, openfortTransactionSignerТак
cdpTransactionSignerЛише дані 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, тому їхні конструктори приймають 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 = unbounded
RequestDelay: 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 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 приймає лише дані 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. Усі три підписують повідомлення:

ConfigТипТочка входу
Chain не задано*fordefi.BlackBoxSignerSignTransaction
Chain задано, PushMode не задано або PushModeAuto*fordefi.NativeAutoSignerSignAndSendTransaction
Chain задано, PushMode: PushModeManual*fordefi.NativeManualSignerModifyAndSignTransaction

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.ChainSolanaMainnet
Fee: &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 має бути платником комісії та має підписувати раніше за будь-якого іншого обов’язкового підписувача, оскільки переписування анулює існуючі підписи. Різниця після переписування не обчислюється, тому перевіряйте результат перед розсиланням.

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 підписує точні байти вашого повідомлення і не розсилає його. Використовуйте цей режим зі сховищем Fordefi black box.

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.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 щодо повного списку модулів і pkg.go.dev щодо довідника API.

Додавання власних підписувачів

Реалізуйте core.SolanaSigner та рівно один інтерфейс можливостей і не додавайте методів, які ваш бекенд не може виконати: виклики визначають можливості через type assertion. Див. посібник із додавання підписувачів, щоб інтегрувати додаткові сервіси керування ключами.

Is this page helpful?