مرحله ۴: سولانای غیرامانی با PDAهای Anchor
۲۰ آوریل ۲۰۲۶ — مرحله ۴ مدل غیرامانی را به سولانا میآورد. دو برنامه Anchor جایگزین جریان کیف پول HD + جاروب برای هر لینک میشوند: payment_link برای پرداختهای یکباره و subscription برای برداشت خودکار با delegate در SPL. برای لینکهای جدید سولانا هیچ عبارت بازیابی (mnemonic) روی سرورهای ما نیست.
خلاصه
- برنامه
payment_linkبرای هر لینک پرداخت مالک یک PDA است که ازseeds = [b"pl", link_id]مشتق میشود. بدون keypair، بدون seed، بدون جاروب. - برنامه
subscriptionاز اختیار delegate در SPL استفاده میکند؛ همان جزء پایهای که در برداشت خودکار به آن تکیه داریم؛ بهعنوان allowance صریحی که پرداختکننده اعطا میکند. - تقسیمها از طریق CPI به همان منطق
fee_splitterکه روی EVM ساختیم و برای حسابهای سولانا تطبیق دادهایم انجام میشوند. - سرویس جاروب api/src/withdrawals/services/solana-withdrawal-transfer.service.ts برای لینکهای دارای پرچم دور زده میشود و در مرحله ۷ حذف میشود.
این مرحله چه چیزی را تغییر میدهد
سمت «بعد» seed مربوط به HD در api/src/payments/wallet.service.ts (شاخه m/44'/501'/i'/0') را با محاسبه کاملاً قطعی PDA جایگزین میکند.
برنامههای معرفیشده
پکیج جدید contracts/solana/ (Anchor).
برنامه payment_link
حسابها برای هر لینک:
#[account]
pub struct PaymentLink {
pub link_id: [u8; 32],
pub expected_mint: Pubkey, // e.g. USDC on SOL
pub expected_amount: u64,
pub split: Split,
pub created_at: i64,
pub status: PaymentStatus,
}
دستورها:
initialize(link_id, expected_mint, expected_amount, split)— تنبل؛ هنگام اولین واریز توسط پوششدهنده شبیه کارخانه فراخوانی میشود.pay(link_id, amount)— mint و مبلغ را اعتبارسنجی میکند، با CPI به ATA مربوط به PDA منتقل میکند و سپس با CPI بهصورت اتمیک تقسیم میکند. رویدادPaymentReceived { link_id, payer, amount }را منتشر میکند.refund(link_id)— نیازمند مولتیسیگ، مشابه بازپرداخت در EVM.
seedهای PDA: [b"pl", link_id]. PDA کلید خصوصی ندارد؛ از طریق invoke_signed امضا میکند.
برنامه subscription
از Approve / Revoke در SPL برای واگذاری اختیار استفاده میکند. پرداختکننده spl_token::approve را امضا میکند و PDA برنامه را تا سقف cap توکن بهعنوان delegate تعیین میکند. سپس:
pub fn charge(ctx: Context<Charge>, id: [u8; 32]) -> Result<()>
هر کسی میتواند فراخوانیاش کند؛ این موارد را بررسی میکند:
clock.unix_timestamp >= next_charge_atclock.unix_timestamp > last_charged_at// monotonicamount_charged + period_amount <= cap- اینکه اختیار delegate هنوز روی ATA پرداختکننده وجود دارد
در صورت قبولی، spl_token::transfer_checked با CPI از ATA پرداختکننده (بهعنوان delegate) برداشت میکند و با CPI تقسیم میکند.
رویدادها: SubscriptionCreated، Charged، ChargeSkipped، Canceled. همان ساختاری که ایندکسر EVM تولید میکند، پس api/src/webhooks به زنجیره وابسته نیست.
تغییرات API
- شاخه سولانا در
WalletService.generateAddressForNetworkدیگر keypair مشتق نمیکند و بهجای آنfindProgramAddressSync(["pl", linkId], programId)را برمیگرداند. برای لینکهای جدید سولانا هیچ ردیفencryptedSeedنوشته نمیشود. - یک سرویس ایندکسر جدید لاگهای برنامه را (با وبهوکهای Helius یا استریمهای Triton) زیر نظر میگیرد و رویدادهای وبهوک موجود را منتشر میکند.
- api/src/scheduler/processors/subscription-auto-charge.processor.ts یک شاخه SOL اضافه میکند: یک تراکنش
chargeمیسازد و با keeper امضا میکند (مسیر بدون گس از طریق تراکنش حمایتشده، مشابه paymaster مرحله ۲). withdrawals/services/solana-withdrawal-transfer.service.tsبرای لینکهای دارای پرچم فراخوانی نمیشود.
گس روی سولانا
کارمزدهای سولانا ناچیزند (معمولاً ۵۰۰۰ lamport برای هر امضا، یعنی ۰٫۰۰۰۰۰۵ SOL). keeper از موجودی SOL داغ با همان الگوی هشدار مرحله ۳ استفاده میکند. چون سولانا paymaster شبیه آلکمی ندارد، تراکنشهای حمایتشده از الگوی ساده همامضاکننده استفاده میکنند: keeper بهعنوان پرداختکننده کارمزد امضا میکند و پرداختکننده اصلاً به SOL نیاز ندارد.
موارد منسوخشده
- مشتقگیری HD برای SOL (
m/44'/501'/i'/0') برای لینکهای جدید — با روشن بودن پرچم فراخوانی نمیشود. solana-withdrawal-transfer.service.ts— برای لینکهای دارای پرچم دور زده میشود.SolanaRelayBridgeExecutorServiceدر api/src/withdrawals/services/bridge/solana-relay-bridge-executor.service.ts باقی میماند: وقتی فروشنده روی EVM است، پل از SOL به EVM همچنان از Relay استفاده میکند، اما محرک آن به برنامه منتقل میشود.
برنامه مهاجرت
همان مدل اختیاری مرحله ۲:
- لینکهای پرداخت جدید: آدرسدهی با PDA.
- اشتراکهای جدید: مبتنی بر delegate در SPL.
- اشتراکهای فعال فعلی: بنر + ایمیل در تمدید بعدی برای واگذاری دوباره به برنامه جدید.
- واریزهای در جریان به آدرسهای قدیمی همچنان روی مسیر قدیمی تسویه میشوند.
ریسکها و راهکارها
- delegate در SPL قابل بازنویسی است. راهکار:
chargeپیش از انتقالdelegated_amountرا دوباره بررسی میکند؛ در صورت شکست،ChargeSkipped(DelegateRevoked)منتشر میشود و همان قالب ایمیلِ حالت لغو allowance در EVM استفاده میشود. - اختیار ارتقای Anchor. راهکار: مولتیسیگ Squads سه از پنج مالک اختیار ارتقاست، با timelock. مطابق رویکرد Safe در EVM از مرحله ۰.
- SOL بستهبندیشده در برابر SOL بومی. راهکار:
payment_linkاز هر دو پشتیبانی میکند و برای جلوگیری از ابهام،expected_mintصریح برای هر لینک دارد. - جهش کارمزد اولویت. راهکار: محاسبه پویای کارمزد اولویت در keeper؛ تلاش مجدد پشتیبان با اولویت بالاتر پس از یک slot.
- حسابرسی. راهکار: حسابرسی هر دو برنامه توسط OtterSec یا Neodyme پیش از استقرار روی mainnet.
چکلیست وضعیت
- ساخت workspace انکر
contracts/solana/ - برنامه
payment_linkبا همه دستورها + آزمونها - برنامه
subscriptionبا همه دستورها + آزمونها - انتشار ایندکسر وبهوک Helius
- شاخه SOL مربوط به keeper در
subscription-auto-charge.processor.ts - انتشار گزارش حسابرسی
- استقرار روی mainnet از مولتیسیگ Squads
- گروه آزمایشی فروشندگان
قدم بعدی
بیتکوین. سختترین زنجیره برای غیرامانی شدن، و جایی که باید درباره محدودیتها صادق باشیم: مرحله ۵.
مستندات مرجع: وضعیت مرحله ۴ · مرجع قراردادها · معماری
