Tab

FEATURES

Subscriptions

Recurring stablecoin charges. Publish a plan, share a link, the subscriber approves the router once. Tab's executor handles every charge after that. Useful for paid Discord communities, creator memberships, SaaS billing, and recurring tips.

How it works

  1. Creator publishes a plan. From /dashboard/subscriptions, you set name, price, period (daily / weekly / monthly / quarterly / yearly) and chain. One tx publishes it on TabSubscriptionRouter.
  2. Subscriber visits the share link. At /subscribe/[planKey] they see the plan card and tap subscribe. Their wallet signs two txs: anapprove() for the lifetime allowance and one subscribe() that charges the first period.
  3. Tab cron charges each period. POST /api/cron/chargewalks active subs and submits the contract's charge(planKey, subscriber). The contract enforces lastChargedAt + period <= now so double-charging is impossible.
  4. Subscriber can cancel anytime. One on-chain tx flips the sub to inactive. Allowance revocation or zero balance also halts charges. Tab catches the revert and marks the subhalted.

Trust model

  • Funds never leave the subscriber's wallet until each charge fires. There's no escrow and no custody by Tab.
  • The router can never increase the price. Planamount and period are immutable on-chain, to change pricing, the creator publishes a new plan and migrates subscribers.
  • Anyone can call charge.The schedule, allowance check, and active-sub check live in the contract, so a malicious caller can't over-charge, only settle a legitimately-due period.
  • Protocol fee. Business-workspace plans take a 1% fee per charge; personal-workspace plans are 0%. Net goes to the creator.

Plan lifecycle

  • pending_create: staged off-chain, on-chain createPlan tx in flight.
  • active: accepting new subscribers and charges.
  • deactivated: no new subscribers; existing subscribers can still cancel but the cron won't advance their charges.

Subscription lifecycle

  • active: being charged each period.
  • cancelled: subscriber or creator stopped it. No further charges; allowance left in place until the subscriber revokes manually.
  • halted: last cron pass reverted (allowance or balance insufficient). The dashboard surfaces this so the creator can ping the subscriber to top up + resume.

Webhooks

Each lifecycle transition fires a webhook to any subscription registered for it:

  • subscription.plan_created
  • subscription.started
  • subscription.charged
  • subscription.cancelled
  • subscription.halted

Gasless plan deactivate / reactivate

Creators pause and resume plans without holding native gas. POST /api/subscriptions/manageroutes the call through the creator's existing 7702 tipping delegation — the delegation's AllowedMethods caveat was extended to whitelist TabSubscriptionRouter.deactivatePlan(bytes32) and reactivatePlan(bytes32)(in addition to ERC-20 transfer). Tab's executor submits the tx, the router sees msg.sender == plan.creator via the 7702 authority chain, and the plan flips state. No new contract deploy was needed; this is purely a delegation caveat extension.

Users whose delegations pre-date the extension (signed before the caveat update) get a clean fallback: the endpoint returns 409 delegation_legacyand the dashboard signs the deactivate / reactivate tx with the user's key directly, same as before. They can re-enable gasless tipping at /dashboard/tabbot to upgrade.

Solana plans

Subscriptions on Solana use a dedicated Tab Subscriptions Program (separate program IDs for the 0%-fee Personal tier and the 1%-fee Business tier). The dashboard's "new plan" flow handles both: pick Solanaas the chain and the creator's plan is published through Tab's fee-payer relay so the publisher never spends SOL. Subscribers pay USDC under a single one-time approval, and each recurring charge is submitted by Tab so renewals stay gasless too.

Contract reference

EVM: see TabSubscriptionRouter. Solana program IDs are listed in /docs/chains/solana.