Security Audit

Our commitment to security - verified through rigorous static analysis and best-practice smart contract design

All Critical Issues: 0

Audit Summary

0
Critical Issues
0
High Severity
3
Medium (Reviewed)
8
Low / Info
5
Contracts Audited

Analysis Tool: Slither v0.11.3

All contracts analyzed using Slither, a leading Solidity static analysis framework by Trail of Bits. Slither detects vulnerabilities, optimizes code, and ensures adherence to best practices.

View Slither on GitHub

Total Lines Analyzed

BlackjackCommitReveal

1,148 lines

TexasHoldemCommitReveal

1,341 lines

RouletteCommitReveal

958 lines

PlinkoCommitReveal

530 lines

Overall Risk Assessment: LOW-MEDIUM — The commit-reveal model eliminates weak PRNG vulnerabilities. Main concerns are operational (timing windows) rather than fundamental security issues.

Audited Contracts

BlackjackCommitReveal.sol

1,148 lines
Passed
  • ReentrancyGuard modifier on all external functions
  • SafeERC20 for token transfers
  • Immutable house wallet & bet limits
  • 75 custom error types for validation

OpenZeppelin imports:

@openzeppelin/contracts/utils/ReentrancyGuard.sol@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

RouletteCommitReveal.sol

958 lines
Passed
  • House + player dual-entropy model
  • Force reveal after 512 block deadline
  • Multi-token betting support
  • Block-based round timing

OpenZeppelin imports:

@openzeppelin/contracts/utils/ReentrancyGuard.sol@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

TexasHoldemCommitReveal.sol

1,341 lines
Passed
  • All players contribute entropy
  • Side pot calculations
  • Hand evaluator separation
  • Inactivity timeout protection

OpenZeppelin imports:

@openzeppelin/contracts/utils/ReentrancyGuard.sol@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

PlinkoCommitReveal.sol

530 lines
TRUSTLESSPassed
  • NO ADMIN KEYS - fully immutable
  • 256 block reveal window
  • Automatic expired game refunds
  • Deterministic path generation

OpenZeppelin imports:

@openzeppelin/contracts/utils/ReentrancyGuard.sol@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol

CommitRevealRNG.sol

251 lines
Passed
  • Global nonce prevents replay attacks
  • MIN_REVEAL_DELAY = 1 block
  • REVEAL_WINDOW = 256 blocks
  • revealMultiple() for batch randomness

Security Architecture

Commit-Reveal RNG

Cryptographically secure randomness through a two-phase commit-reveal scheme. Players commit a hash before revealing, preventing prediction by miners or validators.

Immutable Configuration

All critical parameters (house wallet, fees, bet limits) are set at deployment and cannot be changed. No admin keys, no rug pulls possible.

Battle-Tested Libraries

Built on OpenZeppelin's audited contracts including ReentrancyGuard and SafeERC20. Solidity 0.8.28 with built-in overflow protection.

How Commit-Reveal Ensures Fairness

CommitRevealRNG.sol - Randomness Generation
// Generate randomness from multiple entropy sources:
randomness = uint256(keccak256(abi.encodePacked(
    secret,              // Player's secret (unknown to validator at commit)
    block.prevrandao,    // Unknown to player at commit time
    blockhash(c.commitBlock),  // Anchors to specific commit
    globalNonce,         // Prevents replay attacks
    player               // Unique per player
)));
1

Player Commits Secret

Before joining a game, the player generates a random secret locally and submits only the hash (commitment) to the blockchain.

commitHash = keccak256(abi.encodePacked(secret, msg.sender))
2

Wait for Block Confirmation

After at least MIN_REVEAL_DELAY = 1 block passes, the player can reveal. Maximum window is REVEAL_WINDOW = 256 blocks.

if (block.number <= c.commitBlock + MIN_REVEAL_DELAY - 1) revert CR_RevealTooEarly();
3

Reveal and Verify

The player reveals their secret, which is cryptographically verified against the original commitment.

