Complete flow from raffle creation through leaderboard update · A reference for builders, onboarding, and cross-package workflows
A raffle's lifecycle in Tikka spans 8 components and 6 key stages :
Component
Role
Repository
Client
User interface for raffle creation and participation
client/
SDK
Contract interaction abstraction layer
sdk/
Smart Contract
Onchain raffle state machine and enforcement
tikka-contracts
Backend
API, authentication, metadata, notifications
backend/
Indexer
Blockchain event ingestion and query layer
indexer/
Oracle
Randomness computation and submission
oracle/
Database
Persistent raffle and leaderboard data (Supabase PostgreSQL)
backend/database/
Cache
Redis for real-time data and queue management
External (Redis)
User initiates in Client UI
Client builds transaction via SDK
Contract executes create_raffle()
Backend validates metadata and stores in Supabase
Event emitted to Stellar ledger
RaffleCreated {
raffle_id : u32,
creator : Address ,
params : RaffleParams {
asset : String ,
ticket_price : u128,
max_tickets : u32,
end_time : u64,
}
}
raffle_metadata (Supabase) — stores title, description, image_url, category
Contract state — stores raffle params and state
User Client SDK Contract Backend
│ │ │ │ │
├─ Create Raffle ──▶ │ │ │ │
│ ├─ Build Tx ──────▶ │ │ │
│ │ ├─ Simulate & Sign ─▶ │ │
│ │ │ Execute Tx │ │
│ │◀─ Tx Hash ─────── │◀─ Confirmation ──│ │
│ │ │ │─▶ Emit Event │
│ │ │ │ │
│ │ │ │ │
│ │ (Event to ledger) │
│ │ │ │
│ │ │ Store Metadata ◀─┤
│ │◀─ Success ────────────────────────────────────────────│
│◀─ Raffle Created ───┤
User purchases ticket(s) in Client UI
Client builds and signs transaction via SDK
Contract executes buy_ticket()
Indexer listens to emitted event
Indexer processes and writes to database
Backend serves updated raffle with ticket count
TicketPurchased {
raffle_id : u32,
buyer : Address ,
ticket_ids : Vec <u32>,
total_paid : u128,
timestamp : u64,
}
tickets — stores ticket records with buyer, raffle_id, purchase_timestamp
raffles — updates tickets_sold counter
Redis cache — invalidates raffle detail cache
User Client SDK Contract Indexer Backend
│ │ │ │ │ │
├─ Buy Ticket ────▶│ │ │ │ │
│ ├─ Build Tx ───▶│ │ │ │
│ │ ├─ Sign & Submit ▶ │ │
│ │ │ Execute Tx │ │ │
│ │ │◀─ Confirmation │ │ │
│ │◀─ Tx Hash ────┴──────────┐ │ │ │
│ │ │ │ │ │
│ │ Emit TicketPurchased Event │ │
│ │ │ │ │ │
│ │ │ ├─ Listen ─────▶│ │
│ │ │ │ Process & │ │
│ │ │ │ Write DB │ │
│ │ │ │◀──────────────┤ │
│ │ │ │ Invalidate │ │
│ │ │ │ Cache │ │
│ │ │ │ ├─ API ───────▶(GET /raffles/:id)
│ │◀─ Success ───────────────────────────────────────────────────│
│◀─ Ticket Bought ─│
Stage 3: Draw Request / Trigger
End time reached or manually triggered by host
Client/Backend initiates trigger_draw()
Contract transitions from OPEN → DRAWING
Contract emits DrawTriggered event
Indexer updates raffle status to DRAWING
DrawTriggered {
raffle_id : u32,
ledger : u32,
timestamp : u64,
}
raffles — updates status to DRAWING
User/Host Client SDK Contract Indexer
│ │ │ │ │
├─ End Time ────────│ │ │ │
│ or Manual Trigger │ │ │ │
│ ├─ Build Tx ───────▶│ │ │
│ │ ├─ Invoke ────────▶│ │
│ │ │ trigger_draw() │ │
│ │ │◀─ Confirmation ─▶│ │
│ │ │ │ │
│ │ │ ├─ Emit DrawTriggered
│ │◀─ Tx Confirmed ───┴──────────────────┴─▶│ │
│ │ ├─ Update │
│ │ │ Status │
│ │ │ to DRAWING
│◀─ Draw Started ───┤
Stage 4: Oracle Response (Randomness Computation & Submission)
Oracle listener monitors Stellar ledger for RandomnessRequested event
Oracle dequeues job from Redis Bull queue
Oracle computes randomness :
Checks prize amount
Selects method: VRF (≥ 500 XLM) or PRNG (< 500 XLM)
Oracle submits receive_randomness() to contract
Contract verifies proof and stores randomness
Contract emits RandomnessReceived event
RandomnessRequested {
raffle_id : u32,
request_id : BytesN <32 >,
timestamp : u64,
}
RandomnessReceived {
raffle_id : u32,
seed : BytesN <32 >,
proof : BytesN <64 >,
timestamp : u64,
}
Randomness Method Selection
Prize Amount
Method
Processing Time
Cost
Verifiable
< 500 XLM
PRNG (SHA-256)
Instant
~0 XLM
Yes (deterministic)
≥ 500 XLM
VRF (Ed25519)
~2–5s
Standard tx fee
Yes (cryptographic proof)
PRNG : seed = SHA256(requestId || raffleId || timestamp)
VRF : Uses oracle's Ed25519 keypair to generate cryptographic proof
RandomnessRequested Event
│
▼
Determine Prize
│
├─ Tier ≥ 500 XLM ──▶ HIGH Priority (SLA: 5s)
│
└─ Tier < 500 XLM ──▶ NORMAL Priority
│
Bull Queue (Redis)
│
Processing Pool (5 workers)
│
┌──────┴──────┐
│ │
VRF Service PRNG Service
│ │
└──────┬──────┘
│
Contract Submission
Stellar Ledger Oracle Listener Queue (Redis) Randomness Service Contract
│ │ │ │ │
├─ Emit ────────────▶│ │ │ │
│ RandomnessRequested │ │ │
│ │ │ │ │
│ ├─ Enqueue ──────▶│ │ │
│ │ Job │ │ │
│ │ │ │ │
│ │ ├─ Worker Dequeue ─▶│ │
│ │ │ (Check Prize) │ │
│ │ │ ├─ Select ──┐ │
│ │ │ │ Method │ │
│ │ │ │ (VRF/PRNG)│ │
│ │ │ │◀──────────┘ │
│ │ │ │ │
│ │ │ Compute Randomness │
│ │ │ (seed + proof) │ │
│ │ │◀──────────────────│ │
│ │ │ │ Submit ───▶│
│ │ │ │ receive_randomness
│ │ │ │ │
│ │ │ │◀─ Verify ────│
│ │ │ │ & Store │
│ │ │ │ │
│◀───────────────────▶───────────────▶│ ├─ Emit ────────│
│ Randomness │ │ RandomnessReceived
│ Complete │ │ │
Database Tables Involved (Indexer)
randomness_requests — stores request metadata
randomness_responses — stores seed and proof
Stage 5: Raffle Finalization
Contract receives randomness (from Stage 4)
Contract verifies proof (if VRF)
Contract deterministically selects winner
Contract emits RaffleFinalized event
Indexer processes event and updates database
Backend serves finalized raffle with winner info
RaffleFinalized {
raffle_id : u32,
winner : Address ,
winning_ticket_id : u32,
prize_amount : u128,
timestamp : u64,
}
Winner Selection Algorithm
1. Total tickets: N
2. Random seed: R (from oracle)
3. Winner index: (R as u32) % N
4. Lookup: winning_ticket = tickets[winner_index]
5. Prize transfer: executed atomically by contract
raffles — updates status to FINALIZED, stores winner info
tickets — marks winning ticket
Cache invalidation — clears raffle detail cache
Oracle Contract Indexer Backend User
│ │ │ │ │
├─ Submit ───────────▶│ │ │ │
│ receive_randomness │ │ │ │
│ ├─ Verify Proof │ │ │
│ │ │ │ │
│ ├─ Select Winner │ │ │
│ │ │ │ │
│ ├─ Transfer Prize │ │ │
│ │ │ │ │
│ ├─ Emit ──────────▶│ │ │
│ │ RaffleFinalized ├─ Process │ │
│ │ │ Event │ │
│ │ ├─ Update DB │ │
│ │ │ │ │
│ │ ├─ Notify ─────▶│ │
│ │ │ ├─ Publish ─────▶(GET /raffles/:id)
│ │ │ │ │
│ │ │ │ Queue for ─▶(Leaderboard Update)
│ │ │ │ Leaderboard │
│◀──────────────────────── Tx Confirmed ────────────────────────────────│
│ Randomness Success
Stage 6: Leaderboard Update
Raffle finalized (from Stage 5)
Indexer triggers leaderboard update via webhook/event
Backend processes winner statistics
Backend updates leaderboard tables in Supabase
Backend publishes real-time leaderboard via WebSocket or API
User wins — total raffles won by address
User participation — total raffles entered
User earnings — total prize amount won
Global rankings — top winners by earnings
Category rankings — top winners per raffle category
leaderboard_entries — stores user stats
leaderboard_snapshots — historical snapshots (for trending)
user_statistics — aggregated user performance
Indexer Backend (Leaderboard Service) Supabase DB WebSocket/API
│ │ │ │
├─ Process ───────────────▶│ │ │
│ RaffleFinalized Event │ │ │
│ │ │ │
│ ├─ Calculate Stats ─────▶│ │
│ │ - Increment wins │ │
│ │ - Update earnings │ │
│ │ - Rank updates │ │
│ │◀─ Confirmation ────────│ │
│ │ │ │
│ ├─ Publish Update ──────────────────────▶(WS/API)
│ │ │ │
│◀─ Ack ─────────────────────────────────────────────────────────│
Complete End-to-End Lifecycle Diagram
┌─────────────────────────────────────────────────────────────────────────────┐
│ TIKKA RAFFLE LIFECYCLE (E2E) │
└─────────────────────────────────────────────────────────────────────────────┘
STAGE 1: RAFFLE CREATION
═════════════════════════════════════════════════════════════════
Client ──▶ SDK ──▶ Contract ──▶ RaffleCreated Event ──▶ Indexer ──▶ DB
│ │
└──────────────────────────────────────────┘
▼
Backend validates metadata
(Supabase storage)
STAGE 2: TICKET PURCHASE (Repeating)
═════════════════════════════════════════════════════════════════
User ──▶ Client ──▶ SDK ──▶ Contract ──▶ TicketPurchased Event ──▶ Indexer
│ │
└─────────────────────────────────────────────────┘
▼
Update tickets_sold counter
Cache invalidation
STAGE 3: DRAW TRIGGER
═════════════════════════════════════════════════════════════════
User/Host ──▶ Client ──▶ SDK ──▶ Contract ──▶ DrawTriggered Event ──▶ Indexer
│ │ │
└──────────────────────▶ Status: OPEN → DRAWING ──────────┘
STAGE 4: ORACLE RANDOMNESS
═════════════════════════════════════════════════════════════════
Oracle Listener ──▶ Queue Job (Redis)
│
├─ Check Prize Amount
│
├─ < 500 XLM ──▶ PRNG Service
│
└─ ≥ 500 XLM ──▶ VRF Service
│
└──▶ Compute (seed + proof)
│
└──▶ Contract.receive_randomness()
│
└──▶ RandomnessReceived Event
STAGE 5: FINALIZATION
═════════════════════════════════════════════════════════════════
Contract ──▶ Select Winner ──▶ Transfer Prize ──▶ RaffleFinalized Event
│ │
└────────────────────────────────────┘
▼
Indexer processes event
Update raffles table (winner, prize)
Cache invalidation
STAGE 6: LEADERBOARD UPDATE
═════════════════════════════════════════════════════════════════
Indexer ──▶ Backend (Leaderboard Service)
│
├─ Increment user wins
├─ Update earnings sum
├─ Recalculate rankings
│
└──▶ Supabase (leaderboard tables)
│
└──▶ WebSocket/API broadcast to users
Stage
Event
Source
Processor
Destination
Table Updated
1
RaffleCreated
Contract
—
Backend
raffle_metadata
2
TicketPurchased
Contract
ticket.processor.ts
Indexer → DB
tickets, raffles.tickets_sold
3
DrawTriggered
Contract
raffle.processor.ts
Indexer → DB
raffles.status = 'DRAWING'
4a
RandomnessRequested
Contract
Oracle Listener
Oracle Queue
randomness_requests
4b
RandomnessReceived
Contract
raffle.processor.ts
Indexer → DB
randomness_responses
5
RaffleFinalized
Contract
raffle.processor.ts
Indexer → DB
raffles, tickets (winner marked)
6
(Event trigger)
Indexer
Leaderboard Service
Backend → DB
leaderboard_entries, user_statistics
Key Cross-Package Interactions
Responsibility : SDK abstracts all contract interaction
Reference : sdk/src/modules/raffle/
Interaction : Client consumes SDK methods for createRaffle(), buyTicket(), triggerDraw()
Responsibility : SDK builds, simulates, signs, and submits transactions
Reference : sdk/src/
Interaction : TX building, fee estimation, keypair management
Responsibility : Oracle processes requests and submits randomness
Reference : oracle/src/
Interaction : Cosmos-style 2-step (request event → response submission)
Responsibility : Backend publishes updates for frontend real-time updates
Reference : backend/src/api/rest/notifications/
Interaction : WebSocket broadcasts, Push notifications (via service worker)
Environment Variables & Configuration
VITE_RAFFLE_CONTRACT_ADDRESS — Deployed contract address
VITE_SUPABASE_URL — Backend API endpoint
VITE_SUPABASE_KEY — Anonymous key for auth
DATABASE_URL — Supabase PostgreSQL connection
SUPABASE_URL, SUPABASE_KEY — Admin keys for metadata
REDIS_URL — Queue and cache support
ORACLE_ADDRESS — Authorized oracle address on contract
DATABASE_URL — Local PostgreSQL for indexer state
HORIZON_URL — Stellar Horizon endpoint for event streaming
CONTRACT_ID — Soroban contract ID to watch
STELLAR_NETWORK_PASSPHRASE — Network (public or test)
ORACLE_KEYPAIR — Ed25519 keypair for signing randomness
CONTRACT_ID — Contract to receive randomness
REDIS_URL — Queue persistence
Check backend/src/api/rest/raffles/ — metadata validation
Verify SIWS token valid in Authorization header
Check Supabase connection string
Verify indexer is running and connected to Horizon
Check indexer/src/processors/ticket.processor.ts — event parsing
Query tickets table directly in Supabase
Check Redis cache invalidation
Verify Oracle keypair in env vars
Check oracle/src/listener/ — is event listener active?
Check Redis queue with redis-cli — any queued jobs?
Check priority queue: oracle/PRIORITY_QUEUE_QUICK_REF.md
Verify ORACLE_ADDRESS env var matches contract's authorized oracle
Verify raffle finalized (check raffles.winner IS NOT NULL)
Check backend/src/services/leaderboard/ — service running?
Verify leaderboard tables created in Supabase
Check WebSocket connection in browser console
When implementing new features or fixing issues that affect the raffle lifecycle:
Identify the stage(s) affected using this guide
Reference relevant directories when creating PRs
Update tests in the responsible service
Verify end-to-end using the checklist above
Document changes if the flow changes
See CONTRIBUTING.md for full guidelines.