پرش به مطلب اصلی

مرحله ۳: گسترش به چند زنجیره EVM و افزونگی keeper

· خواندن 6 دقیقه
OrcaRail
Crypto payment rails for web2 apps

۲۰ آوریل ۲۰۲۶ — مرحله ۳ سطح قراردادهای مرحله ۱ و مرحله ۲ را روی همه زنجیره‌های EVM پشتیبانی‌شده می‌برد. همچنین یک keeper دوم اضافه می‌کند تا قطعی NestJS نتواند باعث جا افتادن برداشت اشتراک شود.

خلاصه​

  • استقرار PaymentLinkFactory، پیاده‌سازی PaymentLinkReceiver، FeeSplitter و SubscriptionHub روی Ethereum mainnet، Arbitrum، Optimism و Polygon.
  • همان طرح salt برای CREATE2 در همه‌جا؛ آدرس کارخانه روی همه زنجیره‌ها یکسان است (تمیز برای مستندات، یکپارچه‌سازی‌ها و computeAddress در سمت کلاینت).
  • ثبت Chainlink Automation (ترجیحی) و/یا Gelato Web3 Functions به‌عنوان keeper دوم که همان charge(ids[]) را فراخوانی می‌کند.
  • انتشار SLO: ۹۹٫۵٪ برداشت‌ها ظرف ۱۰ دقیقه از زمان سررسید، به‌صورت اندازه‌گیری‌شده.

این مرحله چه چیزی را تغییر می‌دهد​

چند keeper که همان charge() عمومی را فراخوانی می‌کنند، تضمین زنده‌بودن است. هیچ‌کدام امانت‌دار نیستند؛ هر کدام می‌تواند برداشت سررسیدشده را نجات دهد.

برنامه استقرار​

ترتیب استقرار (هر هفته یک زنجیره، با canary برای هر زنجیره):

  1. Ethereum mainnet (chainId 1)
  2. Arbitrum One (42161)
  3. Optimism (10)
  4. Polygon PoS (137)

آدرس‌ها روی هر زنجیره در /docs/non-custodial/contract-reference منتشر می‌شوند. کلید استقرار همان مولتی‌سیگ Safe سه از پنج تعریف‌شده در مرحله ۰ است که از همان مراسم برای هر زنجیره دوباره مستقر می‌شود.

هر چهار زنجیره از همان پرچم useOnChainReceiver استفاده می‌کنند که بر اساس chainId در api/src/payments/wallet.service.ts تعریف شده است. فروشندگان زنجیره به زنجیره آن را فعال می‌کنند؛ پس از یک هفته canary بدون مشکل، مقدار پیش‌فرض روشن می‌شود.

راهبرد گس برای charge()​

تراکنش‌های charge() مربوط به keeper به گس نیاز دارند. از زیرساخت paymaster موجود در api/src/withdrawals/services/gas-strategy دوباره استفاده می‌کنیم:

  • با paymaster آلکمی (Base، Arbitrum، Optimism، Polygon): keeper تابع SubscriptionHub.charge() را به‌صورت UserOperation با حمایت سیاست فراخوانی می‌کند. هزینه گس برای EOA مربوط به keeper صفر است.
  • Ethereum mainnet (بدون paymaster): keeper از موجودی گس داغ خودش می‌پردازد. کیف پول پلتفرم روزانه آن را شارژ می‌کند و با هشدار Datadog برای موجودی کم پایش می‌شود.
  • گس با یارانه فروشنده (اختیاری): فروشندگان می‌توانند یک gas_refund_address تنظیم کنند و keeper بازپرداخت را از آنجا برداشت کند؛ مطابق الگوی فعلی پشتیبانی/کمیسیون. خارج از دامنه راه‌اندازی اولیه.

keeper دوم برای افزونگی است، نه جایگزینی. در کنار زمان‌بند NestJS کار می‌کند.

  • ثبت یک Upkeep برای هر SubscriptionHub.
  • checkUpkeep() برای همه idهایی که block.timestamp >= nextChargeAt دارند (خوانده‌شده از یک تابع view)، مقدار performData = abi.encode(ids[]) را برمی‌گرداند.
  • performUpkeep(performData) تابع charge(ids) را به‌صورت دسته‌ای فراخوانی می‌کند.
  • با LINK از خزانه پلتفرم تأمین می‌شود؛ موجودی پایش می‌شود.

