Back to blog

Building Vowena: from idea to testnet in 4 weeks

The story of building the first recurring payment protocol on Stellar. The technical decisions, the late nights, and what we learned along the way.

T

Tunde Adebayo

Building Vowena: from idea to testnet in 4 weeks

Four weeks ago, Vowena was a markdown file called VOWENA.md. Today it is a deployed Soroban smart contract with 29 passing tests, a TypeScript SDK on npm, a full-stack dashboard, 40 pages of documentation, and a marketing site with the blog post you are reading right now.

This is how it happened.

The markdown file that started everything

I was building a Stellar app that needed to charge users monthly. On traditional rails, this is a solved problem: Stripe, a webhook handler, some database rows. Done in an afternoon.

On Stellar? Nothing. No subscription primitive. No recurring payment standard. No protocol that handles the full lifecycle of create plan, subscribe, charge, retry, cancel, refund.

I started writing down what this protocol would need to look like. The result was a 270-line markdown document that described every function, every edge case, every data structure. It was obsessively detailed because the contract would handle real money.

Week 1: The contract

The smart contract is the foundation. If it has a bug, people lose money. Every design decision here propagated through the entire system.

The data model came together quickly. Two core types: Plans (billing templates created by merchants) and Subscriptions (the on-chain record linking a subscriber to a plan). Plans are immutable after creation. Subscriptions have a state machine: Active, Paused, Cancelled, Expired.

The hardest problem was handling failed charges. In Soroban, if transfer_from() fails, the entire transaction reverts. The contract cannot record that a failure happened. My first instinct was to use a try/catch pattern. Soroban does not have one.

The solution: pre-check balance() and allowance() before attempting the transfer. If they are insufficient, record failed_at, emit a ChargeFailed event, and return false. Graceful degradation instead of silent failure. This single decision shaped the entire grace period system.

Making charge() permissionless was the other key decision. No require_auth() on the billing function. Anyone can call it. The caller pays $0.00001 in fees. Only the merchant receives the subscription payment. This eliminates single points of failure in billing execution.

By end of week 1: 9 Rust source files, 17 exported functions, 29 passing tests, 18.5KB WASM binary. The contract was the size of a small JPEG.

Week 2: The SDK

A smart contract without an SDK is just documentation. Developers should not need to think about XDR encoding, Soroban simulation, or auth entry extraction. They should write:

const xdr = await client.buildSubscribe(wallet.address, planId)

And get back assembled XDR ready for wallet signing.

The VowenaClient class wraps every contract function into two categories: write methods (return XDR for signing) and read methods (query state directly through simulation). The naming convention is deliberate - build* for writes, get* for reads.

The amount conversion utilities were surprisingly important. USDC on Stellar uses 7 decimal places. toStroops("9.99") returns 99900000n. fromStroops(99900000n) returns "9.99". Sounds trivial, but getting decimal arithmetic wrong in a billing system means charging people the wrong amount.

Week 3: The dashboard

The dashboard serves two fundamentally different users who need the same underlying data.

Merchants need plan management, subscriber tables, revenue analytics, and billing automation controls. They think in terms of MRR, churn rate, and failed payment percentages.

Subscribers need a universal view of every Vowena subscription tied to their wallet, across all merchants, with one-click cancel. They think in terms of "what am I being charged for and can I stop it."

The insight was that both views come from the same on-chain data. The same event indexer that powers merchant analytics also powers the subscriber's billing history. So the dashboard is one app with a shared sidebar, not two separate experiences.

The managed keeper runs as a cron job on the backend. Every interval, it iterates subscriptions that are due for billing and calls charge() for each one. The merchant enables it with a toggle. No DevOps knowledge required.

Week 4: Documentation and the marketing site

Documentation is the product for an open-source protocol. If a developer cannot understand how to use Vowena from the docs alone, the protocol fails regardless of how good the contract is.

40 Mintlify pages covering:

  • Protocol architecture (storage strategy, data models, TTL management)
  • Every API function with parameters, auth requirements, errors, and examples
  • SDK reference with TypeScript types for every method
  • Dashboard guides for merchants and subscribers

The marketing site uses MDX for blog posts. Each post is a .mdx file in a /content/blog folder. No CMS, no external tool. Write markdown, push to git, it becomes a page. The simplest possible publishing workflow.

What I would do differently

Test the grace period state machine more aggressively. The transitions between Active (with failed_at), Paused, and Cancelled have subtle timing dependencies. I caught edge cases late that should have been in the first test suite.

Start with the SDK types, not the contract types. I designed the Rust types first and then mapped them to TypeScript. Going the other direction - starting with the developer-facing types and working backward to storage - would have produced a cleaner API.

Build the event indexer before the dashboard UI. The dashboard pages were initially built with placeholder data because the indexer was not ready. This meant reworking the data layer twice. In hindsight, indexer first, UI second.

What is next

Mainnet deployment is the immediate goal. The contract works on testnet. The SDK works. The dashboard works. The remaining work is:

  1. Security review of the contract
  2. Testnet pilot with 3-5 merchants
  3. Mainnet deployment and SDK version bump
  4. Stellar Community Fund submission with working demo

If you want to build with Vowena, the docs have everything you need. If you want to run a pilot, reach out on X. If you want to contribute, the protocol source and SDK source are both open.

The Stripe of Stellar is not a pitch deck. It is 18.5KB of WASM on a blockchain that costs a fraction of a cent to use.

Let us see where this goes.