Phase 1 — EVM payment-link contracts
Status: Planned.
Replace HD-derived deposit wallets with PaymentLinkFactory (CREATE2) + PaymentLinkReceiver (EIP-1167 clone) + FeeSplitter on Base.
Scope
PaymentLinkFactory,PaymentLinkReceiver,FeeSplitterimplemented and audited.- Deterministic CREATE2 address shown to the payer before any deploy.
Paidevent indexer emits existing webhooks unchanged.useOnChainReceiverfeature flag scoped per chain id.
Code touched
- api/src/payments/wallet.service.ts — EVM branch of
getOrCreateAddressForPaymentLink. - api/src/withdrawals/services/smart-account-wallet.service.ts —
drainSmartAccountWalletbypassed for flagged chains. - New:
api/src/payments/indexer/evm-paid-event.service.ts. - New:
contracts/evm/(Foundry).
Runtime / UX impact
- Payer UI unchanged. Same address format (the predeployed CREATE2 address).
- Merchant webhooks unchanged in shape; non-custodial events add an optional
non_custodial: trueflag. - Dashboard adds a "Non-custodial" badge per link.
Current limitations
- Fee-on-transfer tokens fall back to the legacy path (allowlist-gated).
- Tokens outside the allowlist fall back to the legacy path.
- Refund is restricted to keeper EOA or multisig (matches today).
Linked blog post
Phase 1: Contract-Based Payment Links on EVM
Status checklist
- Foundry project scaffolded
- Contracts implemented with fuzz + invariant tests
- USDT / non-standard ERC-20 fork tests passing
-
useOnChainReceiverfeature flag wired -
evm-paid-event.service.tsindexer shipping - Audit complete with fixes landed
- Base mainnet deploy from Safe multisig
- Canary cohort clean for 14 days