Professor Vaulty mascot
$VAULTY
🛡 Live Draw Engine · Full Specification · v1.0

Provably fair. Independently verifiable.

Every Vaulty competition draw is run by a sealed commit-reveal engine that mixes a pre-committed secret seed with the latest finalized Solana block hash. The result is deterministic, public, and reproducible by anyone with a SHA-256 implementation.

🔐 SHA-256 Commitment⛓ Solana block-hash entropy📜 Signed certificate🧮 Open formula

Lifecycle

Stage 01
🔐

Commit

Before any ticket is sold we generate a 256-bit random seed in a sealed server context, hash it with SHA-256, and publish the hash (the commitment) on the competition page. The raw seed is encrypted and kept private until the draw fires — it cannot be changed without the commitment changing.

Stage 02
🔒

Lock

When the draw timer reaches zero the ticket pool is frozen. Every ticket id and wallet is recorded with a timestamp in competition_tickets. The full ticket list at draw time is what the formula consumes.

Stage 03
🎲

Reveal & Mix

We fetch the latest finalized Solana block hash via Helius RPC and append it to the secret seed, producing `${seed}:solana:${slot}:${blockhash}`. The combined reveal is then published. Because the block hash isn't known until the draw moment, we cannot have pre-selected a winner.

Stage 04
📜

Sign & Settle

We deterministically rank every ticket by SHA-256(reveal | ticket_list | index | ticket_id) and write the top N to competition_winners alongside the commitment, reveal, block hash and slot. A signed certificate is generated and the prize is queued for on-chain USDC payout on Solana.

Deterministic winner formula

Given the revealed seed reveal and the ordered list of ticket ids tickets[]:

concat       = tickets.join("|")
for i in 0..tickets.length-1:
  key_i  = SHA256( reveal + ":" + concat + ":" + i + ":" + tickets[i] )
ranked       = tickets sorted ascending by key_i
winners      = ranked.slice(0, winners_count)

Identical inputs produce identical outputs on any machine. No external service, library or vendor is required to verify.

Verify it yourself

Open any drawn competition's certificate page, copy the reveal and the ticket list, then paste the following into a browser console:

// Paste into any browser console / Node REPL.
// Inputs come straight from the draw certificate.
const reveal   = "<seed>:solana:<slot>:<blockhash>";
const tickets  = ["<ticket_id_1>", "<ticket_id_2>", "..."];   // in the order shown on the certificate
const winnersN = 1;

const enc = new TextEncoder();
async function sha256(str) {
  const buf = await crypto.subtle.digest("SHA-256", enc.encode(str));
  return [...new Uint8Array(buf)].map(b => b.toString(16).padStart(2, "0")).join("");
}

(async () => {
  const concat = tickets.join("|");
  const keyed = await Promise.all(tickets.map(async (id, i) => ({
    id, key: await sha256(reveal + ":" + concat + ":" + i + ":" + id),
  })));
  keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
  console.log("Winners:", keyed.slice(0, winnersN));
})();

Certificate fields

FieldMeaning
draw_seed_commitmentSHA-256 hash of the secret seed, published before entries open.
draw_seed_revealThe full revealed string — `seed:solana:slot:blockhash` — published the instant the draw runs.
block_hash / slotSolana finalized block hash & slot fetched at draw time from Helius RPC. Unpredictable in advance.
draw_proof_urlDirect link to the Solana block on Solscan that supplied the entropy.
competition_winners.ticket_idThe winning ticket — recoverable by anyone from reveal + ticket list.
drawn_atUTC timestamp the draw was executed.

Auto vs manual draws

  • Auto draws — the /api/public/hooks/competitions-auto-draw endpoint is poked by pg_cron the moment draw_at passes. The engine performs commit → mix → reveal → certify in a single atomic run.
  • Manual draws — an admin with the deployer wallet can run the exact same engine on demand from the Live Draws control center. The certificate is identical in shape and equally verifiable.
  • In both modes the commitment is published before entries open and the reveal is published only after winners are written.