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 cachecontracts/
- Solidity smart contracts source codedeploy/
- Deployment scriptsdeployments/
- Deployed contract addresses and ABIs per networkfhevmTemp/
- FHEVM-specific temporary filesnode_modules/
- Dependencies installed by npmtasks/
- Custom Hardhat taskstest/
- Contract tests (likely TypeScript/JavaScript)types/
- TypeScript type definitionshardhat.config.ts
- Hardhat configuration fileLICENSE
- Project licensepackage.json
- Project configuration and dependenciespackage-lock.json
- Locked dependency versionsREADME.md
- Project documentationtsconfig.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
# 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!