1. Baccarat Numbers
  2. Wheel fairness check

SNOW wheel provably fair check: a worked test vector

SNOW’s fairness page publishes the wheel draw formula and its source (draw.go), and its calculator has a ‘시험 예시 넣기’ (load test example) button. This sheet takes that test example and recomputes it by hand. If you get the same numbers here, you can check a real receipt’s proof the same way.

84,923
Draw value (0 to 9,999,999)
Slot 0
Cumulative weight 100,000
0
Redraws needed
8.3×10⁻⁷¹
Chance of a redraw

What a fairness check can show, and what it can’t

A provably fair check doesn’t tell you whether a wheel is generous. It tells you whether a recorded result is what the inputs at the time produce. SNOW’s own page says a matching calculation does not prove when the data was published, how it ties to the ledger, stock levels, or that a prize was paid.

Scope of a wheel check
QuestionCan the check answer it?What you need
Was the server seed changed after the draw?YesThe server_seed_hash you saved before the draw
Were the prize weights changed?YesThe table_version_hash you saved before the draw
Do my seed and commit give the recorded slot?YesThe receipt’s calculation_proof
Was the hash really published before the draw?NoYour own saved copy
Was the prize actually paid?NoPayment records
Slots and live casino resultsNoThe studios’ own systems

For a full check, save the seed hash and table hash before the draw. With only what you download afterwards, you can confirm the arithmetic and nothing more.

The inputs and their rules

Fields in a wheel proof
FieldFormatRule
server_seed64 hex characters = 32 bytesRevealed after the draw. Hash and HMAC use the 32 bytes, not the text
server_seed_hash64 hex charactersSHA-256 of the 32-byte seed, published before the draw
client_seedUTF-8 string1 to 128 bytes after NFC; no control characters, | or @; spaces kept
brand_envStringNot empty; no | or control characters
commit_idLower-case UUIDNew for every draw
canonical_documentJSON textHashed exactly as received; lists slot positions, weights and the default slot

On the calculator, the field labelled ‘대조할 브랜드 환경’ (brand environment to compare) is brand_env. The test example uses test; a real receipt carries its own value.

The test vector, step by step

These are the values behind the calculator’s test example. The server seed is sixty-two zeros followed by 62, and the prize table has three slots: slot 0 weighted 100,000, slot 1 weighted 50,000 and slot 2, the default, weighted 9,850,000. As odds that is 1%, 0.5% and 98.5%.

  1. Server seed hash

    Decode the 64 characters to 32 bytes and take SHA-256.

    0010d2c57c2552b023f787cafcd6cd962d6faa04a7621fba14faa3a9f6de8910

  2. Prize table hash

    SHA-256 of the canonical_document text, byte for byte in UTF-8.

    3e26d57e91ece2366848e3d9f4dc2eaf6288b24e405b12314a87555787b90110

  3. Message

    Join algorithm 1, brand_env, WHEEL, the NFC-normalised client_seed and commit_id with |.

    1|test|WHEEL|c98|aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa

  4. HMAC-SHA256

    The key is the 32-byte server seed; the data is the message in UTF-8.

    8a329eff3157fa17ca047ba0014fbe4c5e1fc27e2c4387e61b47d48b2ed4383b

  5. As a 256-bit number

    Read the 32 bytes big-endian as an unsigned integer H. H is below the limit L, so the first attempt (no suffix) is accepted.

    H = 62508612813635367744208359319450530045745479443396350918367385282301090084923

  6. Draw value

    H modulo 10,000,000. In decimal, that is simply the last seven digits of H.

    value = 84923

  7. Pick the slot

    Add weights in position order and stop at the first slot whose running total exceeds the value. Slot 0 already reaches 100,000, which is more than 84,923, so slot 0 wins.

Removing modulo bias with the limit L

2256 is not a multiple of 10,000,000, so taking the remainder directly would make a few low draw values very slightly more likely than the rest. The fix is to set L to the largest multiple of 10,000,000 not above 2256, and if H is L or more, append |1, then |2 and so on to the message and try again.

L = 115792089237316195423570985008687907853269984665640564039457584007913120000000