expectedHash = keccak256(abi.encodePacked(secret, player))
if (c.commitHash != expectedHash) revert CR_InvalidReveal();

Unpredictable Outcome

The final randomness combines: player secret (unknown to validators) + block.prevrandao (unknown to players at commit time) + blockhash + nonce.

Security guarantee:

Neither party can control both inputs → Manipulation impossible

Detailed Findings Analysis

Medium Severity Findings (3)

Reviewed & Accepted

These findings are operational considerations, not exploitable vulnerabilities. We document them for transparency.

MEDIUMReveal Window Edge Cases

The 256-block reveal window could be problematic during extreme network congestion.

uint256 public constant REVEAL_WINDOW = 256; // blocks

Mitigation: Expired commits trigger automatic refunds. Players can retry with a new commit.

MEDIUMBlock Hash Availability

blockhash() only returns valid data for the most recent 256 blocks. If reveal happens at the edge of the window, blockhash could return 0.

blockhash(c.commitBlock) // Returns 0 if commitBlock is > 256 blocks old

Mitigation: REVEAL_WINDOW matches blockhash availability. Other entropy sources (secret, prevrandao, nonce) remain valid.

MEDIUMForce Reveal Timing (Roulette)

The force reveal mechanism allows anyone to execute reveals after deadline with a 1-block delay.

uint256 public constant REVEAL_DEADLINE = 512; // blocks after round end
uint256 public constant FORCE_REVEAL_DELAY = 1; // blocks to wait

Mitigation: Force reveal uses house entropy committed earlier. Attacker gains no advantage.

False Positives Explained

Static analysis tools flag patterns that appear risky but are safe in our commit-reveal context:

"Weak PRNG" WarningsFalse Positive

Slither flags modulo operations as weak randomness:

uint8 card = uint8(randomValue % 52); // Blackjack
uint8 slot = uint8(randomValue % 37); // Roulette
uint8 slot = uint8(randomValue % 13); // Plinko

Why it's safe: The entropy source is commit-reveal (player secret + block.prevrandao), not on-chain predictable values. The modulo simply maps secure randomness to valid game outcomes.

"Reentrancy" WarningsProtected

All external-facing functions use OpenZeppelin's ReentrancyGuard:

contract BlackjackCommitReveal is ReentrancyGuard {
  function commitAndJoin(...) external nonReentrant {
  function revealAndStart(...) external nonReentrant {
  // All state-changing functions protected
"Locks Ether" WarningBy Design

Blackjack has a receive() function that immediately reverts - this is intentional:

receive() external payable {
  revert BJ_CannotAcceptETH();
}

By design: These are ERC20-only games. ETH cannot be sent or locked.

Security Best Practices Implemented

ReentrancyGuard

All state-changing external functions protected against reentrancy attacks

modifier nonReentrant()

SafeERC20

Safe token transfers that handle non-standard ERC20 implementations

using SafeERC20 for IERC20

Solidity 0.8.28

Latest compiler with built-in overflow/underflow protection

pragma solidity 0.8.28

Checks-Effects-Interactions

State changes before external calls to prevent exploits

// EFFECTS before INTERACTIONS

Immutable State

Critical configuration locked at deployment - no admin functions

address public immutable houseWallet

Custom Errors

75+ custom error types for gas-efficient validation

error BJ_BetTooSmall()

Timeout Protection

Inactivity timeouts prevent funds from being locked indefinitely

INACTIVITY_TIMEOUT = 10 minutes

Refund Mechanisms

Players can reclaim funds if reveals expire or games stall

GameStatus.Expired → refund

Verify It Yourself

Run Slither Locally

# Install Slither

pip3 install slither-analyzer


# Run analysis

slither . --config-file slither.config.json

Source Code

All smart contracts are open source and verified on-chain. Review the code yourself:

Don't trust, verify. All game outcomes are deterministically derived from on-chain data. Anyone can verify any game result by replaying the commit-reveal sequence.

Last Security Analysis

December 17, 2025

Slither v0.11.3Solidity 0.8.28FoundryOpenZeppelin 5.1.0