Skip to content

Getting Started

Welcome to the Hello FHEVM website!
This guide will help you navigate through the platform and quickly start building private, encrypted smart contracts on FHEVM — Zama’s Fully Homomorphic Encryption–enabled EVM.

Using the Project

Explore Examples:

Here’s a modular breakdown of a simple FHEVM dice game contract

# FHEVM Hardhat Template Project Structure
fhevm-hardhat-template/
├── artifacts/
├── cache/
├── contracts/
├── deploy/
├── deployments/
├── fhevmTemp/
├── node_modules/
├── tasks/
├── test/
├── types/
├── hardhat.config.ts
├── LICENSE
├── package.json
├── package-lock.json
├── README.md
└── tsconfig.json

Directory Overview

  • artifacts/ - Compiled contract artifacts (ABIs, bytecode)
  • cache/ - Hardhat compilation cache
  • contracts/ - Solidity smart contracts source code
  • deploy/ - Deployment scripts
  • deployments/ - Deployed contract addresses and ABIs per network
  • fhevmTemp/ - FHEVM-specific temporary files
  • node_modules/ - Dependencies installed by npm
  • tasks/ - Custom Hardhat tasks
  • test/ - Contract tests (likely TypeScript/JavaScript)
  • types/ - TypeScript type definitions
  • hardhat.config.ts - Hardhat configuration file
  • LICENSE - Project license
  • package.json - Project configuration and dependencies
  • package-lock.json - Locked dependency versions
  • README.md - Project documentation
  • tsconfig.json - TypeScript configuration

Each snippet represents a key component, showing how traditional Solidity evolves to handle encrypted data with euint32 and ebool types. Imports FHE library for encrypted types (euint32 for 32-bit integers, ebool for booleans). Inherits SepoliaConfig for testnet compatibility.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
 
import {FHE, euint32, externalEuint32, ebool} from "@fhevm/solidity/lib/FHE.sol";
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
 
