Skip to content

FHEVM Dice Game DApp 🎲

Brief and Full summary

A privacy-preserving dice guessing game built with Zama's FHEVM (Fully Homomorphic Encryption Virtual Machine) technology. Players can make encrypted guesses and roll dice with complete privacy - only they can decrypt their own results.

CDN Based Project Structure

fhevm-dice-dapp-cdn/
β”œβ”€β”€ contracts/             # Smart contracts
β”œβ”€β”€ frontend/              # React application
β”œβ”€β”€ tasks/                 # Hardhat tasks
β”œβ”€β”€ test/                  # Contract tests
β”œβ”€β”€ deploy/                # Deployment scripts
β”œβ”€β”€ hardhat.config.ts
β”œβ”€β”€ package.json
└── README.md

SDK Based Project Structure

hello-fhevm-dice-sdk/
β”œβ”€β”€ LICENSE
β”œβ”€β”€ README.md
β”œβ”€β”€ package.json
β”œβ”€β”€ package-lock.json
β”œβ”€β”€ tsconfig.json
β”œβ”€β”€ scripts/
β”‚
└── packages/
    β”œβ”€β”€ fhevm-hardhat-template/    # Smart contracts & deployment
    β”œβ”€β”€ fhevm-react/               # FHE encryption utilities
    β”œβ”€β”€ postdeploy/                # Post-deployment scripts
    └── site/                      # React Frontend application

Game Overview

The FHEVM Dice Game is a privacy-first gambling DApp where:

  1. Players pick a number (1-6) or let the system choose randomly
  2. Entropy is generated through mouse movement for true randomness
  3. Encryption happens client-side using FHEVM SDK
  4. Computation occurs on-chain with encrypted values
  5. Results remain private until players decrypt them

Privacy Features

  • Encrypted Guesses: Your guess is encrypted before submission
  • Encrypted Dice Rolls: Generated on-chain using encrypted seeds
  • Private Results: Only you can decrypt your game outcomes
  • Verifiable Fairness: Cryptographic proofs ensure game integrity

Architecture Overview

Flowchart

Complete Encryption & Computation Flow

1. Entropy Collection & Seed Generation

Flowchart

Implementation Details:
// Mouse entropy collection in PlayDice.js
const startEntropyCapture = () => {
  const onMove = (e) => {
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    entropyRef.current.push(`${x},${y},${e.timeStamp}`);
  };
  
  // Collect for 2 seconds
  setTimeout(() => {
    const data = entropyRef.current.join('|');
    const hash = hash32(data); // FNV-1a hash
    const seedNum = hash % 1000000007; // Prime modulo
    setSeed(String(seedNum));
  }, 2000);
};

2. Client-Side Encryption Process

Flowchart Code Implementation:

// Encryption in useDiceGame.js hook
const playDice = async (seedNumber, guessNumber) => {
  // Initialize FHEVM SDK
  await window.relayerSDK.initSDK();
  const config = { ...window.relayerSDK.SepoliaConfig, network: window.ethereum };
  const fhevm = await window.relayerSDK.createInstance(config);
 
  // Encrypt seed
  const seedInput = await fhevm.createEncryptedInput(contractAddress, account);
  await seedInput.add32(seedNumber);
  const seedEnc = await seedInput.encrypt();
 
  // Encrypt guess
  const guessInput = await fhevm.createEncryptedInput(contractAddress, account);
  await guessInput.add32(guessNumber);
  const guessEnc = await guessInput.encrypt();
 
  // Submit to contract
  const tx = await contract.playDice(
    seedEnc.handles[0], seedEnc.inputProof,
    guessEnc.handles[0], guessEnc.inputProof,
    { value: entryFee }
  );
};

3. On-Chain FHE Computation

Flowchart

Smart Contract Logic:
function playDice(
    externalEuint32 inputSeed,
    bytes calldata seedProof,
    externalEuint32 inputGuess,
    bytes calldata guessProof
) external payable {
    // Convert external encrypted inputs
    euint32 encryptedSeed = FHE.fromExternal(inputSeed, seedProof);
    euint32 encryptedGuess = FHE.fromExternal(inputGuess, guessProof);
 
    // Store encrypted values
    _playerGuess = encryptedGuess;
    _lastDiceRoll = _generateDiceRoll(encryptedSeed);
 
    // FHE computation: Check if player won
    _isWinner = FHE.eq(_lastDiceRoll, _playerGuess);
 
    // Grant decryption permissions
    FHE.allow(_lastDiceRoll, msg.sender);
    FHE.allow(_playerGuess, msg.sender);
    FHE.allow(_isWinner, msg.sender);
}

4. Verifiable Fair Dice Generation

The dice generation uses a deterministic but unpredictable algorithm:

function _generateDiceRoll(euint32 seed) internal returns (euint32) {
    euint32 salt = FHE.asEuint32(42);           // Fixed salt
    euint32 combined = FHE.add(seed, salt);      // Combine with seed
    euint32 mask = FHE.asEuint32(7);            // Mask for modulo
    euint32 masked = FHE.and(combined, mask);    // Apply mask
    euint32 one = FHE.asEuint32(1);             // Add 1 (shift 0-7 to 1-8)
    euint32 result = FHE.add(masked, one);       // Get 1-8 range
    euint32 six = FHE.asEuint32(6);             // Cap at 6
    return FHE.min(result, six);                 // Return 1-6
}
Why This Is Fair:
  • Deterministic: Same seed always produces same result
  • Unpredictable: Player provides seed via mouse entropy
  • Transparent: Algorithm is public on blockchain
  • Cryptographically Secure: FHE operations prevent manipulation