Gelato Web3 Functions (جایگزین)​

  • یک Web3 Function به زبان JS هاب را بررسی می‌کند، IDهای سررسیدشده را پیدا می‌کند و یک تراکنش charge(ids[]) ارسال می‌کند.
  • هزینه به‌صورت پس‌پرداخت از 1Balance در Gelato پرداخت می‌شود.

هر کدام به‌تنهایی کافی است؛ بر اساس تجربه عملیاتی در مرحله ۳ انتخاب می‌کنیم. اجرای هر دو هم مشکلی ندارد؛ به لطف lastChargedAt، تابع charge() در هر دوره idempotent است.

SLOها و پایش​

اهداف SLO منتشرشده:

معیارهدف
charge() ظرف ۱۰ دقیقه از nextChargeAt۹۹٫۵٪
charge() ظرف ۱ ساعت از nextChargeAt۹۹٫۹٪
موجودی گس داغ EOA مربوط به keeper بالاتر از آستانه هشدار۹۹٫۹۹٪
ایندکس شدن رویدادها ظرف ۲ دقیقه از قطعیت بلاک۹۹٫۵٪

پایش به تنظیمات موجود Sentry که در مهارت گردش کار Sentry مخزن مستند شده وصل می‌شود. هشدارها:

  • revertهای charge() در هر ساعت > آستانه ← PagerDuty
  • موجودی بومی EOA مربوط به keeper کمتر از N gwei × gasLimit × 100 ← PagerDuty
  • ناسالم بودن upkeep در Chainlink/Gelato ← هشدار
  • تأخیر ایندکسر رویداد Paid بیش از ۵ دقیقه ← هشدار

موارد منسوخ‌شده​

مانند مراحل ۱ و ۲، اما روی زنجیره‌های بیشتر. هنوز چیزی حذف نمی‌شود؛ حذف منتظر مرحله ۷ می‌ماند.

برنامه مهاجرت​

  • لینک‌های پرداخت جدید: روی هر زنجیره‌ای که پرچم روشن دارد از گیرنده‌های قراردادی استفاده می‌کنند.
  • اشتراک‌های جدید: روی هر زنجیره‌ای که پرچم روشن دارد از SubscriptionHub استفاده می‌کنند.
  • مشتریان فعلی: مهاجرت اختیاری هنگام تمدید، مانند مرحله ۲.
  • تسویه بین‌زنجیره‌ای (Relay): سطح بدون تغییر؛ keeper همچنان اجراکننده‌های Relay را در api/src/withdrawals/services/bridge فراخوانی می‌کند، فقط از یک EOA بدون امانت‌داری.

ریسک‌ها و راهکارها​

  • ناهمخوانی آدرس بین زنجیره‌ها. راهکار: استقراردهنده قطعی CREATE2 (مثلاً 0x4e59b44847b379578588920cA78FbF26c0B4956C) با هش init-code ثابت؛ کارخانه‌ها روی هر چهار زنجیره آدرس یکسان دارند.
  • محدودیت‌های سیاست paymaster در هر زنجیره. راهکار: پایش جداگانه هر زنجیره و هشدار سهمیه سیاست در ۷۰٪ مصرف.
  • تغییرات مکرر ثبت upkeep در Chainlink. راهکار: یک upkeep برای هر زنجیره؛ فهرست IDها روی زنجیره خوانده می‌شود، پس ثبت‌نام فروشندگان نیازی به تغییر پیکربندی Chainlink ندارد.
  • برداشت دوباره توسط keeperهای مختلف. راهکار: lastChargedAt یکنوا به‌علاوه ChargeSkipped برای موارد تکراری؛ هر دو keeper ذاتاً idempotent هستند.
  • جهش ناگهانی گس روی mainnet. راهکار: تنظیم اندازه دسته برای هر زنجیره؛ سقف قیمت گس که بالاتر از آن charge() یک بلاک عقب می‌افتد.

چک‌لیست وضعیت​

  • تکمیل استقرار روی Ethereum، Arbitrum، Optimism و Polygon
  • انتشار آدرس کارخانه + هاب در مرجع قراردادها
  • اعتبارسنجی سیاست‌های paymaster برای هر زنجیره
  • فعال بودن upkeep در Chainlink Automation دست‌کم روی ۲ زنجیره
  • داشبورد SLO در Datadog
  • runbook برای قطعی keeper (فراخوانی دستی charge() از CLI فروشنده)
  • راه‌اندازی فروشندگان canary برای هر زنجیره

قدم بعدی​

سولانا زنجیره بزرگ دیگری است که باید غیرامانی شود: مرحله ۴.


مستندات مرجع: وضعیت مرحله ۳ · راهبرد keeper · مرجع قراردادها