How to Verify a Bet on Duel.com (Step by Step)
Verify a Duel.com bet with Duel's verifier or your own code: the documented HMAC-SHA256 scheme, runnable Node and Python scripts, and fixes for mismatches.
On this pageShowHide
- The easy route: Duel's verifier
- What you need
- Check 1: the commitment
- Check 2: recompute the result
- How Duel turns seeds into a number
- Game formulas
- Node.js script for games that use your seed pair
- The same in Python
- The fairness range
- Crash and Castle Roulette
- Using a browser HMAC tool
- Troubleshooting a mismatch
- If it still does not match
- What verification does not tell you
- Summary
Quick answer
Verifying a bet means recomputing its result from the seeds and checking that the server seed was fixed before you played. On Duel.com you can do it in two ways: with Duel's verifier at duel.com/fairness/verify, or independently with a short script. Both are covered here.
This applies to the Duel Originals. Third-party slots and live dealer tables cannot be verified this way.
The easy route: Duel's verifier
Duel's verifier is at duel.com/fairness/verify. It comes with one condition: a round can be checked there only after you have rotated the seed pair it was played on, because the server seed is hidden until then. After rotation you can also raise the nonce for that old pair and see what the following rounds would have been.
Save the hashed server seed before you play
Copy the hash shown for your active seed pair into a note outside the site. This is the commitment.
Play, then rotate
Rotation reveals the server seed of the pair you just used. See how to rotate your seed.
Enter the round in the verifier
Use the revealed server seed, your client seed and the nonce of the bet. The result must equal the one in your bet history.
Check the hash yourself
Hash the revealed seed with SHA-256 and compare it with the hash you saved. The code is in the next section.
We have not been able to inspect the verifier page itself, so its field labels are not described here. If you cannot find where the seeds and nonce of a bet are shown, ask the 24/7 live chat.
The verifier is Duel's own code. It is convenient, but an independent check means running the same calculation on your own machine. The rest of this page does that.
What you need
- The hashed server seed that was displayed while you were betting
- The revealed server seed for that pair, available after you rotate
- The client seed that was active for the bet
- The nonce of the bet, taken from the bet detail
- The result you were shown, and the game it was on
- For Crash and Castle Roulette: the round's server seed and its drand round ID instead of a client seed and nonce
Check 1: the commitment
The server seed is a 64-character hex string, and Duel shows you its SHA-256 hash before you bet. After rotation, the revealed seed must produce that same hash.
Duel does not document whether the published hash is the SHA-256 of the seed as text or of the 32 bytes the hex represents, and its worked examples do not include a hashed seed. Try the text first, then the bytes. This script prints both and compares them with the hash you saved:
// Commitment check: does the revealed server seed match the hash you saved before betting?
const crypto = require("node:crypto");
const sha256 = (data) => crypto.createHash("sha256").update(data).digest("hex");
const revealedSeed = "e8c83961a3961baee67cc08da34ada1c00a4515450a3cdbd2b1db81a35ca7f72"; // seed from Duel's published Dice example
const savedHash = "PASTE_THE_HASH_YOU_SAVED_BEFORE_BETTING";
const asText = sha256(revealedSeed); // the 64 characters hashed as text
const asBytes = sha256(Buffer.from(revealedSeed, "hex")); // the 32 bytes the hex stands for
console.log("SHA-256 of the seed as text: ", asText);
console.log("SHA-256 of the seed as bytes:", asBytes);
if (savedHash === asText) console.log("MATCH: the published hash is of the seed text");
else if (savedHash === asBytes) console.log("MATCH: the published hash is of the hex-decoded bytes");
else console.log("NO MATCH with either form");
Save it as commitment.js and run node commitment.js. With the example seed it prints these two hashes, and NO MATCH until you paste a real saved hash:
SHA-256 of the seed as text: 8150149e80274b32391fd1fdf80f0f2bda11705a462a1c17737104996aac772e
SHA-256 of the seed as bytes: 6205003b9efc5f676a4471bb7316f544d4f7cc10d346aa09f1e492f015a13339
Without Node.js, the same two hashes on macOS, Linux or Git Bash. Use printf, not echo, because echo adds a line break that changes the hash. On macOS, replace sha256sum with shasum -a 256.
printf '%s' "e8c83961a3961baee67cc08da34ada1c00a4515450a3cdbd2b1db81a35ca7f72" | sha256sum
printf '%s' "e8c83961a3961baee67cc08da34ada1c00a4515450a3cdbd2b1db81a35ca7f72" | xxd -r -p | sha256sum
The first line hashes the text, the second the bytes. If one of them equals the hash you recorded before betting, the seed was fixed before any bet on that pair.
Check 2: recompute the result
How Duel turns seeds into a number
Every documented game starts the same way:
| Step | What happens |
|---|---|
| 1. Key | The unhashed server seed, decoded from hex into 32 bytes |
| 2. Message | clientSeed:nonce:cursor. Dice has no cursor and uses clientSeed:nonce |
| 3. HMAC | HMAC-SHA256 of the message with that key, giving 64 hex characters |
| 4. Number | The first 4 bytes (8 hex characters) read as a number from 0 to 4,294,967,295 |
| 5. Fairness range | If the number is at or above the game's limit, the next 4 bytes are used instead |
| 6. Game formula | The number is turned into a roll, a card, a direction or a shuffle step |
The cursor counts steps inside one round: each card in Blackjack, each row in Plinko, each shuffle step in Mines. It starts at 0.
Game formulas
| Game | HMACs per round | From number to result |
|---|---|---|
| Dice | 1 | Number mod 10001, divided by 100: a roll from 0.00 to 100.00 |
| Blackjack | 1 per card | Number mod 52 is the card index: 0 is 2D, 1 is 2H, 4 is 3D |
| Plinko | 1 per row, 8 to 16 rows | Odd is right, even is left. The bucket is the number of rights |
| Mines | 24 | Fisher-Yates shuffle of tiles 0 to 24. The first N entries are the mines |
| Keno | 39 | Shuffle of 0 to 39. The first 10 entries, plus 1 each, are the draw |
| Beef | 19 | Shuffle of 0 to 19. The first 10, 5, 3 or 1 entries are the death positions on Extreme, Hard, Medium or Easy |
| Video Poker | 51 | Shuffle of 52 cards. The first 5 are your hand, the next 5 the replacements |
| Crash | 1 | 2^32 divided by (number + 1), times 0.999 |
| Castle Roulette | 1 | Number mod 48 is the roll, mapped to a multiplier |
The shuffle works the same way in all four shuffle games. Step 1 takes the number mod the list length and swaps the entry at that position with the last entry, which is then locked. Step 2 takes the next number mod one less and swaps with the second-to-last entry, and so on until one entry is left.
Duel's sitemap also lists Rock Paper Scissors, Limbo and Groomers Van as Originals. Duel has not published their derivations as of October 2026, so their formulas are not on this page.
Node.js script for games that use your seed pair
No packages needed. Save as duel-verify.js and run node duel-verify.js.
// Verifier for the Duel Originals that use your seed pair:
// Dice, Blackjack, Plinko, Mines, Keno, Beef and Video Poker.
// The inputs at the bottom are Duel's published worked examples.
const crypto = require("node:crypto");
// The HMAC key is the server seed decoded from hex into 32 bytes,
// not the 64 characters of text you see on screen.
const hmacHex = (serverSeed, message) =>
crypto.createHmac("sha256", Buffer.from(serverSeed, "hex")).update(message).digest("hex");
// Take the first 4 bytes as a 32-bit number. If it is outside the fairness range
// for this modulus, move on to the next 4 bytes, as Duel documents.
function value(serverSeed, message, modulus) {
const hex = hmacHex(serverSeed, message);
const limit = 2 ** 32 - (2 ** 32 % modulus); // largest multiple of the modulus up to 2^32
for (let i = 0; i + 8 <= hex.length; i += 8) {
const n = parseInt(hex.slice(i, i + 8), 16);
if (n < limit) return n;
}
throw new Error("All eight 4-byte groups were rejected; Duel does not document this case");
}
// Dice: the message is clientSeed:nonce and the roll runs from 0.00 to 100.00
const dice = (seed, client, nonce) => (value(seed, `${client}:${nonce}`, 10001) % 10001) / 100;
// Cards: index 0 is 2D, 1 is 2H, 2 is 2S, 3 is 2C, 4 is 3D, and so on up to 51, which is AC
const RANKS = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];
const SUITS = ["D", "H", "S", "C"];
const cardName = (index) => RANKS[Math.floor(index / 4)] + SUITS[index % 4];
// Blackjack: one HMAC per card. Cursor 0 is the first card dealt, 1 the second, and so on.
const blackjackCard = (seed, client, nonce, cursor) =>
value(seed, `${client}:${nonce}:${cursor}`, 52) % 52;
// Plinko: one HMAC per row. An odd value means right (+1), an even value means left (+0).
function plinko(seed, client, nonce, rows) {
let bucket = 0;
for (let row = 0; row < rows; row++) bucket += value(seed, `${client}:${nonce}:${row}`, 2) % 2;
return bucket;
}
// Fisher-Yates shuffle used by Mines, Keno, Beef and Video Poker. Each step picks one of the
// positions that are not locked yet and swaps it with the last unlocked position.
function shuffle(seed, client, nonce, size) {
const order = [...Array(size).keys()];
for (let cursor = 0; cursor < size - 1; cursor++) {
const remaining = size - cursor;
const pick = value(seed, `${client}:${nonce}:${cursor}`, remaining) % remaining;
[order[pick], order[remaining - 1]] = [order[remaining - 1], order[pick]];
}
return order;
}
const mines = (seed, client, nonce, mineCount) => shuffle(seed, client, nonce, 25).slice(0, mineCount);
const keno = (seed, client, nonce) => shuffle(seed, client, nonce, 40).slice(0, 10).map((n) => n + 1);
const beef = (seed, client, nonce, deaths) =>
shuffle(seed, client, nonce, 20).slice(0, deaths).sort((a, b) => a - b);
const videoPoker = (seed, client, nonce) => shuffle(seed, client, nonce, 52).slice(0, 10).map(cardName);
// Duel's published examples. Replace these with the values from your own bet.
const seedA = "e8c83961a3961baee67cc08da34ada1c00a4515450a3cdbd2b1db81a35ca7f72";
const seedB = "cb8bd577e2b1f62e91c0fe5296c145330bc8ae4307729a372aa4fe639a5af24c";
console.log("dice roll:", dice(seedA, "UzR5CIqog4DXox5H", 21).toFixed(2));
const card = blackjackCard(seedA, "UzR5CIqog4DXox5H", 470, 0);
console.log("blackjack first card index:", card, "=", cardName(card));
console.log("plinko bucket, 16 rows:", plinko(seedB, "62OUyVeUGCRnTpU6", 8796, 16));
console.log("mines, 3 mines, tiles:", mines(seedB, "62OUyVeUGCRnTpU6", 9521, 3).join(", "));
console.log("beef, Extreme, death lanes:", beef(seedB, "62OUyVeUGCRnTpU6", 9837, 10).join(", "));
console.log(
"keno draw:",
keno("72fd40e4d9866aa8d0894461f8f3a23ead87d2e165bc4dc6212719c7f7da83fb", "4nAG7A714SPliuM2", 2459).join(", "),
);
console.log(
"video poker, hand then replacements:",
videoPoker("095d6816179b10fb2edaa28f5ca48260a09045ed00d6529621fe037e5ba9d0b7", "u6bEgvYMZivkzbAv", 3693).join(" "),
);
Output, which matches the result Duel publishes for each example:
dice roll: 83.28
blackjack first card index: 4 = 3D
plinko bucket, 16 rows: 14
mines, 3 mines, tiles: 3, 4, 11
beef, Extreme, death lanes: 6, 7, 8, 9, 10, 11, 13, 15, 17, 18
keno draw: 11, 6, 5, 10, 30, 38, 28, 26, 34, 3
video poker, hand then replacements: AH 5D 2H 3H 7H JH 8C 4H 2S 3C
To check your own bet, replace the seed, client seed and nonce in the matching line and compare the output with your bet history.
The same in Python
Python 3, standard library only. This version covers Dice, Blackjack, Plinko, Mines and Crash.
# Duel Originals verifier, Python 3, standard library only.
# The inputs at the bottom are Duel's published worked examples.
import hashlib
import hmac
def hmac_hex(server_seed, message):
# The key is the server seed decoded from hex into 32 bytes, not the text.
return hmac.new(bytes.fromhex(server_seed), message.encode("utf-8"), hashlib.sha256).hexdigest()
def value(server_seed, message, modulus):
digest = hmac_hex(server_seed, message)
limit = 2**32 - (2**32 % modulus) # fairness range: largest multiple of the modulus
for i in range(0, len(digest), 8):
n = int(digest[i : i + 8], 16)
if n < limit:
return n
raise ValueError("all eight 4-byte groups rejected; Duel does not document this case")
def dice(seed, client, nonce):
return value(seed, f"{client}:{nonce}", 10001) % 10001 / 100
def blackjack_card(seed, client, nonce, cursor):
return value(seed, f"{client}:{nonce}:{cursor}", 52) % 52
def plinko(seed, client, nonce, rows):
return sum(value(seed, f"{client}:{nonce}:{row}", 2) % 2 for row in range(rows))
def shuffle(seed, client, nonce, size):
order = list(range(size))
for cursor in range(size - 1):
remaining = size - cursor
pick = value(seed, f"{client}:{nonce}:{cursor}", remaining) % remaining
order[pick], order[remaining - 1] = order[remaining - 1], order[pick]
return order
def mines(seed, client, nonce, mine_count):
return shuffle(seed, client, nonce, 25)[:mine_count]
def crash_point(server_seed, drand_hex):
# drand hex -> bytes -> UTF-8 text with invalid sequences replaced -> ":0" appended
text = bytes.fromhex(drand_hex).decode("utf-8", errors="replace")
digest = hmac_hex(server_seed, text + ":0")
return 2**32 / (int(digest[:8], 16) + 1) * (1 - 0.001)
SEED_A = "e8c83961a3961baee67cc08da34ada1c00a4515450a3cdbd2b1db81a35ca7f72"
SEED_B = "cb8bd577e2b1f62e91c0fe5296c145330bc8ae4307729a372aa4fe639a5af24c"
CRASH_SEED = "72ebf12bfaf8d996438fcf8a4a88c01dbb022c05f4ac831b392d196a67ff9261"
CRASH_DRAND = "95812946c71502a50b85c0b6c8ad0bb821de4d5a2c011717ba0a668843295bfecd85aee347544c89cf809bad32a9d3fc"
print("dice roll:", f"{dice(SEED_A, 'UzR5CIqog4DXox5H', 21):.2f}")
print("blackjack first card index:", blackjack_card(SEED_A, "UzR5CIqog4DXox5H", 470, 0))
print("plinko bucket, 16 rows:", plinko(SEED_B, "62OUyVeUGCRnTpU6", 8796, 16))
print("mines, 3 mines, tiles:", mines(SEED_B, "62OUyVeUGCRnTpU6", 9521, 3))
print("crash point:", f"{crash_point(CRASH_SEED, CRASH_DRAND):.2f}")
Output:
dice roll: 83.28
blackjack first card index: 4
plinko bucket, 16 rows: 14
mines, 3 mines, tiles: [3, 4, 11]
crash point: 2.50
The fairness range
A 32-bit number has 4,294,967,296 possible values, and that is not a multiple of 52, 25 or 10,001. Taking a plain remainder would make the lowest results very slightly more likely. Duel avoids this by rejecting the few values at the top of the range and moving to the next 4 bytes of the same HMAC.
| Game | Number is accepted if it is under | Values rejected |
|---|---|---|
| Dice | 4,294,959,453 | 7,843 |
| Blackjack | 4,294,967,248 | 48 |
| Mines, first shuffle step | 4,294,967,275 | 21 |
| Beef, first shuffle step | 4,294,967,280 | 16 |
| Plinko | No limit needed, 2^32 is even | 0 |
Each limit is the largest multiple of the modulus that fits in 2^32, which is what the scripts compute. Three caveats:
- Duel documents the limit for the first shuffle step only. The scripts apply the same rule to the later steps, where the modulus shrinks by one each time.
- For Keno, Duel states the same limit as Mines, 4,294,967,275, although Keno's first step uses mod 40, for which the largest multiple is 4,294,967,280. The two differ for 5 values in 4.29 billion.
- None of Duel's published examples hits a rejected value, so this branch is implemented as documented but not tested against a real round. It matters on about 1 Dice roll in 548,000 and far less often elsewhere.
Crash and Castle Roulette
These two are shared rounds, so they have no player client seed and no rising nonce. In place of the client seed they use a value from drand, a public randomness beacon run by a group of independent organisations. Drand publishes a new value every 3 seconds on its quicknet network, each round has a number, and nobody, including Duel, can predict or choose a round's value before it is published.
Duel shows the hashed server seed and the drand round ID before the round starts. The round ID points to a value that does not exist yet. That locks the result: Duel cannot change its seed without breaking the hash, and it cannot influence the drand value.
To verify a round:
- Check that the round's revealed server seed hashes to the hash shown beforehand, using Check 1 above.
- Fetch the drand value for the round ID from drand's public API. It is the
signaturefield, 96 hex characters. - Recompute the result with the script below.
curl -s https://api.drand.sh/v2/beacons/quicknet/rounds/22842664
{"round":22842664,"signature":"95812946c71502a50b85c0b6c8ad0bb821de4d5a2c011717ba0a668843295bfecd85aee347544c89cf809bad32a9d3fc"}
That is the round used in Duel's published Crash example.
The message is built in an unusual way. The drand hex is turned into bytes, the bytes are decoded as UTF-8 text with every invalid sequence replaced by the Unicode replacement character, and :0 is appended because the nonce is always 0. The key is again the server seed as hex bytes.
// Verifier for Crash and Castle Roulette, which use a public drand value instead of a client seed.
const crypto = require("node:crypto");
// Message: the drand value (hex) is turned into bytes, decoded as UTF-8 text with invalid
// sequences replaced by U+FFFD, then ":0" is appended (the nonce is always 0).
// Key: the server seed decoded from hex into 32 bytes.
function drandValue(serverSeed, drandHex) {
const text = new TextDecoder().decode(Buffer.from(drandHex, "hex"));
const hex = crypto
.createHmac("sha256", Buffer.from(serverSeed, "hex"))
.update(text + ":0")
.digest("hex");
return parseInt(hex.slice(0, 8), 16);
}
// Crash point = (2^32 / (value + 1)) * (1 - 0.001). The 0.001 is the 0.1% house edge.
const crashPoint = (serverSeed, drandHex) => (2 ** 32 / (drandValue(serverSeed, drandHex) + 1)) * (1 - 0.001);
// Castle Roulette: roll = value mod 48, then the published payout table.
function castleRoulette(serverSeed, drandHex) {
const roll = drandValue(serverSeed, drandHex) % 48;
const multiplier = roll === 0 ? 48 : roll <= 2 ? 24 : roll <= 5 ? 16 : roll <= 11 ? 8 : roll <= 23 ? 4 : 2;
return { roll, multiplier };
}
// When a quicknet round is published: genesis 1692803367 (Unix seconds), one round every 3 seconds.
const roundTime = (round) => new Date((1692803367 + (round - 1) * 3) * 1000).toISOString();
// Duel's published Crash example, drand quicknet round 22842664
const serverSeed = "72ebf12bfaf8d996438fcf8a4a88c01dbb022c05f4ac831b392d196a67ff9261";
const drand =
"95812946c71502a50b85c0b6c8ad0bb821de4d5a2c011717ba0a668843295bfecd85aee347544c89cf809bad32a9d3fc";
const point = crashPoint(serverSeed, drand);
console.log("value:", drandValue(serverSeed, drand));
console.log("crash point:", point.toFixed(2), "(unrounded " + point.toFixed(6) + ")");
console.log("round 22842664 was published at", roundTime(22842664));
Output:
value: 1715376526
crash point: 2.50 (unrounded 2.501301)
round 22842664 was published at 2025-10-24T18:42:36.000Z
Duel's published example gives the same value, 1,715,376,526, and a crash point of 2.50. Duel does not document whether the displayed multiplier is rounded or cut off after two decimals; in this example both give 2.50.
For Castle Roulette, call castleRoulette with the round's server seed and drand signature. The payout table is in the roulette guide.
roundTime gives the moment a drand round was published. If you noted when the round ID was shown to you, this lets you confirm the value was still in the future at that point.
Using a browser HMAC tool
A browser HMAC tool such as cryptii is enough to check a single step by hand. Three settings matter:
- Algorithm: SHA-256.
- Key: the unhashed server seed, with the key format set to hexadecimal. Left as text, the output is wrong.
- Message:
clientSeed:nonce:cursoras plain text, for exampleUzR5CIqog4DXox5H:470:0. For Crash and Castle Roulette the message contains bytes that cannot be typed, so it has to be entered as hex. The snippet below produces it.
Then take the first 8 hex characters of the output, convert them to a decimal number and apply the game formula from the table above.
For Crash and Castle Roulette, paste this into a browser console, or run it with Node.js, to get the message as hex:
// Prints the Crash or Castle Roulette HMAC message as hex, ready to paste into an HMAC tool
const drand = "95812946c71502a50b85c0b6c8ad0bb821de4d5a2c011717ba0a668843295bfecd85aee347544c89cf809bad32a9d3fc";
const bytes = Uint8Array.from(drand.match(/.{2}/g).map((pair) => parseInt(pair, 16)));
const text = new TextDecoder().decode(bytes) + ":0";
console.log(Array.from(new TextEncoder().encode(text), (b) => b.toString(16).padStart(2, "0")).join(""));
For the Crash example above it prints a 180-character hex string that starts efbfbdefbfbd2946 and ends 3a30. The last four characters are the :0.
Troubleshooting a mismatch
| Symptom | Likely cause | Fix |
|---|---|---|
| Every result is wrong | Server seed used as a text key | Decode the seed from hex; set the key format to hex in browser tools |
| Hash does not match on the first try | Trailing space or line break in the pasted seed | Paste again; use printf, not echo |
| Hash still differs | You hashed the text and Duel hashes the bytes, or the reverse | Try the other form; the commitment script prints both |
| Hash matches, one result differs | Nonce off by one | Use the nonce shown on that bet. The counter is shared by all Originals, so do not count by hand |
| Blackjack, Mines or Plinko result differs | Wrong cursor | The cursor starts at 0 and goes up by 1 per card, row or shuffle step |
| Crash result differs | Wrong drand value | Use the signature field for the exact round ID, from the quicknet network |
| No server seed available | The pair is still active | Rotate first |
| Compared against the wrong hash | You recorded the hash of the next pair | Match each seed to the hash shown while it was active |
A true failure is narrow: exact inputs, the documented formula, and either the revealed seed does not hash to the commitment you recorded beforehand, or the recomputed result differs from your bet history. That would be serious.
If it still does not match
- Save the evidence: bet ID, both seeds, the nonce, the recorded hash with its timestamp, and your script's output.
- Ask Duel.com's 24/7 live chat to walk through the calculation for that bet ID, then follow up at support@duel.com so there is a written record.
- If you get no answer, the escalation route is third-party mediation such as Casino Guru and then the Anjouan regulator. Duel.com help describes that path.
What verification does not tell you
Provably fair proves one thing: the result was fixed before your bet and not changed afterwards.
- Whether you will win the next bet
- Whether the operator is solvent or will pay a withdrawal
- Whether slots or live dealer results are honest; those use the provider's RNG or a physical table
- Whether your bet limits or account will be changed
- Whether the payout attached to a result is good value; the proof covers the random result, and the multiplier is a separate published rule
For those questions see is Duel.com legit and run a small test withdrawal.
Summary
Save the hashed server seed, play, rotate, then verify at duel.com/fairness/verify or with your own script. Duel's documented method is HMAC-SHA256 keyed with the server seed as hex bytes, over clientSeed:nonce:cursor, with the first 4 bytes mapped by a per-game formula. Crash and Castle Roulette replace the client seed with a public drand value. The scripts on this page reproduce Duel's published examples, so a result they cannot reproduce from correct inputs is worth raising with support.
Frequently asked questions
Where do I verify a Duel.com bet?
Duel's own verifier is at duel.com/fairness/verify. Enter the server seed, client seed and nonce of a past round after rotating the seed pair. For an independent check, run the scripts on this page, which reproduce Duel's published worked examples exactly.
Can I verify a bet before the server seed is revealed?
No, not for games that use your seed pair. While a pair is active you only have the hash of the server seed, which lets you record the commitment but not recompute results. Rotate the pair to reveal the seed, then verify. Every bet placed on the old pair stays verifiable after rotation.
Why does my HMAC tool give a different result from Duel?
The usual cause is the key format. Duel's HMAC-SHA256 key is the server seed decoded from hex into 32 bytes, not the 64 characters as text. In a browser tool such as cryptii, set the key field to hexadecimal. Other causes are a nonce that is off by one or a stray space in a pasted value.
How do I verify a Duel Crash round?
Crash has no client seed. Take the round's server seed and its drand round ID, fetch that round's signature from api.drand.sh, and compute HMAC-SHA256 with the server seed as hex bytes. The crash point is 2^32 divided by the first 4 bytes plus 1, multiplied by 0.999.
Do I need special software to verify?
No. Duel's verifier runs in the browser, and an independent check needs only Node.js or Python with no extra packages. Running your own code is stronger evidence than a verifier hosted by the casino, because you control every step.
Does verifying one bet prove the whole site is fair?
It proves that seed pair was honest: one revealed server seed reproduces every bet made on it. It does not cover third-party slots or live dealer games, and it says nothing about the house edge, withdrawals or account decisions.
Duel.help Games Desk
Casino games, sportsbook & provably fair
The games desk documents game rules, house edge, RTP concepts, provably fair verification and sportsbook mechanics in plain English.
- Published
- Last reviewed
Independent and unaffiliated with Duel.com. Read our editorial policy to see how these guides are researched and reviewed.
Was this page helpful?
Feedback stays on your device and helps you track what you have already read.
Parent category
Provably Fair · 4 articles
Seeds, hashes and step-by-step verification of individual bet results.
Related articles
Next steps and adjacent topics chosen from the same part of the help center.
Provably Fair Explained: How Duel.com Verifies Game Results
How provably fair works on Duel.com Originals: the SHA-256 commitment, Duel's HMAC-SHA256 formula for each game, drand for Crash, and what the proof does not cover.
Server Seed, Client Seed and Nonce Explained
What the server seed, client seed, nonce and cursor do in a Duel.com bet, how HMAC-SHA256 combines them, and Duel's own Dice example worked through.
How to Rotate Your Seed on Duel.com
When and how to rotate your seed pair on Duel.com: record the hash, reveal the server seed, run the SHA-256 check, and verify past rounds in Duel's verifier.
Dice on Duel.com: Rules, Payouts and House Edge
How Duel Dice works: setting win chance, reading the payout multiplier, the exact maths of the house edge, how Duel's Zero Edge makes dice 100% RTP within the daily allowance, how each roll from 0.00 to 100.00 is generated and verified, and why dice is the best game for learning casino maths.
Crash on Duel.com: Rules, Strategy and House Edge
How Duel Crash works: the multiplier curve, auto cash-out, Duel's published crash-point formula and its 0.1% edge, how Zero Edge makes it 100% RTP within the daily allowance, drand-based provably fair verification, and the traps that cost players money.
Duel.com Help: Independent Help Center
Duel.com help hub: how to reach official 24/7 support (live chat and support@duel.com), what Duel.help can and cannot do, dispute escalation, and fixes for the 10 most common problems.