إنتقل إلى المحتوى الرئيسي

المرحلة 1: روابط دفع قائمة على العقود في EVM

· 5 دقائق قراءة
OrcaRail
Crypto payment rails for web2 apps

20 أبريل 2026 — المرحلة 1 هي البداية الهندسية الفعلية لـ خارطة الطريق غير الاحتجازية. نستبدل حسابات EOA المشتقة من HD لكل رابط وحسابات Alchemy الذكية بنمط مصنع + مستقبِل كنسخة مصغّرة على EVM. تُقسَّم الأموال ذريًا في معاملة الدفع نفسها؛ دون كنس ودون حفظ.

باختصار​

  • تعيد PaymentLinkFactory.computeAddress(linkId) عنوان CREATE2 حتميًا. نعرض هذا العنوان على الدافع قبل نشر أي شيء.
  • PaymentLinkReceiver نسخة مصغّرة EIP-1167. يقبل العملة الأصلية أو ERC-20، ويصدر Paid، ويستدعي FeeSplitter.split() في المعاملة نفسها.
  • يتوقف api/src/payments/wallet.service.ts عن إنشاء محافظ HD لروابط EVM؛ ويستدعي دالة العرض في المصنع بدلًا من ذلك.
  • يُتجاوز مسار الكنس (drainSmartAccountWallet وgetSmartAccountClientForDepositWallet) للروابط المنشأة تحت العلم useOnChainReceiver.

ما الذي تغيّره هذه المرحلة​

في جهة «بعد» لا يوجد أي صندوق نتحكم به يحتفظ بالأموال.

العقود الجديدة​

كلها ضمن حزمة جديدة contracts/evm/.

PaymentLinkFactory​

  • computeAddress(bytes32 linkId) view returns (address) — عنوان CREATE2 حتمي مشتق من linkId (معرّف UUID الحالي لرابط الدفع بعد تجزئته إلى bytes32).
  • deployAndPay(bytes32 linkId, Split split, address token, uint256 amount) — نشر كسول + دفع في معاملة واحدة. ينسخ المصنع تطبيق PaymentLinkReceiver، ويهيئه بالـ Split، ويمرر الأموال.
  • isDeployed(bytes32 linkId) view returns (bool) — دالة مساعدة يستخدمها المفهرس والواجهة.
  • قابل للترقية خلف proxy من نوع UUPS، يملكه التوقيع المتعدد للنشر (Safe ثلاثة من خمسة)، مع قفل زمني 48 ساعة على upgradeTo.

PaymentLinkReceiver (نسخة)​

  • غير قابل للتغيير بعد التهيئة. يخزّن struct الـ Split وعنوان FeeSplitter.
  • pay(address token, uint256 amount) — يتعامل مع العملة الأصلية وERC-20 في نقطة دخول واحدة. يستخدم SafeERC20 لتعمل الرموز غير القياسية (USDT). يصدر Paid(linkId, payer, token, amount).
  • يستدعي FeeSplitter.split() ذريًا. إذا فشل التقسيم (revert) يفشل الدفع.
  • دالة refund(bytes32 txId) صريحة لا يستدعيها إلا حساب EOA الخاص بـ keeper التاجر أو التوقيع المتعدد للنشر.

FeeSplitter​

  • split(Split s, address token, uint256 amount) — يوزّع بنقاط الأساس. يذهب الـ wei المتبقي إلى التاجر (دون فقدان الفتات).
  • يصدر Split(txId, merchant, platform, referral, bridgeFee) ليتطابق المفهرس مع حمولات webhook القديمة.

تغييرات API​

أكبر تغيير في الكود موجود في api/src/payments/wallet.service.ts. اليوم، تشتق getOrCreateAddressForPaymentLink من hd_wallet_seed، وتحسب عنوان Alchemy الافتراضي (counterfactual)، وتخزنه في wallet_address. بعد المرحلة 1، ولشبكات EVM التي يكون العلم مفعّلًا لها، تصبح الدالة:

async function getOrCreateAddressForPaymentLink(paymentLinkId, network) {
if (isEvm(network) && featureFlags.useOnChainReceiver(network.chainId)) {
const address = await factory.computeAddress(hashLinkId(paymentLinkId))
return { address, walletAddressId: null, derivationPath: null }
}
// legacy path unchanged
}