5. Decryption & Result Verification

Flowchart

Decryption Implementation:
const userDecryptValue = async (ciphertextHandle, valueType) => {
  // Initialize FHEVM instance
  const instance = await window.relayerSDK.createInstance(config);
  
  // Generate keypair for decryption
  const keypair = instance.generateKeypair();
  
  // Create EIP-712 signature for authorization
  const eip712 = instance.createEIP712(keypair.publicKey, ...);
  const signature = await signer.signTypedData(eip712.domain, eip712.types, eip712.message);
  
  // Perform decryption
  const result = await instance.userDecrypt(
    [{ handle: ciphertextHandle, contractAddress }],
    keypair.privateKey,
    keypair.publicKey,
    signature,
    ...
  );
  
  return valueType === 'bool' ? Boolean(result[ciphertextHandle]) : Number(result[ciphertextHandle]);
};

Security & Privacy Features

1. Input Privacy

  • Player guesses are encrypted client-side
  • Seeds are generated from unpredictable mouse movement
  • No plaintext values are transmitted

2. Computation Privacy

  • All operations happen on encrypted data
  • Contract never sees plaintext values
  • Results are computed homomorphically

3. Access Control

// Only the player can decrypt their own results
FHE.allow(_lastDiceRoll, msg.sender);
FHE.allow(_playerGuess, msg.sender);
FHE.allow(_isWinner, msg.sender);

4. Fairness Verification

// Frontend tracks fairness proof
setFairness({ 
  seed: seedNumber, 
  guess: guessNumber, 
  commitment: sha256(seed),
  txHash: tx.hash 
});

Technical Implementation

Frontend Architecture

Key Components:
  • PlayDice.js: Handles user interaction, entropy collection, and game submission
  • DiceResults.js: Manages encrypted result display and decryption
  • useDiceGame.js: Custom hook managing contract interactions and FHEVM SDK
  • useWallet.js: Wallet connection and network management
State Management Flow:
// Game state in useDiceGame hook
const [encryptedState, setEncryptedState] = useState({
  lastDiceRoll: '0x',
  playerGuess: '0x', 
  winnerStatus: '0x'
});
 
const [decryptedState, setDecryptedState] = useState({
  lastDiceRoll: null,
  playerGuess: null,
  winnerStatus: null
});

Smart Contract Architecture

Core State Variables:
euint32 private _lastDiceRoll;    // Encrypted dice result
euint32 private _playerGuess;     // Encrypted player guess  
ebool private _isWinner;          // Encrypted win status
Access Pattern:
  1. Play: Submit encrypted inputs with entry fee
  2. Compute: Generate dice roll and check winner (all encrypted)
  3. Store: Save encrypted results with player access permissions
  4. Decrypt: Player can decrypt their own results off-chain

Prerequisites & Setup

Requirements:

  • Node.js 18+ (LTS version)
  • MetaMask wallet
  • Sepolia testnet ETH

Quick Start:

# 1. Clone and setup
git clone https://github.com/chimmykk/fhevm-dice-dapp
cd fhevm-dice-dapp
npm install
 
# 2. Configure environment
cp .env.example .env
# Add your MNEMONIC and INFURA_API_KEY
 
# 3. Build and test
npx hardhat compile
npx hardhat test
 
# 4. Deploy (choose one)
# Local development:
npx hardhat node  # Terminal 1
npx hardhat deploy --network localhost  # Terminal 2
 
# Or deploy to Sepolia:
npx hardhat deploy --network sepolia
 
# 5. Start frontend
cd frontend
npm install
npm start

How to Play

  1. Connect Wallet: Connect MetaMask to Sepolia testnet
  2. Generate Entropy: Hover mouse over dice area for 2 seconds
  3. Make Guess: Click a dice face (1-6) or let system choose randomly
  4. Roll Dice: Click "Roll Dice" to submit encrypted guess
  5. Decrypt Results: Click "Decrypt All" to reveal your outcome

Testing & Verification

Automated Testing:

npx hardhat test

Manual Testing:

# Play via CLI (choose your network)
# For Sepolia testnet:
npx hardhat --network sepolia task:play-dice --guess 4 --seed 12345
 
# For local hardhat node:
npx hardhat --network localhost task:play-dice --guess 4 --seed 12345
 
# View results
# Use the frontend to decrypt and verify results

Frontend Testing:

  • Test wallet connection
  • Verify entropy collection
  • Check encryption/decryption flow
  • Validate game fairness

🎲 FHEVM Dice Game Mastery

Test your understanding of the complete FHEVM dice game implementation!

Question 1 of 6Randomness & Entropy

How is randomness generated in the FHEVM dice game?


Live Demo

  • Deployed Contract (Sepolia):

    • CDN Version: 0xA6915c97f44f1708e37dD871CCE991fD6D45E943
    • SDK Version: 0x31630746e870D3D57e8B708ac479f269e1Da4c10
  • Frontend Demos:

Learn More

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

Built with ❀️ using Zama FHEVM - Where Privacy Meets Smart Contracts