Devnet

Test funds only. Transactions are not real.

Technical Design

Deep dive into the smart contract architecture and implementation details.

Architecture Overview

The Daily Lottery is built on Solana using an account-based program architecture.

┌─────────────────────────────────────────────────────────────────┐
│                      Daily Lottery Program                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────┐  ┌──────────────┐  ┌────────────────────────┐ │
│  │   Config     │  │   Lottery    │  │   Participant          │ │
│  │   Account    │  │   Account    │  │   Accounts             │ │
│  └──────────────┘  └──────────────┘  └────────────────────────┘ │
│                                                                  │
│  ┌──────────────┐  ┌────────────────────┐  ┌─────────────────┐ │
│  │  Vault PDA   │  │   Vote Tally PDA   │  │ Winners Ledger  │ │
│  └──────────────┘  └────────────────────┘  └─────────────────┘ │
│                                                                  │
│  Instructions: create_lottery, buy_tickets, attest_uploaded,     │
│                upload_reveals, finalize_winners, settle_payout   │
└─────────────────────────────────────────────────────────────────┘

Account Structure

Config Account

pub struct Config {
    pub authority: Pubkey,
    pub ticket_price_lamports: u64,
    pub service_charge_bps: u16,
    pub buy_window_secs: u32,
    pub upload_window_secs: u32,
    pub max_winners_cap: u32,
}

Lottery Account (simplified)

pub struct Lottery {
    pub id: u64,
    pub authority: Pubkey,
    pub buy_deadline_unix: i64,
    pub upload_deadline_unix: i64,
    pub total_tickets: u64,
    pub total_funds: u64,
    pub attested_count: u64,
    pub selected_number_of_winners: u64,
    pub poc_aggregate_hash: [u8; 32],
    pub winners_merkle_root: [u8; 32],
    pub settled: bool,
}

Participant Account (simplified)

pub struct Participant {
    pub lottery: Pubkey,
    pub wallet: Pubkey,
    pub proof_of_chance_hash: [u8; 32],
    pub tickets_bought: u64,
    pub attested_uploaded: bool,
    pub voted_number_of_winners: u64,
    pub reveal_score: u64, // informational only
}

PDA seeds: Lottery = ['lottery', config_pubkey, lottery_id_le_bytes], Participant = ['participant', lottery_pubkey, wallet_pubkey], Vault = ['vault', lottery_pubkey].

State Machine

PhaseDescriptionAllowed Actions
BuyAccepting ticket purchasesbuy_tickets
UploadAttestation windowattest_uploaded
SettlementProvider uploads + winner finalizationupload_reveals, finalize_winners, settle_payout_batch
SettledWinners paid or refunds issued(read-only)

Key Instructions

create_lottery

Creates a new lottery instance and its vault PDA.

buy_tickets

Creates or updates the participant account and stores immutable commitment hash on first purchase.

attest_uploaded

Records upload attestation and vote for winner count.

upload_reveals

Verifies reveal plaintexts against commitments and computes vote tally. reveal_score is stored for transparency analytics, not payout entropy.

finalize_winners

Runs reveal-plaintext draw v2 from reveal-included participants, stores winners merkle root and settlement metadata.

settle_payout_batch

Processes payout batches with merkle proof verification.

finalize_no_attesters

Refund path for no-attester rounds and for attested rounds where zero reveals were uploaded by deadline. The instruction requires config, lottery, vault, and the configured authority signer. Single-participant rounds auto-complete upload when the upload/attestation phase opens and may settle through this path immediately; multi-participant refunds remain gated on the upload deadline.

Winner Algorithm (Reveal-Plaintext Draw v2)

finalize_winners computes winners from reveal-included commitments and tickets:

  1. Sort eligible participants (tickets > 0 and reveal-included) by wallet bytes ascending.
  2. Build seed chain:
S0 = SHA256(0x494B494741495F5250445F56325F53454544 || lottery_id_le64 || eligible_count_le64 || total_revealed_tickets_le64 || poc_aggregate_hash_32)
S_next = SHA256(S_prev || wallet_32 || tickets_le64)
  1. For each round j:
Dj = SHA256(0x494B494741495F5250445F56325F44524157 || S || j_le64)
rj = LE_u128(Dj[0..16]) mod total_remaining_tickets
  1. Walk ticket ranges to select winner, remove winner, repeat.

This is deterministic, human-auditable, and independent of reveal string length.

Security Measures

Access Control

  • Only admin can create/configure lotteries.
  • Only authorized signer can execute admin settlement instructions.
  • PDA validation is enforced for config/lottery/vault/participant accounts.

Economic Security

  • Prize pool is locked in a vault PDA.
  • Payouts are merkle-verified winner transfers.
  • Refund flow remains available for no-attester rounds and zero-reveal rounds after the upload deadline, plus immediate single-participant outcomes after upload opens.

Integrity and Replay Resistance

  • Commitments are immutable per participant.
  • Winner selection is deterministic from immutable on-chain fields.
  • Settlement state prevents re-finalization and double processing.

Contract Addresses

See Addresses for deployed contract addresses per network.

Edit this pageLast updated: January 2026