Every Raffle has one End Prize. After the Raffle closes, one ticket is picked from the full list by a public random number that did not exist while entries were being taken. This page sets out exactly how. It explains the mechanics only; if it ever differs from the Competition Terms and Conditions, the Terms apply.
How the draw is done
- The beacon. drand is a public randomness beacon run by independent organisations. It publishes a new signed number every 3 seconds, and nobody, including us, can know a number before it is published or change it afterwards. We do not choose which of its numbers decides a Raffle until the ticket list is locked (step 3).
- While the Raffle is open every entry of every kind is given a unique 10-digit ticket number. Purchased, free, postal, referral and bonus entries are one ticket each. There is no weighting.
- At close every ticket is gathered into one list, sorted by ticket number. The list's SHA-256 fingerprint is recorded and the full list is published. From this point nothing can be added or removed without the fingerprint changing. Only then is the drand round chosen: the first round broadcast at least 20 seconds later. It is recorded once and published.
- The round is broadcast about 20 seconds later. We fetch it and check it twice: its random number must equal the SHA-256 of its signature, and the signature must verify against the beacon network's public key. A forged or altered number is rejected.
- The random number is divided by the number of tickets. The remainder is the winning position in the sorted list, counting from zero. Every position is equally likely.
- The result is recorded with everything needed to reproduce it: the winning ticket, the list size, the fingerprint, the round, the random number and its signature. The draw locks itself so it cannot run twice.
The Raffle Page says whether the draw runs automatically shortly after close or is triggered by a member of our team. Either way the list and its fingerprint are fixed before anyone presses a button, and the round is chosen only after that (for a triggered draw, when the draw is started), so the trigger decides only when the draw runs, never its outcome. If a winner forfeits under the Terms, the original result is kept on record marked void, the account is excluded, and the draw runs again over the same list with a new future round.
If the beacon is unavailable. Each Raffle is created with one of two draw methods, fixed once it is published: drand, the normal case, or a backup method that needs nothing outside our own system. A drand Raffle switches to the backup only if drand is unavailable: its round must be at least five minutes overdue, our failed attempts to fetch it must be on record, and one more live check of every drand server, made and recorded at that moment, must also fail. An automatic draw never switches because drand returned a number whose signature does not check out. The backup picks the winner from the same sorted list in the same way, using the SHA-256 of the secret's fingerprint, the ticket list's fingerprint and the time of the draw in milliseconds, all published with the result. Because that time comes from our own server's clock, the backup relies on our controls rather than an outside source, which is why it is used only when drand cannot be.
The exact recipe
// At close: one list of every ticket, sorted, fingerprinted, published.
sorted_tickets = sort(all_ticket_numbers) // 10-digit strings
entry_hash = sha256(sorted_tickets.join(",")) // published at close
// Chosen only AFTER the fingerprint is recorded: the first drand Quicknet
// round broadcast at least 20 seconds later. Recorded once, never changed.
round = first round with broadcast_time >= fingerprint_time + 20
// After the round is broadcast:
beacon = fetch("https://api.drand.sh/<chain>/public/" + round)
require sha256(beacon.signature) == beacon.randomness
require bls_verify(beacon.signature, round, quicknet_public_key)
// The draw.
winner_index = BigInt("0x" + beacon.randomness) mod sorted_tickets.length
winner = sorted_tickets[winner_index]The draw is run by SafeDraw, our open-source package. The code that runs the draw is the code that verifies it, and it runs on your own device from the app's Re-play Draw screen.
import { conductDrawSteps } from "@prizemart/safedraw";
// The same call runs the draw on our servers and re-runs it on your phone.
for await (const step of conductDrawSteps({
entries: ticketNumbers, // the published list
drandRound: 32374632, // the round chosen after the fingerprint
closedAt: 1789927200, // Unix seconds the Raffle closed
expectedHash: publishedHash, // the fingerprint published at close
})) {
// sort → hash → timeline → fetch_beacon → verify_randomness
// → verify_signature → select → done
if (!step.ok) throw new Error(step.phase + " failed");
if (step.phase === "select") console.log(step.detail.winner);
}Demonstration
A complete draw over 24 dummy tickets, run in your browser with the same package. The round is a real drand round, fetched live and verified on this device. Each step pauses so you can follow what it did.
- Demonstration Raffle (dummy entries)
- 24
- 20 Sept 2026, 18:00:00 UTC
- 32,374,632
- cb65abfcf063…c854cbbb1006
- Load the entry list
- Sort the list
- Fingerprint the list and compare it with the fingerprint published at close
- Check the timeline
- Fetch the random number from drand
- Verify the random number
- Verify the drand signature
- Calculate the winning index
- Find the winner
Check a real draw yourself
- In the app, open a drawn Raffle and use Re-play Draw. It fetches the published list and the round and runs the steps above on your phone.
- By hand: download the published ticket list, compute its SHA-256 fingerprint with any standard tool and compare it with the published one. Fetch the round from drand, convert its random number to a whole number, divide by the number of tickets and look up the remainder in the sorted list.
- The package is published as @prizemart/safedraw under the MIT licence.