/// @title A privacy-preserving FHE dice game contract
contract FHEDiceGame is SepoliaConfig {

State Variables Use encrypted types (euint32, ebool) for private storage. In traditional Solidity, these would be uint32 and bool; here, encryption ensures only authorized users can decrypt off-chain.

euint32 private _lastDiceRoll;
euint32 private _playerGuess;
ebool private _isWinner;
address public owner;
uint256 public constant ENTRY_FEE = 0.0002 ether;

Constructor

This is usually same as the solidity code. Constructor Sets the deployer as owner for admin functions like withdrawals.

    constructor() {
        owner = msg.sender;
    }

View Functions (e.g., getLastDiceRoll) Returns encrypted values; decryption happens off-chain via fhevmjs. This is very similar to Getter function on solidity. but here returns opaque encrypted data for privacy.

function getLastDiceRoll() external view returns (euint32) {
    return _lastDiceRoll;
}

Derive Function: LogicImplementation

FHE.fromExternal verifies and internalizes encrypted inputs; FHE.eq compares encrypted values. Players encrypt inputs off-chain (using fhevmjs), submit proofs; contract computes winner status homomorphically without revealing data.

function getLastDiceRoll() external view returns (euint32) {
    return _lastDiceRoll;
}

Internal Computation: _generateDiceRoll Homomorphic operations (FHE.add, FHE.and) on encrypted data. Generates a pseudo-random roll (1-6) entirely encrypted, unlike traditional RNG which exposes values. This is with respect to a Dice Guess Puzzle game

function _generateDiceRoll(euint32 seed) internal returns (euint32) {
    euint32 salt = FHE.asEuint32(42);
    euint32 combined = FHE.add(seed, salt);
    euint32 mask = FHE.asEuint32(7);
    euint32 masked = FHE.and(combined, mask);
    return FHE.add(masked, FHE.asEuint32(1));

Withdraw and Receive

Similar to Solidity, perform transfer and withdrawals of assets from contracts,

function withdraw() external {
    if (msg.sender != owner) revert OnlyOwnerCanWithdraw();
    payable(owner).transfer(address(this).balance);
}
 
receive() external payable {}

Full Snippet of the smart Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
 
import {FHE, euint32, externalEuint32, ebool} from "@fhevm/solidity/lib/FHE.sol";
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
 
/// @title A privacy-preserving FHE dice game contract
/// @author fhevm-hardhat-template
contract FHEDiceGame is SepoliaConfig {
    euint32 private _lastDiceRoll;
    euint32 private _playerGuess;
    ebool private _isWinner; // Encrypted winner status
    address public owner;
 
    uint256 public constant ENTRY_FEE = 0.0002 ether;
 
    // Custom errors for gas optimization
    error IncorrectEntryFee();
    error OnlyOwnerCanWithdraw();
 
    constructor() {
        owner = msg.sender;
    }
 
    /// @notice Returns the last encrypted dice roll
    function getLastDiceRoll() external view returns (euint32) {
        return _lastDiceRoll;
    }
 
    /// @notice Returns the player's encrypted guess
    function getPlayerGuess() external view returns (euint32) {
        return _playerGuess;
    }
 
    /// @notice Returns the encrypted winner status
    function getWinnerStatus() external view returns (ebool) {
        return _isWinner;
    }
 
    /// @notice Play the dice game with encrypted inputs
    /// @param inputSeed the encrypted seed
    /// @param seedProof the seed proof
    /// @param inputGuess the encrypted guess (1-6)
    /// @param guessProof the guess proof
    function playDice(
        externalEuint32 inputSeed,
        bytes calldata seedProof,
        externalEuint32 inputGuess,
        bytes calldata guessProof
    ) external payable {
        if (msg.value != ENTRY_FEE) revert IncorrectEntryFee();
 
        // Convert external encrypted inputs to internal euint32
        euint32 encryptedSeed = FHE.fromExternal(inputSeed, seedProof);
        euint32 encryptedGuess = FHE.fromExternal(inputGuess, guessProof);
 
        // Store encrypted values
        _playerGuess = encryptedGuess;
        _lastDiceRoll = _generateDiceRoll(encryptedSeed);
 
        // Core FHE Logic: Determine if the player is a winner on-chain
        _isWinner = FHE.eq(_lastDiceRoll, _playerGuess);
 
        // Allow the player to access their own encrypted
        // values for off-chain decryption
        FHE.allowThis(_lastDiceRoll);
        FHE.allow(_lastDiceRoll, msg.sender);
        FHE.allowThis(_playerGuess);
        FHE.allow(_playerGuess, msg.sender);
        FHE.allowThis(_isWinner);
        FHE.allow(_isWinner, msg.sender);
    }
 
    /// @notice Generate a dice roll from encrypted seed
    function _generateDiceRoll(euint32 seed) internal returns (euint32) {
        euint32 salt = FHE.asEuint32(42);
        euint32 combined = FHE.add(seed, salt);
        euint32 mask = FHE.asEuint32(7);
        euint32 masked = FHE.and(combined, mask);
        euint32 one = FHE.asEuint32(1);
        euint32 result = FHE.add(masked, one);
        euint32 six = FHE.asEuint32(6);
        return FHE.min(result, six);
    }
 
    /// @notice Withdraw contract balance (owner only)
    function withdraw() external {
        if (msg.sender != owner) revert OnlyOwnerCanWithdraw();
        payable(owner).transfer(address(this).balance);
    }
 
    /// @notice Fund the contract
    receive() external payable {}
}

This helps you learn FHEVM step by step and see how private computations work on-chain.


Encryption & Decryption

Can we batch decrypt multiple transactions at once instead of individually? Yes,

Encryption/Decryption Single/Multi value

Example Encrpyt single/multi values

Decrypt single/multi values

However the game was design with user decrypt only not public and same goes for decrpyt with single value decrypt

You can implement the various functions / features, using this

https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-multiple-values

https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-multiple-values

(FYI This tutorial is based on User multi value encrypt + User single decrypt value)

Smart Contract Development: FHEVM Confidential Dice Guessing Game

Follow these corrected steps to build your own confidential dice guessing game on Hardhat using FHEVM.

Clone the Official FHEVM Hardhat Repository

Start by cloning the official FHEVM hardhat repo by Zama:

git clone https://github.com/zama-ai/fhevm-hardhat-template.git

Navigate to the Cloned Repository

cd fhevm-hardhat-template
Alternative if you want to specify a custom location:
# Navigate to your preferred directory first
cd /your-preferred/specified-location
 
# Then clone the repository
git clone https://github.com/zama-ai/fhevm-hardhat-template.git
 
# Navigate into the cloned repository
cd fhevm-hardhat-template

Install Hardhat and Dependencies

npm install

Configure Networks and RPC Endpoints

Check the hardhat.config.js or hardhat.config.ts file and configure your networks:

// Example network configuration
import "@fhevm/hardhat-plugin";
import "@nomicfoundation/hardhat-chai-matchers";
import "@nomicfoundation/hardhat-ethers";
import "@nomicfoundation/hardhat-verify";
import "@typechain/hardhat";
import "hardhat-deploy";
import "hardhat-gas-reporter";
import type { HardhatUserConfig } from "hardhat/config";
import { vars } from "hardhat/config";
import "solidity-coverage";
 
import "./tasks/accounts";
import "./tasks/FHEDiceGame";
 
// Run 'npx hardhat vars setup' 
// to see the list of variables that need to be set
 
const MNEMONIC: string = vars.get(
  "MNEMONIC",
  "zzzzzzzzzzz",
);
const INFURA_API_KEY: string = vars.get("INFURA_API_KEY", "");
 
const config: HardhatUserConfig = {
  defaultNetwork: "hardhat",
  namedAccounts: {
    deployer: 0,
  },
  etherscan: {
    apiKey: {
      sepolia: process.env.ETHERSCAN_API_KEY || "",
    },
    customChains: [
      {
        network: "sepolia",
        chainId: 11155111,
        urls: {
          apiURL: "https://api-sepolia.etherscan.io/api",
          browserURL: "https://sepolia.etherscan.io",
        },
      },
    ],
  },
 
  gasReporter: {
    currency: "USD",
    enabled: process.env.REPORT_GAS ? true : false,
    excludeContracts: [],
  },
  networks: {
    hardhat: {
      accounts: {
        mnemonic: MNEMONIC,
      },
      chainId: 31337,
    },
    anvil: {
      accounts: {
        mnemonic: MNEMONIC,
        path: "m/44'/60'/0'/0/",
        count: 10,
      },
      chainId: 31337,
      url: "http://localhost:8545",
    },
    sepolia: {
      accounts: {
        mnemonic: MNEMONIC,
        path: "m/44'/60'/0'/0/",
        count: 10,
      },
      chainId: 11155111,
      url: `https://sepolia.infura.io/v3/${INFURA_API_KEY}`,
    },
  },
  paths: {
    artifacts: "./artifacts",
    cache: "./cache",
    sources: "./contracts",
    tests: "./test",
  },
  solidity: {
    version: "0.8.27",
    settings: {
      metadata: {
        // Not including the metadata hash
        // https://github.com/paulrberg/hardhat-template/issues/31
        bytecodeHash: "none",
      },
      // Disable the optimizer when debugging
      // https://hardhat.org/hardhat-network/#solidity-optimizer-support
      optimizer: {
        enabled: true,
        runs: 800,
      },
      evmVersion: "cancun",
    },
  },
  typechain: {
    outDir: "types",
    target: "ethers-v6",
  },
};
 
export default config;

Next Steps:

After completing these setup steps, you can now:

  • Create your confidential dice guessing game smart contract
  • Use FHEVM's encrypted computation capabilities
  • Deploy and test on the Sepolia Testnet

Make sure to set up your environment variables (like PRIVATE_KEY MNEMONIC) in a .env file before deploying.


Getting Started with FHEVM

Test your understanding of the FHEVM project structure and development workflow!

Question 1 of 6Project Structure

What is the purpose of the 'contracts/' directory in an FHEVM project?