لا اشتقاق HD. ولا أي بيانات سرية في قاعدة البيانات للروابط الجديدة.

تشترك خدمة جديدة، payments/indexer/evm-paid-event.service.ts، في أحداث Paid على كل شبكة مفعّلة عبر viem.watchContractEvent. عند التطابق:

  1. تستخرج paymentLinkId من الحدث (مفهرس بصيغة bytes32).
  2. تُدرج أو تحدّث صف PaymentTransaction بالشكل نفسه المستخدم اليوم.
  3. تصدر الـ webhooks نفسها التي يصدرها مسار اكتمال الكنس اليوم (api/src/webhooks/webhooks.service.ts).

العلم useOnChainReceiver محدد لكل chain id. الرابط المنشأ أثناء إيقاف العلم يبقى على مسار الكنس القديم إلى الأبد؛ لا ننقل الروابط الجارية.

الإيقافات​

في هذه المرحلة نتجاوز دون أن نحذف:

  • SmartAccountWalletService.drainSmartAccountWallet — لم يعد يُستدعى للشبكات المفعّل لها العلم.
  • SmartAccountWalletService.getSmartAccountClientForDepositWallet — غير مطلوب عندما تأتي العناوين من المصنع.
  • صفوف wallet_address — لا تُكتب لشبكات EVM المفعّل لها العلم.

يحدث الحذف الفعلي في المرحلة 7، بعد تسوية كل الروابط القديمة على الشبكة.

خطة الترحيل​

  • الروابط الجديدة على Base: تحصل على عنوان CREATE2 يحسبه المصنع. تعرضه الواجهة كما هو الحال اليوم.
  • الروابط المفتوحة الحالية على Base: تبقى على مسار الكنس القديم حتى تنتهي صلاحيتها أو تُسوّى. لا ترحيل إجباري.
  • شبكات EVM الأخرى: على المسار القديم حتى تفعّل المرحلة 3 العلم لها.

المخاطر ووسائل الحد منها​

  • عرض عنوان CREATE2 قبل وجود أي كود. الحل: توثيق أن إمكانية الاسترداد تعتمد على ناشر المصنع. ووضع سقف maxLinkValue في المصنع خلال أول 30 يومًا.
  • USDT ورموز ERC-20 غير القياسية الأخرى. الحل: SafeERC20 في كل من PaymentLinkReceiver وFeeSplitter؛ واختبارات fuzz في Foundry على نسخة (fork) من الشبكة الرئيسية مع USDT.
  • الرموز التي تقتطع رسومًا عند التحويل. الحل: قائمة رموز مسموح بها؛ والرموز خارجها تعود إلى المسار القديم.
  • تأخر مفهرس الشبكة. الحل: اشتراك مزدوج (Alchemy + RPC عام احتياطي)؛ ومهام مطابقة كل ساعة؛ وإدراج/تحديث idempotent.
  • ملاحظة تدقيق أثناء التطوير. الحل: التدقيق شرط مسبق؛ لا نشر على الشبكة الرئيسية قبل تقرير إصلاح كل الملاحظات.

قائمة الحالة​

  • إنشاء مشروع Foundry في contracts/evm/
  • تنفيذ PaymentLinkFactory + PaymentLinkReceiver + FeeSplitter
  • اختبارات fuzz + invariant لحسابات التقسيم (يحصل التاجر دائمًا على ≥ المتوقع ناقص الرسوم)
  • نجاح اختبارات الـ fork لـ USDT / ERC-20 غير القياسية
  • ربط العلم useOnChainReceiver في api/src/payments
  • إطلاق المفهرس evm-paid-event.service.ts وإصداره للـ webhooks الحالية
  • بدء التدقيق ودمج الإصلاحات
  • النشر على Base mainnet من التوقيع المتعدد
  • مجموعة تجار تجريبية (canary) مع تفعيل العلم

ما التالي​

مع انتقال تجار Base إلى المستقبِلات القائمة على العقود، تفعل المرحلة 2 الشيء نفسه للاشتراكات: اشتراكات بسحب الـ allowance.


التوثيق المرجعي: حالة المرحلة 1 · مرجع العقود · البنية