في المقالة السابقة، استكملنا نظام التحكم في المخاطر في منصة التداول. ستتناول هذه المقالة دمج محافظ منصات التداول في سلسلة سولانا. يختلف نموذج حساب سولانا، وتخزين السجلات، وآلية التأكيد بشكل كبير عن سلاسل الإيثيريوم. قد يؤدي اتباع نهج الإيثيريوم بسهولة إلى الوقوع في مشاكل. سنوضح أدناه النهج العام لسولانا. فهم سولانا الفريدة: نموذج حساب سولانا: يستخدم سولانا نموذجًا يفصل البرامج عن البيانات. يمكن مشاركة البرامج، بينما تُخزن بيانات البرنامج بشكل منفصل من خلال حسابات PDA (عنوان مشتق من البرنامج). لأن البرامج مشتركة، يلزم استخدام Token Mint للتمييز بين الرموز المختلفة. يخزن حساب Token Mint بيانات تعريفية عامة للرمز، مثل صلاحية سك العملة، وإجمالي المعروض، والأرقام العشرية. لكل رمز عنوان حساب Mint فريد كمعرّف. على سبيل المثال، عنوان Mint لعملة USD Coin (USDC) على شبكة Solana الرئيسية هو EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v. تستخدم Solana برنامجي رموز: SPL Token وSPL Token-2022. لكل رمز SPL حساب رمز مرتبط (ATA) مستقل خاص به لتخزين رصيد المستخدم. تتضمن عمليات نقل الرموز في الواقع استدعاء برامجها الخاصة لنقل الرموز بين حسابات ATA. قيود سجل Solana: على Ethereum، يتم الحصول على عمليات نقل الرموز من خلال تحليل سجلات التحويل التاريخية. مع ذلك، لا تُحفظ سجلات تنفيذ Solana بشكل دائم افتراضيًا. فهي ليست جزءًا من حالة دفتر الأستاذ (ولا تحتوي على مُرشِّح سجلات بلوم)، وقد تُقتطع المُخرجات أثناء التنفيذ. لذلك، لا يُمكننا إجراء مُطابقة الإيداعات عن طريق "مسح السجلات"، بل يجب استخدام `getBlock` أو `getSignaturesForAddress` لتحليل التعليمات. تأكيد وإعادة تنظيم Solana: يبلغ زمن كتلة Solana 400 مللي ثانية، ويصل إلى مرحلة الاكتمال بعد 32 تأكيدًا (حوالي 12 ثانية). إذا لم تكن مُتطلبات الوقت الفعلي عالية، فإن الطريقة البسيطة هي الوثوق بالكتل المُكتملة فقط. إذا كُنت ترغب في أداء أعلى في الوقت الفعلي، فيجب مراعاة إمكانية إعادة تنظيم الكتل، على الرغم من ندرة حدوثها. مع ذلك، لا يعتمد إجماع Solana على parentBlockHash لتكوين بنية سلسلة، ولا يُمكنه تحديد التفرعات من خلال الفرق بين parentBlockHash وblockHash في قاعدة البيانات، كما هو الحال مع Ethereum. إذن، ما هي الطريقة المُثلى لتحديد ما إذا أُعيد تنظيم كتلة؟ عند مسح الكتل محليًا، نحتاج إلى تسجيل تجزئة الكتلة للفتحة. إذا طرأ تغيير على تجزئة الكتلة لنفس الفتحة، فهذا يعني حدوث تراجع. يتيح لنا فهم الاختلافات في Solana بدء التنفيذ. لنبدأ أولًا بفحص تعديلات قاعدة البيانات اللازمة: تصميم جدول قاعدة البيانات: بما أن Solana تحتوي على نوعين من الرموز، نحتاج إلى إضافة حقل "token_type" إلى جدول "tokens" للتمييز بين "spl-token" و"spl-token-2022". على الرغم من اختلاف عناوين Solana عن عناوين Ethereum، إلا أنه لا يزال من الممكن اشتقاقها باستخدام BIP32 وBIP44، وإن كان ذلك من خلال مسارات مختلفة. لذلك، نحتاج فقط إلى استخدام جدول "wallets" الحالي. ومع ذلك، لدعم تعيين عنوان ATA ومسح كتلة Solana، يلزم إضافة الجداول الثلاثة التالية: style="text-align: left;">
اسم الجدول
الحقول الرئيسية
الوصف
solana_slots
slot
,block_hash,status,parent_slot
معلومات زائدة عن الحاجة لتسهيل اكتشاف الشوكة واستعادة الزناد
يسجل تعيينات ATA للمستخدم. يمكن لوحدة المسح البحث العكسي عن الحسابات الداخلية حسب ata_address
حيث:
solana_slots سيسجل confirmed/finalized/skipped. يقرر الماسح الضوئي ما إذا كان سيكتب إلى قاعدة البيانات أو يتراجع بناءً على الحالة. ...>
solana_slots سيسجل المؤكد/النهائي/المتخطي.
أين:
solana_slots سيتم تسجيل مؤكد/نهائي/مُتخطّى.
حيث:
solana_transactions تُخزَّن في قاعدة البيانات باستخدام "lamports" أو أصغر وحدة رمزية، مع حقل "type" للتمييز بين سيناريوهات العمل مثل الإيداع/السحب. لا تزال عمليات الكتابة الحساسة تتطلب توقيعات التحكم في المخاطر.
solana_token_accounts تُنشئ علاقة مفتاح خارجي مع "المحافظ"/"المستخدمين" لضمان تفرد ATAs ('wallet_address + token_mint' unique)، وهو أيضًا الفهرس الأساسي لمنطق المسح.
للحصول على تعريفات مفصلة للجدول، يُرجى مراجعة db_gateway/database.md
معالجة عمليات شحن المستخدم
تتطلب معالجة عمليات شحن المستخدم مسحًا مستمرًا للبيانات على سلسلة Solana. هناك طريقتان عامتان:
مسح التوقيعات: getSignaturesForAddress()
مسح الكتل: getBlock()
الطريقة الأولى: مسح توقيع العنوان عن طريق استدعاء `getSignaturesForAddress(address, { before, until, limit })`، مع تمرير العنوان الذي يهمنا كمعامل. هذا العنوان هو عنوان ATA الذي أنشأناه للمستخدم، أو يمكن أن يكون معرف البرنامج (لاحظ نقل رمز spl). يسترجع استدعاء التعليمات (الذي لا يتضمن عنوان mint) التوقيعات المتزايدة باستمرار من خلال التحكم في معلمات before وuntil، ثم يحصل على بيانات معلومات المعاملة من خلال getTransaction(signature). هذه الطريقة مناسبة للحالات التي تحتوي على كمية بيانات صغيرة أو عدد قليل من الحسابات. إذا كان عدد الحسابات كبيرًا جدًا، فإن مسح الكتل يكون أكثر ملاءمة؛ نستخدم هنا طريقة مسح الكتل. الطريقة 2: تحصل طريقة مسح الكتل باستمرار على أحدث فتحة، وتستدعي getBlock(slot) للحصول على تفاصيل المعاملات أو التوقيعات أو الحسابات كاملة، ثم تقوم بتصفية البيانات التي نحتاجها بناءً على التعليمات والحساب. ملاحظة: نظرًا لحجم المعاملات الكبير ومعدل TPS العالي لـ Solana، في بيئة الإنتاج، قد لا تواكب سرعة التحليل والتصفية سرعة توليد الكتل في Solana. في هذه الحالة، يتم إنشاء قائمة انتظار رسائل. كان من الضروري تصفية جميع عمليات نقل الرموز ودفع "أحداث الإيداع المحتملة" المحتملة إلى قائمة انتظار رسائل مثل Kafka/RabbitMQ. بعد ذلك، تقوم وحدات مستهلكي قائمة الانتظار اللاحقة بتصفية البيانات وكتابتها بدقة في قاعدة البيانات. لتسريع عملية التصفية، يجب تخزين بعض البيانات الساخنة في Redis لتجنب تراكم قائمة الانتظار. إذا كان هناك العديد من عناوين المستخدمين، يمكن استخدام التجزئة حسب عنوان ATA، مع استماع العديد من المستهلكين إلى شظايا مختلفة لتحسين الكفاءة. بدلاً من ذلك، إذا كنت لا ترغب في مسح الكتل بنفسك، يمكنك الاستعانة بمزود خدمة RPC خارجي لتقديم خدمات فهرسة إضافية، مثل خطافات الويب، ومراقبة الحساب، ودعم التصفية المتقدمة، والتي يمكنها التعامل مع ضغط تحليل كميات كبيرة من البيانات.
عملية مسح الكتل
استخدمنا الطريقة الثانية. الكود ذو الصلة موجود في blockScanner.ts وtxParser.ts ضمن وحدة scan/solana-scan. العملية الرئيسية هي كما يلي:
1. مرحلة المزامنة الأولية، ملء الكتل التاريخية (performInitialSync)
ابدأ من آخر خانة ممسوحة ضوئيًا، وامسح واحدة تلو الأخرى حتى آخر خانة
كل 100 خانة، تحقق مما إذا تم إنشاء خانة جديدة وحدّث الهدف ديناميكيًا
استخدم `confirmed` و`commitment` للحصول على الكتل، مع ضمان الأداء الفوري
كرر عملية التكرار عبر `transaction.message.instructions` و`meta.innerInstructions` لكل معاملة.
عالج المعاملات الناجحة فقط (`tx.meta.err === null`)
4. تحليل التعليمات (`txParser.parseInstruction`)
نقل SOL: يطابق نوع `transfer` لبرنامج النظام (11111...)`.
نقل SOL: يطابق نوع `transfer` لبرنامج النظام (11111...)`.
نقل SOL: يطابق نوع `transfer` لبرنامج النظام (11111...)`.
نقل SOL: يطابق نوع `transfer` لبرنامج النظام (11111...)`.
معالجة التراجع: سيستعيد البرنامج "الفتحة النهائية" باستمرار. عندما تكون الفتحة ≤ "الفتحة النهائية"، تُوضع علامة "مكتملة". بالنسبة للكتل التي لا تزال في حالة "مؤكدة"، يتحقق البرنامج مما إذا كانت تجزئة الكتلة قد تغيرت لتحديد ما إذا كان سيتم التراجع.
// txParser.ts - تحليل تعليمات النقل for (const tx of block.transactions) { if (tx.meta?.err) continue; // تخطي المعاملات الفاشلة const instructions = [ ...tx.transaction.message.instructions, ...(tx.meta.innerInstructions ?? []).flatMap(i => i.instructions) ]; for (const ix of instructions) { // نقل SOL if (ix.programId === SYSTEM_PROGRAM_ID && ix.parsed?.type === 'transfer') { if (monitoredAddresses.has(ix.parsed.info.destination)) { // ...
// نقل الرمز if (ix.programId === TOKEN_PROGRAM_ID || ix.programId === TOKEN_2022_PROGRAM_ID) { if (ix.parsed?.type === 'transfer' || ix.parsed?.type === 'transferChecked')) { const ataAddress = ix.parsed.info.destination; // عنوان ATA const walletAddress = ataToWalletMap.get(ataAddress); // تعيين إلى عنوان المحفظة if (walletAddress && monitoredAddresses.has(walletAddress)) { // ... بعد مسح معاملة الإيداع، يتم الحفاظ على أمان التوقيع المزدوج باستخدام بوابة DB مع التحكم في المخاطر. بعد التحقق، تُكتب البيانات في رصيد جدول تدفق الأموال. بعد ذلك، تتشابه عملية سحب Solana مع عملية سلسلة EVM، ولكن هناك اختلافات في بناء المعاملات: يوجد في Solana نوعان من الرموز: رمز SPL عادي ورمز SPL 2022. تختلف معرفات برامج الرمزين، ويجب التمييز بينهما عند بناء تعليمات المعاملة. (حاليًا، يُعد رمز SPL 2022 نادرًا نسبيًا؛ يمكنك أيضًا اختيار عدم دعم رمز 2022). تتكون معاملات Solana من جزأين: التوقيعات (مجموعة من توقيعات ed25519) ورسالة (تحتوي على الرأس، ومفاتيح الحساب، وتجزئة الكتلة الأخيرة، والتعليمات). يتم تجزئة محتوى الرسالة وتوقيعه، ووضعه في "التوقيعات". لا... استخدم رموزًا عشوائية؛ بدلاً من ذلك، يُستخدم `recentBlockhash` لتقييد مدة صلاحية المعاملة. مدة صلاحية `recentBlockhash` 150 كتلة فقط (دقيقة واحدة تقريبًا). لذلك، في كل مرة تبدأ فيها معاملة، يجب الحصول على `recentBlockhash` من سلسلة الكتل في الوقت الفعلي لتحديثها. إذا تطلبت معاملة سحب مراجعة يدوية، فيجب الحصول على `recentBlockhash` مرة أخرى لتلبية هيكل المعاملة وطلب التوقيع مرة أخرى.
عملية السحب
في الواقع، هذا هو المكان الذي احصل على معاملة Blockhash
Gain a broader understanding of the crypto industry through informative reports, and engage in in-depth discussions with other like-minded authors and readers. You are welcome to join us in our growing Coinlive community:https://t.me/CoinliveSG