🔥 Loading…
🔥 Loading…
Guide • 8 min read
Last updated: October 3, 2026
Every winner on FinixArena is picked by a fair system that nobody can control — not even us. Here's how it works, in simple words.
Six steps. No secrets after the fact.
Before the draw, our system creates a secret random number (32 bytes) and locks it inside a "sealed envelope". We show everyone the envelope's seal — a code like a3f9…c21e. Nobody can open the envelope early or swap it without everyone noticing.
Live wheel draws: the seal is shown before the first spin.
When you enter, your name appears in the public participant list. You can always find yourself in it.
At the closing time, the list is frozen. Nobody can be added or removed after that.
Our system mixes the secret number with the frozen list and shuffles everyone into an order.
It's like a recipe: the same ingredients always make the same dish. Same secret number + same list = the exact same result, every single time.
On stream, we reveal that shuffle step by step — players drop out in waves until one winner is left. The animation doesn't decide anything; it just shows the result the shuffle already made.
After the reveal, we publish the secret number. Anyone can check that it matches the seal we showed before, and redo the shuffle to get the exact same winner.
✅ Sealed before the first spin. Secret revealed when the draw completes. Full verification via the script below.
Picked with secure randomness, but not sealed in advance — so they can't be checked with this method.
Sealed from day one. Full verification will work the same way.
Imagine we write a secret number on a piece of paper, seal it in an envelope, and stick that envelope on the wall where everyone can see it. When the giveaway ends, we open it in front of everyone. Because the envelope stayed sealed the whole time, we couldn't have changed the number after seeing who entered.
Its seal (SHA-256 hash) was public before the draw. Changing the number would change the hash — everyone would notice.
The entry list is public, and it's frozen at closing time. Fake entries would be visible to everyone.
Once a draw starts, it's saved forever. Pressing start again gives the same winner — the shuffle is deterministic.
The shuffle already decided the full order. If the winner doesn't claim within 24 hours (or didn't complete the sponsor's tasks), the prize goes to the next person in that order — the last player eliminated — and so on. Nobody picks a replacement by hand.
No. Everyone in the list has exactly the same chance. The shuffle uses the final frozen list — when you entered makes zero difference.
No. One entry per Discord account. Very new Discord accounts may not be allowed for some giveaways, to stop people making fake accounts.
Some giveaways only allow accounts created before a certain date. This stops people from creating lots of accounts just to enter.
Those are the sponsor's entry tasks. The winner must show proof they completed them, or the prize goes to the next person in the pre-determined order.
No. The winner comes from the sealed number and the public list — see "Why we can't cheat" above.
Open the "For the technical folks" section below. There's a copy-paste Node.js script — just paste the server seed and the entry list, and it prints the winner.
serverSeed = 32 cryptographically random bytes (hex string). seedHash = SHA-256(serverSeed), published before entries close (commit–reveal scheme).createdAt ascending.serverSeed + counter → same bytes.winnerCount of the permutation. Elimination order = remaining entries reversed.crypto. It reproduces the shuffle from serverSeed + entry IDs, checks seedHash, and prints the winner(s).Copy this into a file (e.g., verify-wheel.js) and run with node verify-wheel.js:
const crypto = require("crypto");
// PASTE THESE FROM THE DRAW PAGE:
const serverSeed = "PASTE_SERVER_SEED_HERE"; // 64 hex chars (32 bytes)
const seedHash = "PASTE_SEED_HASH_HERE"; // 64 hex chars (SHA-256 of serverSeed)
const entryIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // ascending entry IDs from frozen snapshot
const winnerCount = 3; // number of winners to show
// 1. Verify seed hash matches the commit hash shown BEFORE the draw
const computedHash = crypto.createHash("sha256").update(serverSeed).digest("hex");
if (computedHash !== seedHash) {
throw new Error(`Seed hash mismatch! Expected: ${seedHash}, Got: ${computedHash}`);
}
console.log("✓ Seed hash verified");
// 2. Seeded PRNG using HMAC-SHA256 keystream (exact copy from seededShuffle.ts)
class SeededPRNG {
/** @param {string} seed */
constructor(seed) {
this.seed = seed;
this.counter = 0;
}
/** Generate next batch of random bytes (32 bytes from HMAC-SHA256) */
_nextBytes() {
const hmac = crypto.createHmac("sha256", this.seed);
hmac.update(String(this.counter++));
return hmac.digest();
}
/**
* Get a random integer in [0, max) using rejection sampling (no modulo bias).
* @param {number} max
* @returns {number}
*/
nextInt(max) {
if (max <= 1) return 0;
// Determine how many bytes we need (e.g., max=256 needs 2 bytes for 16-bit range)
const bytesNeeded = Math.ceil(Math.log2(max) / 8) || 1;
const maxRange = Math.pow(256, bytesNeeded);
const limit = maxRange - (maxRange % max); // largest multiple of max in range
let attempts = 0;
while (attempts++ < 100) {
// Safety: bail after 100 attempts (vanishingly rare for valid inputs)
const bytes = this._nextBytes();
let value = 0;
for (let i = 0; i < bytesNeeded; i++) {
value = value * 256 + bytes[i];
}
if (value < limit) {
return value % max;
}
}
// Fallback (should never hit): use last generated value
return this._nextBytes()[0] % max;
}
}
/**
* Fisher-Yates shuffle using seeded PRNG.
* @param {Array} items
* @param {string} seed
* @returns {Array}
*/
function seededShuffle(items, seed) {
const arr = [...items];
const prng = new SeededPRNG(seed);
for (let i = arr.length - 1; i > 0; i--) {
const j = prng.nextInt(i + 1);
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
// 3. Compute the shuffle
const shuffled = seededShuffle(entryIds, serverSeed);
console.log("\nShuffled order:", shuffled);
console.log(`Winner${winnerCount > 1 ? "s" : ""}:`, shuffled.slice(0, winnerCount));
if (winnerCount > 1) {
console.log("Reroll order:", shuffled.slice(winnerCount));
}
serverSeed = 32 cryptographically random bytes (hex string). seedHash = SHA-256(serverSeed), published before entries close.snapshotHash = SHA-256 of IDs joined with ",".rand(i) = first 8 bytes of HMAC-SHA256(key = serverSeed as raw bytes, message = snapshotHash + ":" + i), as unsigned 64-bit integer.j = rand(i) % (i + 1). order[0] is winner; order[k] is k-th reroll.Our system knows the secret number before entries close, so the public participant list is part of the protection. If you ever see suspicious entries, tell an admin.
Free entry. Discord login. Under two minutes from here to registered. Each of these is taking players today:
No tournaments open right now — new Finix Cups go live every week. 📺
Check the live list →New to brackets and check-ins? Read MLBB Tournament Formats, Rules & Prize Pools Explained → so nothing surprises you on game day.