15 Minutes to Decentralization: The Zoltaran Speaks Story

>2026-01-22|5 min read

Explore my tools: agents-skills-plugins

The Challenge

My friend Nathan Wosnack, CEO of Ubitquity and Bitcoin OG, built Zoltaran Speaks, a fortune-telling game for the nDAO Arcade. Users make wishes, spin a crystal ball, and win tokens. Classic arcade game with a crypto twist.

When he showed it to me, my first thought was: why isn't this decentralized?

The game already used blockchain for payments. Users connected their Proton wallets. Token transfers happened on-chain. But the game logic? That ran through a PHP backend. Credits were tracked in JSON files on a server.

Nathan asked if I could help make it fully decentralized. I said yes.

Fifteen minutes later, it was done.

What I Found

Here's what the original architecture looked like:

Frontend (index.html)

  • Proton WebAuth SDK for wallet connections
  • Web Crypto API for random number generation
  • Client-side game logic with SHA-256 seed verification

Backend (PHP)

  • credits.php
    for tracking purchased wishes
  • psychic_queue.php
    for payout processing
  • log.txt
    for game history
  • wishes.json
    for user data

The backend was the centralization point. Every wish purchase, every credit check, every payout queued through PHP scripts writing to flat files on a server.

But here's what Nathan had already built correctly:

  1. Token transfers were on-chain. Purchases used real Proton transactions with proper contract calls.
  2. Randomness was cryptographic. The Web Crypto API generated outcomes, not a server.
  3. Verification data was embedded. Every result included the seed, random value, and timestamp for fairness verification.

The pieces were there. The PHP backend was doing work that the blockchain could already do.

Centralized vs decentralized architecture
Centralized vs decentralized architecture

The Tech Stack

Proton Chain Integration

javascriptconst CHAIN_CONFIG = { chainId: '384da888112027f0321850a169f737c33e53b388aad48b5adace4bab97f437e0', endpoints: [ 'https://proton.greymass.com', 'https://proton.eosusa.io' ] };

Proton is an EOSIO-based chain with WebAuth, meaning users authenticate with their wallet directly. No email/password. No OAuth. Just cryptographic proof of identity.

The game accepts 11 different tokens: XUSDC, ARCADE, NFTP, TITLET, UBQTX, UBQT, NDAO, NDAOX, and others. Each token has its own contract and precision settings.

Web Crypto API for Fairness

javascriptasync function generateSecureRandom() { const array = new Uint32Array(1); crypto.getRandomValues(array); return array[0] / (0xFFFFFFFF + 1); } async function generateSeed() { const timestamp = Date.now().toString(); const randomBytes = crypto.getRandomValues(new Uint8Array(32)); const combined = timestamp + Array.from(randomBytes).join(''); const encoder = new TextEncoder(); const data = encoder.encode(combined); const hashBuffer = await crypto.subtle.digest('SHA-256', data); return Array.from(new Uint8Array(hashBuffer)) .map(b => b.toString(16).padStart(2, '0')) .join(''); }

Every game outcome includes:

  • The cryptographic seed
  • The random value generated
  • The timestamp
  • A hash that proves the outcome wasn't tampered with

This is provably fair gaming. Anyone can verify that the outcome matches the inputs.

Cryptographic randomness visualization
Cryptographic randomness visualization

The Outcome Distribution

javascriptconst OUTCOMES = { WISH_GRANTED: { probability: 0.05, weight: 5 }, TOKENS_250: { probability: 0.10, weight: 10 }, TOKENS_500: { probability: 0.08, weight: 8 }, TOKENS_1000: { probability: 0.02, weight: 2 }, FREE_SPIN: { probability: 0.25, weight: 25 }, TRY_AGAIN: { probability: 0.50, weight: 50 } };

50% house edge on TRY_AGAIN. Fair for an arcade game. And crucially: these probabilities are in the client code. Anyone can read them. No hidden server-side manipulation.

The Decentralization

The PHP backend was tracking two things:

  1. Wish credits (how many plays you have left)
  2. Payout queue (pending token rewards)

Both of these can be derived from on-chain state.

Credits become token balances. Instead of a PHP script tracking how many wishes you've purchased, the game reads your token balance directly. Bought 10 wishes? You transferred 10 tokens. Your balance is your credit count.

Payouts become direct transfers. Instead of queueing payouts in a text file for later processing, wins trigger immediate on-chain transfers. The transaction either succeeds or fails. No intermediate state.

The key insight: the blockchain is already a database. Nathan had built the token transfer logic. He just wasn't using it as the source of truth.

What Remained

Some server-side functionality still makes sense:

  • Leaderboards can read from on-chain transaction history
  • Activity feeds can aggregate recent plays
  • Anti-abuse logging for detecting manipulation attempts

But these are read-only views of on-chain data, not authoritative state. The game works without them. They're nice-to-haves, not dependencies.

Why It Took 15 Minutes

15 minutes to decentralization
15 minutes to decentralization

Because Nathan had already done the hard work.

Integrating Proton WebAuth? Done. Token transfer logic for 11 different tokens? Done. Cryptographically secure randomness? Done. Provably fair outcome verification? Done.

The remaining PHP code was legacy thinking. It existed because that's how web apps traditionally work: frontend talks to backend, backend owns the data.

But once you have wallet-based authentication and on-chain token transfers, the paradigm shifts. The blockchain becomes your backend. The user's wallet becomes their account. Token balances become application state.

I didn't write much code. I deleted code. The PHP backend became unnecessary once we trusted the chain to be the source of truth.

The Lesson

Decentralization isn't always a massive rewrite. Sometimes the architecture is already there, buried under assumptions about how web apps "should" work.

Nathan built Zoltaran Speaks with blockchain payments from day one. He just hadn't fully committed to the model. The PHP backend was a safety net that turned out to be unnecessary.

When you're building on-chain applications, ask yourself: what is the server doing that the blockchain already does?

  • Tracking balances? The chain does that.
  • Recording transactions? The chain does that.
  • Proving identity? Wallets do that.
  • Generating randomness? The client can do that cryptographically.

The server becomes optional. And when the server is optional, you're decentralized.

Try It

Zoltaran Speaks is open source under MIT license: github.com/ubitquity/Zoltaran_Speaks

The commercial version runs on ndao.org alongside other arcade games.

Connect a Proton wallet, make a wish, and watch on-chain gaming in action. Every spin is verifiable. Every payout is a real transaction. No server required.

That's decentralization.

>Comments

>_Eric Engine

Ask me anything

Type your question below

>