What is a Keeper?
A keeper is an automated script that callscharge() on the Vowena contract for subscriptions that are due. Because charge() is permissionless, anyone can trigger it - the contract validates all conditions on-chain. The keeper simply pays the tiny transaction fee (~$0.00001 in XLM), while the subscription payment flows directly from the subscriber to the merchant.
You don’t need to run your own keeper. The Vowena Dashboard provides a managed keeper service. This page is for developers who want to self-host or customize their keeper.
How It Works
- The keeper polls for subscription IDs that may be due for charging
- For each subscription, it builds a
charge()transaction using the SDK - It signs the transaction with its own keypair (the keeper wallet)
- It submits the transaction to the network
- If the charge succeeds, USDC transfers from subscriber to merchant. If it fails (not yet due, insufficient balance, etc.), the contract rejects it harmlessly.
Setup
The standalone keeper bot lives atsdk/keeper/src/index.ts in the Vowena repository.
Fund a Keeper Wallet
Create a new Stellar keypair and fund it with a small amount of XLM for transaction fees. On testnet, use the Stellar Friendbot.A few XLM is sufficient for thousands of charge transactions.
Configure Environment Variables
The keeper bot reads its configuration from environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
CONTRACT_ID | Yes | - | Vowena contract address (e.g., CABC...XYZ) |
KEEPER_SECRET | Yes | - | Stellar secret key for the keeper wallet (S...) |
RPC_URL | No | https://soroban-testnet.stellar.org | Soroban RPC endpoint |
NETWORK_PASSPHRASE | No | Test SDF Network ; September 2015 | Network passphrase |
INTERVAL_MS | No | 60000 | Polling interval in milliseconds |
How charge() Is Permissionless
When a subscriber signs thesubscribe() transaction, they authorize a SEP-41 token allowance to the Vowena contract. From that point on, the contract itself can pull funds via transfer_from() - it does not matter who initiates the charge() call. The contract enforces all rules:
- The billing period must have elapsed
- The subscription must be active
- The subscriber must have sufficient balance and allowance
- Trial periods must be complete
- The max period limit must not be exceeded
Production Considerations
Use an event indexer instead of iterating IDs
Use an event indexer instead of iterating IDs
The basic keeper iterates subscription IDs sequentially, which works for small-scale testing. For production, use the dashboard’s event indexer (or build your own) to maintain a database of active subscriptions and their next charge dates. This way you only attempt charges for subscriptions that are actually due, saving RPC calls and transaction fees.
Handle rate limits
Handle rate limits
Public Soroban RPC endpoints have rate limits. For production keepers, use a dedicated RPC provider or self-host a Stellar node to avoid throttling during high-traffic periods.
Monitor and alert
Monitor and alert
Set up monitoring for your keeper process. Track metrics like successful charges, failed charges, and RPC errors. Alert on prolonged failures so you can intervene before subscriptions start pausing due to missed charges.
Run multiple instances carefully
Run multiple instances carefully
If you run multiple keeper instances, they may attempt to charge the same subscription simultaneously. The contract handles this safely - only the first charge succeeds, and subsequent attempts fail harmlessly. However, you will pay unnecessary transaction fees. Use a coordination layer or partition subscription IDs across keepers.