The chance that H lands at or above L is about 8.3×10−71, so a redraw will essentially never happen. Your checker still needs the loop to match the reference code. A receipt showing a non-zero attempt means that number was appended to the message.

What one character of client seed changes

Same server seed and commit, different client_seed. One change of case or one trailing space changes the whole HMAC and gives an unrelated draw value.

Same server seed, client_seed varied
client_seedFirst 8 hex of HMACDraw valueSlot
c988a329eff84,9230
c991980c8c3286,1672
C98af77648c4,399,1912
c98␠ (trailing space)73a66e657,553,3812
baccaratnumbersddb636c4904,1492

Values below 100,000 land on slot 0, 100,000 to 149,999 on slot 1, and 150,000 or more on slot 2.

When your numbers don’t match

  • You hashed the seed text. Feeding the 64 hex characters to SHA-256 gives 1cd48b66…. Decode them to 32 bytes first and you get 0010d2c5….
  • You rebuilt the table JSON. A changed line break, indent or key order changes the table hash. Paste the text exactly as received.
  • You skipped NFC. é is 2 bytes composed and 3 bytes decomposed. Text copied from elsewhere can carry the decomposed form.
  • You trimmed a trailing space. Spaces are kept by rule, and the table above shows what one does.
  • You used today’s seed for an old draw. Your current account seed and settings may differ from the ones used then. Use the values stored on that draw’s receipt.
  • You left out sold-out slots. Sold-out slots keep their weight and still count towards the running total. Only zero-weight slots can never be drawn.

A Python check you can run

Standard library only. Replace the values in proof with the calculation_proof from a receipt to recompute a real draw. Only use seeds that have already been revealed.

import hashlib, hmac, json, unicodedata

proof = {
    "brand_env": "test",
    "commit_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
    "client_seed": "c98",
    "server_seed": "62".zfill(64),
    "canonical_document": '{"alg":"1","feature":"WHEEL","slots":[{"position":0,"weight":100000,"is_default":false},{"position":1,"weight":50000,"is_default":false},{"position":2,"weight":9850000,"is_default":true}]}',
}

seed = bytes.fromhex(proof["server_seed"])   # 32 bytes, not the 64-character text
print("seed hash :", hashlib.sha256(seed).hexdigest())
print("table hash:", hashlib.sha256(proof["canonical_document"].encode()).hexdigest())

client = unicodedata.normalize("NFC", proof["client_seed"])
base = "|".join(["1", proof["brand_env"], "WHEEL", client, proof["commit_id"]])
TOTAL = 10_000_000
L = (2**256 // TOTAL) * TOTAL
attempt = 0
while True:
    msg = base if attempt == 0 else f"{base}|{attempt}"
    digest = hmac.new(seed, msg.encode(), hashlib.sha256).digest()
    H = int.from_bytes(digest, "big")
    if H < L:
        break
    attempt += 1
value = H % TOTAL
print("hmac      :", digest.hex())
print("attempt   :", attempt, " value:", value)

cumulative = 0
for slot in sorted(json.loads(proof["canonical_document"])["slots"], key=lambda s: s["position"]):
    cumulative += slot["weight"]
    if cumulative > value:
        print("selected  : position", slot["position"])
        break

It prints seed hash 0010d2c5…, table hash 3e26d57e…, hmac 8a329eff…, attempt 0, value 84923 and position 0. SNOW’s own calculator performs the same steps in the browser using Web Crypto only.

SNOW wheel and verification status when checked

Status on 2 and 3 November 2026
ItemStatus
Snow Wheel‘곧 열려요’ (opening soon); cannot be spun yet
LootboxNo active prize table; full verification including external proof marked as not yet complete
RaffleNo verifiable proof linked for the current round
External verification toolNot yet connected
Receipt lookupBy PF code or receipt number; sample wheel codes can’t be looked up
Slots and live casinoRun on studios’ separate systems; studio certificates listed as coming later

On the Korean-language site, proofs open from the Snow Wheel result screen, from ‘검증’ (Verify) in 내 주문 내역 (My Orders), and from ‘공정성 확인’ (Check fairness) under 보너스 마켓 › 내 휠 내역 (Bonus Market › My Wheel History). Because the wheel had not opened yet, this sheet uses the public test vector only.