Go

الإصدار 2.0.0 هو أول إصدار مستقر لوحدات Go، لذا لا يوجد ما يستدعي الترحيل. دليل الترحيل إلى 2.0 يغطي التقسيم نفسه للقدرات في Rust وTypeScript.

التثبيت

كل واجهة خلفية هي وحدة Go مستقلة، لذا لا يحتوي بناؤك ولا رسم الوحدات (module graph) لديك إلا على الواجهات الخلفية التي تستوردها:

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 وتكون حاجزة (blocking). كما تطبّق كل واجهة خلفية واجهة قدرة واحدة بالضبط:

  • 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 الخاص بـ سولانا CLI):

import "github.com/solana-foundation/solana-keychain/go/signers/memory/v2"
signer, err := memory.New(memory.Config{
PrivateKeyPath: "/path/to/id.json",
})

لا توفر Go طريقة موثوقة لتصفير الذاكرة: فقد ينسخ جامع النفايات بايتات المفاتيح ويحتفظ بها (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 للإرسال فقط: إذ تنفّذ واجهتها البرمجية كل معاملة معتمدة على جانب الخادم، لذا يحتوي *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.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 كتابة الرسالة (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 من نوع الصندوق الأسود.

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 تعداد Signer ولدى TypeScript الدالة createKeychainSigner؛ أما Go فلا تملك أياً منهما عن قصد. لا تحذف Go الشيفرة الميتة عبر عبارة توجيه وقت التشغيل (dispatch switch)، لذا ستفرض الحزمة الشاملة إدخال حزمة SDK الخاصة بمزوّد كل واجهة خلفية (AWS وGCP وغيرهما) في بناء كل مستهلك. استيراد حزمة الواجهة الخلفية التي تحتاجها هو أداة الاختيار. ولاختيار واجهة خلفية وقت التشغيل، اكتب عبارة الاختيار في شيفرتك أنت على الواجهات الخلفية التي تدعمها فقط، واحتفظ بالنتيجة بوصفها 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 الأصلي التلقائي، لا يعني الفشل أن شيئاً لم يصل إلى السلسلة. فعندما يكون المزوّد قد قبل المعاملة، يكون الرمز 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 مع واجهة قدرة واحدة بالضبط، ولا تضف أي دالة لا تستطيع واجهتك الخلفية الوفاء بها: إذ يكتشف المستدعون القدرة عبر تأكيد النوع. راجع دليل إضافة الموقِّعين لدمج خدمات إدارة مفاتيح إضافية.

Is this page helpful?