الإصدار 2.0 هو أول إصدار مستقر من حزمة Python. ويغطي دليل الترحيل إلى 2.0 أشكال التوقيع التي تشترك فيها مع Rust وTypeScript.
التثبيت
ثبّت الحزمة الأساسية، بالإضافة إلى إضافة اختيارية واحدة لكل واجهة خلفية تتطلب تبعية من المزوّد:
pip install solana-keychain # memory, vault and parapip install 'solana-keychain[aws-kms]' # AWS KMS (boto3)pip install 'solana-keychain[gcp-kms]' # GCP KMS (google-cloud-kms)pip install 'solana-keychain[privy]' # Privypip install 'solana-keychain[turnkey]' # Turnkeypip install 'solana-keychain[fireblocks]' # Fireblockspip install 'solana-keychain[cdp]' # Coinbase Developer Platformpip install 'solana-keychain[crossmint]' # Crossmintpip install 'solana-keychain[dfns]' # Dfnspip install 'solana-keychain[openfort]' # Openfortpip install 'solana-keychain[utila]' # Utilapip install 'solana-keychain[fordefi]' # Fordefi# Several at oncepip install 'solana-keychain[aws-kms,privy]'
يتطلب Python 3.10 أو أحدث. المعاملات هي كائنات
solders من نوع VersionedTransaction؛
وتُقبل رسائل legacy وv0 وv1 جميعها، باستثناء Fireblocks مع
use_program_call=True، التي تقبل legacy وv0 فقط.
يمكن استيراد Memory وVault وPara من جذر الحزمة. أما كل واجهة خلفية أخرى
فتُستورد من وحدتها الفرعية الخاصة بها (على سبيل المثال solana_keychain.aws_kms)،
واستيراد إحداها دون إضافتها الاختيارية يُطلق الاستثناء ImportError الذي يذكر اسم الإضافة
المطلوب تثبيتها. أما Ledger فهو متاح في Rust فقط.
الاستخدام الأساسي
لكل واجهة خلفية dataclass للإعدادات ودالة مصنع غير متزامنة create_<backend>_signer
تُرجع موقّعًا جاهزًا للاستخدام. جميع طرق التوقيع هي coroutines:
import asynciofrom solana_keychain import MemorySignerfrom solders.transaction import VersionedTransactionasync def sign(transaction: VersionedTransaction) -> str:signer = MemorySigner.from_private_key_file("/path/to/id.json")print("Signing with:", signer.pubkey)# Check availability (useful for remote signers)if not await signer.is_available():raise RuntimeError("Signer offline")result = await signer.sign_transaction(transaction)print("Signature:", result.signature)print("Fully signed:", result.is_complete)return result.encoded_transaction # base64 wire transaction
تُرجع sign_transaction كائن SignedTransaction يحتوي على encoded_transaction
وsignature وis_complete وtransaction. والحقل transaction هو المعاملة الموقّعة
المعتمدة: تابع العمل انطلاقًا منه، خاصةً مع موقّع معدِّل، إذ تكون نتيجته كائنًا
مختلفًا عن الكائن الذي مررته.
المصنع الموحّد
تأخذ create_keychain_signer اسم الواجهة الخلفية وdataclass الإعدادات الخاص بها،
وتستورد الواجهة الخلفية عند الحاجة فقط، لذا لا يلزم تثبيت سوى الإضافات التي
تستخدمها:
from solana_keychain import create_keychain_signerfrom solana_keychain.privy import PrivySignerConfigsigner = await create_keychain_signer("privy",PrivySignerConfig(app_id="your-app-id",app_secret="your-app-secret",wallet_id="your-wallet-id",),)
الأسماء مدرجة في SUPPORTED_BACKENDS: aws-kms وcdp وcrossmint و
dfns وfireblocks وfordefi وgcp-kms وmemory وopenfort وpara و
privy وturnkey وutila وvault. ويؤدي اسم غير معروف، أو إعدادات من
فئة خاطئة، إلى إطلاق SignerError مع SignerErrorCode.CONFIG_ERROR. وقد
حُدِّد نوع المصنع على أنه يُرجع SolanaSigner الأساسي، لذا ضيّق نوعه باستخدام
isinstance قبل التوقيع (انظر قدرات الموقّع).
عملاء HTTP البعيدون
تقبل الواجهات الخلفية البعيدة عبر HTTP معامل http_client اختياريًا (من نوع httpx.AsyncClient)
لتخصيص TLS أو الوكلاء. وعند عدم تعيينه، يستخدم كل طلب عميلًا لمرة واحدة
بمهلة 60 ثانية، مع فرض HTTPS على عنوان URL الأساسي، ورفض
إعادة التوجيه.
قدرات الموقّع
ترث كل واجهة خلفية من الفئة المجردة SolanaSigner (pubkey وsign_message و
is_available) بالإضافة إلى فئة قدرة واحدة بالضبط:
TransactionSigner: تقومsign_transaction(tx)بتوقيع معاملتك في مكانها وتُرجعSignedTransaction. وأنت من يبثّها.ModifyingSigner: تتيحmodify_and_sign_transaction(tx)للمزوّد إعادة كتابة المعاملة، ثم توقّعها دون بثّها. ويبقى كائنك دون تغيير؛ تابع العمل انطلاقًا منSignedTransaction.transactionالمُرجَعة.SendingSigner: تقومsign_and_send_transaction(tx)بالتوقيع والبثّ عبر المزوّد وتُرجعSignatureالخاص بالمعاملة التي استقرت على الشبكة. ولا يتم تعديل معاملتك أبدًا.
| الواجهة الخلفية | فئة القدرة | sign_message |
|---|---|---|
| memory, vault, privy, turnkey, aws-kms, fireblocks, gcp-kms, dfns, para, openfort | TransactionSigner | نعم |
| cdp | TransactionSigner | حمولات UTF-8 فقط، وإلا SERIALIZATION_ERROR |
| utila | TransactionSigner | يُطلق SIGNING_FAILED |
fordefi black box (FordefiBlackBoxSigner) | TransactionSigner | نعم |
fordefi native manual (FordefiNativeManualSigner) | ModifyingSigner | نعم |
fordefi native auto (FordefiNativeAutoSigner) | SendingSigner | نعم |
| crossmint | SendingSigner | يُطلق SIGNING_FAILED |
ضيّق النوع باستخدام isinstance. جميع فئات القدرات مُصدَّرة من جذر
الحزمة:
from solana_keychain import ModifyingSigner, SendingSigner, TransactionSignerif isinstance(signer, SendingSigner):signature = await signer.sign_and_send_transaction(transaction)elif isinstance(signer, ModifyingSigner):result = await signer.modify_and_sign_transaction(transaction)elif isinstance(signer, TransactionSigner):result = await signer.sign_transaction(transaction)
التهيئة
تتولى دوال المصنع create_* وcreate_keychain_signer أي إعداد مطلوب
نيابةً عنك. وإذا أنشأت فئة الموقّع الخاصة بـ Privy أو Fireblocks أو Dfns أو Crossmint أو Para أو Openfort أو
Utila مباشرةً، فاستدعِ await signer.init() قبل الاستخدام. وإلى ذلك الحين،يُطلق pubkey وكل استدعاء توقيع الخطأ SignerErrorCode.NOT_INITIALIZED.
إعدادات الواجهة الخلفية
Memory
أنشئ الموقّع من ملف keypair الخاص بـ سولانا CLI، أو سلسلة base58 أو "[1,2,...]"، أو بايتات
خام، أو كائن Keypair من solders:
from solana_keychain import MemorySigner, MemorySignerConfig, create_memory_signerfrom solders.keypair import Keypairsigner = MemorySigner.from_private_key_file("/path/to/id.json")signer = MemorySigner.from_private_key_string("base58_private_key")signer = MemorySigner.from_bytes(secret_key_bytes) # 64 or 32 bytessigner = await create_memory_signer(MemorySignerConfig(keypair=Keypair()))
يبقى المفتاح في ذاكرة العملية، ولا يستطيع Python تصفيره. استخدم هذه الواجهة الخلفية للتطوير والاختبار.
HashiCorp Vault
يجب أن يكون المفتاح من نوع ed25519 في محرك transit. ولا يُقبل HTTP العادي
إلا لعنوان loopback، مع vault server -dev:
from solana_keychain import VaultSignerConfig, create_vault_signersigner = await create_vault_signer(VaultSignerConfig(api_base_url="https://vault.example.com:8200",token="hvs.xxxxx",key_name="my-solana-key",public_key="base58_public_key",))
AWS KMS
key_id هو معرّف مفتاح أو ARN أو اسم مستعار لمفتاح ECC_NIST_EDWARDS25519. مرّر
client لـ boto3 KMS مُنشأً مسبقًا لاستخدام بيانات اعتماد أو نقاط نهاية مخصصة؛ وعندها
يتم تجاهل region:
from solana_keychain.aws_kms import AwsKmsSignerConfig, create_aws_kms_signersigner = await create_aws_kms_signer(AwsKmsSignerConfig(key_id="alias/my-solana-key",public_key="base58_public_key",region="us-east-1", # optional, defaults to the AWS config chain))
GCP KMS
يجب أن يكون إصدار المفتاح من نوع EC_SIGN_ED25519. ويقبل client اختياريًا
كائن KeyManagementServiceAsyncClient مُنشأً مسبقًا:
from solana_keychain.gcp_kms import GcpKmsSignerConfig, create_gcp_kms_signersigner = await create_gcp_kms_signer(GcpKmsSignerConfig(key_name="projects/my-project/locations/us-east1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1",public_key="base58_public_key",))
Privy
from solana_keychain.privy import PrivySignerConfig, create_privy_signersigner = await create_privy_signer(PrivySignerConfig(app_id="app_id",app_secret="app_secret",wallet_id="wallet_id",))
تأخذ المحافظ المحمية بمفتاح تفويض كائن PrivyAuthorizationConfig على أنه
authorization_context.
Turnkey
from solana_keychain.turnkey import TurnkeySignerConfig, create_turnkey_signersigner = await create_turnkey_signer(TurnkeySignerConfig(api_public_key="api_public_key", # hex P-256api_private_key="api_private_key", # hex P-256organization_id="org_id",private_key_id="private_key_id",public_key="base58_public_key",))
Fireblocks
from solana_keychain.fireblocks import FireblocksSignerConfig, create_fireblocks_signersigner = await create_fireblocks_signer(FireblocksSignerConfig(api_key="api_key",private_key_pem="-----BEGIN RSA PRIVATE KEY-----\n...",vault_account_id="0",asset_id="SOL", # or "SOL_TEST" for devnet))
يوقّع use_program_call=True باستخدام عملية PROGRAM_CALL بدلًا من RAW. ويُرسَل
للتوقيع فقط، لذا لا تعيد Fireblocks كتابة المعاملة ولا تبثّها،
وتبثّها أنت كالمعتاد. ويتطلب محفظة ساخنة (hot wallet) ويجب أن
تفعّله Fireblocks لمساحة عملك.
CDP (Coinbase Developer Platform)
from solana_keychain.cdp import CdpSignerConfig, create_cdp_signersigner = await create_cdp_signer(CdpSignerConfig(api_key_id="api_key_id",api_key_secret="api_key_secret",wallet_secret="wallet_secret",address="base58_address",network="solana", # or "solana-devnet"; required for address lookup tables))
Crossmint
from solana_keychain.crossmint import CrossmintSignerConfig, create_crossmint_signersigner = await create_crossmint_signer(CrossmintSignerConfig(api_key="api_key",wallet_locator="wallet_locator",))signature = await signer.sign_and_send_transaction(transaction)
Crossmint للإرسال فقط. فواجهتها البرمجية تنفّذ دائمًا المعاملة المعتمدة
من جانب الخادم وتتحمل رسوم الغاز، لذا فإن CrossmintSigner هو SendingSigner
وتُطلق sign_message الخطأ SIGNING_FAILED. ويحدد التوقيع المُرجَع
المعاملة التي استقرت عبر Crossmint، وقد تختلف عن معاملتك. ويؤدي تعيين
signer_secret (سرّ موقّع مفوَّض بصيغة xmsk1_...) إلى اعتماد المعاملات
تلقائيًا، مما يفوّض إلى Crossmint اختيار ما يتم اعتماده.
Dfns
from solana_keychain.dfns import DfnsSignerConfig, create_dfns_signersigner = await create_dfns_signer(DfnsSignerConfig(auth_token="auth_token",cred_id="cred_id",private_key_pem="-----BEGIN EC PRIVATE KEY-----\n...",wallet_id="wallet_id",))
Openfort
from solana_keychain.openfort import OpenfortSignerConfig, create_openfort_signersigner = await create_openfort_signer(OpenfortSignerConfig(secret_key="sk_...",account_id="acc_...",wallet_secret="wallet_secret",))
Para
from solana_keychain import ParaSignerConfig, create_para_signersigner = await create_para_signer(ParaSignerConfig(api_key="sk_...",wallet_id="wallet_uuid",))
Utila
توقّع Utila المعاملات فقط؛ وتُطلق sign_message الخطأ SIGNING_FAILED.
from solana_keychain.utila import UtilaSignerConfig, create_utila_signersigner = await create_utila_signer(UtilaSignerConfig(service_account_email="sa@example.com",service_account_private_key_pem="-----BEGIN PRIVATE KEY-----\n...",vault_id="vault_id",wallet_id="wallet_id",network="networks/solana-devnet",))
Fordefi
لدى Fordefi ثلاثة أوضاع للتوقيع، تتحدد عند الإنشاء بواسطة chain و
push_mode. تختار create_fordefi_signer الفئة المناسبة، وترفض كل فئة
إعدادات مخصصة لفئة أخرى، وتوقّع الفئات الثلاث جميعها الرسائل:
| الوضع | الإعدادات | الفئة | الطريقة |
|---|---|---|---|
| Black box | chain غير معيَّن | FordefiBlackBoxSigner | sign_transaction |
| Native auto | chain معيَّن، وpush_mode غير معيَّن أو "auto" | FordefiNativeAutoSigner | sign_and_send_transaction |
| Native manual | chain معيَّن، وpush_mode="manual" | FordefiNativeManualSigner | modify_and_sign_transaction |
تُصادَق طلبات واجهة API بتوقيع طلب ECDSA P-256: قدّم
المفتاح عبر private_key_pem، أو أبقه في KMS/HSM بتمرير
فئة فرعية من FordefiRequestSigner على أنها request_signer.
وضع Native auto: عيّن chain ("solana_devnet" أو "solana_mainnet")، وقد
تستبدل Fordefi الـ blockhash والرسوم، ثم توقّع المعاملة وتبثّها
بنفسها. ولا يقبل إلا معاملة غير موقّعة يكون الموقّع الوحيد المطلوب فيها
هو الخزنة (vault). ويُمرَّر fee إلى Fordefi كما هو حرفيًا:
import osfrom solana_keychain.fordefi import FordefiSignerConfig, create_fordefi_signersigner = await create_fordefi_signer(FordefiSignerConfig(access_token=os.environ["FORDEFI_ACCESS_TOKEN"],vault_id=os.environ["FORDEFI_VAULT_ID"],public_key=os.environ["FORDEFI_PUBLIC_KEY"], # vault address (base58)private_key_pem=open("./secret/private.pem").read(),chain="solana_devnet",fee={"type": "custom", "priority_fee": "1000"}, # optional))signature = await signer.sign_and_send_transaction(transaction)
وضع Native manual: عيّن chain وpush_mode="manual". تعيد Fordefi كتابة
الرسالة (أحدث blockhash، وتعليمات رسوم Compute Budget) وتوقّعها،
لكنها لا تبثّها. وتحلّ المعاملة المُرجَعة محل التي أرسلتها،
لذا تابع دائمًا انطلاقًا من result.transaction. يجب أن تكون Fordefi هي دافع الرسوم
وأن توقّع قبل أي موقّع مطلوب آخر: تُقبل المعاملة التي تحمل بالفعل
توقيعات، لكن إعادة الكتابة تُبطلها. ولا تُعرَض فروق إعادة الكتابة،
لذا افحص النتيجة قبل البثّ، وابثّ بسرعة، لأن Fordefi
لا تُرجع ارتفاع انتهاء صلاحية الـ blockhash الجديد.
signer = await create_fordefi_signer(FordefiSignerConfig(access_token=os.environ["FORDEFI_ACCESS_TOKEN"],vault_id=os.environ["FORDEFI_VAULT_ID"],public_key=os.environ["FORDEFI_PUBLIC_KEY"],private_key_pem=open("./secret/private.pem").read(),chain="solana_devnet",push_mode="manual",))result = await signer.modify_and_sign_transaction(transaction)... # inspect result.transaction, then add any remaining signatures
ابثّ المعاملة التي فحصتها كما في التوقيع والإرسال، وقم بالمطابقة قبل إعادة المحاولة إذا فشل الإرسال.
وضع Black box: احذف chain. توقّع Fordefi بايتات رسالتك كما هي تمامًا
ولا تبثّها. استخدم هذا الوضع مع خزنة Fordefi من نوع black box.
signer = await create_fordefi_signer(FordefiSignerConfig(access_token=os.environ["FORDEFI_ACCESS_TOKEN"],vault_id=os.environ["FORDEFI_BB_VAULT_ID"],public_key=os.environ["FORDEFI_BB_PUBLIC_KEY"],private_key_pem=open("./secret/private.pem").read(),))
التوقيع والإرسال
تضع sign_and_send_transaction المعاملة على السلسلة أيًّا كان شكل
الموقّع. فـ SendingSigner يبثّ عبر مزوّده ولا يُستدعى
ما قدّمته من دالة إرسال أبدًا. أما TransactionSigner أو ModifyingSigner فيوقّع، وتقوم دالة
الإرسال لديك ببثّ النتيجة المشفّرة بصيغة base64 (الحزمة لا تتضمن عميل RPC):
import httpxfrom solana_keychain import sign_and_send_transactionfrom solders.signature import Signatureasync def send(encoded: str) -> Signature:async with httpx.AsyncClient() as client:response = await client.post("https://api.devnet.solana.com",json={"jsonrpc": "2.0","id": 1,"method": "sendTransaction","params": [encoded, {"encoding": "base64"}],},)return Signature.from_string(response.json()["result"])signature = await sign_and_send_transaction(signer, transaction, send)
يؤدي موقّع للتوقيع فقط دون دالة إرسال إلى إطلاق CONFIG_ERROR، وتؤدي
معاملة لا تزال تفتقد توقيعات بعد التوقيع إلى إطلاق SIGNING_FAILED.
معالجة الأخطاء
كل إخفاق هو SignerError برمز code ثابت من SignerErrorCode:
BROADCAST_UNCONFIRMED وCONFIG_ERROR وEXPECTED_SOLANA_SIGNER وHTTP_ERROR و
INVALID_PRIVATE_KEY وINVALID_PUBLIC_KEY وIO_ERROR وNOT_AVAILABLE و
NOT_INITIALIZED وOTHER وPARSING_ERROR وREMOTE_API_ERROR و
SERIALIZATION_ERROR وSIGNING_FAILED. اعتمد على code وليس على الرسالة:
فـ str() وrepr() يحملان رسالة عامة ثابتة فقط، ولا يحملان أبدًا مادة المفاتيح أو
استجابات المزوّد الخام. ويتم تعيين status_code وprovider_error_code عندما
يأتي الإخفاق من استجابة المزوّد.
يعني SignerErrorCode.BROADCAST_UNCONFIRMED ("SIGNER_BROADCAST_UNCONFIRMED") أن
المعاملة ربما استقرت على الشبكة:
- في Crossmint وFordefi native auto، يتم تعيين
provider_transaction_idعندما يُقبل الإنشاء، ويحملidempotency_keyالمفتاح المشتق من الرسالة التي أُرسل الإنشاء بموجبها. إعادة إرسال المعاملة المطابقة بايتًا ببايت آمنة؛ أما المعاملة المعاد بناؤها (مثلًا بـ blockhash جديد) فهي تحويل جديد. - من
sign_and_send_transaction، تُطلقه دالة إرسال فاشلة مع تعيينtransaction_signatureإلى توقيع المعاملة المكتملة. - تُطلقه Fireblocks فقط إذا أظهر الاستعلام الدوري أن Fireblocks بثّت
معاملة
use_program_callرغم أنها أُرسلت للتوقيع فقط.
from solana_keychain import SignerError, SignerErrorCodetry:signature = await sign_and_send_transaction(signer, transaction, send)except SignerError as error:if error.code is SignerErrorCode.BROADCAST_UNCONFIRMED:reconcile(error.provider_transaction_id,error.idempotency_key,error.transaction_signature,)raise
يعيد الاستدعاء الملغى إطلاق asyncio.CancelledError، الذي لا يحمل أي حقول. لاسترداد
معرّف قبله المزوّد قبل الإلغاء، مرّر
PendingTransactionId() (من solana_keychain.core) على أنه pending_transaction_id في إعدادات Crossmint أو
Fordefi native auto، واقرأ .get() بعد
الإلغاء. وقم بالمطابقة مع المزوّد قبل إعادة المحاولة.
يتحقق Keychain من مرحلة التوقيع، لا مما تفعله المعاملة: فالتوقيع الذي يُرجعه المزوّد يُتحقَّق منه بـ ed25519 مقابل الرسالة المحسوبة محليًا قبل إرفاقه. ولا يستطيع Python تصفير مخازن المفاتيح المؤقتة، لذا مع الواجهات الخلفية ذات المفاتيح المحلية (Memory، ومفاتيح API وملفات PEM المحفوظة في إعدادات الواجهات الخلفية البعيدة) تعامل مع ذاكرة العملية كلها على أنها حساسة. راجع نموذج الأمان لمعرفة ما يضمنه كل شكل من أشكال التوقيع.
إضافة موقّعين مخصصين
قم بإنشاء فئة فرعية من فئة القدرات الوحيدة التي تتوافق مع واجهتك الخلفية (TransactionSigner أو
ModifyingSigner أو SendingSigner) من solana_keychain.core. راجع
دليل إضافة الموقّعين لدمج
خدمات إدارة المفاتيح الإضافية.
الموارد
Is this page helpful?