Phase 0: Designing a Non-Custodial Payment Stack
April 20, 2026 — Before we write a line of Solidity, we freeze the spec. Phase 0 is the design and sign-off phase of the non-custodial roadmap: what moves on-chain, what does not, which chain ships first, who audits the contracts, and who holds the deployer keys.
TL;DR
- Fee split is frozen to four buckets: merchant, platform, referral/affiliate, bridge fee. Everything in the on-chain splitter is expressible in basis points; no off-chain fix-ups.
- Base is the launch chain for Phase 1. Cheap, fast, already in the wallet service chain list, and USDC-native.
- Subscriptions adopt ERC-20 approve + pull (EVM) and SPL delegate (Solana). Escrow and session-key designs were rejected — see the decision matrix below.
- Contracts are deployed by a 3-of-5 Safe multisig (EVM) and 3-of-5 Squads multisig (Solana). Per-link receivers are immutable. Factories and
SubscriptionHubsit behind a small proxy with a 48-hour timelock.
What this phase changes
Nothing in production. Phase 0 is a written spec, three audit proposals, a deployer-key ceremony, and a couple of merged ADRs.
The fee split spec (frozen)
Today, SmartAccountWalletService.drainSmartAccountWallet in api/src/withdrawals/services/smart-account-wallet.service.ts composes transfers into a UserOperation with three recipients: the merchant (toAddress), the support wallet (supportWalletAddress), and the commission wallet. The contract version mirrors this exactly:
struct Split {
address merchant; // main receiver
address platform; // support / platform fee
address referral; // commission / affiliate (optional, zero = skip)
address bridgeFee; // Relay bridge fee carrier (optional, zero = skip)
uint16 platformBps;
uint16 referralBps;
uint16 bridgeFeeBps;
}
Everything the keeper does today in TypeScript is computable from this struct and the payment amount. Anything the current keeper cannot express (e.g. dynamic taxes, merchant-level holdbacks) is out of scope for Phase 1 — we add it in a later phase if needed.
Chain selection
Launch chain for Phase 1 is Base:
- Already in our chainId map (
8453 → base,84532 → baseSepolia) inapi/src/payments/wallet.service.ts. - Gas is cheap enough that
PaymentLinkReceiverdeploy + split on first pay is pennies per transaction. - Alchemy paymaster policy already supports it, which gives us an escape hatch if we want the keeper on
SubscriptionHub.charge()sponsored. - Native USDC (not bridged), which matters for subscription allowances.
Ethereum mainnet, Arbitrum, Optimism, and Polygon follow in Phase 3. Solana is a separate program, tracked in Phase 4.
Subscription model: the decision matrix
We evaluated three designs. Only one works for us.
| Model | Custody | Payer UX | Wallet compatibility | Keeper requirement | Verdict |
|---|---|---|---|---|---|
| ERC-20 approve + pull (chosen) | None | One-time approve | MetaMask, Phantom, Ledger, Safe — native | Any caller; keeper is optional | Ship this |
| Escrow deposit | None | Deposit N months | All wallets | Any caller | Worse UX; funds locked |
| Smart-wallet + session keys | None | Onboard to 4337 | 4337 wallets only | Session-key signer | Too narrow compatibility |
Approve + pull is the only one that works with the wallets payers already use (MetaMask, Phantom, hardware wallets, Safes). It is also the design we already publish in Auto-charge, which documents approve(spender, amount) and SPL delegation. Phase 2 replaces the pull side with a smart contract and leaves the payer side unchanged.
The long-form argument is in Why allowance-pull beats escrow and session keys.
Deployer key custody
- EVM: Safe (formerly Gnosis Safe) on Base with 3-of-5 threshold. Signers: two founders, one ops lead, one advisor, one cold backup. Each signer uses a hardware wallet; no key on a server.
- Solana: Squads multisig with the same 3-of-5 setup and the same signers.
- Rotation: every signer rotates annually or on departure; any single rotation is a Safe/Squads transaction.
- No hot keeper can deploy. The NestJS keeper EOA (used to call
charge()in Phase 2) has no deploy authority, no upgrade authority, and no fee address.
Upgrade policy
- Per-link
PaymentLinkReceiver: immutable. No upgrade. This is a trust win for payers — the code they are paying into can't change after the fact. PaymentLinkFactory: small UUPS proxy owned by the multisig, 48-hour timelock onupgradeTo. Upgrades cannot change already-deployed receivers.SubscriptionHub: same pattern.charge()andcancel()semantics are stable between versions; if we ever need a breaking change, we deploySubscriptionHubV2and migrate instead of upgrading.- Refund and dispute semantics: on-chain
refund(txId)callable by the merchant signer (via their API key → keeper EOA) or the deployer multisig. Payer cannot self-refund (matches today's product).
Audit vendor strategy
We will shortlist three firms and run one primary audit + one competitive review:
- Primary candidates (EVM): Spearbit, Trail of Bits, Zellic.
- Secondary candidates (Solana): OtterSec, Neodyme.
- Scope:
PaymentLinkFactory,PaymentLinkReceiver,FeeSplitter,SubscriptionHub. The Anchor programs get their own audit in Phase 4. - Budget: treat the audit as a hard prerequisite for Base mainnet launch. No mainnet before the fix-all report.
- Public disclosure: audit reports land in the repo and link from /docs/non-custodial/security-model.
Risks and mitigations
- Spec scope creep. Mitigation: everything not expressible as basis-points-split is deferred to a later phase. Phase 1 does not block on dynamic taxation or per-merchant overrides.
- Multisig signer availability. Mitigation: 3-of-5, not 2-of-3, so any two signers can be unavailable without freezing deploys.
- Audit timing. Mitigation: start vendor conversations now; ship contracts to testnet during audit; mainnet deploy is gated.
- Forgotten fee case. Mitigation: reproduce every path in
drainSmartAccountWallettoday (native + ERC-20, with/without support, with/without commission) as a Foundry invariant test before audit.
Status checklist
- Fee split struct approved and tagged
- Base selected as Phase 1 launch chain
- Three audit vendors shortlisted
- Safe (Base) and Squads (Solana) deployer multisigs created and tested
- ADR merged covering upgrade policy and timelock
- ADR merged covering subscription model decision
- Refund / dispute semantics documented
What is next
Phase 1 implements the spec: Contract-based payment links on EVM.
Reference docs: Phase 0 status · Security model · Architecture
