All cases

Fortulox2026fortulox.com

A USDT crypto lottery where anyone can verify every draw

The gist in 10 seconds

A USDT lottery where anyone can recompute the result of every draw. From a backend archive we built the site, cabinet, admin, verifier and a Telegram bot in two days.

28
pages: site, cabinet, admin
73
API endpoints
59
database tables
10/10
fairness checks per draw
66
automated tests
72
public URLs in two languages
3
domains: site, cabinet, admin
2
days from archive to demo
Fortulox

Task

Build everything the player and the operator see on top of a fixed economy, and make fairness checkable: any person, not only us, must be able to recompute a draw result.

Result

A working full-cycle demo on three domains: a player registers, buys a pass, takes part in a draw and verifies the result. Real money stays off until the client's licence and legal details arrive.

What was done

  • Brand and the Luck Observatory design system: mark, palette, typography, a design-lab page with live components
  • Public site on Next.js 16 and React 19: home, how it works, fairness, draws, results, status, help, responsible play, 9 legal pages, EN and RU
  • Player cabinet on its own domain: sign-up and login, pass purchase, tickets, wallet, withdrawal, address book, settings
  • Database-backed sessions with an HttpOnly, Secure, SameSite=Strict cookie on the cabinet host only; argon2id passwords
  • Draw orchestrator: seed commit-reveal, entry snapshot with a Merkle tree, unbiased winner selection, proof bundle
  • Independent verifier: check a draw in the browser or from the command line, 10 checks per draw
  • Admin on a third domain behind three locks: basic auth, a closed nginx gate, password plus TOTP; 7 roles, dual approval of withdrawals, immutable audit log
  • Responsible play: limits, self-exclusion, eligibility check inside the purchase transaction under a lock
  • NestJS API: 26 controllers and 73 endpoints, payment idempotency, database-level locks
  • Telegram bot with a webhook and a Mini App for viewing draws
  • Infrastructure: three systemd services, five nginx server blocks, Let's Encrypt, a deploy script, an EVIDENCE check log and a DECISIONS log in the repository

Screens

Explore the details — the full picture

Where we started

The client brought an archive: economy, ledger, payments and withdrawals written by a previous contractor. There was no site, the cabinet was a stub, sessions lived in process memory. The economy was off limits: everything had to work exactly as in the archive.

We kept three documents in the repository and ran all work through them: a task list, a check log with evidence and a decision log for cases where the archive, the client and the brief disagree.

Site and brand

The Luck Observatory design system: a dark stage, an acid accent, a split-aperture mark. A design-lab page shows live components. The site runs on Next.js 16 in English and Russian, 72 public addresses with no broken link.

On a phone, blocks do not pop in while scrolling. Each section has one motion that states a fact, and it plays when the block reaches the middle of the screen.

Cabinet and admin

The cabinet sits on its own domain so the session cookie is never visible to the main site. A player signs up, buys a pass, sees tickets and wallet, withdraws winnings to an address-book address, sets limits or self-exclusion.

The admin lives on a third domain behind three locks: nginx basic auth, a secret gate header and a password with TOTP. Seven roles, withdrawals need two approvals, the action log cannot be altered.

Fairness you can check

A seed commitment is published before the draw. After it, the seed, the entry snapshot with its Merkle root and a proof bundle are published. Winners are selected without bias.

The verifier is a separate package and shares no code with the draw. A result can be checked in the browser on the draw page or from the command line. Ten checks per draw.

Engineering

NestJS API: 26 controllers and 73 endpoints. PostgreSQL: 59 tables, 132 indexes, four triggers. Money is stored only as integer micro-USDT. A purchase runs in a transaction under a lock, payments are idempotent.

66 automated tests and 59 scenario scripts on a live database. One-script deploys, three systemd services, five nginx server blocks.

Honest: what is not finished

  • This is a demo without real money: payments and withdrawals are simulated until the client's licence and legal details arrive.
  • The randomness source is a public fixture for now. An external randomness beacon is the chosen path for production; the decision is recorded, the implementation is not done yet.
  • The site is closed to indexing until launch, so SEO, a sitemap and analytics are still ahead.

Stack Next.js 16React 19NestJSDrizzle ORMPostgreSQLRedis / BullMQTypeScriptVitestTelegram Bot APInginx / systemd

request

Tell us the idea. A couple of lines is enough.

We reply within a business day in the messenger you choose. No newsletters.

Or straight to Telegram: @lovelysup

We need a @handle, phone or e-mail — otherwise we cannot find you.
Preferred channel

By sending you agree to data processing needed to answer your request.