مرحله ۱: لینکهای پرداخت مبتنی بر قرارداد روی EVM
۲۰ آوریل ۲۰۲۶ — مرحله ۱ شروع واقعی کار مهندسی نقشه راه غیرامانی است. EOAهای مشتق از HD برای هر لینک و smart accountهای آلکمی را با الگوی کارخانه + گیرنده بهشکل کلون حداقلی روی EVM جایگزین میکنیم. وجوه در همان تراکنش پرداخت بهصورت اتمیک تقسیم میشوند؛ بدون جاروب و بدون امانتداری.
خلاصه
PaymentLinkFactory.computeAddress(linkId)یک آدرس قطعی CREATE2 برمیگرداند. این آدرس را پیش از استقرار هر چیزی به پرداختکننده نشان میدهیم.PaymentLinkReceiverیک کلون حداقلی EIP-1167 است. توکن بومی یا ERC-20 میپذیرد، رویدادPaidمنتشر میکند و در همان تراکنشFeeSplitter.split()را فراخوانی میکند.- api/src/payments/wallet.service.ts دیگر برای لینکهای EVM کیف پول HD نمیسازد؛ بهجای آن تابع view کارخانه را صدا میزند.
- مسیر جاروب (
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 سه از پنج)، با timelock چهلوهشت ساعته روی
upgradeTo.
PaymentLinkReceiver (کلون)
- پس از مقداردهی تغییرناپذیر است. struct مربوط به
Splitو آدرسFeeSplitterرا ذخیره میکند. pay(address token, uint256 amount)— توکن بومی و ERC-20 را در یک نقطه ورود مدیریت میکند. ازSafeERC20استفاده میکند تا توکنهای غیراستاندارد (USDT) هم کار کنند. رویدادPaid(linkId, payer, token, amount)را منتشر میکند.FeeSplitter.split()را بهصورت اتمیک فراخوانی میکند. اگر تقسیم revert شود، پرداخت هم revert میشود.- تابع صریح
refund(bytes32 txId)که فقط EOA مربوط به keeper فروشنده یا مولتیسیگ استقرار میتواند فراخوانی کند.
FeeSplitter
split(Split s, address token, uint256 amount)— بر اساس واحد پایه توزیع میکند. wei باقیمانده به فروشنده میرسد (بدون هدر رفتن خردهها).- رویداد
Split(txId, merchant, platform, referral, bridgeFee)را منتشر میکند تا ایندکسر با payloadهای وبهوک قدیمی همخوان بماند.
تغییرات API
بزرگترین تغییر کد در api/src/payments/wallet.service.ts است. امروز getOrCreateAddressForPaymentLink از hd_wallet_seed مشتقگیری میکند، آدرس counterfactual آلکمی را محاسبه میکند و در wallet_address ذخیره میکند. پس از مرحله ۱، برای زنجیرههای 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 با viem.watchContractEvent روی هر زنجیره فعال مشترک رویدادهای Paid میشود. هنگام تطابق:
paymentLinkIdرا از رویداد (ایندکسشده بهصورتbytes32) پیدا میکند.- یک ردیف
PaymentTransactionدرج یا بهروزرسانی میکند؛ با همان ساختار امروز. - همان وبهوکهایی را منتشر میکند که امروز مسیر تکمیل جاروب منتشر میکند (api/src/webhooks/webhooks.service.ts).
پرچم useOnChainReceiver برای هر chain id جداگانه تعریف میشود. لینکی که در زمان خاموش بودن پرچم ساخته شود، برای همیشه روی مسیر جاروب قدیمی میماند؛ لینکهای در جریان را مهاجرت نمیدهیم.
موارد منسوخشده
در این مرحله این موارد را دور میزنیم اما حذف نمیکنیم:
SmartAccountWalletService.drainSmartAccountWallet— برای زنجیرههایی که پرچم فعال دارند دیگر فراخوانی نمیشود.SmartAccountWalletService.getSmartAccountClientForDepositWallet— وقتی آدرسها از کارخانه میآیند لازم نیست.- ردیفهای
wallet_address— برای زنجیرههای EVM با پرچم فعال نوشته نمیشوند.
حذف واقعی در مرحله ۷ انجام میشود؛ پس از تسویه همه لینکهای قدیمی روی آن زنجیره.
برنامه مهاجرت
- لینکهای جدید روی Base: یک آدرس CREATE2 محاسبهشده توسط کارخانه میگیرند. رابط کاربری آن را مثل امروز نمایش میدهد.
- لینکهای باز فعلی روی Base: تا انقضا یا تسویه روی مسیر جاروب قدیمی میمانند. مهاجرت اجباری در کار نیست.
- دیگر زنجیرههای EVM: تا وقتی مرحله ۳ پرچمشان را روشن کند، روی مسیر قدیمیاند.
ریسکها و راهکارها
- نمایش آدرس CREATE2 پیش از وجود هر کدی. راهکار: مستند کنیم که قابلیت بازیابی به استقراردهنده کارخانه بستگی دارد. سقف
maxLinkValueدر کارخانه برای ۳۰ روز اول. - USDT و دیگر توکنهای ERC-20 غیراستاندارد. راهکار:
SafeERC20در هر دو قراردادPaymentLinkReceiverوFeeSplitter؛ آزمونهای fuzz در Foundry روی فورک mainnet با USDT. - توکنهایی که هنگام انتقال کارمزد کم میکنند. راهکار: فهرست مجاز توکنها؛ توکنهای خارج از آن به مسیر قدیمی برمیگردند.
- تأخیر ایندکسر زنجیره. راهکار: اشتراک دوگانه (آلکمی + RPC عمومی پشتیبان)؛ کارهای تطبیق هر ساعت؛ درج/بهروزرسانی idempotent.
- یافته حسابرسی در حین توسعه. راهکار: حسابرسی پیششرط است؛ پیش از گزارش رفع همه موارد، استقرار روی mainnet انجام نمیشود.
چکلیست وضعیت
- ساخت پروژه Foundry در
contracts/evm/ - پیادهسازی
PaymentLinkFactory+PaymentLinkReceiver+FeeSplitter - آزمونهای fuzz + invariant برای محاسبات تقسیم (فروشنده همیشه ≥ مبلغ مورد انتظار منهای کارمزد دریافت میکند)
- قبولی آزمونهای فورک برای USDT / ERC-20 غیراستاندارد
- اتصال پرچم
useOnChainReceiverدرapi/src/payments - انتشار ایندکسر
evm-paid-event.service.tsو ارسال وبهوکهای موجود - شروع حسابرسی و اعمال اصلاحات
- استقرار روی Base mainnet از مولتیسیگ
- گروه آزمایشی (canary) از فروشندگان با پرچم فعال
قدم بعدی
با رفتن فروشندگان Base به گیرندههای قراردادی، مرحله ۲ همین کار را برای اشتراکها انجام میدهد: اشتراک مبتنی بر allowance.
مستندات مرجع: وضعیت مرحله ۱ · مرجع قراردادها · معماری
