Audit Verdict
The repo is the credential. You don't have to trust us — every finding ships as code. Run npm test to re-run the full audit: 27 verification steps, 10M simulated rounds, the C(52,5) = 2,598,960-hand optimal-strategy enumeration, and 5,400 live bets re-verified.
Video Poker Audit Overview
This audit independently validates the Video Poker game operated by Duel.com across five domains: deterministic outcome generation, entropy integrity, live-to-verifier parity, RTP mathematical accuracy, and fairness integrity testing. We placed 5,400 real bets across 110 seed pairs and independently verified every single deal and final hand using our own implementation of the algorithm.
What Was Audited
- The RNG algorithm is deterministic and verifiable
- Server seeds are cryptographically committed via SHA-256 before play
- Client seed is browser-generated and players can customize it
- Nonces increment correctly and are never reused
- Each bet's 52-card deck is computed via HMAC-SHA256 backward Fisher-Yates with bias-free rejection sampling
- Deal outcomes (initial 5 cards) and draw outcomes (final 5 cards after holds) are reproducible from server seed, client seed, and nonce
- Hand classification matches the published 10-rank pay table for all 5,400 bets
- Optimal-play RTP is 99.9% (exhaustive C(52,5) = 2,598,960-hand enumeration)
- Bet amount does not influence the deck or replacement pool
- Players can independently verify every bet — both the deal and the draw
What Audit Covers
| Area | Description |
|---|---|
| Commit-Reveal System | SHA-256 server seed hashing, pre-bet commitment, reveal on rotation |
| Client Seed Origin | Player-controlled seed, browser-generated — server commits before your seed is known |
| Seed Handling | Client seed control, nonce lifecycle, seed pair rotation |
| RNG Analysis | HMAC-SHA256 backward Fisher-Yates 52-card shuffle, rejection sampling, bias analysis |
| Two-Step Game Flow | Deal (5 initial cards) and Draw (final hand from holds + replacement pool) — both halves verified independently |
| Payout Logic | 10-rank pay table accuracy, house edge verification, bet-size invariance (Phase C) |
| Live Parity | Independent deck shuffle + held-card replacement recomputation vs live game results |
| RTP Validation | Anti-circularity proof (deal-only), simulated RTP (10M rounds), optimal-play enumeration (2,598,960 hands), cross-validation against 9/6 Jacks or Better, cherry-pick detection (Pass 2) |
| Hand Classification | Independent re-evaluation of all 5,400 final hands against the 10-rank pay table |
| Hold Pattern Coverage | Phase E systematic hold patterns across 4 distinct hold counts (0, 1, 2, 5) |
| Fairness Integrity | Standard integrity matrix — tests across commit-reveal, determinism, payout, isolation, and parameter enforcement |
What Audit Guarantees
- Outcomes are deterministic and reproducible from the recorded inputs
- Live game results match independent recomputation for the verified sample (5,400 / 5,400)
- Both halves of every bet are verified — the initial deal AND the final hand after holds
- Optimal-play RTP is proven analytically: exhaustive C(52,5) = 2,598,960-hand enumeration with 134,459 canonical classes returns 99.9000%; cross-validated by re-running the same solver against the standard 9/6 Jacks or Better pay table to its known 99.5439% target
- Client seed is a genuine, browser-generated input that materially influences results (100% Deck Change)
- The house edge is a flat 0.1% — no scaling edge, no bet-size dependence
- The player's hold choices cannot influence the deck composition — the 10-card outcome is fixed at deal time, holds only select which positions consume the replacement pool
What Audit Excludes
- Infrastructure or server security
- Wallet, payments, or operational systems outside game logic
- Rakeback layer — 99.9% is the certified RTP; rakeback is operator-side
- Cross-account sampling
- Max win cap enforcement — not embedded in game logic
References
Video Poker — Game Rules6 sections▶
Video Poker on Duel.com is a 5-card draw poker game played against a fixed pay table. The player is dealt 5 cards from a 52-card deck, chooses which to hold and which to discard, and the discarded cards are replaced from a pre-determined pool to form a final 5-card hand. The full 52-card shuffle (initial 5 + 5-card replacement pool) is committed via HMAC-SHA256 backward Fisher-Yates before the deal animation — your hold choice cannot change the cards available to you.
1. Enter bet amount — Choose how much to wager on the hand.
2. Deal — The platform deals 5 cards from positions 0–4 of the shuffled 52-card deck.
3. Hold — Select which of the 5 cards to keep (0 to 5 cards). The remaining cards are discarded.
4. Draw — The platform replaces the discarded cards with positions 5–9 of the same shuffle, consumed left-to-right.
5. Outcome — The final 5-card hand is evaluated. Payout = bet amount × multiplier for the hand rank.
The win condition in Video Poker depends on the rank of your final 5-card hand against the pay table.
| Outcome | Condition | Example |
|---|---|---|
| Royal Flush | 10, J, Q, K, A all of the same suit | 10♠ J♠ Q♠ K♠ A♠ → 812.97× — ~1 in 649,740 chance |
| Straight Flush | Five sequential cards of the same suit | 5♠ 6♠ 7♠ 8♠ 9♠ → 58× |
| Four of a Kind | Four cards of the same rank | K♠ K♥ K♦ K♣ 4♠ → 26× |
| Full House | Three of a kind plus a pair | J♠ J♥ J♦ 5♣ 5♠ → 9× |
| Flush | Five cards of the same suit (non-sequential) | 2♠ 7♠ 9♠ J♠ K♠ → 6× |
| Straight | Five sequential cards (mixed suits) | 5♦ 6♠ 7♥ 8♦ 9♣ → 4× |
| Three of a Kind | Three cards of the same rank | Q♠ Q♥ Q♦ 6♣ 2♦ → 3× |
| Two Pair | Two pairs of different ranks | 10♠ 10♥ 4♦ 4♣ 7♠ → 2× |
| Jacks or Better | A pair of Jacks, Queens, Kings, or Aces | K♦ K♠ 7♥ 5♣ 3♠ → 1× |
| No Win | Anything below a pair of Jacks | 9♦ 6♠ 4♥ J♣ 2♦ → 0× |
The core mechanic of Video Poker is the player's hold decision — which cards to keep from the initial 5-card deal.
- Hold choice does not change the deck — the 10-card window (5 initial + 5 replacement pool) is committed before the deal; holds only select which positions consume from the pool
- Hold choice determines RTP achieved — the game's headline 99.9% RTP is the optimal-play return, achievable only when each hand is held according to the EV-maximizing strategy
- Sub-optimal play earns less — a player who randomly held cards (deal-only RTP) would receive 33.7% return; the strategy gap between random and optimal is the player's responsibility
- House edge is flat at 0.1% — there is one pay table; bet size, time of day, and seed history have no effect on RTP
| Parameter | Value | Notes |
|---|---|---|
| Deck Size | 52 cards | Standard playing-card deck (4 suits × 13 ranks) |
| Initial Hand Size | 5 cards | Dealt from positions 0–4 of the shuffle |
| Replacement Pool | 5 cards | Positions 5–9 of the same shuffle, consumed left-to-right by hold pattern |
| Hand Ranks | 10 (Royal Flush → No Win) | Standard 5-card poker rankings |
| Hold Choices | 0–5 cards | Player decides per hand which to keep |
| House Edge | 0.1% flat | No scaling — same edge for all bet sizes |
| Optimal-Play RTP | 99.9000% | From exhaustive C(52,5) = 2,598,960 × 32 hold patterns enumeration |
| Deal-Only RTP | 33.7238% | Theoretical RTP if cards were randomly held — anti-circularity proof |
| RNG Algorithm | HMAC-SHA256 | Backward Fisher-Yates over 52 cards with bias-free rejection sampling |
Every Video Poker hand uses three cryptographic inputs to generate the deck.
| Seed Type | Format | Example | Purpose |
|---|---|---|---|
| Server Seed | 64-char hex (32 bytes) | 2424d436197860c9… | Casino-provided randomness |
| Client Seed | Alphanumeric string | cpc4ihmPtvpHpapv | Player-contributed entropy |
| Nonce | Integer (0–49) | 6 | Ensures uniqueness per bet within epoch |
Payouts in Video Poker are determined by the rank of the final 5-card hand. The multiplier table is calibrated so that the expected return under optimal play is exactly 99.9%.
win_amount = bet_amount × multiplier_table[hand_rank]| Hand Rank | Multiplier | Hand Count (of 2,598,960) | P(rank) × multiplier |
|---|---|---|---|
| Royal Flush | 812.9719× | 4 | 0.0012512 |
| Straight Flush | 58× | 36 | 0.0008034 |
| Four of a Kind | 26× | 624 | 0.0062425 |
| Full House | 9× | 3,744 | 0.0129652 |
| Flush | 6× | 5,108 | 0.0117924 |
| Straight | 4× | 10,200 | 0.0156986 |
| Three of a Kind | 3× | 54,912 | 0.0633854 |
| Two Pair | 2× | 123,552 | 0.0950780 |
| Jacks or Better | 1× | 337,920 | 0.1300212 |
| No Win | 0× | 2,062,860 | 0.0000000 |
Why Provably Fair Matters▶
Traditional online casinos require players to trust that games are fair. Provably fair systems eliminate this trust requirement by allowing players to mathematically verify that outcomes were not manipulated. In a Provably Fair system:
- The casino commits to a result before the player bets
- The player contributes randomness that the casino cannot predict
- Anyone can verify the outcome after the fact
High-Level Overview7 sections▶
Checklist Reference
Based on the ProvablyFair.org Audit Execution Checklist, here are the tests covered under this audit document.
1. Commit-Reveal System & Seed Handling
| Test | Description |
|---|---|
| Server seed commit exists before play | SHA-256 hash shown to player before betting |
| Server seed reveal matches commit | SHA-256(hexDecode(revealed)) = committed hash |
| Client seed control | Player can set/change client seed via rotation UI |
| Nonce increments correctly | Starts at 0, +1 per bet, resets at epoch boundary |
| Full determinism | Same inputs → same deck (same initial cards AND same replacement pool) |
2. Randomness & Entropy Model
| Test | Description |
|---|---|
| RNG depends only on seeds + nonce | No external inputs (drand absent from Video Poker) |
| No mixed entropy sources | No timestamps, Math.random, etc. |
| Unbiased mapping | Bias-free rejection sampling with maxFair ceiling eliminates modulo bias for all ranges (2–52) |
| No state leakage | RNG isolated per round — each shuffle step uses unique cursor |
3. Verifier ↔︎ Live Parity
| Test | Description |
|---|---|
| Live outcomes match verifier | 5,400 / 5,400 bets recomputed with 0 mismatches — both initial cards AND final hand after holds |
| Multi-phase verification | Phases A (broad sampling, 3,000), B (targeted hands, 1,000), C (bet-size invariance, 100), D (auditor seed, 500), E (systematic hold patterns, 800) |
| Bet-size invariance | $10 bets produce same decks as $0.01 bets (Phase C, 100/100 verified) |
| Hold pattern coverage | All 4 systematic hold counts verified (hold-0/1/2/5: 100/500/100/100 in Phase E) |
4. Game Logic & RTP Validation
| Test | Description |
|---|---|
| Anti-circularity proof | Σ P(rank) × multiplier = 33.7238% from independent C(52,5) = 2,598,960 enumeration (deal-only) |
| Optimal-play RTP solver | 99.9000% from exhaustive C(52,5) × 32 hold patterns enumeration; 134,459 canonical strategy classes |
| Solver cross-validation | Same solver on standard 9/6 Jacks or Better → 99.5439% (target match to 6 decimal places) |
| House edge audit | Flat 0.1% confirmed across all 5,400 bets — no scaling edge |
| Payout rules correctness | Win amount matches multiplier × bet within 1e-8 |
| Simulated RTP convergence | 10M Pass 1 rounds, Fisher's combined p = 0.7982; deal-only RTP converges on theoretical 33.7238% |
| Cherry-pick detection | 5/110 captured server seeds flagged at α=0.05 (binomial p = 0.6485, well within noise) — no evidence of seed pre-selection |
5. Fairness Integrity & Player Verification
| Test | Description |
|---|---|
| Player can reproduce results offline | Using seeds + nonce + backward Fisher-Yates 52-card shuffle |
| Verifier logic matches live logic | Same HMAC-SHA256 backward Fisher-Yates algorithm |
| Verifier publicly accessible | ProvablyFair.org verifier — no login required |
| No reliance on private APIs | Fully client-side verification |
| Hold/discard integrity (VP-specific) | Hold choice cannot alter deck composition — 5,400/5,400 verified across 4 hold counts |
| 27 verification steps | Commit-reveal, determinism, payout, distribution, anti-manipulation, hand classification, hold validity, replacement pool, deck integrity, optimal-play RTP |
To get an overview of how the process works, here is a high-level breakdown:
1. Player Bets — Selects bet amount and clicks “deal”
2. Seeds Combined — HMAC-SHA256(hexDecode(serverSeed), clientSeed:nonce:cursor) for each shuffle step
3. RNG Output — Backward Fisher-Yates over 52 cards: 51 iterations from i=51 downto 1, each step swaps positions[i] with a bias-free random index
4. Initial Deal — deck[0..4] become the player’s 5-card initial hand
5. Player Holds — Player selects which cards to keep (0–5 cards); discarded positions are tracked
6. Draw — Discarded positions are filled from deck[5..9], consumed left-to-right
7. Hand Evaluation — Final 5-card hand classified into one of 10 ranks
8. Payout Result — win_amount = bet_amount × multiplier_table[hand_rank]
Provably fair gambling systems use cryptographic primitives to guarantee the integrity of outcomes. The model relies on three components: a server seed committed via hash before play, a player-controlled client seed, and an incrementing nonce. These inputs are combined using HMAC-SHA256 to produce deterministic, verifiable results. This section documents the global provably fair architecture used by almost all casinos and all relevant games.
The Commit-Reveal model is integral to ensuring fairness and transparency in online gambling. This model involves several key phases:
Commit Phase:
Before any bets are placed, the casino generates a random server seed. To prove its authenticity and prevent later manipulation, only the SHA-256 hash of the hex-decoded seed is sent to the player. This ensures that while the player cannot know the seed initially, they can verify it later.
Bet Phase:
The player places their bet. The server computes the entire 52-card shuffled deck using HMAC-SHA256 with the server seed (hex-decoded) as the HMAC key and the client seed, nonce, and cursor as the message. The same server seed and client seed pair are used for every bet in the epoch; the player can rotate the seed pair at any time to end the epoch.
Reveal Phase:
After the player rotates to a new seed pair, the server reveals the plaintext server seed from the completed epoch. The player can now independently verify SHA-256(hexDecode(serverSeed)) = committedHash.
Verify Phase:
Anyone can recompute every bet's full 52-card shuffled deck from the revealed server seed, client seed, and nonce using the published backward Fisher-Yates algorithm. The initial cards (deck[0..4]) and replacement pool (deck[5..9]) are both verifiable. Combined with the recorded hold pattern, the final hand is fully reconstructible.
The player's client seed is generated by the browser and submitted to the server via the seed rotation UI. Players can set their own client seed at any time. This ensures:
- The casino cannot predict the full RNG input
- Players contribute entropy that they control
- Results depend on both parties' inputs
Nonce Lifecycle
The nonce is a counter that increments with each bet within an epoch:
- Starts at 0 for each new server seed
- Increments by exactly 1 per bet
- Never reused within the same seed epoch
- Resets to 0 when the player rotates to a new server seed
Seed Epoch: (serverSeed, clientSeed)
Bet 1: nonce = 0 → Deck A → Initial + Pool
Bet 2: nonce = 1 → Deck B → Initial + Pool
Bet 3: nonce = 2 → Deck C → Initial + Pool
...
Bet N: nonce = N−1 → Deck Z → Initial + Pool
[Player rotates seed — epoch complete]
Next bet: nonce = 0 → Deck X (new seed pair)Determinism Guarantee
Given identical inputs, the output is always identical:
HMAC-SHA256(hexDecode(serverSeed), clientSeed:nonce:cursor) → Always same hash
Same hash per step → Always same swap index in Fisher-Yates
Same swaps across all 51 steps → Always same shuffled 52-card deck
Same deck → Always same initial cards (deck[0..4]) AND same replacement pool (deck[5..9])
Same initial + same hold pattern + same pool → Always same final hand
Same final hand → Always same hand rank → Always same payoutTechnical Glossary5 categories▶
| Term | Definition |
|---|---|
| Provably Fair | A cryptographic system that allows players to mathematically verify that game outcomes were not altered. Relies on commit-reveal schemes and deterministic algorithms. |
| Commit-Reveal Protocol | A two-phase process in which the casino commits to a result (by showing its hash) before the player bets, then reveals the actual value after the bet. |
| Determinism | The property that identical inputs always produce identical outputs. Same server seed, client seed, and nonce must always generate the same shuffled deck. |
| Client Seed Origin | The method by which the client seed is generated. Full Pass: browser-generated default + player customizable seed. Conditional Pass: server-assigned default + player customizable seed. Hard Fail: player cannot set own seed. |
| Term | Definition |
|---|---|
| Server Seed | A random 32-byte value generated by Duel.com, transmitted as a 64-character hex string. Hashed and shown to players before betting, revealed after epoch rotation. |
| Client Seed | A random value controlled by the player, generated by the browser. Players can change it at any time via the seed rotation UI. |
| Nonce | A sequential counter that increments per bet within an epoch. Starts at 0 and resets to 0 when the player rotates to a new server seed. |
| Hashed Server Seed | SHA-256(hexDecode(serverSeed)) — the commitment hash shown before betting. After rotation, players verify the revealed seed produces this hash. |
| Cursor | Index used in the HMAC message during the backward Fisher-Yates shuffle. cursor = 51 − i, starting at 0 for the first step (i=51). Each cursor value produces one shuffle step. |
| Epoch | A sequence of consecutive bets sharing the same server seed and client seed pair. Ends when the player rotates to a new seed pair. This audit covered 110 bet-bearing epochs. |
| Term | Definition |
|---|---|
| HMAC-SHA256 | Hash-based Message Authentication Code using SHA-256. Duel Video Poker uses HMAC-SHA256 with the hex-decoded server seed as key and clientSeed:nonce:cursor as message. 51 calls per bet (one per shuffle step). |
| SHA-256 | Secure Hash Algorithm 256-bit. Used for server seed commitment: SHA-256(hexDecode(serverSeed)) = serverSeedHashed. |
| Hex Decoding | Converting a 64-character hex string to 32 raw bytes. Critical for the HMAC key — using the hex string as UTF-8 produces wrong outputs. |
| Backward Fisher-Yates Shuffle | A variant of the Fisher-Yates algorithm that iterates from index 51 down to 1, swapping each position with a random lower index. Produces a uniformly random permutation of the 52-card deck. |
| Term | Definition |
|---|---|
| Verifier | A tool that independently calculates the shuffled 52-card deck (initial + replacement pool) using provided seeds and nonce. The ProvablyFair.org verifier is built from the audit codebase. |
| Parity | Degree of matching between verifier and live game results. 100% parity = every deck (both halves) matches and every final hand reconstructs. This audit: 5,400/5,400 exact match. |
| Anti-Circularity | Proof that RTP derives from first principles, not casino's own claims. Σ P(rank) × multiplier from independent C(52,5) enumeration = 33.7238% (deal-only); cross-validated optimal-play solver returns 99.9000%. |
| Optimal-Play RTP | The expected return achievable with the EV-maximizing hold decision for every dealt hand. Computed by exhaustive enumeration of all 2,598,960 hands × 32 hold patterns. Duel.com Video Poker = 99.9000%. |
| Cross-Validation | Re-running the optimal-play solver against the standard 9/6 Jacks or Better pay table (target 99.5439%) before computing the Duel.com RTP. Confirms the solver is correct independent of the audited pay table. Match: 99.543904% vs 99.5439%. |
| Rejection Sampling | Bias elimination technique used in the shuffle. A maxFair ceiling discards values that would cause modulo bias, ensuring uniform randomness for all ranges (2–52). |
| Term | Definition |
|---|---|
| Initial Cards | The first 5 cards dealt to the player (deck[0..4]). Determined cryptographically before the deal animation plays. |
| Replacement Pool | The next 5 cards in the shuffled deck (deck[5..9]). These replace any cards the player discards, consumed left-to-right by the hold pattern. |
| Hold Pattern | A 5-bit pattern indicating which positions the player chose to keep from the initial deal. The player can hold 0 to 5 cards. Held cards remain; non-held positions consume from the replacement pool in order. |
| Final Hand | The 5-card hand after the draw. Constructed deterministically from (initial_cards, hold_pattern, replacement_pool). Evaluated against the 10-rank pay table. |
| Hand Rank | The poker rank of the final 5-card hand: Royal Flush, Straight Flush, Four of a Kind, Full House, Flush, Straight, Three of a Kind, Two Pair, Jacks or Better, or No Win. |
| Pay Table | The mapping from hand rank to multiplier. Duel.com uses a custom pay table calibrated for 99.9% optimal-play RTP, with a Royal Flush multiplier of 812.97× to support the headline RTP claim. |
| Anti-Circularity (Deal-Only RTP) | The expected return computed from independent textbook 5-card poker hand counts × the casino's pay table. Equals 33.7238% — a non-circular proof of the underlying card probabilities. |
Every Video Poker hand on Duel.com is generated from three inputs: server seed, client seed, and nonce. The casino commits to its server seed by publishing a SHA-256 hash before you place any bets. After you rotate your seed, the server reveals the actual seed — and anyone can verify that the hash matches. This cryptographic commitment makes it impossible for the casino to secretly change the deck after you bet — and because the entire 52-card shuffle (your initial 5 cards plus the 5-card replacement pool) is determined at deal time, your hold choice cannot alter the cards available to you.
What We Verified
- Casino commits to the server seed hash before any bet is placed
- Client seed is browser-generated — server cannot know it at commitment time (Full Pass origin)
- Players can set or change their client seed at any time via the rotation UI
- Nonce increments by 1 per bet across all 110 seed pairs
- The full 52-card deck — and therefore both the initial deal and the final hand — is determined by the seed inputs before the deal is shown
- Identical inputs always produce the same shuffled deck — confirmed across all 5,400 bets
- Your client seed is a genuine input — changing it changes the deck
What This Means for You
- The casino cannot change your initial 5 cards or your replacement pool after you bet
- You contribute randomness the server cannot predict or pre-select against
- Every bet is unique — the nonce ensures no two bets share an outcome
- Any deck and final hand can be independently verified using the public tools and repo
- Outcomes are tamper-proof and verifiable even months later
- Cherry-picking favourable seeds is structurally impossible
| Test | Status | Finding |
|---|---|---|
| Server seed committed before bet | Pass | SHA-256 hash of server seed published before play — casino cannot change randomness after betting |
| Client seed origin | Pass | Browser-generated (Full Pass) — server commits before client seed is known |
| Client seed control | Pass | Player can set/change client seed via rotation UI at any time. |
| Nonce sequencing | Pass | Sequential within each seed pair, 0 gaps, 0 duplicates across 110 seed pairs |
| Hash consistency within epoch | Pass | serverSeedHashed constant across all bets within each of 110 epochs |
| Seed hash integrity | Pass | 110 / 110 revealed seeds hash-verified — commitment chain intact |
| Deterministic output | Pass | Same (serverSeed, clientSeed, nonce) always produces same 52-card deck — 5,400 / 5,400 confirmed (initial deal + final hand) |
| Client seed participation | Pass | Client seed is a genuine input — changing it changes the deck |
All 110 revealed seeds hash-verified. Every seed rotation was verified — the next seed the casino pre-committed always matched what was actually used. Outcomes are fully deterministic — the same server seed, client seed, and nonce always produce the same 52-card deck, which fixes both the initial 5-card deal and the replacement pool used for the draw. The casino cannot change your result after you bet.
This section verifies that Duel.com's Video Poker random number generation produces cryptographically sound, unbiased outputs using only the disclosed inputs. The RNG uses a backward Fisher-Yates shuffle of the 52-card deck with HMAC-SHA256 — each of the 51 swap steps derives an index from a 4-byte HMAC chunk with bias-free rejection sampling via the maxFair ceiling. We independently implemented this algorithm, verified it produces the same shuffle as the live game across 5,400 bets, and confirmed no hidden inputs can influence outcomes.
What We Verified
- HMAC-SHA256 produces cryptographically sound, unpredictable output for each shuffle step
- Only disclosed inputs affect outcomes — no timestamps, no server-side state, no hidden entropy
- Rejection sampling via `maxFair` ceiling eliminates modulo bias for all ranges (2–52)
- Hand-rank distribution follows the textbook C(52,5) = 2,598,960 expectation (confirmed over 10M simulated rounds via Fisher's combined test)
- Per-card rank and suit distributions are uniform across all 27,000 dealt cards
- Consecutive outcomes are statistically independent — no patterns, no streaks
- 100.0% of outcomes change with a different client seed (500/500 sampled bets)
What This Means for You
- Each shuffled deck is generated fairly and cannot be skewed
- All 52 cards are equally likely to land in any deck position — no positional bias
- No hidden randomness or server-side tricks influence which cards appear
- Consecutive bets are not correlated — past results don't affect future outcomes
- The algorithm depends only on seeds you can verify
| Test | Status | Finding |
|---|---|---|
| RNG derived only from disclosed inputs | Pass | HMAC-SHA256(hexDecode(serverSeed), clientSeed:nonce:cursor) — no hidden entropy |
| Entropy purity | Pass | No timestamps, external APIs, Math.random, or server-side mutable state |
| Algorithm independently implemented | Pass | Independent implementation produces identical 52-card decks for all 5,400 bets |
| Modulo bias | Pass | Rejection sampling via maxFair ceiling eliminates bias for all ranges (2–52) |
| Key encoding verified | Pass | Server seed hex-decoded to bytes (not UTF-8) — confirmed via 5,400-bet recomputation |
| Hand-rank distribution | Pass | Fisher's combined p = 0.7982 across 10 streams × 1M rounds; aggregate chi-squared p = 0.7193 across 11M cherry-pick rounds |
| Card-level distribution | Pass | Rank χ²(12) = 8.39, p = 0.7541; Suit χ²(3) = 0.52, p = 0.9150 across 27,000 live-bet cards |
| Serial independence | Pass | Lag-1 autocorrelation = -0.000233 and runs test p = 0.1000 across 10M rounds |
The Video Poker RNG uses only the disclosed inputs, produces a hand-rank distribution matching the C(52,5) = 2,598,960 textbook expectation across 10M simulated rounds, and shows no serial dependence. The client seed is a genuine input — changing it changes the deck.
This section validates that the independent verifier produces the exact same shuffled deck as the live game for every single bet. Because Video Poker is a two-step game (deal then draw), both halves must match — the initial 5-card hand from deck[0..4] AND the final hand reconstructed by applying the player's hold choice to the replacement pool at deck[5..9]. Any mismatch in either half would invalidate the fairness guarantee.
What We Verified
- Every bet independently recomputed from seeds — full 52-card deck verified, not just the payout
- Both halves of every bet verified — initial 5-card deal AND final 5-card hand after holds
- Hand classification: server's combination field matches independent evaluateHand() for all 5,400 bets across all 10 hand ranks
- Payout correctness: amount_won = amount_coins × multiplier, exact for all 5,400 bets
- The 10-rank pay table produces the correct multiplier for every hand classification
- Bet amount is not an input to the RNG — decks depend only on seeds and nonce
- The player's hold choice does not change the deck — the 10-card outcome is fixed at deal time
What This Means for You
- The verifier isn't a simulation — it produces the exact same deal, replacement pool, and final hand as the live game
- Every bet you play can be independently recomputed by anyone after seed rotation
- No hidden logic alters outcomes based on how much you bet, what your client seed is, or which cards you hold
- The game engine in production matches the published algorithm exactly
| Test | Status | Finding |
|---|---|---|
| Deck recomputation | Pass | 5,400/5,400 exact match — initial cards deck[0..4] and replacement pool deck[5..9] verified for every bet |
| Hand classification | Pass | 5,400/5,400 — independent evaluateHand(final_cards) matches server combination across all 10 hand ranks |
| Final-hand composition | Pass | 5,400/5,400 — final cards reconstruct deterministically from (initial_cards, held_cards, replacement_pool) |
| Payout correctness | Pass | All 5,400 bets: amount_won = amount_coins × multiplier, tolerance 1×10⁻⁶ |
| Pay table integrity | Pass | All 10 hand ranks → multiplier match videoPokerConfig.json for every bet |
| Bet-size independence | Pass | Phase C (100 bets at $10/hand) — same algorithm produces the deck regardless of bet amount |
| Hold pattern coverage | Pass | Phase E (800 bets, 8 hold patterns × 4 hold counts) — deck composition independent of hold choice |
All 5,400 bets matched the independent verifier exactly — initial deck, replacement pool, final hand after holds, hand classification, multiplier, and payout all reproduce from `(serverSeed, clientSeed, nonce)` across all five capture phases. Both halves of every bet (deal and draw) are recoverable from the recorded inputs.
Video Poker has two RTPs: a deal-only RTP — the raw return from the cards alone, no strategy — proven from textbook combinatorial counts times the paytable, and the headline optimal-play RTP of 99.9%, computed exactly by enumerating all C(52,5) = 2,598,960 deals and solving the best hold for each. It cross-validates against the published 9/6 Jacks-or-Better return of 99.5439%, uses the operator's paytable alone (no operator RTP figure enters the chain), and is corroborated by a 10-million-round Monte Carlo. Every captured hand reconciles against the published paytable, and cherry-pick detection replays every committed server seed to rule out seed pre-selection.
What We Verified
- House edge is exactly 0.1% — flat across all bets, all hand classes, all bet sizes
- Optimal-play RTP proven from first principles: exhaustive C(52,5) = 2,598,960-hand enumeration with 134,459 canonical strategy classes returns exactly 99.9000%
- Cross-validated by re-running the same solver against the standard 9/6 Jacks or Better pay table (target 99.5439%) — match to 6 decimal places
- Deal-only RTP (no strategy) is 33.7238% theoretical / 33.6999% simulated across 10M rounds
- Cherry-pick detection: 110 captured server seeds × 100,000 simulated nonces — no evidence of seed pre-selection
- Bet amount does not influence the deck — confirmed at $0.01 and $10
What This Means for You
- The house keeps exactly 0.1% — no scaling structure, no bet-size dependence
- The 99.9% claim is the optimal-play RTP — players who hold sub-optimally earn less
- The RTP proof is derived independently — it doesn't rely on trusting the casino
- The casino's seeds show no evidence of being chosen to produce favourable early outcomes
- Your bet amount doesn't affect which cards are dealt
| Test | Status | Finding |
|---|---|---|
| Anti-circularity (deal-only) | Pass | Σ P(rank) × multiplier(rank) = 33.7238% from independent C(52,5) = 2,598,960 enumeration — derived from textbook 5-card poker counts, no casino-supplied probability data |
| Optimal-play RTP solver | Pass | Exhaustive C(52,5) × 32 hold patterns enumeration → 99.900000%; 134,459 canonical strategy classes |
| Solver cross-validation | Pass | Same solver against standard 9/6 JoB pay table → 99.543904% (target 99.5439%, tolerance 1×10⁻⁶) |
| House edge audit | Pass | Flat 0.1% house edge derived from Duel's paytable in videoPokerConfig.json; effective_edge metadata field constant at 0.1 across all 5,400 live bets (metadata-tampering check); 99.9000% optimal-play RTP cross-validated against 99.5439% 9/6 JoB target |
| Simulated RTP (Pass 1) | Pass | 10M rounds, Fisher's combined p = 0.7982; 0/10 streams fail chi-squared at α=0.01 |
| Cherry-pick detection (Pass 2) | Pass | 110 captured server seeds × 100,000 nonces each — 5/110 flagged at α=0.05 (binomial p = 0.6485, well within noise); aggregate χ² p = 0.7193 |
| Bet-size invariance | Pass | Phase C: 100/100 bets at $10/hand — same algorithm produces the deck regardless of bet amount |
| Pay table integrity | Pass | All 5,400 live bets — observed multiplier matches videoPokerConfig.json for all 10 hand ranks |
Both RTPs verified. The deal-only RTP of 33.7238% is proven from textbook combinatorial counts × multipliers — a first-principles non-circular proof of the underlying card probabilities. The optimal-play RTP of 99.9000% is proven from an exhaustive C(52,5) = 2,598,960-hand strategy enumeration cross-validated against standard 9/6 Jacks or Better. 10M simulated rounds and 110-seed cherry-pick detection confirm no anomalies. The house edge is flat at 0.1%.
Sections 1–4 prove the game is mathematically fair. Section 5 proves the implementation maintains integrity under non-standard conditions. We applied 16 fairness integrity tests covering nonce integrity, seed commitment, outcome determinism, cross-player isolation, payout integrity, and a Video-Poker-specific hold/discard state-integrity check. 14 tests passed, 1 raised a FLAG (server-side input-validation gap on a duplicate held card — disclosed to Duel.com, no fairness impact), and 1 is not applicable to this game type (no replay surface — card sequence is fixed at deal time).
What We Verified
- Nonce tampering — can the sequence be forced, replayed, or skipped?
- Seed injection — can server or client seed fields be overridden via API?
- Outcome replay — can a completed bet be replayed for duplicate payouts?
- Cross-player isolation — can one player's seeds or outcomes affect another's?
- Payout tampering — can multiplier or payout values be injected client-side?
- Parameter limits — can invalid held cards or bet amounts be submitted?
- Hold/discard integrity — can the player's hold choice alter the deck, or can replacement cards be redirected?
What This Means for You
- No one — not the player, not the casino — can alter outcomes through the API
- Once a bet is placed, the result cannot be changed or replayed
- Each bet is cryptographically unique and isolated
- Your results are independent of every other player
- Your hold choice cannot influence the deck — it only selects which positions consume from the pre-shuffled pool
- The server rejects malformed, out-of-range, and duplicate requests
| Test | Status | Finding |
|---|---|---|
| Nonce integrity | Pass | Sequential, server-controlled, no gaps or duplicates across 110 bet-bearing epochs |
| Seed commitment integrity | Pass | Locked at bet acceptance, unique per epoch — 115/115 seed entries verified |
| Outcome determinism | Pass | Identical inputs produce identical decks — 5,400/5,400 confirmed; both halves (deal + draw) recover from (serverSeed, clientSeed, nonce) |
| Hold/discard integrity | Pass | Player hold choice cannot alter deck composition — 5,400/5,400 verified across 4 hold counts in Phase E (800 bets) |
| Round & player isolation | Pass | Per-user seeds, serial independence confirmed (Fisher's combined p = 0.7982 across 10M rounds) |
| Payout integrity | Flag | FI-PAYOUT-001 flagged: a duplicate held card (`["8D","8D"]`) accepted by server — input-validation gap, no fairness impact (final hand still computed from pre-committed shuffle). FI-PAYOUT-002 pass: injected payout/card fields ignored. Disclosed to Duel.com. |
16 fairness integrity tests (15 standard + 1 Video-Poker-specific): 14 pass, 1 N/A (no replay surface — card sequence fixed at deal time), 1 FLAG (FI-PAYOUT-001: a duplicate held card accepted by server — input-validation gap, disclosed to Duel.com, does not break fairness).
Every Video Poker outcome can be independently reproduced using publicly disclosed inputs. No hidden variables, no private backend data. If your computed deck matches the live result, the bet was provably fair. This section walks you through the process — and provides an independent verification tool built from the same code used in this audit.
Key Principles
- Every Video Poker outcome can be independently reproduced
- No hidden variables — no private backend data
- If your computed deck matches the game result, the bet was provably fair
- Most players can verify directly through the Duel.com fairness UI
What You Need
- Server Seed — revealed after seed rotation (casino entropy)
- Client Seed — your player-controlled seed
- Nonce — the bet number in sequence (ensures uniqueness)
- Hold Pattern — which positions you held
Only disclosed inputs are used. Identical inputs always produce identical output — both the initial deal and the final hand after holds.
This section consolidates the open-source repository, datasets, output artifacts, and reproducibility posture of the audit. Every finding, every statistic, every pass/fail result can be independently reproduced by anyone with a computer and an internet connection. The repository is the credential — not this report.
Repository Details
- GitHub: ProvablyFair-org/duel-video-poker
- Commit: 071d7e081d9fa1c63fa37d187ff4a1395e229c98
- Game: Video Poker (duel-video-poker)
- Public Verifier: audit.provablyfair.org/casino/duel/tools/verify-bets
Prerequisites
- Node.js 18+
- npm 8+
- Git
- TypeScript (installed via npm)
Repository Structure
Commands to Reproduce
Audit Reproducibility Pinning
All audit results can be independently reproduced using the pinned commit, dataset, and commands above. The dataset hash ensures you're running against the same 5,400 bets across 115 seeds; the optimal-play RTP artifact is pinned to a specific pay table hash for tamper detection.



