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 التي تقبل المعاملات القديمة و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 بوضع الصندوق الأسود (fordefi.BlackBoxSigner)TransactionSignerنعم
fordefi بالوضع الأصلي اليدوي (fordefi.NativeManualSigner)ModifyingSignerنعم
fordefi بالوضع الأصلي التلقائي (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 المعاملة التي أعاد مزوّده كتابتها. لا تعتمد النواة على أي 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)
})

التوقيع على دفعات

يتوفر التوقيع على دفعات كدوال مساعدة حرة، تعمل بالتزامن مع فاصل زمني اختياري لكل طلب لواجهات برمجة التطبيقات ذات حدود المعدل:

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 بالوضع الأصلي التلقائي) ولا Fordefi بالوضع الأصلي اليدوي عبرها على دفعات؛ استدعِهم معاملة واحدة في كل مرة. أما الموقِّع الذي تنشئ استدعاءاته عملًا لدى المزوّد (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 طريقة موثوقة لتصفير الذاكرة: فقد ينسخ جامع المهملات بايتات المفاتيح ويحتفظ بها (keypairs الذاكرة، والمفاتيح التي تشتقها 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 للمطورين)

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.

الوضع الأصلي التلقائي: قد تستبدل 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)

الوضع الأصلي اليدوي: اضبط 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)

وضع الصندوق الأسود: اترك 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 لا تحذف الشيفرة الميتة عبر عبارة تبديل وقت التشغيل، لذا ستفرض حزمة شاملة إدخال حزمة 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 لمرجع واجهة برمجة التطبيقات.

إضافة موقِّعين مخصصين

طبّق core.SolanaSigner مع واجهة قدرة واحدة بالضبط، ولا تضف أي دالة لا تستطيع واجهتك الخلفية الوفاء بها: إذ يكتشف المستدعون القدرة عبر تأكيد النوع. راجع دليل إضافة الموقِّعين لدمج خدمات إدارة مفاتيح إضافية.

Is this page helpful?