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 розсилає через свого провайдера й ігнорує функцію надсилання; будь-який інший тип підписує, а вашa функція розсилає результат у форматі 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.
}

Дивіться модель безпеки щодо гарантій кожного типу підписання, Go README щодо повного списку модулів і pkg.go.dev щодо довідника API.

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

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

Is this page helpful?