Why Allowance-Pull Beats Escrow and Session Keys
April 20, 2026 — Every crypto subscription product has to solve the same problem: how does a payer authorize future payments without giving up custody? There are three realistic answers. We picked allowance-pull, and this post explains why.
The three models
1. Allowance-pull (ERC-20 approve + SPL delegate)
The payer signs approve(spender, cap) — once — on the token contract. Later, the spender (our SubscriptionHub or Solana program) calls transferFrom (EVM) or transfers with delegate authority (SOL) when a period is due. The payer keeps custody; the spender can pull up to cap over the subscription's life.
Cancellation: payer signs approve(spender, 0) or Revoke. From that point, no pull can succeed.
This is what Auto-charge already uses today. Phase 2 moves the spender from a platform-controlled hot wallet to an on-chain contract, but the payer side does not change.
2. Escrow deposit
The payer deposits N periods of funds into a per-subscription escrow contract. The contract releases to the merchant on schedule. Payer can withdraw the unused balance at any time (minus paid periods).
3. Smart-wallet with session keys
The payer uses an ERC-4337 smart wallet and grants a scoped session key to the subscription contract. The session key can only do things the scope allows (e.g. "transfer USDC up to X per 30 days to this address"). Payer revokes by removing the session key.
The scoring
We evaluated all three on five axes:
| Axis | Allowance-pull | Escrow | Session keys |
|---|---|---|---|
| Custody during active sub | Payer | Escrow contract | Payer (via smart wallet) |
| Steps for payer to set up | 1 signature | 1 deposit (any gas) | 1 onboarding to 4337 wallet |
| Payer wallet compatibility | All wallets | All wallets | 4337 wallets only |
| Funds locked / working capital | None | N months locked | None |
| Cancel UX | Revoke approve | Withdraw + cancel | Remove session key |
| Keeper risk | Any caller | Any caller | Any caller |
| Merchant-side complexity | Minimal | Release accounting | 4337 bundler setup |
| Works with MetaMask / Phantom | Yes | Yes | Not natively |
| Works with Ledger / hardware | Yes | Yes | Partial |
| Regulatory clarity | High | Lower (smells escrow) | High |
Why not escrow
Escrow has the best-sounding pitch ("payer funds are locked in a contract, not a company") but worst real-world UX:
- Payers do not like locking N months of funds. A $20/month subscription with a 12-month escrow means $240 locked. Ceiling on conversion rate.
- Withdrawals become complicated: the contract must track paid vs unpaid periods, handle partial refunds, and prevent the merchant from reading future funds.
- "Escrow that holds customer funds" has a regulatory smell in several jurisdictions that "allowance to a smart contract" does not.
Why not session keys
Session keys on 4337 smart wallets are elegant. But they require the payer's wallet to be a smart wallet. Most payers in 2026 still use EOAs (MetaMask, Rabby, hardware wallets, Safe signers) and Solana payers use Phantom-style keypair wallets. Forcing 4337 onboarding for a subscription is a conversion killer.
There is a path where 4337 becomes the default payer wallet and session keys become trivial. We are not there yet.
Why allowance-pull wins today
- Zero change to payer wallet. Every wallet that can sign an ERC-20
approveworks, which is all of them. - Zero funds locked. Payer keeps custody; pull happens just-in-time.
- Revoke is universal.
approve(0)orRevokeis a primitive every wallet UI already surfaces. - Keeper is commodity.
SubscriptionHub.charge(id)is public and permissionless. NestJS, Chainlink, Gelato, or an EigenLayer AVS (see Phase 6) can all call it. - Regulatory story is simpler. The platform never holds funds in transit.
Known limitations and how we mitigate them
Allowance-pull is not perfect. The honest drawbacks:
1. Infinite approvals
Wallets often default to infinite approve values. Malicious contracts with infinite allowances drain wallets. Our mitigation: UI explicitly asks for a capped approval (e.g. 12 × monthly_amount for an annual sub), shows the dollar number, and offers "cancel anytime" copy. The contract enforces cap separately from the ERC-20 allowance.
2. Revoke-between-cycles
A payer can revoke mid-subscription and the next charge will fail. Same failure mode as a saved card being canceled. Handled via existing webhook + email: subscription-insufficient-allowance.hbs and subscription-past-due.hbs.
3. Stale allowance after price change
If the merchant raises the price mid-subscription, the old cap may be too small. Handled by the cap invariant reverting the charge() and the merchant UI prompting for a re-approve.
4. Reorg-induced double-charge
Bot-scraping a block reorg can replay charge(). Handled by monotonic lastChargedAt in SubscriptionHub.
What changes for payers today
Nothing. Today's Auto-charge flow already asks for approve(spender, amount) and SPL Approve — the same steps. Phase 2 moves the spender to a non-custodial contract address. Your wallet sees the same permission screen.
What changes for merchants
Nothing in the API response shape. The approval_spender_address field in the subscription's auto_charge object just points at the SubscriptionHub contract instead of an OrcaRail hot wallet. The webhooks fire at the same moments.
Summary
| Consideration | Winner |
|---|---|
| Payer onboarding UX | Allowance-pull |
| Working capital | Allowance-pull / session keys |
| Wallet compatibility | Allowance-pull |
| Regulatory clarity | Allowance-pull / session keys |
| Keeper flexibility | All three |
| Contract simplicity | Allowance-pull |
Allowance-pull wins outright on wallet compatibility and working capital; it ties or wins on every other axis. That is why OrcaRail subscriptions are built on it.
Related: Phase 2: Allowance-pull subscriptions · Auto-charge docs · Security model
