Skip to main content

Why Allowance-Pull Beats Escrow and Session Keys

· 5 min read
OrcaRail
Crypto payment rails for web2 apps

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:

AxisAllowance-pullEscrowSession keys
Custody during active subPayerEscrow contractPayer (via smart wallet)
Steps for payer to set up1 signature1 deposit (any gas)1 onboarding to 4337 wallet
Payer wallet compatibilityAll walletsAll wallets4337 wallets only
Funds locked / working capitalNoneN months lockedNone
Cancel UXRevoke approveWithdraw + cancelRemove session key
Keeper riskAny callerAny callerAny caller
Merchant-side complexityMinimalRelease accounting4337 bundler setup
Works with MetaMask / PhantomYesYesNot natively
Works with Ledger / hardwareYesYesPartial
Regulatory clarityHighLower (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 approve works, which is all of them.
  • Zero funds locked. Payer keeps custody; pull happens just-in-time.
  • Revoke is universal. approve(0) or Revoke is 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

ConsiderationWinner
Payer onboarding UXAllowance-pull
Working capitalAllowance-pull / session keys
Wallet compatibilityAllowance-pull
Regulatory clarityAllowance-pull / session keys
Keeper flexibilityAll three
Contract simplicityAllowance-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