Live Web3 App: strikebiller.vercel.app
GitHub Repository: 1rishuraj/strikebiller
Devnet Program ID: DJPman6YhQpGAFp3PkLXjqcf6fXrdVrwmrjve7odk8j
Pattern: Subscription Billing System & Escrow Engine
Video Demo: HERE
graph TD
subgraph Off-Chain Actors
Merchant([Merchant Wallet])
Subscriber([Subscriber Wallet])
Cranker[[Cranker Bot / Node.js]]
end
subgraph StrikeBiller Anchor Program
PlanPDA[SubscriptionPlan PDA<br/>Seeds: 'plan', Merchant, PlanID]
SubPDA[Subscription PDA<br/>Seeds: 'subscription', Subscriber, PlanPDA]
end
subgraph Token Accounts Escrow
SubATA[(Subscriber USDC ATA)]
VaultATA[(Vault USDC ATA<br/>Owner: Subscription PDA)]
MerchATA[(Merchant USDC ATA)]
end
%% Initialization & Subscription
Merchant -->|1. initialize_plan| PlanPDA
Subscriber -->|2. subscribe| SubPDA
SubATA -.->|Locks upfront USDC| VaultATA
%% Execution
Cranker -->|3. process_billing| SubPDA
SubPDA -->|Validates Clock::get & Rules| VaultATA
VaultATA -.->|Transfers if due| MerchATA
%% Cancellation
Subscriber -->|4. cancel_subscription| SubPDA
VaultATA -.->|Refunds unbilled USDC| SubATA
In a traditional SaaS architecture (e.g., Node.js + PostgreSQL + Stripe):
- State Storage: User status (
is_active,next_billing_date) is stored in a centralized database row. - Execution (Active Cron Jobs): A backend server runs a continuous background cron job. When a billing date arrives, the server actively triggers an API call to a payment processor to charge a saved credit card.
- Trust Model: The user surrenders their payment details to the merchant. The user must blindly trust that the merchant will not overcharge them, double-bill them, or shut down operations after taking a 30-day upfront payment.
StrikeBiller reframes Solana as a distributed, lazy state-machine backend.
- State Storage (PDAs): The traditional database is replaced by Program Derived Addresses (PDAs).
SubscriptionPlanandSubscriptionstates are publicly verifiable bytes on the ledger. - Trustless Escrow Vaults: Instead of trusting a merchant with a credit card, the subscriber locks their USDC into an isolated Vault ATA (owned by the
SubscriptionPDA). The merchant can only claim funds after the time has elapsed. - Lazy Execution (The Cranker Bot): Solana smart contracts cannot "wake themselves up" like Web2 cron jobs. They are passive. To solve this, StrikeBiller uses an off-chain "Cranker" bot (Node.js). The bot queries the blockchain for subscriptions where
next_billing_atis in the past, and submits aprocess_billingtransaction. - The Contract is the Bouncer: The cranker bot has no administrative power. When it calls
process_billing, the Rust program acts as the ultimate authority. It checksClock::get()?.unix_timestampand physically blocks the transaction if the bill is not legitimately due. - Strike System & Atomic Bundling: If a vault is empty, the contract issues "Strikes". At 3 strikes, the account is hard-paused. Users can execute an atomic "Top Up & Resume" bundle to instantly refill the vault, unpause, and reset their billing timeline relative to the paused duration.
- Instant Cancellation & Guaranteed Refunds: Unlike Web2 where users must trust a company to honor a cancellation and stop future charges, StrikeBiller users maintain absolute sovereignty. Calling the
cancel_subscriptioninstruction instantly tears down theSubscriptionPDA, closes the Vault ATA, and refunds all unbilled USDC directly back to the user's wallet.
- Storage Costs (Rent): Storing subscription state in a Web2 database is practically free. On Solana, allocating bytes for the
SubscriptionandPlanPDAs requires a small amount of SOL for rent exemption. - Clock Drift: Because the decentralized Solana network cluster calculates time slightly differently than a local server clock, the off-chain Cranker bot must account for slight network drift, otherwise the Rust contract will reject the transaction with a
BillingCycleNotDueerror. - Execution Centralization: Currently, the cranker bot is a centralized Node.js script. If the script crashes, automated billing stops. However, because the smart contract is permissionless, anyone (even the user or a decentralized oracle network like Clockwork/Switchboard) can manually sign the cranker transaction to push the state machine forward.
This monorepo contains the complete lifecycle of the billing engine:
strikebiller/
├── programs/biller/ # Anchor Rust smart contract (Backend State Machine)
│ ├── src/instructions/ # Core logic (init_plan, subscribe, toggle, cancel)
│ ├── src/state/ # PDA structures (Subscription, SubscriptionPlan)
│ └── tests/biller.ts # E2E Escrow and state transition tests
├── frontend/ # Next.js Web3 client (Deployed on Vercel)
│ └── src/
│ ├── components/ # UI components (FundVaultModal, BillerHero)
│ └── services/ # Blockchain interaction logic & IDL parsing
└── cranker/ # Node.js daemon worker for lazy execution
└── src/index.ts # Off-chain bot querying and billing due subscriptions
- Rust Tests: Navigate to
/programs-> runanchor testto verify all escrow constraints and state transitions. - Frontend: Navigate to
/frontend->npm install->npm run dev. - Cranker Bot: Navigate to
/cranker->npm install->npx ts-node src/index.ts.
(Note: The live frontend requires Devnet USDC. Mint: Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr)
Below is the complete, sequential transaction history proving the lifecycle of the system on Devnet.
Actors & Tokens
- Merchant Wallet:
A5czjgBJ4Wqnexyxy2K2NDhiwz8LqJsV5Wd921tUw9yz - Subscriber Wallet:
FyFwjP4gqdzgRxwv3sMYGJ9yYvYYvgU1RN3xZgh5ubvt - Program Deployment:
5bQLebEeuxQBiCbZYGfwRbdtHVP72nJkwZvcB22gx5TRjryLzggwT2xNcGcxokxMxYpZnrQbS4mnKmbgzZbrQquV
1. Initialization & Subscription Flow
- Initialize "Pro Plan" (Merchant):
3unP88Y... - Subscribe & Lock Escrow Funds (Subscriber):
3QLEgNG...
2. Lazy Execution (The Cranker Bot)
- Cranker Successfully Bills Subscription:
2vgNET2... - Cranker Issues 3 Strikes for Empty Vault & Pauses:
- Strike 1:
4fWSVh7... - Strike 2:
3Ppfu8F... - Strike 3 (Hard Pause):
4pEozzj...
- Strike 1:
3. State Toggling & Cancellation
- Manual Pause (Vacation Mode):
3Yy7zvq... - Manual Resume:
2PMB4XF... - Cancel Subscription & Refund Remaining Escrow:
3gNX5PS...
4. Additional Flow Testing
- Initialize "Basic Plan":
MSYoGyu... - Subscribe to "Basic Plan":
2hRtt97...