Testing Promo UI

Giveaway draw details and verification inputs for independent reproduction.

Published inputs

Entries count
1
Total weight
10
Entries hash (SHA-256)
e3fe48ca83cfb3f11bbf121f44d07c13ee7a6ce4fac51d01e59e0efcf6b8b955
Beacon source
https://drand.cloudflare.com/public/latest
Beacon round
5465095
Beacon value
3bdc65932f84d7625162f98b76561f6d36f30790da97d2cffce07dbc5f9396d1
Seed
e8c143738d761eb4ea3c1f2570540b2b922fdaaac5706c8ba331b6db54e70904
Drawn at (UTC)
2025-10-02T05:44:41.807Z
Winner entry id(s)
149ed8a8-10fa-4217-ac38-6c55c9e1c054

Verification algorithm

Deterministic weighted selection using HMAC-SHA256 seeded randomness from SHA256(entries_hash + '|' + beacon_value). Each entry has a weight based on their total giveaway entries earned. Selection uses cumulative weight distribution to ensure fair proportional chances.

Verify this draw (Node.js)

Copy and run with Node 18+. Download the entries array separately to avoid cluttering this view.

Download as entries.json and place in same folder as verification script
// Node 18+
const crypto = require('crypto');

function sha256Hex(input) {
  return crypto.createHash('sha256').update(input).digest('hex');
}

function deterministicWeightedSelection(weightedEntries, seedHex, winnersCount) {
  const winningEntryIds = [];
  const usedEntries = new Set();
  const seedBytes = Buffer.from(seedHex, 'hex');
  
  for (let i = 0; i < winnersCount && usedEntries.size < weightedEntries.length; i++) {
    // Create a new seed for each selection to avoid patterns
    const selectionSeed = sha256Hex(seedBytes.toString('hex') + i.toString());
    const selectionSeedBytes = Buffer.from(selectionSeed, 'hex');
    
    // Generate deterministic random number (0-1) from seed
    const randomBytes = selectionSeedBytes.slice(0, 8);
    const randomValue = randomBytes.readBigUInt64BE(0);
    const maxBigInt = BigInt('0xFFFFFFFFFFFFFFFF');
    const normalizedRandom = Number(randomValue) / Number(maxBigInt);
    
    // Calculate cumulative weights for available entries
    const availableEntries = weightedEntries.filter(e => !usedEntries.has(e.uuid));
    const availableTotalWeight = availableEntries.reduce((sum, e) => sum + e.weight, 0);
    
    let targetWeight = normalizedRandom * availableTotalWeight;
    let cumulativeWeight = 0;
    
    for (const entry of availableEntries) {
      cumulativeWeight += entry.weight;
      if (targetWeight <= cumulativeWeight) {
        winningEntryIds.push(entry.uuid);
        usedEntries.add(entry.uuid);
        break;
      }
    }
  }
  
  return winningEntryIds;
}

// Load entries from downloaded file
const fs = require('fs');
const entries = JSON.parse(fs.readFileSync('entries.json', 'utf8'));

// Published inputs
const publishedEntriesHash = "e3fe48ca83cfb3f11bbf121f44d07c13ee7a6ce4fac51d01e59e0efcf6b8b955";
const beaconValue = "3bdc65932f84d7625162f98b76561f6d36f30790da97d2cffce07dbc5f9396d1";
const publishedWinners = ["149ed8a8-10fa-4217-ac38-6c55c9e1c054"];

// 1) Verify entries hash
const entriesHash = sha256Hex(JSON.stringify(entries));
if (entriesHash !== publishedEntriesHash) {
  console.error('Entries hash mismatch:', { entriesHash, publishedEntriesHash });
  process.exit(1);
}

// 2) Derive seed
const seed = sha256Hex(publishedEntriesHash + '|' + beaconValue);

// 3) Perform weighted selection and compare winners
const winners = deterministicWeightedSelection(entries, seed, publishedWinners.length);
console.log({ winners });
console.log('Matches published:', JSON.stringify(winners) === JSON.stringify(publishedWinners));

Note: We publish opaque entry IDs only. No user handles or PII are exposed.

Back to giveaways