Every outcome, open to inspection
PackSnatch outcomes are the output of a deterministic, published function over inputs neither side can control alone. Nothing here asks for your trust - every claim on this page can be checked, including right now, in your browser.
Why choose PackSnatch
Guaranteed delivery
Every open delivers an item to your inventory. There is no empty outcome and no losing ticket - each open is a digital product purchase.
Provably fair by construction
Outcomes come from a commit-reveal scheme: we lock in our secret before you act, and reveal it afterwards so you can check the math.
Verify in your browser
The verifier on this page recomputes any past open locally with WebCrypto. Nothing is sent to our servers, so nothing can be massaged.
Outcome first, animation second
Your result is resolved and written to the ledger server-side before the reel ever moves. The animation replays a decision that is already final.
Transparent economics
Every pack page shows each item, its declared value, and its exact ticket range out of 1,000,000 - before you open.
Instant sell-back
Any delivered item can be sold back for site credit at its declared value, or withdrawn. What you see is what it is worth here.
How PackSnatch ensures fairness
For pack opens, we combine three ingredients: a secret server seed (whose SHA-512 hash is published to your account before you open anything), your client seed, and an auto-incrementing nonce. They are fed through HMAC-SHA256 and reduced to a ticket number between 1 and 1,000,000, which maps to exactly one item in the pack's published ranges. Because we commit to our seed before seeing your input, we cannot steer the result; because you never see our seed until after, neither can you.
When your server seed rotates, the previous seed's plaintext is revealed in your account. From that moment, every open made against it is independently checkable: hash the revealed seed and confirm it matches the commitment you were shown, then recompute each ticket with the function below.
Battles work on the same primitive with one difference: instead of per-player client seeds (which a bot could grind for advantage), every seat's every round derives from one shared beacon, committed by hash before the battle starts and revealed at settlement. One reveal verifies the whole battle. Our schema already reserves the fields to derive that beacon from the hash of a future public blockchain block - a public source of randomness produced by a decentralized network that neither the players nor PackSnatch can predict or influence.
The algorithm
The ticket is an HMAC-SHA256 keyed by the server seed over the message `${clientSeed}:${nonce}`. The first 13 hex characters of the digest become an integer, reduced to a ticket in [1, 1,000,000]. This is the exact production source, published in our open-source @packsnatch/fair package:
import { createHash, createHmac, randomBytes } from 'node:crypto';
const TICKET_RANGE = 1_000_000;
// Generated server-side with a cryptographically secure RNG.
export function generateServerSeed(): string {
return randomBytes(32).toString('hex');
}
// The commitment shown to you BEFORE any open.
export function hashServerSeed(serverSeed: string): string {
return createHash('sha512').update(serverSeed, 'utf8').digest('hex');
}
// The function that decides every open.
export function getTicketNumber(
serverSeed: string,
clientSeed: string,
nonce: number,
): number {
const message = `${clientSeed}:${nonce}`;
const hex = createHmac('sha256', serverSeed)
.update(message, 'utf8')
.digest('hex');
return (Number.parseInt(hex.slice(0, 13), 16) % TICKET_RANGE) + 1;
}A note for the cryptographically curious: keying the HMAC with the server seed is a strictly stronger construction than hashing a concatenated string - it is the standard way to build a keyed pseudorandom function.
Terms & definitions
Ticket number
An integer between 1 and 1,000,000 produced by combining the server seed, your client seed, and the nonce. Each pack encodes its items as non-overlapping ranges covering that full space, so every ticket maps to exactly one item.
Server seed
32 random bytes generated by PackSnatch with a cryptographically secure generator. You are shown its SHA-512 hash before any open - a commitment we cannot back out of. When the seed rotates, the plaintext is revealed: hash it yourself and it must match the commitment you saw.
Client seed
A string you control, set from your profile at any time (a random default is assigned at signup). Because we cannot predict it, we cannot pre-compute your outcomes even though we know our own seed.
Nonce
A counter that increases by one on every open against the active server seed. It makes each open in a multi-open purchase unique while the seeds stay fixed.
Battle beacon
Battles use one shared random beacon for every seat and round, committed by hash before the battle starts and revealed at settlement - a single reveal verifies the entire battle. The schema reserves fields for deriving this beacon from the hash of a future public blockchain block, which removes even PackSnatch from the loop once enabled.
Ticket ranges
The per-item intervals over [1, 1,000,000] shown on every pack page. Ranges are cumulative and cover the whole space exactly - the Guaranteed Delivery invariant.
Verify an open
Your seeds and nonces live in your account: the current commitment under Account → Fairness, and the full inputs for every past open in your pack history. Paste the revealed server seed, your client seed, and the nonce below. The ticket is recomputed locally with your browser's WebCrypto - nothing is sent to PackSnatch, so the result cannot be tampered with.
Prefer the command line? Every open against a rotated seed can also be reproduced with our open-source verifier:
npx @packsnatch/fair fair-verify ./my-open-proof.json
