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

مرحله ۲: اشتراک مبتنی بر allowance

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

۲۰ آوریل ۲۰۲۶ — مرحله ۲ صورت‌حساب دوره‌ای را به یک SubscriptionHub روی زنجیره منتقل می‌کند. پرداخت‌کننده یک approve در کیف پولش امضا می‌کند و وقتی دوره‌ای سررسید شود، هر کسی (از جمله keeper ما) می‌تواند charge(id) را فراخوانی کند. وجوه مستقیماً از پرداخت‌کننده به سهم‌های فروشنده می‌رود؛ OrcaRail هرگز آن‌ها را نگه نمی‌دارد.

خلاصه​

  • SubscriptionHub.createSubscription(...) زمان‌بندی را روی زنجیره ثبت می‌کند.
  • پرداخت‌کننده یک بار در کیف پول معمولش approve(USDC, hub, cap) را امضا می‌کند. این همان گامی است که پیش‌تر در برداشت خودکار مستند کرده‌ایم.
  • هر EOAی می‌تواند charge(id) را فراخوانی کند. زمان‌بند NestJS ما به‌طور پیش‌فرض این کار را می‌کند؛ از مرحله ۳ به بعد Chainlink/Gelato پشتیبان آن خواهند بود.
  • محافظت در برابر reorg با lastChargedAt یکنوا. بدون برداشت دوباره.
  • مشترکان فعلی به‌صورت اختیاری و هنگام تمدید مهاجرت می‌کنند.

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

امروز api/src/scheduler/processors/subscription-auto-charge.processor.ts از یک smart account تحت کنترل OrcaRail استفاده می‌کند تا از طریق approve موجود از پرداخت‌کننده برداشت کند. کار می‌کند، اما smart account متعلق به ماست؛ اعتماد از مسیر ما عبور می‌کند.

پس از مرحله ۲:

allowance همچنان از پرداخت‌کننده به یک قرارداد داده می‌شود، اما قرارداد قابل حسابرسی و تغییرناپذیر است و هر کسی (نه فقط ما) می‌تواند keeper باشد.

قراردادهای معرفی‌شده​

SubscriptionHub​

یک نمونه برای هر زنجیره. سطح حداقلی:

function createSubscription(
bytes32 id,
address payer,
IERC20 token,
uint256 amount,
uint64 interval,
uint256 cap,
Split calldata split
) external;

function charge(bytes32 id) external; // any caller
function cancel(bytes32 id) external; // payer only
function subscription(bytes32 id) view returns (Subscription memory);

event SubscriptionCreated(bytes32 indexed id, address indexed payer, ...);
event Charged(bytes32 indexed id, uint256 amount, uint64 nextChargeAt);
event ChargeSkipped(bytes32 indexed id, bytes32 reason);
event Canceled(bytes32 indexed id);

اصول ثابتی که charge() اعمال می‌کند​

  • block.timestamp >= nextChargeAt — برداشت زودهنگام ممکن نیست.
  • block.timestamp > lastChargedAt — یکنوا، تا reorgها نتوانند دوبار برداشت کنند.
  • amountCharged + amount <= cap — سقف تأیید را علاوه بر allowance در ERC-20 اعمال می‌کند.
  • token.allowance(payer, hub) >= amount — revert صریح با InsufficientAllowance برای متن وب‌هوک روشن.
  • token.balanceOf(payer) >= amount — همین‌طور، با InsufficientBalance.

Split از مرحله ۱ دوباره استفاده می‌شود؛ همان FeeSplitter و همان منطق واحد پایه.

تغییرات API​

تغییر بزرگ این است که api/src/scheduler/processors/subscription-auto-charge.processor.ts دیگر از smart account ما برداشت نمی‌کند و به‌جای آن با یک EOA مربوط به keeper تابع SubscriptionHub.charge(id) را فراخوانی می‌کند.

به‌طور مشخص:

  • EOA مربوط به keeper یک کیف پول داغ برای هر زنجیره است که فقط گس دارد. هیچ اختیاری روی وجوه فروشنده ندارد.
  • اگر زنجیره سیاست paymaster آلکمی داشته باشد (api/src/withdrawals/services/gas-strategy را ببینید)، keeper از مسیر حمایت‌شده استفاده می‌کند؛ در غیر این صورت از موجودی گس خودش می‌پردازد.
  • هنگام revert، پردازشگر selector خطا را تجزیه می‌کند و همان وضعیت‌های SubscriptionAutoChargeEntity را که امروز استفاده می‌کنیم (past_due، insufficient_balance و غیره) به‌روزرسانی می‌کند.
  • وب‌هوک‌ها و ایمیل‌های api/src/mail/mail-templates/subscription-*.hbs بدون تغییر ارسال می‌شوند.

تجربه پرداخت‌کننده در pay دو تنظیم جزئی دارد:

  1. گام approve() اکنون spender را به‌جای کیف پول داغ ما به آدرس SubscriptionHub نشانه می‌رود. این یک تغییر متن و یک مقدار پیکربندی است؛ جریانی که پرداخت‌کننده می‌بیند یکسان است.
  2. دکمه «لغو مجوز» که SubscriptionHub.cancel(id) را فراخوانی می‌کند و سپس برای پاک‌سازی کامل یک approve(hub, 0) روی زنجیره درخواست می‌کند. مستندات فعلی لغو برداشت خودکار همچنان دقیق است.

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

  • مسیر برداشت امانی در subscription-auto-charge.processor.ts — با پرچم فعال، فقط شاخه SubscriptionHub.charge اجرا می‌شود.
  • smart account متعلق به OrcaRail به‌عنوان approval_spender_address — اشتراک‌های جدید به‌جای آن آدرس هاب را ذخیره می‌کنند.
  • فیلد approval_spender_address در پاسخ API باقی می‌ماند؛ فقط به جای جدید و قابل حسابرسی اشاره می‌کند.

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

مشترکان فعلی هرگز به‌اجبار مهاجرت داده نمی‌شوند. دو مسیر پیشنهاد می‌کنیم:

  1. هنگام تمدید: در دوره بعد، بنر داشبورد از پرداخت‌کننده می‌خواهد approve(hub, cap) را روی SubscriptionHub جدید دوباره امضا کند. یک کلیک. allowance قبلی به کیف پول داغ قدیمی را می‌توان نگه داشت یا لغو کرد؛ دیگر اهمیتی ندارد.
  2. به ابتکار کاربر: دکمه «انتقال به غیرامانی» در صفحه جزئیات اشتراک همین کار را فوراً انجام می‌دهد.

اشتراک‌هایی که هرگز مهاجرت نکنند تا زمان لغو روی مسیر امانی می‌مانند. این مسیر در مرحله ۷ با اطلاع‌رسانی کافی خاموش می‌شود.

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

  • سردرگمی پرداخت‌کننده هنگام مهاجرت. راهکار: قالب ایمیل از زبان subscription-auto-charge-approved.hbs موجود پیروی می‌کند؛ بنر در داشبورد؛ پرسش متداول در /docs/non-custodial/migration-guide.
  • قطعی keeper باعث از دست رفتن برداشت می‌شود. راهکار: charge() بدون مجوز است؛ هر نمونه Chainlink Automation یا Gelato می‌تواند پشتیبان باشد؛ فراخوانی دستی charge() توسط فروشنده هم ممکن است. برنامه کامل در مرحله ۳.
  • برداشت دوباره بر اثر reorg. راهکار: اصل ثابت یکنوای lastChargedAt؛ آزمون واحد با reorg روی فورک.
  • لغو مجوز توسط پرداخت‌کننده بین دو دوره. راهکار: charge() رویداد ChargeSkipped(AllowanceRevoked) را منتشر می‌کند؛ قالب ایمیل موجود subscription-insufficient-allowance.hbs ارسال می‌شود؛ اشتراک به past_due می‌رود.
  • انحراف فهرست مجاز توکن‌ها. راهکار: هاب فقط توکن‌هایی را می‌پذیرد که SubscriptionHub.isAllowed(token) برایشان برقرار است؛ تغییرات نیازمند مولتی‌سیگ است.

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

  • پیاده‌سازی SubscriptionHub با همه اصول ثابت
  • آزمون‌های fuzz برای حالت‌های سقف، allowance، reorg و رد شدن
  • تکمیل حسابرسی تکمیلی برای SubscriptionHub
  • تأمین EOA مربوط به keeper برای هر زنجیره، با پایش گس از طریق Sentry + Datadog
  • تغییر مقصد approve در اپ پرداخت به SubscriptionHub
  • به‌روزرسانی اندپوینت approve-auto-charge برای پذیرش allowance با هاب به‌عنوان spender
  • بنر داشبورد برای اشتراک‌های فعلی
  • مهاجرت گروه آزمایشی و پایداری به مدت ۱۴ روز

قدم بعدی​

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


مستندات مرجع: وضعیت مرحله ۲ · راهنمای مهاجرت · مرجع قراردادها · چرا برداشت مبتنی بر allowance