Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
This guide helps you set up the Zama Plugin in the official Remix IDE, enabling seamless development and management of smart contracts using the fhEVM.
Before starting, make sure you have the following:
A web browser (such as Chrome, Firefox).
Basic familiarity with Ethereum smart contracts.
A crypto wallet (we recommend using MetaMask for this tutorial).
Zama Plugin allows you to interact with confidential contracts directly within Remix. To set it up:
Open the Remix IDE by navigating to https://remix.ethereum.org.
In the left sidebar, click on the Plugin Manager.
In the Plugin Manager, select Connect to a Local Plugin.
Use the following configurations:
Plugin Name (required) : Enter Zama
.
URL(required): Enter https://remix.zama.ai/
.
Type of connection(required): Select Iframe
Location in remix (required):Select Side Panel
Click OK
.
Now that you've configured the plugin, you are able to deploy and interact with fhEVM encrypted contracts directly directly via Remix interface. Next, let's dive into the contract deployment.
This tutorial guides you to start quickly with Zama’s Fully Homomorphic Encryption (FHE) technology for building confidential smart contracts.
In about 20 minutes, you will:
Build your first confidential ERC20 contract that leverages FHE.
Deploy the contract on the Sepolia Network.
Mint tokens and perform transactions in FHE.
Build a frontend application for your contract.
A basic understanding of Solidity library and Ethereum.
A certain amount of Sepolia ETH available.
If you don’t have enough ETH, use a Sepolia faucet to request free SepoliaETH for testing such as Alchemy Faucet or QuickNode Faucet.
The contract that you will build with this tutorial is called ConfidentialERC20Mintable
— a privacy-preserving ERC20 implementation that leverages FHE to keep balances and transactions confidential. To understand this contract, let’s first introduce the foundational concepts.
RC20
ERC20 is a widely used token standard on Ethereum that defines a set of rules for creating and managing fungible tokens. These tokens are efficient but lack privacy — balances and transactions are visible to anyone on the blockchain.
Confidential ERC20
Zama’s ConfidentialERC20
introduces privacy to ERC20 tokens by storing balances and transactions in an encrypted format using FHE.
The ConfidentialERC20
contract still supports standard ERC20 functions such as transfer
, approve
, transferFrom
, balanceOf
, and totalSupply
but ensures these operations are processed securely with encrypted data.
To explore the implementation details of ConfidentialERC20, check out the Zama blog post.
Confidential ERC-20 Mintable
The contract that we will build in this tutorial is ConfidentialERC20Mintable
. It's built on top of ConfidentialERC20
by adding secure minting capabilities. This allows authorized accounts to create new tokens, while maintaining the privacy guarantees of encrypted balances and transactions.
The ConfidentialERC20Mintable
contract ensures:
Enhanced privacy: Balances are stored as encrypted values (euint64
), preventing public inspection of account balances.
Secure transactions: Token transfers are processed securely, maintaining confidentiality of amounts.
Owner visibility: Only account owners can decrypt and view their balances.
Choose your path and get started:
Remix Guide – Rapid in‐browser setup, great for learning and fast prototyping.
Hardhat Guide – Full-fledged development environment, suitable for production.
This tutorial covers the essentials steps to quickly write and deploy confidential ERC20 smart contract using the Zama Plug in.
Remix is a powerful in-browser IDE for Ethereum smart contract development. It offers a fast, intuitive way to write and deploy confidential ERC20 contracts using Zama’s fhEVM plugin—no local setup required.
In this tutorial, you will learn to:
Set up Remix for fhEVM development.
Connect your wallet (e.g., MetaMask) to Remix.
Deploy a ConfidentialERC20 contract.
Interact with your deployed contract directly in the Remix interface.
By the end, you’ll have a working confidential ERC20 token on Sepolia network and know how to perform encrypted transactions.
fhEVM is a technology that enables confidential smart contracts on the EVM using Fully Homomorphic Encryption (FHE).
Learn the basics of fhEVM, set it up, and make it run with ease.
Start developing fhEVM smart contracts in Solidity by exploring its core features, discovering essential guides, and learning more with user-friendly tutorials.
Access to additional resources and join the Zama community.
Explore the technical architecture of the fhEVM protocol and the underlying cryptographic principles that power it.
Refer to the API and access additional resources for in-depth explanations while working with fhEVM.
Ask technical questions and discuss with the community. Our team of experts usually answers within 24 hours in working days.
Collaborate with us to advance the FHE spaces and drive innovation together.
Zama 5-Question Developer Survey
We want to hear from you! Take 1 minute to share your thoughts and help us enhance our documentation and libraries. 👉 Click here to participate.
Hardhat is a flexible, extensible development environment for Ethereum, providing a robust toolkit for compiling, testing, and deploying smart contracts. With Zama’s fhEVM integration, you can develop confidential ERC20 contracts locally and then seamlessly deploy them to a real fhEVM node.
In this tutorial, you will learn to:
Write confidential contracts utilizing encrypted data types.
Test your contracts in mocked mode (for rapid feedback and coverage).
Deploy your confidential ERC20 contract to the Sepolia test network.
Interact with your deployed contract, including performing encrypted transfers.
By following these steps, you’ll get familiar with the complete development workflow using Hardhat, and be able to build, test and deploy confidential ERC20 smart contracts on Sepolia testnet.
This guide will walk you through installing all necessary dependencies from scratch, preparing you for developing and deploying fhEVM smart contracts using Hardhat.
To use Hardhat to build and deploy confidential smart contract, you need to have the following:
Node.js (v20 or later)
A package manager: npm
, yarn
, or pnpm
Git for version control
Foundry for using the command cast
To run the installation commands, open a terminal:
Use your system's built-in terminal (e.g., Terminal on macOS, Command Prompt or PowerShell on Windows, or a Linux shell).
Instead of installing Node.js directly, we recommend using Node Version Manager (nvm
), which allows you to manage multiple versions of Node.js:
Install nvm
following the recommended tutorials:
Install Node.js using nvm. This installs the latest version of Node.js:
Install pnpm (optional but recommended for better speed and efficiency). After installing Node.js, you automatically have npm, so run:
Alternatively, you can continue using npm
or yarn
if you prefer.
Git is required for managing source code, cloning repositories, and handling version control. Install Git based on your operating system:
Linux: Use your distribution's package manager (e.g., sudo apt-get install git
on Ubuntu/Debian).
We will need the command cast
in our tutorial. This command is available with installing Foundry.
Install Foundry using the official installer:
Reload your terminal configuration:
Run the Foundry installer:
This will install all Foundry components, including cast
, forge
, anvil
, and chisel
.
To verify the installation, run:
In this guide, you'll learn how to connect your wallet and the Zama Plugin in Remix IDE to interact with fhEVM smart contracts.
Before starting, ensure you have the following:
MetaMask or another Ethereum-compatible wallet installed.
Note thate when using Remix to connect a wallet, issues may arise if multiple wallet extensions are installed (e.g., MetaMask, Phantom). This is a known issue of Remix that can affect wallet connection functionality. If you encounter errors, consider keeping only MetaMask as the active wallet extension, removing other wallet extensions, and refreshing Remix cookies and try to reconnect.
If you're using Metamask, the Sepolia testnet should be pre-configured. Follow the steps to set it up:
Open MetaMask.
Click the network dropdown at the top left corner, and select Sepolia Test Network.
Ensure you have Sepolia ETH available. If you don’t have enough ETH, use a Sepolia faucet to request free SepoliaETH for testing:
If Sepolia isn’t visible:
Go to Settings > Advanced.
Toggle Show test networks to ON.
If Sepolia isn’t pre-configured in your wallet, add it manually:
Open your wallet’s network settings.
Click Add Network or Add Network Manually.
Enter the following details:
Network Name: Sepolia
RPC URL: (provided by your node provider, e.g., Alchemy or Infura)
Chain ID: 11155111
Currency Symbol: SepoliaETH
Block Explorer URL: https://sepolia.etherscan.io
Zama Plugin provides the Zama Coprocessor - Sepolia configuration that ensures Remix and the wallet are properly set up to interact with fhEVM smart contracts.
To complete the configuration:
Open the Zama Plugin in Remix from the side pannel.
Click Connect your wallet.
Confirm the connection in MetaMask.
In the Zama Plugin, select Zama Coprocessor - Sepolia.
Click Use this configuration to finalize the setup.
Once successful, you should see the green text in the terminal indicating that the configuration is ready.
Follow the steps to connect your wallet to Remix:
Open Remix and navigate to Deploy & run transactions.
Under Environment, select Injected Provider - MetaMask.
MetaMask will prompt a connection request. Click Connect to proceed.
Choose your wallet address in Account.
Now that your wallet is connected and your SepoliaETH balance is ready, you can proceed to deploy the ConfidentialERC20Mintable
contract!
This tutorial covers the essential steps to quickly write and deploy a confidential ERC20 smart contract using the .
Set up a Hardhat project with the .
If you already have these installed, skip ahead to
Alternatively, use the integrated terminal in a IDE such as .
VS code provide the official for an enhanced development experience with Hardhat.
Windows: .
macOS: Install via with brew install git
or .
You now have all the necessary dependencies installed. Continue to to begin creating and deploying your FHE smart contracts!
Zama Plugin installed in Remix IDE ()
After deploying your first fhEVM contract using Hardhat, this guide shows you how to interact with it using Hardhat tasks.
Before interacting with your deployed contract, ensure the following:
Deployment completed: You have successfully deployed the MyConfidentialERC20
contract (see previous section)
Contract address: You have saved the deployed contract address
Sepolia ETH: You have some Sepolia ETH in your wallet for transaction fees
Environment setup: Your .env
file is properly configured with your private key
First, let's mint some confidential tokens to your account:
Once successful, you'll see a transaction confirmation in the terminal that looks like this:
You can check the total supply of tokens to confirm your mint was successful:
Once successful, you'll see a transaction confirmation in the terminal:
To verify your account balance:
Note to remind yourself what was are the private keys of the accounts of the MNEMONIC SEED you can always check it by running:
Once successful, you'll see a transaction confirmation in the terminal.
To transfer confidential tokens to another account:
Once successful, you'll see a transaction confirmation in the terminal.
After the transfer, you can check both accounts balances:
If both balances have changed accordingly the transaction was successful.
Here's a complete list of available Hardhat tasks for interacting with your contract:
mint
: Mint new encrypted tokens
transfer
: Transfer encrypted tokens to another address
balance
: Check encrypted balance of an account
totalSupply
: Get the total token supply
For detailed help on any task, run:
🎉 Congratulations on completing this tutorial! You’ve taken the first step in building confidential smart contracts using fhEVM. It's time now to take the next step and build your own confidential dApps!
To continue your journey and deepen your knowledge, explore the resources below.
Read the white paper: Understand the core technology behind fhEVM, including its cryptographic foundations and use cases.
See more demos and tutorials: Expand your skills with hands-on demos and tutorials crafted to guide you through various real-world scenarios.
Try out AI coding assistant: If you have a ChatGPT plus account, try out our custom ChatGPT model tailored for Solidity and fhEVM developers.
Use out-of-box templates and frameworks designed for developers to build confidential dApps easily.
Smart contract development
Hardhat Template: A developer-friendly starting point for building and testing smart contracts on fhEVM.
fhEVM Contracts Library: Access standardized contracts for encrypted operations.
Frontend development
React.js Template: Quickly develop FHE-compatible dApps using a clean React.js setup.
Next.js Template: Build scalable, server-rendered dApps with FHE integration.
Vue.js Template: Develop responsive and modular dApps with FHE support in Vue.js.
Join the community to shape the future of blockchain together with us.
Discord: Join the community to get the latest update, have live discussion with fellow developers and Zama team.
Community Forum: Get support on all technical questions related to fhEVM
Zama Bounty Program: Participate to tackle challenges and earn rewards in cash.
This guide walks you through setting up your Hardhat environment using Zama's fhEVM Hardhat template.
Open the repository: Zama fhEVM Hardhat Template
Click "Use this template" to create a new repository under your GitHub account.
Clone the repository you have created to your local environment
Copy the repository URL of your newly created repository.
Open a terminal and run:
Copy the environment configuration template:
Install project dependencies: Depending on your package manager, run one of the following:
contracts/
: Write your Solidity smart contracts here.
test/
: Place your test scripts for smart contract testing.
deploy/
: Deployment scripts for deploying your contracts.
hardhat.config.js
: The pre-configured Hardhat setup file for deploying on Sepolia.
.env
: The environment file that stores sensitive or environment-specific variables such as private keys and API keys.
To learn more about Hardhat, refer to the official Hardhat documentation.
You are now ready to start building your confidential smart contracts with fhEVM!
This tutorial walks you through performing tests in the mocked mode provided by the fhEVM Hardhat template.
Before proceeding, ensure you have:
A configured Hardhat project using the fhEVM Hardhat template. (See the previous section)
Basic knowledge of Solidity and Hardhat testing. (See the Hardhat testing documentation)
fhEVM provides a mocked mode in Hardhat that allows for:
Faster testing on a local Hardhat network.
The ability to analyze code coverage.
A simulated version of encrypted types (they are not truly encrypted).
Access to Hardhat features such as snapshots (evm_snapshot
), time manipulation (evm_increaseTime
), and debugging (console.log
).
To learn more about the fhEVM mocked mode, refer to the README in the fhEVM Hardhat template repository.
To run tests in mocked mode, open a terminal in your project's root directory and execute:
This command runs all tests locally in mocked mode. You should see the test results in your console.
For most development and demonstration scenarios, mocked mode is sufficient. However, for production-ready development and a real testing environment, you need to run your tests on a real network where the coprocessor is deployed for example Sepolia. Refer to the next section on how to deploy your contract on Sepolia test network.
This document explains how to write confidential smart contract using fhEVM in Hardhat projects.
Before proceeding, ensure you have:
A working Hardhat environment set up (see previous section).
Basic knowledge of Solidity.
An understanding of ERC20 tokens.
The Hardhat template includes an example contract in the contracts/
folder - MyConfidentialERC20.sol
. This contract enables:
Private ERC20 token transfers.
Encrypted balances.
Minting functionality for authorized addresses.
Let's break down the contract.
TFHE.sol
: The core Solidity library of Zama's fhEVM. It enables encrypted data type like euint64
, secures encrypted operations, such as addition and comparison and allows access control.
SepoliaZamaFHEVMConfig
: A configuration contract that automatically sets up the required configurations for real-time encrypted operations on the Sepolia testnet.
ConfidentialERC20Mintable.sol
: The confidential smart contract that allows for full ERC20 compatibility with FHE encryption.
This contract inherits SepoliaZamaFHEVMConfig
and ConfidentialERC20Mintable
.
The constructor initializes the ERC20 token with a name and symbol, setting the deployer as the initial owner.
This is a simple basic contract that we will deploy and use in this tutorial. To write more complex confidential smart contracts or customize your own functions:
Explore the full range of fhEVM capabilities in the Smart Contract section.
Use the fhevm-contracts
library and extend from the basic contract templates.
The fhevm-contracts is a Solidity library designed for developers to easily develop confidential smart contracts using fhEVM. It provides:
Ready-to-use confidential contracts: Pre-built implementations of common token standards with FHE capabilities
Base contracts: Foundational building blocks for creating custom confidential smart contracts
Extensions: Additional features and utilities that can be added to base contracts
Testing utilities: Tools to help test FHE-enabled smart contracts
See more details in the fhEVM-contracts documentation.
Your contract is ready! Let's move on to testing and deployment.
fhEVM is a suite of solutions that enables confidential smart contracts on the EVM using Fully Homomorphic Encryption (FHE). This document provides a high-level overview of the fhEVM suite along with onboarding guidance tailored to specific audiences.
The fhEVM Protocol provides a TFHE
Solidity library for building confidential smart contracts, a fhevm.js
Javascript library to enable front‐end FHE interactions, and a range of developer tools, examples, and templates to streamline the usage for developers.
Solidity library for FHE operations (e.g., encryption/decryption, arithmetic) within smart contracts.
Hardhat template with scripts for compiling, deploying, and testing FHE‐enabled contracts.
fhevm-foundry-template - coming soon
Foundry template for building FHE smart contracts.
Ready‐to‐use FHE smart contract example covering finance, governance, and ERC‐20 tokens use cases.
JavaScript library for client‐side FHE, enabling encryption, decryption, and data handling.
React.js template to quickly spin up FHE‐enabled dApps.
Next.js template for integrating FHE in server‐side rendered or hybrid web apps.
Vue.js template for creating privacy‐preserving dApps with encrypted data
Sample decentralized applications demonstrating FHE with real‐world code.
Explore open challenges and submit contributions to earn rewards.
A curated list by the team at Zama of blog posts, libraries, research papers, and tutorials on Fully Homomorphic Encryption (FHE).
To integrate FHE at the protocol level or operate an FHE‐enabled network, fhEVM offers the fhevm backend modules. These repositories include the foundational implementations that enables FHE in blockchain systems, ensuring that privacy remains at the core of your network architecture.
Rust backend & Go‐Ethereum modules, enabling native or coprocessor‐based FHE.
Go implementation of the FHE Virtual Machine
Modified go-ethereum with enhanced FHE support
In this tutorial, you'll learn how to deploy a confidential token contract using Zama's fhEVM. We'll create MyConfidentialERC20.sol
to demonstrate the essential features.
Ensure the following before deploying the smart contract:
The Zama Plugin installed is installed in the Remix IDE(see Step 1).
Your wallet is connected to the Sepolia testnet(see Step 2).
First, let's create a file for our confidential ERC20 contract:
Open the File explorer from the side menu.
Navigate to the contracts folder.
Click the Create new file icon.
Name the file MyConfidentialERC20.sol
and press Enter.
The foundational structure includes importing Zama's libraries and connecting to Sepolia's fhEVM configuration.
Copy the following code in the MyConfidentialERC20.sol
that you just created:
It should appear as follows:
Remix automatically saves any changes as you type. Upon saving, it imports the following libraries:
TFHE.sol
: The core Solidity library of Zama's fhEVM. It enables encrypted data type like euint64
, secures encrypted operations, such as addition and comparison and allows access control.
SepoliaZamaFHEVMConfig
: A configuration contract that automatically sets up the required configurations for real-time encrypted operations on the Sepolia testnet.
Next, we'll enhance our contract by importing the fhevm-contracts
library.
The fhevm-contracts is a Solidity library designed for developers to easily develop confidential smart contracts using fhEVM. It provides:
Ready-to-use confidential contracts: Pre-built implementations of common token standards with FHE capabilities
Base contracts: Foundational building blocks for creating custom confidential smart contracts
Extensions: Additional features and utilities that can be added to base contracts
Testing utilities: Tools to help test FHE-enabled smart contracts
See more details in the fhEVM-contracts documentation.
The fhevm-contracts
library includes the ConfidentialERC20Mintable
contract, which is an extention of ConfidentialERC20
with minting capabilities, providing:
Private token transfers and encrypted balances
Minting functionality for authorized addresses
Full ERC20 compatibility
It inherits all base ConfidentialERC20
features while adding secure token creation and distribution capabilities.
To use ConfidentialERC20Mintable
contract, simply update your MyConfidentialERC20.sol
with the following code:
It should appear as follows:
Now the contract is ready, the next step is to compile it:
Select MyConfidentialERC20.sol
.
Go to Solidity compiler in Remix.
Click Compile.
If successful, you will see a green checkmark on the Solidity Compiler, indicating "Compilation successful"
Now the contract is ready to be deployed:
Make sure that the envrionment is set up properly
Envrionment: Injected Provider - Metamask
Account: Your wallet address
Expand the Deploy section.
Fill the constructor parameters:
Name: Your token’s name (e.g., "My Private Token").
Symbol: Token symbol (e.g., "MPT").
Click Transact and confirm the transaction in MetaMask.
Once successfully deployed, your contract will appear under Deployed Contracts. You can also view your contract on Etherscan by clicking the contract address.
By following these steps, you’ve successfully created and deployed an confidential ERC-20 token using Zama's fhEVM!🎉 Let's see how the transaction works in the next chapter.
After deploying your first fhEVM contract using Remix, this guide shows you how to interact with it directly in Remix using the Zama Plugin.
Before interacting with your deployed contract, ensure the following:
Deployment completed: You have successfully deployed the MyConfidentialERC20
contract (see Step 3).
MetaMask wallet: Your MetaMask wallet is connected to the Sepolia testnet(see Step 2). You might want to prepare an additional wallet as the receiver to mock the transfer function.
Zama Plugin in Remix: The Zama Plugin is installed and accessible in Remix (see Step 1).
To perform transactions directly in Remix, your contract needs to be connected to the Zama Plugin:
Open Deploy & run transaction from the side bar
In "Deployed Contract", copy the address of the MYCONFIDENTIALERC20
contract that you just deployed.
Open the Zama Plugin from the side menu.
Paste the contract address into the "At address" field under the Deploy section.
Click At address.
If the address was entered correctly, the MyConfidentialERC20.sol
contract will be displayed in the "Deployed Contract" inside the Zama Plugin.
Click to expand the contract, you'll see the interface to interact with all the functions of your contract. ConfidentialERC20Mintable
supports all standard ERC-20 functions, but adapted for encrypted values, including:
transfer
: Securely transfers encrypted tokens.
approve
: Approves encrypted amounts for spending.
transferFrom
: Transfers tokens on behalf of another address.
balanceOf
: Returns the encrypted balance of an account.
totalSupply
: Returns the total token supply.
From here, you can mint confidential ERC20 token to your account:
Copy your wallet address from MetaMask.
Inside Zama Plugin, click to expand the mint function of your contract.
Enter your wallet address and the amount of tokens to mint (e.g., 1000
).
Click Submit
.
Confirm the transaction in MetaMask.
Once sccussful, you should see the message in the terminal.
After a successful mint transaction, click the totalSupply to reflect the minted tokens (e.g., 1000
).
To verify your account balance:
Click to expand the balanceOf function.
Enter your wallet address.
Click Submit
to verify your account balance.
Your balance is stored as encrypted data using FHE. No one else can view if except you.
To view the balance in plaintext:
Click the re-encrypt option
Confirm the transaction in Metamask
You can see that the ciphertext is decrypted and your balance is the amount that you just minted.
To transfer confidential ERC20 tokens to another account:
Copy the address of the receiver's wallet.
Click transfer to expand the function, set the following parameters:
To: Enter the wallet address of reveiver.
encryptedAmount: Specify the amount that you want to transfer (e.g.1000
). Choose euint64
.
inputProof: Check the input box.
Click Submit to proceed.
Confirm the transaction in MetaMask.
If successful, you should see the message in the terminal.
After making a transfer, you can verify your updated account balance:
Use the balanceOf function again to check the updated balance of your original account (see the Step 5: Check your balance.)
Perform re-encryption to confirm the changes, you should see the remaining token in your account.(e.g., 900
tokens remaining).
Always re-encrypt to validate ciphertext transformations and confirm operations.
🎉 Congratulations on completing this tutorial! You’ve taken the first step in building confidential smart contracts using fhEVM. It's time now to take the next step and build your own confidential dApps!
To continue your journey and deepen your knowledge, explore the resources below.
Read the Whitepaper: Understand the core technology behind fhEVM, including its cryptographic foundations and use cases.
See more demos and tutorials: Expand your skills with hands-on demos and tutorials crafted to guide you through various real-world scenarios.
Try out AI coding assistant: If you have a chatGPT plus account, try out our custom ChatGPT model tailored for Solidity and fhEVM developers.
Use out-of-box templates and frameworks designed for developers to build confidential dapps easily.
Smart contract development
Hardhat Template: A developer-friendly starting point for building and testing smart contracts on fhEVM.
fhEVM Contracts Library: Access standardized contracts for encrypted operations.
Frontend development
React.js Template: Quickly develop FHE-compatible dApps using a clean React.js setup.
Next.js Template: Build scalable, server-rendered dApps with FHE integration.
Vue.js Template: Develop responsive and modular dApps with FHE support in Vue.js.
Join the community to shape the future of blockchain together with us.
Discord: Join the community to get the latest update, have live discussion with fellow developers and Zama team.
Community Forum: Get support on all technical questions related to fhEVM
Zama Bounty Program: Participate to tackle challenges and earn rewards in cash.
This guide walks you through deploying your Confidential ERC20 smart contract on the Sepolia test network.
Before proceeding, ensure you have:
A configured Hardhat project using the fhEVM Hardhat Template (see previous sections).
A crypto wallet installed (e.g., Metamask).
Some Sepolia ETH available for testing. If you don’t have enough ETH, use a Sepolia faucet to request free SepoliaETH for testing:
Generate a mnemonic seed for accounts using cast
:
(cast
is a Foundry function. If you don't have Foundry installed, run curl -L https://foundry.paradigm.xyz | bash
first):
Obtain a Sepolia RPC URL:
Sign up for a node provider such as Alchemy - https://www.alchemy.com/
or Infura - https://infura.io/
. Copy your Sepolia RPC URL.
Open the .env
file:
Past your mnemonic in the code:
MNEMONIC=<Your mnemonic generated>
Paste a Sepolia RPC URL:
SEPOLIA_RPC_URL=<Your node provider URL>
Verify generated accounts:
Open your wallet (e.g, MetaMask)
Import the first 2 accounts (e.g., Alice and Bob) into your wallet with their private keys.
Fund these 2 accounts with some Sepolia ETH.
In the deploy/
directory, there is a preconfigured deploy.ts
file that handles the deployment process. You can customize it or add your own scripts.
To deploy the contracts to Sepolia, run:
Once deployment is successful, you should see a console output that includes the contract address such as:
You can verify this contract on Sepolia Etherscan by searching for the deployed address.
Congratulations! 🎉 You have deployed your first confidential ERC20 smart contract. Let's mint a few tokens and perform some encrypted transactions!
This document introduces the encrypted integer types provided by the TFHE
library in fhEVM and explains their usage, including casting, state variable declarations, and type-specific considerations.
The TFHE
library offers a robust type system with encrypted integer types, enabling secure computations on confidential data in smart contracts. These encrypted types are validated both at compile time and runtime to ensure correctness and security.
Encrypted integers function similarly to Solidity’s native integer types, but they operate on Fully Homomorphic Encryption (FHE) ciphertexts.
Arithmetic operations on e(u)int
types are unchecked, meaning they wrap around on overflow. This design choice ensures confidentiality by avoiding the leakage of information through error detection.
Future versions of the TFHE
library will support encrypted integers with overflow checking, but with the trade-off of exposing limited information about the operands.
Encrypted integers with overflow checking will soon be available in the TFHE
library. These will allow reversible arithmetic operations but may reveal some information about the input values.
Encrypted integers in fhEVM are represented as FHE ciphertexts, abstracted using ciphertext handles. These types, prefixed with e
(for example, euint64
) act as secure wrappers over the ciphertext handles.
The TFHE
library currently supports the following encrypted types:
Higher-precision integer types are available in the TFHE-rs
library and can be added to fhEVM
as needed.
This document provides an overview of key features of the fhEVM smart contract library.
Smart contracts using fhEVM require proper configuration and initialization:
Environment setup: Import and inherit from environment-specific configuration contracts
Gateway configuration: Configure secure gateway access for cryptographic operations
Initialization checks: Validate encrypted variables are properly initialized before use
fhEVM introduces encrypted data types compatible with Solidity:
Booleans: ebool
Unsigned Integers: euint4
, euint8
, euint16
, euint32
, euint64
, euint128
, euint256
Addresses: eaddress
Bytes: ebytes64
, ebytes128
, ebytes256
Input: einput
for handling encrypted input data
Encrypted data is represented as ciphertext handles, ensuring secure computation and interaction.
fhEVM provides functions to cast between encrypted types:
Casting between encrypted types: TFHE.asEbool
converts encrypted integers to encrypted booleans
Casting to encrypted types: TFHE.asEuintX
converts plaintext values to encrypted types
Casting to encrypted addresses: TFHE.asEaddress
converts plaintext addresses to encrypted addresses
Casting to encrypted bytes: TFHE.asEbytesX
converts plaintext bytes to encrypted bytes
fhEVM enables symbolic execution of encrypted operations, supporting:
Arithmetic: TFHE.add
, TFHE.sub
, TFHE.mul
, TFHE.min
, TFHE.max
, TFHE.neg
, TFHE.div
, TFHE.rem
Note: div
and rem
operations are supported only with plaintext divisors
Bitwise: TFHE.and
, TFHE.or
, TFHE.xor
, TFHE.not
, TFHE.shl
, TFHE.shr
, TFHE.rotl
, TFHE.rotr
Comparison: TFHE.eq
, TFHE.ne
, TFHE.lt
, TFHE.le
, TFHE.gt
, TFHE.ge
Advanced: TFHE.select
for branching on encrypted conditions, TFHE.randEuintX
for on-chain randomness.
fhEVM enforces access control with a blockchain-based Access Control List (ACL):
Persistent access: TFHE.allow
, TFHE.allowThis
grants permanent permissions for ciphertexts.
Transient access: TFHE.allowTransient
provides temporary access for specific transactions.
Validation: TFHE.isSenderAllowed
ensures that only authorized entities can interact with ciphertexts.
This document outlines the operations supported on encrypted types in the TFHE
library, enabling arithmetic, bitwise, comparison, and more on Fully Homomorphic Encryption (FHE) ciphertexts.
The following arithmetic operations are supported for encrypted integers (euintX
):
Division (TFHE.div) and remainder (TFHE.rem) operations are currently supported only with plaintext divisors.
The TFHE library also supports bitwise operations, including shifts and rotations:
The shift operators TFHE.shr
and TFHE.shl
can take any encrypted type euintX
as a first operand and either a uint8
or a euint8
as a second operand, however the second operand will always be computed modulo the number of bits of the first operand. For example, TFHE.shr(euint64 x, 70)
is equivalent to TFHE.shr(euint64 x, 6)
because 70 % 64 = 6
. This differs from the classical shift operators in Solidity, where there is no intermediate modulo operation, so for instance any uint64
shifted right via >>
would give a null result.
Encrypted integers can be compared using the following functions:
The TFHE.select
function is a ternary operation that selects one of two encrypted values based on an encrypted condition:
You can generate cryptographically secure random numbers fully on-chain:
Example Overloaded operators make code more concise:
Here are some best practices to follow when using encrypted operations in your smart contracts:
Choose the smallest encrypted type that can accommodate your data to optimize gas costs. For example, use euint8
for small numbers (0-255) rather than euint256
.
❌ Avoid using oversized types:
✅ Instead, use the smallest appropriate type:
Some TFHE operators exist in two versions : one where all operands are ciphertexts handles, and another where one of the operands is an unencrypted scalar. Whenever possible, use the scalar operand version, as this will save a lot of gas.
❌ For example, this snippet cost way more in gas:
✅ Than this one:
Despite both leading to the same encrypted result!
TFHE arithmetic operators can overflow. Do not forget to take into account such a possibility when implementing fhEVM smart contracts.
❌ For example, if you wanted to create a mint function for an encrypted ERC20 token with an encrypted totalSupply
state variable, this code is vulnerable to overflows:
✅ But you can fix this issue by using TFHE.select
to cancel the mint in case of an overflow:
Notice that we did not check separately the overflow on balances[msg.sender]
but only on totalSupply
variable, because totalSupply
is the sum of the balances of all the users, so balances[msg.sender]
could never overflow if totalSupply
did not.
Zama 5-Question Developer Survey
The fhEVM Contracts standard library streamlines the development of confidential smart contracts by providing templates and utilities for tokens, governance, and error management. These contracts have been rigorously tested by Zama's engineers and are designed to accelerate development while enhancing security.
Install the library using your preferred package manager:
When testing your contracts locally, you can use the SepoliaZamaFHEVMConfig
which provides a mock configuration for local development and testing. This allows you to test your contracts without needing to connect to a real network:
When deploying to Sepolia, you can use the SepoliaZamaFHEVMConfig
which provides the correct configuration for the Sepolia testnet:
When inheriting from configuration contracts, the order of inheritance is critical. Since constructors are evaluated from left to right in Solidity, you must inherit the configuration contract first to ensure proper initialization.
✅ Correct Order:
❌ Wrong order:
This document explains how to enable encrypted computations in your smart contract by setting up the fhEVM
environment. Learn how to integrate essential libraries, configure encryption, and add secure computation logic to your contracts.
To utilize encrypted computations in Solidity contracts, you must configure the TFHE library and Gateway addresses. The fhevm
package simplifies this process with prebuilt configuration contracts, allowing you to focus on developing your contract’s logic without handling the underlying cryptographic setup.
TFHE library: Sets up encryption parameters and cryptographic keys.
Gateway: Manages secure cryptographic operations, including reencryption and decryption.
Network-specific settings: Adapts to local testing, testnets (Sepolia for example), or mainnet deployment.
By inheriting these configuration contracts, you ensure seamless initialization and functionality across environments.
This configuration contract initializes the fhEVM environment with required encryption parameters.
Import based on your environment:
Purpose:
Sets encryption parameters such as cryptographic keys and supported ciphertext types.
Ensures proper initialization of the FHEVM environment.
Example: using Sepolia configuration
To perform decryption or reencryption, your contract must interact with the Gateway, which acts as a secure bridge between the blockchain, coprocessor, and Key Management System (KMS).
Import based on your environment
Purpose
Configures the Gateway for secure cryptographic operations.
Facilitates reencryption and decryption requests.
Example: Configuring the gateway with Sepolia settings
isInitialized
The isInitialized
utility function checks whether an encrypted variable has been properly initialized, preventing unexpected behavior due to uninitialized values.
Function signature
Purpose
Ensures encrypted variables are initialized before use.
Prevents potential logic errors in contract execution.
Example: Initialization Check for Encrypted Counter
By leveraging prebuilt configuration contracts like ZamaFHEVMConfig.sol
and ZamaGatewayConfig.sol
, you can efficiently set up your smart contract for encrypted computations. These tools abstract the complexity of cryptographic initialization, allowing you to focus on building secure, confidential smart contracts.
For more information see .
For more information see .
For more information see .
For more information on operations, see .
For more information on conditional branching, see .
For more information on random number generation, see .
For more information see .
For more details, refer to the document.
The TFHE
library supports operator overloading for encrypted integers (e.g., +
, -
, *
, &
) using the Solidity syntax. These overloaded operators currently perform unchecked operations, meaning they do not include overflow checks.
For detailed API specifications, visit the .
Check our for upcoming features or submit a feature request on .
Join the discussion on the .
We want to hear from you! Take 1 minute to share your thoughts and helping us enhance our documentation and libraries. 👉 to participate.
This guide explains how to use the . This library provides secure, extensible, and pre-tested Solidity templates designed for developing smart contracts on fhEVM using the TFHE library.
For a list of all available contracts see the page
The provides a comprehensive collection of secure, pre-tested Solidity templates optimized for fhEVM development. These templates leverage the TFHE library to enable encrypted computations while maintaining security and extensibility.
The library includes templates for common use cases like tokens and governance, allowing developers to quickly build confidential smart contracts with battle-tested components. For detailed implementation guidance and best practices, refer to the .
: Standard ERC20 with encryption.
: ERC20 with minting capabilities.
: ERC20 with integrated error handling.
: ERC20 with both minting and error handling.
: Confidential ERC20 governance token implementation. .
: A governance contract for managing proposals and votes. .
: Provides error management utilities for encrypted contracts.
: A smart contract for conducting blind auctions where bids are encrypted and the winning bid remains private.
: A blockchain-based identity management system using smart contracts to store and manage encrypted personal data.
: A privacy-preserving implementation of the popular word game Wordle where players guess a secret encrypted word through encrypted letter comparisons.
: A multiplayer game where players must defuse an encrypted bomb by guessing the correct sequence of numbers.
: Suffragium is a secure, privacy-preserving voting system that combines zero-knowledge proofs (ZKP) and Fully Homomorphic Encryption (FHE) to create a trustless and tamper-resistant voting platform.
: A multiplayer game where players must defuse an encrypted bomb by guessing the correct sequence of numbers.
- Nov 2024
- Nov 2024
- Jul 2024
We want to hear from you! Take 1 minute to share your thoughts and helping us enhance our documentation and libraries. 👉 to participate.
- February 2024
- November 2023
- October 2023
- July 2023
- June 2023
- April 2024
- January 2024
- October 2023
- October 2023
- July 2023
ebool
Yes
euint4
Yes
euint8
Yes
euint16
Yes
euint32
Yes
euint64
Yes
euint128
Yes
euint256
Yes
eaddress
Yes
ebytes64
Yes
ebytes128
Yes
ebytes256
Yes
eint8
No, coming soon
eint16
No, coming soon
eint32
No, coming soon
eint64
No, coming soon
eint128
No, coming soon
eint256
No, coming soon
Add
TFHE.add
+
Binary
Subtract
TFHE.sub
-
Binary
Multiply
TFHE.mul
*
Binary
Divide (plaintext divisor)
TFHE.div
Binary
Reminder (plaintext divisor)
TFHE.rem
Binary
Negation
TFHE.neg
-
Unary
Min
TFHE.min
Binary
Max
TFHE.max
Binary
Bitwise AND
TFHE.and
&
Binary
Bitwise OR
TFHE.or
|
Binary
Bitwise XOR
TFHE.xor
^
Binary
Bitwise NOT
TFHE.not
~
Unary
Shift Right
TFHE.shr
Binary
Shift Left
TFHE.shl
Binary
Rotate Right
TFHE.rotr
Binary
Rotate Left
TFHE.rotl
Binary
Equal
TFHE.eq
Binary
Not equal
TFHE.ne
Binary
Greater than or equal
TFHE.ge
Binary
Greater than
TFHE.gt
Binary
Less than or equal
TFHE.le
Binary
Less than
TFHE.lt
Binary
Select
TFHE.select
Ternary
Name
Function Name
Symbol
Type
Random Unsigned Integer
TFHE.randEuintX()
Random
This document provides a detailed guide on implementing decryption in your smart contracts using the GatewayContract
in fhEVM. It covers the setup, usage of the Gateway.requestDecryption
function, and testing with Hardhat.
GatewayContract
set upThe GatewayContract
is pre-deployed on the fhEVM testnet. It uses a default relayer account specified in the PRIVATE_KEY_GATEWAY_RELAYER
or ADDRESS_GATEWAY_RELAYER
environment variable in the .env
file.
Relayers are the only accounts authorized to fulfill decryption requests. The role of the GatewayContract
, however, is to independently verify the KMS signature during execution. This ensures that the relayers cannot manipulate or send fraudulent decryption results, even if compromised. However, the relayers are still trusted to forward decryption requests on time.
Gateway.requestDecryption
functionThe interface of the Gateway.requestDecryption
function from previous snippet is the following:
The first argument, ctsHandles
, should be an array of ciphertexts handles which could be of different types, i.e uint256
values coming from unwrapping handles of type either ebool
, euint4
, euint8
, euint16
, euint32
, euint64
or eaddress
.
ct
is the list of ciphertexts that are requested to be decrypted. Calling requestDecryption
will emit an EventDecryption
on the GatewayContract
contract which will be detected by a relayer. Then, the relayer will send the corresponding ciphertexts to the KMS for decryption before fulfilling the request.
callbackSelector
is the function selector of the callback function which will be called by the GatewayContract
contract once the relayer fulfils the decryption request. Notice that the callback function should always follow this convention, if passSignaturesToCaller
is set to false
:
Or, alternatively, if passSignaturesToCaller
is set to true
:
Notice that XXX
should be the decrypted type, which is a native Solidity type corresponding to the original ciphertext type, following this table of conventions:
ebool
bool
euint4
uint8
euint8
uint8
euint16
uint16
euint32
uint32
euint64
uint64
euint128
uint128
euint256
uint256
eaddress
address
Here callbackName
is a custom name given by the developer to the callback function, requestID
will be the request id of the decryption (could be commented if not needed in the logic, but must be present) and x_0
, x_1
, ... x_N-1
are the results of the decryption of the ct
array values, i.e their number should be the size of the ct
array.
msgValue
is the value in native tokens to be sent to the calling contract during fulfillment, i.e when the callback will be called with the results of decryption.
maxTimestamp
is the maximum timestamp after which the callback will not be able to receive the results of decryption, i.e the fulfillment transaction will fail in this case. This can be used for time-sensitive applications, where we prefer to reject decryption results on too old, out-of-date, values.
passSignaturesToCaller
determines whether the callback needs to transmit signatures from the KMS or not. This is useful if the dApp developer wants to remove trust from the Gateway service and prefers to check the KMS signatures directly from within his dApp smart contract. A concrete example of how to verify the KMS signatures inside a dApp is available here in the requestBoolTrustless
function.
WARNING: Notice that the callback should be protected by the
onlyGateway
modifier to ensure security, as only theGatewayContract
contract should be able to call it.
Finally, if you need to pass additional arguments to be used inside the callback, you could use any of the following utility functions during the request, which would store additional values in the storage of your smart contract:
With their corresponding getter functions to be used inside the callback:
For example, see this snippet where we add two uint256
s during the request call, to make them available later during the callback:
When the decryption request is fufilled by the relayer, the GatewayContract
contract, when calling the callback function, will also emit the following event:
The first argument is the requestID
of the corresponding decryption request, success
is a boolean assessing if the call to the callback succeeded, and result
is the bytes array corresponding to the return data from the callback.
In your hardhat tests, if you sent some transactions which are requesting one or several decryptions and you wish to await the fulfillment of those decryptions, you should import the two helper methods initGateway
and awaitAllDecryptionResults
from the asyncDecrypt.ts
utility file. This would work both when testing on a fhEVM node or in mocked mode. Here is a simple hardhat test for the previous TestAsyncDecrypt
contract (more examples can be seen here):
You should initialize the gateway by calling initGateway
at the top of the before
block - more specifically, before doing any transaction which could involve a decryption request. Notice that when testing on the fhEVM, a decryption is fulfilled usually 2 blocks after the request, while in mocked mode the fulfillment will always happen as soon as you call the awaitAllDecryptionResults
helper function. A good way to standardize hardhat tests is hence to always call the awaitAllDecryptionResults
function which will ensure that all pending decryptions are fulfilled in both modes.
This document introduces the concept of encrypted inputs in the fhEVM, explaining their role, structure, validation process, and how developers can integrate them into smart contracts and applications.
Understanding how encryption, decryption and reencryption works is a prerequisite before implementation, see Encryption, Decryption, Re-encryption, and Computation
Encrypted inputs are a core feature of fhEVM, enabling users to push encrypted data onto the blockchain while ensuring data confidentiality and integrity.
Encrypted inputs are data values submitted by users in ciphertext form. These inputs allow sensitive information to remain confidential while still being processed by smart contracts. They are accompanied by Zero-Knowledge Proofs of Knowledge (ZKPoKs) to ensure the validity of the encrypted data without revealing the plaintext.
Confidentiality: Data is encrypted using the public FHE key, ensuring that only authorized parties can decrypt or process the values.
Validation via ZKPoKs: Each encrypted input is accompanied by a proof verifying that the user knows the plaintext value of the ciphertext, preventing replay attacks or misuse.
Efficient packing: All inputs for a transaction are packed into a single ciphertext in a user-defined order, optimizing the size and generation of the zero-knowledge proof.
When a function in a smart contract is called, it may accept two types of parameters for encrypted inputs:
einput
: Refers to the index of the encrypted parameter, representing a specific encrypted input handle.
bytes
: Contains the ciphertext and the associated zero-knowledge proof used for validation.
Here’s an example of a Solidity function accepting multiple encrypted parameters:
In this example, param1
, param2
, and param3
are encrypted inputs, while inputProof
contains the corresponding ZKPoK to validate their authenticity.
To interact with such a function, developers can use the fhevmjs library to create and manage encrypted inputs. Below is an example implementation:
In this example:
add64
, addBool
, and add8
: Specify the types and values of inputs to encrypt.
encrypt
: Generates the encrypted inputs and the zero-knowledge proof.
Smart contracts process encrypted inputs by verifying them against the associated zero-knowledge proof. This is done using the TFHE.asEuintXX
, TFHE.asEbool
, or TFHE.asEaddress
functions, which validate the input and convert it into the appropriate encrypted type.
This example demonstrates a function that performs multiple encrypted operations, such as updating a user's encrypted balance and toggling an encrypted boolean flag:
encryptedERC20.sol
smart contractHere’s an example of a smart contract function that verifies an encrypted input before proceeding:
Input verification:
The TFHE.asEuintXX
function ensures that the input is a valid ciphertext with a corresponding ZKPoK.
Type conversion:
The function transforms the einput
into the appropriate encrypted type (euintXX
, ebool
, etc.) for further operations within the contract.
Input packing: Minimize the size and complexity of zero-knowledge proofs by packing all encrypted inputs into a single ciphertext.
Frontend encryption: Always encrypt inputs using the FHE public key on the client side to ensure data confidentiality.
Proof management: Ensure that the correct zero-knowledge proof is associated with each encrypted input to avoid validation errors.
Encrypted inputs and their validation form the backbone of secure and private interactions in the fhEVM. By leveraging these tools, developers can create robust, privacy-preserving smart contracts without compromising functionality or scalability.
This document explains how to perform re-encryption. Re-encryption is required when you want a user to access their private data without it being exposed to the blockchain.
Re-encryption in fhEVM enables the secure sharing or reuse of encrypted data under a new public key without exposing the plaintext. This feature is essential for scenarios where encrypted data must be transferred between contracts, dApps, or users while maintaining its confidentiality.
Before implementing re-encryption, ensure you are familiar with the foundational concepts of encryption, re-encryption and computation. Refer to Encryption, Decryption, Re-encryption, and Computation.
Re-encryption is particularly useful for allowing individual users to securely access and decrypt their private data, such as balances or counters, while maintaining data confidentiality.
The re-encryption process involves retrieving ciphertext from the blockchain and performing re-encryption on the client-side. In other words we take the data that has been encrypted by the KMS, decrypt it and encrypt it with the users private key, so only he can access the information.
This ensures that the data remains encrypted under the blockchain’s FHE key but can be securely shared with a user by re-encrypting it under the user’s NaCl public key.
Re-encryption is facilitated by the Gateway and the Key Management System (KMS). The workflow consists of the following:
Retrieving the ciphertext from the blockchain using a contract’s view function.
Re-encrypting the ciphertext client-side with the user’s public key, ensuring only the user can decrypt it.
To retrieve the ciphertext that needs to be re-encrypted, you can implement a view function in your smart contract. Below is an example implementation:
Here, balanceOf
allows retrieval of the user’s encrypted balance stored on the blockchain.
Re-encryption is performed client-side using the fhevmjs
library. Refer to the guide to learn how to include fhevmjs
in your project. Below is an example of how to implement reencryption in a dApp:
This code retrieves the user’s encrypted balance, re-encrypts it with their public key, and decrypts it on the client-side using their private key.
instance.generateKeypair()
: Generates a public-private keypair for the user.
instance.createEIP712(publicKey, CONTRACT_ADDRESS)
: Creates an EIP712 object for signing the user’s public key.
instance.reencrypt()
: Facilitates the re-encryption process by contacting the Gateway and decrypting the data locally with the private key.
This section explains how to handle decryption in fhEVM. Decryption allows plaintext data to be accessed when required for contract logic or user presentation, ensuring confidentiality is maintained throughout the process.
Understanding how encryption, decryption and reencryption works is a prerequisit before implementation, see Encryption, Decryption, Re-encryption, and Computation.
Decryption is essential in two primary cases:
Smart contract logic: A contract requires plaintext values for computations or decision-making.
User interaction: Plaintext data needs to be revealed to all users, such as revealing the decision of the vote.
To learn how decryption works see Encryption, Decryption, Re-encryption, and Computation
Decryption in fhEVM is an asynchronous process that involves the Gateway and Key Management System (KMS). Contracts requiring decryption must extend the GatewayCaller contract, which imports the necessary libraries and provides access to the Gateway.
Here’s an example of how to request decryption in a contract:
Configuration imports: The configuration contracts are imported to set up the FHEVM environment and Gateway.
GatewayCaller
import:
The GatewayCaller
contract is imported to enable decryption requests.
Explore advanced decryption techniques and learn more about re-encryption:
This page provides detailed instructions and examples on how to use and implement the ACL (Access Control List) in fhEVM. For an overview of ACL concepts and their importance, refer to the access control list (ACL) overview.
The ACL system allows you to define two types of permissions for accessing ciphertexts:
Function: TFHE.allow(ciphertext, address)
Purpose: Grants persistent access to a ciphertext for a specific address.
Storage: Permissions are saved in a dedicated ACL contract, making them available across transactions.
Function: TFHE.allowTransient(ciphertext, address)
Purpose: Grants temporary access for the duration of a single transaction.
Storage: Permissions are stored in transient storage to save gas costs.
Use Case: Ideal for passing encrypted values between functions or contracts during a transaction.
Function: TFHE.allowThis(ciphertext)
Equivalent To: TFHE.allow(ciphertext, address(this))
Purpose: Simplifies granting permanent access to the current contract for managing ciphertexts.
Some functions automatically grant transient allowances to the calling contract, simplifying workflow. These include:
Type Conversion:
TFHE.asEuintXX()
, TFHE.asEbool()
, TFHE.asEaddress()
Random Value Generation:
TFHE.randXX()
Computation Results:
TFHE.add()
, TFHE.select()
When processing ciphertexts as input, it’s essential to validate that the sender is authorized to interact with the provided encrypted data. Failing to perform this verification can expose the system to inference attacks where malicious actors attempt to deduce private information.
Consider an Encrypted ERC20 token. An attacker controlling two accounts, Account A and Account B, with 100 tokens in Account A, could exploit the system as follows:
The attacker attempts to send the target user's encrypted balance from Account A to Account B.
Observing the transaction outcome, the attacker gains information:
If successful: The target's balance is equal to or less than 100 tokens.
If failed: The target's balance exceeds 100 tokens.
This type of attack allows the attacker to infer private balances without explicit access.
To prevent this, always use the TFHE.isSenderAllowed()
function to verify that the sender has legitimate access to the encrypted amount being transferred.
By enforcing this check, you can safeguard against inference attacks and ensure that encrypted values are only manipulated by authorized entities.
If a ciphertext can be reencrypted by a user, explicit access must be granted to them. Additionally, the reencryption mechanism requires the signature of a public key associated with the contract address. Therefore, a value that needs to be reencrypted must be explicitly authorized for both the user and the contract.
Due to the reencryption mechanism, a user signs a public key associated with a specific contract; therefore, the ciphertext also needs to be allowed for the contract.
By understanding how to grant and verify permissions, you can effectively manage access to encrypted data in your fhEVM smart contracts. For additional context, see the ACL overview.
This document describes the Access Control List (ACL) system in fhEVM, a core feature that governs access to encrypted data. The ACL ensures that only authorized accounts or contracts can interact with specific ciphertexts, preserving confidentiality while enabling composable smart contracts. This overview provides a high-level understanding of what the ACL is, why it's essential, and how it works.
The ACL is a permission management system designed to control who can access, compute on, or decrypt encrypted values in fhEVM. By defining and enforcing these permissions, the ACL ensures that encrypted data remains secure while still being usable within authorized contexts.
Encrypted data in fhEVM is entirely confidential, meaning that without proper access control, even the contract holding the ciphertext cannot interact with it. The ACL enables:
Granular permissions: Define specific access rules for individual accounts or contracts.
Secure computations: Ensure that only authorized entities can manipulate or decrypt encrypted data.
Gas efficiency: Optimize permissions using transient access for temporary needs, reducing storage and gas costs.
Permanent allowance:
Configured using TFHE.allow(ciphertext, address)
.
Grants long-term access to the ciphertext for a specific address.
Stored in a dedicated contract for persistent storage.
Transient allowance:
Configured using TFHE.allowTransient(ciphertext, address)
.
Grants access to the ciphertext only for the duration of the current transaction.
Stored in transient storage, reducing gas costs.
Ideal for temporary operations like passing ciphertexts to external functions.
Syntactic sugar:
TFHE.allowThis(ciphertext)
is shorthand for TFHE.allow(ciphertext, address(this))
. It authorizes the current contract to reuse a ciphertext handle in future transactions.
Developers can use functions like allow
, allowThis
, and allowTransient
to grant permissions:
allow
: Grants permanent access to an address.
allowThis
: Grants the current contract access to manipulate the ciphertext.
allowTransient
: Grants temporary access to an address for the current transaction.
To check if an entity has permission to access a ciphertext, use functions like isAllowed
or isSenderAllowed
:
isAllowed
: Verifies if a specific address has permission.
isSenderAllowed
: Simplifies checks for the current transaction sender.
Confidential parameters: Pass encrypted values securely between contracts, ensuring only authorized entities can access them.
Secure state management: Store encrypted state variables while controlling who can modify or read them.
Privacy-preserving computations: Enable computations on encrypted data with confidence that permissions are enforced.
This documentation covers the asEbool
, asEuintXX
, asEaddress
and asEbytesXX
operations provided by the TFHE library for working with encrypted data in the fhEVM. These operations are essential for converting between plaintext and encrypted types, as well as handling encrypted inputs.
The operations can be categorized into three main use cases:
Trivial encryption: Converting plaintext values to encrypted types
Type casting: Converting between different encrypted types
Input handling: Processing encrypted inputs with proofs
Trivial encryption simply put is a plain text in a format of a ciphertext.
Trivial encryption is the process of converting plaintext values into encrypted types (ciphertexts) compatible with TFHE operators. Although the data is in ciphertext format, it remains publicly visible on-chain, making it useful for operations between public and private values.
This type of casting involves converting plaintext (unencrypted) values into their encrypted equivalents, such as:
bool
→ ebool
uint
→ euintXX
bytes
→ ebytesXX
address
→ eaddress
Note: When doing trivial encryption, the data is made compatible with FHE operations but remains publicly visible on-chain unless explicitly encrypted.
ebytesXX
typesThe TFHE.padToBytesXX
functions facilitate this trivial encryption process for byte arrays, ensuring compatibility with ebytesXX
types. These functions:
Pad the provided byte array to the appropriate length (64
, 128
, or 256
bytes).
Prevent runtime errors caused by improperly sized input data.
Work seamlessly with TFHE.asEbytesXX
for trivial encryption.
Important: Trivial encryption does NOT provide any privacy guarantees. The input data remains fully visible on the blockchain. Only use trivial encryption when working with public values that need to interact with actual encrypted data.
Pad Input Data: Use the padToBytesXX
functions to ensure your byte array matches the size requirements.
Encrypt the Padded Data: Use TFHE.asEbytesXX
to encrypt the padded byte array into the corresponding encrypted type.
Grant Access: Use TFHE.allowThis
and TFHE.allow
optionally, if you want to persist allowance for those variables for later use.
ebytesXX
Below is an example demonstrating how to encrypt and manage ebytes64
, ebytes128
, and ebytes256
types:
This type of casting is used to reinterpret or convert one encrypted type into another. For example:
euint32
→ euint64
ebytes128
→ ebytes256
Casting between encrypted types is often required when working with operations that demand specific sizes or precisions.
Important: When casting between encrypted types:
Casting from smaller types to larger types (e.g.
euint32
→euint64
) preserves all informationCasting from larger types to smaller types (e.g.
euint64
→euint32
) will truncate and lose information
The table below summarizes the available casting functions:
Casting between encrypted types is efficient and often necessary when handling data with differing precision requirements.
Encrypted input casting is the process of interpreting a handle (ciphertext reference) and its proof as a specific encrypted type. This ensures the validity of the input before it is used in computations.
Encrypted input casting validates:
The input handle references a valid ciphertext.
The accompanying proof matches the expected type.
This document explains how to implement conditional logic (if/else branching) when working with encrypted values in fhEVM. Unlike typical Solidity programming, working with Fully Homomorphic Encryption (FHE) requires specialized methods to handle conditions on encrypted data.
To facilitate conditional assignments, fhEVM provides the TFHE.select
function, which acts as a ternary operator for encrypted values.
TFHE.select
for conditional logicThe TFHE.select
function enables branching logic by selecting one of two encrypted values based on an encrypted condition (ebool
). It works as follows:
condition
: An encrypted boolean (ebool
) resulting from a comparison.
valueIfTrue
: The encrypted value to return if the condition is true.
valueIfFalse
: The encrypted value to return if the condition is false.
Here's an example of using conditional logic to update the highest winning number in a guessing game:
Comparison:
The TFHE.lt
function compares highestBid
and bid
, returning an ebool
(isAbove
) that indicates whether the new bid is higher.
Selection:
The TFHE.select
function updates highestBid
to either the new bid or the previous highest bid, based on the encrypted condition isAbove
.
Permission Handling:
After updating highestBid
, the contract reauthorizes itself to manipulate the updated ciphertext using TFHE.allowThis
.
Value change behavior: Each time TFHE.select
assigns a value, a new ciphertext is created, even if the underlying plaintext value remains unchanged. This behavior is inherent to FHE and ensures data confidentiality, but developers should account for it when designing their smart contracts.
Gas consumption: Using TFHE.select
and other encrypted operations incurs additional gas costs compared to traditional Solidity logic. Optimize your code to minimize unnecessary operations.
Access control: Always use appropriate ACL functions (e.g., TFHE.allowThis
, TFHE.allow
) to ensure the updated ciphertexts are authorized for use in future computations or transactions.
TFHE.select
is a powerful tool for conditional logic on encrypted values.
Encrypted booleans (ebool
) and values maintain confidentiality, enabling privacy-preserving logic.
Developers should account for gas costs and ciphertext behavior when designing conditional operations.
This guide explains how to estimate gas costs for Fully Homomorphic Encryption (FHE) operations in your smart contracts on Zama's fhEVM. Understanding gas consumption is critical for designing efficient confidential smart contracts.
FHE operations in fhEVM are computationally intensive, resulting in higher gas costs compared to standard Ethereum operations. This is due to the complex mathematical operations required to ensure privacy and security.
Native Gas:
Standard gas used for operations on the underlying EVM chain.
On fhEVM, native gas consumption is approximately 20% higher than in mocked environments.
FHEGas:
Represents gas consumed by FHE-specific computations.
A new synthetic kind of gas consumed by FHE-specific computations.
FHEGas is tracked in each block by the FHEGasLimit contract to prevent DDOS attacks.
If too many FHE operations are requested in the same block, the transaction will revert once the FHEGas block limit is reached.
FHEGas is consistent across both mocked and real fhEVM environments.
Note: Gas values provided are approximate and may vary based on network conditions, implementation details, and contract complexity.
To monitor gas usage during development, use the following tools:
getFHEGasFromTxReceipt
:
Extracts FHEGas consumption from a transaction receipt.
Works only in mocked fhEVM environments, but gives the exact same value as in non-mocked environments.
Import as: import { getFHEGasFromTxReceipt } from "../coprocessorUtils";
.gasUsed
from ethers.js transaction receipt:
Standard ethers.js transaction receipt property that returns the native gas used.
In mocked mode, this value underestimates real native gas usage by ~20%.
Works in both mocked and real fhEVM environments, as it's a standard Ethereum transaction property.
The following code demonstrates how to measure both FHEGas and native gas during a transaction:
The current devnet has a FHEGas limit of 10,000,000 per block. Here's what you need to know:
If you send a transaction that exceeds this limit or if the FHEGas block limit is exceeded, depending on other previous transaction in the same block:
The transaction will revert
Any native gas fees (but not FHEGas) will still be charged
You should do one of the following:
Reduce the number of FHE operations in your transaction
Wait for the next block when the FHEGas limit resets
Split your operations across multiple transactions
ebool
)Gas costs increase with the bit-width of the encrypted integer type. Below are the detailed costs for various operations on encrypted types.
euint4
)euint8
)euint16
)euint32
)euint64
)euint128
)euint256
)To resolve a failed transaction due to gas limits:
Open MetaMask and go to Settings
Navigate to Advanced Settings
Enable "Customize transaction nonce"
When resending the transaction:
Use the same nonce as the failed transaction
Set an appropriate gas limit under 10M
Adjust other parameters as needed
This allows you to "replace" the failed transaction with a valid one using the correct gas parameters.
This document explains how to generate cryptographically secure random encrypted numbers fully on-chain using the TFHE
library in fhEVM. These numbers are encrypted and remain confidential, enabling privacy-preserving smart contract logic.
On-chain execution: Random number generation must be executed during a transaction, as it requires the pseudo-random number generator (PRNG) state to be updated on-chain. This operation cannot be performed using the eth_call
RPC method.
Cryptographic security: The generated random numbers are cryptographically secure and encrypted, ensuring privacy and unpredictability.
Random number generation must be performed during transactions, as it requires the pseudo-random number generator (PRNG) state to be mutated on-chain. Therefore, it cannot be executed using the eth_call
RPC method.
The TFHE
library allows you to generate random encrypted numbers of various bit sizes. Below is a list of supported types and their usage:
To generate random numbers within a specific range, you can specify an upper bound. The random number will be in the range [0, upperBound - 1]
.
To generate larger random values, you can use encrypted bytes. These are ideal for scenarios requiring high-precision or high-entropy data.
Cryptographic security: The random numbers are generated using a cryptographically secure pseudo-random number generator (CSPRNG) and remain encrypted until explicitly decrypted.
Gas consumption: Each call to a random number generation function consumes gas. Developers should optimize the use of these functions, especially in gas-sensitive contracts.
Privacy guarantee: Random values are fully encrypted, ensuring they cannot be accessed or predicted by unauthorized parties.
This document explains how to handle loops when working with Fully Homomorphic Encryption (FHE), specifically when a loop break is based on an encrypted condition.
❌ In FHE, it is not possible to break a loop based on an encrypted condition. For example, this would not work:
If your code logic requires looping on an encrypted boolean condition, we highly suggest to try to replace it by a finite loop with an appropriate constant maximum number of steps and use TFHE.select
inside the loop.
✅ For example, the previous code could maybe be replaced by the following snippet:
In this snippet, we perform 10 iterations, adding 4 to x
in each iteration as long as the iteration count is less than maxValue
. If the iteration count exceeds maxValue
, we add 0 instead for the remaining iterations because we can't break the loop.
The previous paragraph emphasized that branch logic should rely as much as possible on TFHE.select
instead of decryptions. It hides effectively which branch has been executed.
However, this is sometimes not enough. Enhancing the privacy of smart contracts often requires revisiting your application's logic.
For example, if implementing a simple AMM for two encrypted ERC20 tokens based on a linear constant function, it is recommended to not only hide the amounts being swapped, but also the token which is swapped in a pair.
✅ Here is a very simplified example implementation, we suppose here that the rate between tokenA and tokenB is constant and equals to 1:
Notice that to preserve confidentiality, we had to make two inputs transfers on both tokens from the user to the AMM contract, and similarly two output transfers from the AMM to the user, even if technically most of the times it will make sense that one of the user inputs encryptedAmountAIn
or encryptedAmountBIn
is actually an encrypted zero.
This is different from a classical non-confidential AMM with regular ERC20 tokens: in this case, the user would need to just do one input transfer to the AMM on the token being sold, and receive only one output transfer from the AMM on the token being bought.
Using encrypted indexes to pick an element from an array without revealing it is not very efficient, because you would still need to loop on all the indexes to preserve confidentiality.
However, there are plans to make this kind of operation much more efficient in the future, by adding specialized operators for arrays.
For instance, imagine you have an encrypted array called encArray
and you want to update an encrypted value x
to match an item from this list, encArray[i]
, without disclosing which item you're choosing.
❌ You must loop over all the indexes and check equality homomorphically, however this pattern is very expensive in gas and should be avoided whenever possible.
For a detailed explanation of the ACL's functionality, including code examples and advanced configurations, see .
Encrypted inputs is in depth explained in the following section:
For more information, see the
In fhEVM, when you perform , the result is an encrypted boolean (ebool
). Since encrypted booleans do not support standard boolean operations like if
statements or logical operators, conditional logic must be implemented using specialized methods.
This is a simplified example to demonstrate the functionality. For a complete implementation with proper error handling and additional features, see the .
For more information on the supported operations, see the .
Transient
Temporary access during a transaction.
Transient storage (EIP-1153)
Calling external functions or computations with ciphertexts. Use when wanting to save on gas costs.
Permanent
Long-term access across multiple transactions.
Dedicated contract storage
Persistent ciphertexts for contracts or users requiring ongoing access.
euintX
euintX
TFHE.asEuintXX
ebool
euintX
TFHE.asEuintXX
euintX
ebool
TFHE.asEboolXX
Trivial encryption
TFHE.asEuintXX(x)
uintX
euintX
TFHE.asEbool(x)
bool
ebool
TFHE.asEbytesXX(x)
bytesXX
ebytesXX
TFHE.asEaddress(x)
address
eaddress
Conversion between types
TFHE.asEuintXX(x)
euintXX
/ebool
euintYY
TFHE.asEbool(x)
euintXX
ebool
Encrypted input
TFHE.asEuintXX(x, y)
einput
, bytes
proof
euintX
TFHE.asEbool(x, y)
einput
,bytes
proof
ebool
TFHE.asEbytesXX(x, y)
einput
,bytes
proof
ebytesXX
TFHE.asEaddress(x, y)
einput
, bytes
proof
eaddress
and
/or
/xor
26,000
not
30,000
add
/sub
65,000
add
/sub
(scalar)
65,000
mul
150,000
mul
(scalar)
88,000
div
(scalar)
139,000
rem
(scalar)
286,000
and
/or
/xor
32,000
shr
/shl
116,000
shr
/shl
(scalar)
35,000
rotr
/rotl
116,000
rotr
/rotl
(scalar)
35,000
eq
/ne
51,000
ge
/gt
/le
/lt
70,000
min
/max
121,000
min
/max
(scalar)
121,000
neg
60,000
not
33,000
select
45,000
add
/sub
94,000
add
/sub
(scalar)
94,000
mul
197,000
mul
(scalar)
159,000
div
(scalar)
238,000
rem
(scalar)
460,000
and
/or
/xor
34,000
shr
/shl
133,000
shr
/shl
(scalar)
35,000
rotr
/rotl
133,000
rotr
/rotl
(scalar)
35,000
eq
/ne
53,000
ge
/gt
/le
/lt
82,000
min
/max
128,000
min
/max
(scalar)
128,000
neg
95,000
not
34,000
select
47,000
randEuint8()
100,000
add
/sub
133,000
add
/sub
(scalar)
133,000
mul
262,000
mul
(scalar)
208,000
div
(scalar)
314,000
rem
(scalar)
622,000
and
/or
/xor
34,000
shr
/shl
153,000
shr
/shl
(scalar)
35,000
rotr
/rotl
153,000
rotr
/rotl
(scalar)
35,000
eq
/ne
54,000
ge
/gt
/le
/lt
105,000
min
/max
153,000
min
/max
(scalar)
150,000
neg
131,000
not
35,000
select
47,000
randEuint16()
100,000
add
/sub
162,000
add
/sub
(scalar)
162,000
mul
359,000
mul
(scalar)
264,000
div
(scalar)
398,000
rem
(scalar)
805,000
and
/or
/xor
35,000
shr
/shl
183,000
shr
/shl
(scalar)
35,000
rotr
/rotl
183,000
rotr
/rotl
(scalar)
35,000
eq
/ne
82,000
ge
/gt
/le
/lt
128,000
min
/max
183,000
min
/max
(scalar)
164,000
neg
160,000
not
36,000
select
50,000
randEuint32()
100,000
add
/sub
188,000
add
/sub
(scalar)
188,000
mul
641,000
mul
(scalar)
356,000
div
(scalar)
584,000
rem
(scalar)
1,095,000
and
/or
/xor
38,000
shr
/shl
227,000
shr
/shl
(scalar)
38,000
rotr
/rotl
227,000
rotr
/rotl
(scalar)
38,000
eq
/ne
86,000
ge
/gt
/le
/lt
156,000
min
/max
210,000
min
/max
(scalar)
192,000
neg
199,000
not
37,000
select
53,000
randEuint64()
100,000
add
/sub
218,000
add
/sub
(scalar)
218,000
mul
1,145,000
mul
(scalar)
480,000
div
(scalar)
857,000
rem
(scalar)
1,499,000
and
/or
/xor
41,000
shr
/shl
282,000
shr
/shl
(scalar)
41,000
rotr
/rotl
282,000
rotr
/rotl
(scalar)
41,000
eq
/ne
88,000
ge
/gt
/le
/lt
190,000
min
/max
241,000
min
/max
(scalar)
225,000
neg
248,000
not
38,000
select
70,000
add
/sub
253,000
add
/sub
(scalar)
253,000
mul
2,045,000
mul
(scalar)
647,000
div
(scalar)
1,258,000
rem
(scalar)
2,052,000
and
/or
/xor
44,000
shr
/shl
350,000
shr
/shl
(scalar)
44,000
rotr
/rotl
350,000
rotr
/rotl
(scalar)
44,000
eq
/ne
100,000
ge
/gt
/le
/lt
231,000
min
/max
277,000
min
/max
(scalar)
264,000
neg
309,000
not
39,000
select
90,000
eq
/ne
90,000
cast
200
trivialEncrypt
(basic)
100-800
trivialEncrypt
(extended)
1,600-6,400
randBounded
100,000
select
43,000-300,000
rand
100,000-400,000
This document explains how to handle errors effectively in fhEVM smart contracts. Since transactions involving encrypted data do not automatically revert when conditions are not met, developers need alternative mechanisms to communicate errors to users.
In the context of encrypted data:
No automatic reversion: Transactions do not revert if a condition fails, making it challenging to notify users of issues like insufficient funds or invalid inputs.
Limited feedback: Encrypted computations lack direct mechanisms for exposing failure reasons while maintaining confidentiality.
To address these challenges, implement an error handler that records the most recent error for each user. This allows dApps or frontends to query error states and provide appropriate feedback to users.
For a complete implementation of error handling, see our reference contracts:
EncryptedErrors.sol - Base error handling contract
ConfidentialERC20WithErrors.sol - Example usage in an ERC20 token
The following contract demonstrates how to implement and use an error handler:
Define error codes:
NO_ERROR
: Indicates a successful operation.
NOT_ENOUGH_FUNDS
: Indicates insufficient balance for a transfer.
Record errors:
Use the setLastError
function to log the latest error for a specific address along with the current timestamp.
Emit the ErrorChanged
event to notify external systems (e.g., dApps) about the error state change.
Conditional updates:
Use the TFHE.select
function to update balances and log errors based on the transfer condition (canTransfer
).
Frontend integration:
The dApp can query _lastErrors
for a user’s most recent error and display appropriate feedback, such as "Insufficient funds" or "Transaction successful."
The frontend or another contract can query the _lastErrors
mapping to retrieve error details:
User feedback:
Provides actionable error messages without compromising the confidentiality of encrypted computations.
Scalable error tracking:
Logs errors per user, making it easy to identify and debug specific issues.
Event-driven notifications:
Enables frontends to react to errors in real time via the ErrorChanged
event.
By implementing error handlers as demonstrated, developers can ensure a seamless user experience while maintaining the privacy and integrity of encrypted data operations.
This document provides instructions on how to build with Node.js
using the fhevmjs
library.
First, you need to install the library:
fhevmjs
uses ESM format for web version and commonjs for node version. You need to set the type to "commonjs" in your package.json to load the correct version of fhevmjs. If your node project use "type": "module"
, you can force the loading of the Node version by using import { createInstance } from 'fhevmjs/node';
An instance receives an object containing:
chainId
(optional): the chainId of the network
network
(optional): the Eip1193 object provided by window.ethereum
(used to fetch the public key and/or chain id)
networkUrl
(optional): the URL of the network (used to fetch the public key and/or chain id)
publicKey
(optional): if the public key has been fetched separately (cache), you can provide it
gatewayUrl
(optional): the URL of the gateway to retrieve a reencryption
coprocessorUrl
(optional): the URL of the coprocessor
You can now use your instance to encrypt parameters or do a reencryption.
This guide explains how to use Foundry with fhEVM for developing smart contracts.
While a Foundry template is currently in development, we strongly recommend using the Hardhat template for now, as it provides a fully tested and supported development environment for fhEVM smart contracts.
However, you could still use Foundry with the mocked version of the fhEVM, but please be aware that this approach is NOT recommended, since the mocked version is not fully equivalent to the real fhEVM node's implementation (see warning in hardhat). In order to do this, you will need to rename your TFHE.sol
imports from fhevm/lib/TFHE.sol
to fhevm/mocks/TFHE.sol
in your solidity source files.
Save this in your .env
file:
TFHE_EXECUTOR_CONTRACT
0x687408aB54661ba0b4aeF3a44156c616c6955E07
ACL_CONTRACT
0xFee8407e2f5e3Ee68ad77cAE98c434e637f516e5
PAYMENT_CONTRACT
0xFb03BE574d14C256D56F09a198B586bdfc0A9de2
KMS_VERIFIER_CONTRACT
0x9D6891A6240D6130c54ae243d8005063D05fE14b
GATEWAY_CONTRACT
0x33347831500F1e73f0ccCBb95c9f86B94d7b1123
PUBLIC_KEY_ID
0301c5dd3e2702992b7c12930b7d4defeaaa52cf
GATEWAY_URL
https://gateway.sepolia.zama.ai/
This document guides you through building a web application using the fhevmjs library. You can either start with a template or directly integrate the library into your project.
fhevmjs
is working out of the box and we recommend you to use it. We also provide three GitHub templates to start your project with everything set.
You can use this template to start an application with fhevmjs, using Vite + React + TypeScript.
You can also use this template to start an application with fhevmjs, using Vite + Vue + TypeScript.
You can also use this template to start an application with fhevmjs, using Next + TypeScript.
As an alternative to use the real coprocessor deployed on Sepolia to help you develop your dApp faster and without needing testnet tokens, you can use a mocked fhevm. Currently, we recommend you to use the ConfidentialERC20
dApp example available on the mockedFrontend
branch of the React template. Follow the README on this branch, and you will be able to deploy exactly the same dApp both on Sepolia as well as on the mocked coprocessor seamlessly.
fhevmjs
consists of multiple files, including WASM files and WebWorkers, which can make packaging these components correctly in your setup cumbersome. To simplify this process, especially if you're developing a dApp with server-side rendering (SSR), we recommend using our CDN.
Include this line at the top of your project.
In your project, you can use the bundle import if you install fhevmjs
package:
If you prefer You can also use the fhevmjs
as a ES module:
Install the fhevmjs
library to your project:
fhevmjs
uses ESM format. You need to set the type to "module" in your package.json. If your node project use "type": "commonjs"
or no type, you can force the loading of the web version by using import { createInstance } from 'fhevmjs/web';
To use the library in your project, you need to load the WASM of TFHE first with initFhevm
.
Once the WASM is loaded, you can now create an instance. An instance receives an object containing:
chainId
(optional): the chainId of the network
network
(optional): the Eip1193 object provided by window.ethereum
(used to fetch the public key and/or chain id)
networkUrl
(optional): the URL of the network (used to fetch the public key and/or chain id)
publicKey
(optional): if the public key has been fetched separately (cache), you can provide it
gatewayUrl
(optional): the URL of the gateway to retrieve a reencryption
coprocessorUrl
(optional): the URL of the coprocessor
You can now use your instance to encrypt parameters or do a reencryption.
The fhevmjs
Command-Line Interface (CLI) tool provides a simple and efficient way to encrypt data for use with the blockchain's Fully Homomorphic Encryption (FHE) system. This guide explains how to install and use the CLI to encrypt integers and booleans for confidential smart contracts.
Ensure you have Node.js installed on your system before proceeding. Then, globally install the fhevmjs
package to enable the CLI tool:
Once installed, you can access the CLI using the fhevm
command. Verify the installation and explore available commands using:
To see specific options for encryption, run:
The CLI allows you to encrypt integers and booleans for use in smart contracts. Encryption is performed using the blockchain's FHE public key, ensuring the confidentiality of your data.
--node
: Specifies the RPC URL of the blockchain node (e.g., http://localhost:8545
).
<CONTRACT_ADDRESS>
: The address of the contract interacting with the encrypted data.
<USER_ADDRESS>
: The address of the user associated with the encrypted data.
<DATA:TYPE>
: The data to encrypt, followed by its type:
:64
for 64-bit integers
:1
for booleans
Encrypt the integer 71721075
(64-bit) and the boolean 1
for the contract at 0x8Fdb26641d14a80FCCBE87BF455338Dd9C539a50
and the user at 0xa5e1defb98EFe38EBb2D958CEe052410247F4c80
:
This guide explains how to use the debug.decrypt[XX]
functions for debugging encrypted data in mocked environments during development with fhEVM.
The debug.decrypt[XX]
functions should not be used in production as they rely on private keys.
The debug.decrypt[XX]
functions allow you to decrypt encrypted handles into plaintext values. This feature is useful for debugging encrypted operations such as transfers, balance checks, and other computations involving FHE-encrypted data.
Environment: The debug.decrypt[XX]
functions work only in mocked environments (e.g., hardhat
network).
Production limitation: In production, decryption is performed asynchronously via the Gateway and requires an authorized onchain request.
Encrypted types: The debug.decrypt[XX]
functions supports various encrypted types, including integers, booleans, and ebytesXX
.
Bypass ACL authorization: The debug.decrypt[XX]
functions allow decryption without ACL authorization, useful for verifying encrypted operations during development and testing.
Decrypts encrypted integers of different bit-widths (euint4
, euint8
, ..., euint256
).
decrypt4
bigint
euint4
decrypt8
bigint
euint8
decrypt16
bigint
euint16
decrypt32
bigint
euint32
decrypt64
bigint
euint64
decrypt128
bigint
euint128
decrypt256
bigint
euint256
Decrypts encrypted booleans (ebool
).
decryptBool
boolean
ebool
Decrypts encrypted byte arrays of various sizes (ebytesXX
).
decryptEbytes64
string
ebytes64
decryptEbytes128
string
ebytes128
decryptEbytes256
string
ebytes256
Decrypts encrypted addresses.
decryptAddress
string
eaddress
To utilize the debug functions, import the utils.ts file.
For a more complete example, refer to the ConfidentialERC20 test file.
Each decryption function includes a type verification step to ensure the provided handle matches the expected encrypted type. If the type is mismatched, an error is thrown.
The functions only work in the hardhat
network. Attempting to use them in a production environment will result in an error.
Use only for debugging: These functions require access to private keys and are meant exclusively for local testing and debugging.
Production decryption: For production, always use the asynchronous Gateway-based decryption.
The fhEVM architecture provides the foundation for confidential smart contracts on EVM-compatible blockchains. At its core is FHE, a cryptographic technique enabling computations directly on encrypted data, ensuring privacy at every stage.
This system relies on three key types:
The public key: used for encrypting data.
The private key: used for decryption and securely managed by the Key Management System or KMS
The evaluation key: enabling encrypted computations performed by the coprocessor.
The fhEVM leverages Zama's TFHE library, integrating seamlessly with blockchain environments to address transparency, composability, and scalability challenges. Its hybrid architecture combines:
On-chain smart contracts for encrypted state management and access controls.
Off-chain coprocessors for resource-intensive FHE computations.
The Gateway to coordinate between blockchain, KMS, and coprocessors.
The KMS for secure cryptographic key management and proof validation.
This architecture enables developers to write private, composable smart contracts using symbolic execution and zero-knowledge proofs, ensuring data confidentiality and computational integrity.
For a detailed exploration of the fhEVM architecture, including components, workflows, and deployment models, see Architecture Overview.
Save this in your .env
file:
TFHE_EXECUTOR_CONTRACT
0x687408aB54661ba0b4aeF3a44156c616c6955E07
ACL_CONTRACT
0xFee8407e2f5e3Ee68ad77cAE98c434e637f516e5
PAYMENT_CONTRACT
0xFb03BE574d14C256D56F09a198B586bdfc0A9de2
KMS_VERIFIER_CONTRACT
0x9D6891A6240D6130c54ae243d8005063D05fE14b
GATEWAY_CONTRACT
0x33347831500F1e73f0ccCBb95c9f86B94d7b1123
PUBLIC_KEY_ID
0301c5dd3e2702992b7c12930b7d4defeaaa52cf
GATEWAY_URL
https://gateway.sepolia.zama.ai/
This document introduces the core cryptographic operations in the fhEVM system, including how data is encrypted, decrypted, re-encrypted and computed upon while maintaining privacy.
The fhEVM system ensures end-to-end confidentiality by leveraging Fully Homomorphic Encryption (FHE). The encryption, decryption, re-encryption, and computation processes rely on a coordinated flow of information and cryptographic keys across the fhEVM components. This section details how these operations work and outlines the role of the FHE keys in enabling secure and private processing.
Public Key:
Location: Directly accessible from the frontend or the smart contract.
Role: Used to encrypt plaintext data before submission to the blockchain or during contract execution.
Private Key:
Location: Stored securely in the Key Management System (KMS).
Role: Used for decrypting ciphertexts when necessary, either to verify results or enable user-specific plaintext access.
Evaluation Key:
Location: Stored on the coprocessor.
Role: Enables operations on ciphertexts (e.g., addition, multiplication) without decrypting them.
Encryption is the starting point for any interaction with the fhEVM system, ensuring that data is protected before it is transmitted or processed.
How It Works:
The frontend or client application uses the public key to encrypt user-provided plaintext inputs.
The encrypted data (ciphertext) is submitted to the blockchain as part of a transaction or stored for later computation.
Data Flow:
Source: Frontend or smart contract.
Destination: Blockchain (for storage and symbolic execution) or coprocessor (for processing).
You can read about the implementation details in our encryption guide.
Encrypted computations are performed using the evaluation key on the coprocessor.
How it works:
The blockchain initiates symbolic execution, generating a set of operations to be performed on encrypted data.
These operations are offloaded to the coprocessor, which uses the evaluation key to compute directly on the ciphertexts.
The coprocessor returns updated ciphertexts to the blockchain for storage or further use.
Data flow:
Source: Blockchain smart contracts (via symbolic execution).
Processing: Coprocessor (using the evaluation key).
Destination: Blockchain (updated ciphertexts).
Decryption is used when plaintext results are required for contract logic or for presentation to a user. After the decryption is performed on the blockchain, the decrypted result is exposed to everyone who has access to the blockchain.
How it works: Validators on the blockchain do not possess the private key needed for decryption. Instead, the Key Management System (KMS) securely holds the private key. If plaintext values are needed, the process is facilitated by a service called the Gateway, which provides two options:
For Smart contract logic: The Gateway acts as an oracle service, listening for decryption request events emitted by the blockchain. Upon receiving such a request, the Gateway interacts with the KMS to decrypt the ciphertext and sends the plaintext back to the smart contract via a callback function.
For dApps: If a dApp needs plaintext values, the Gateway enables re-encryption of the ciphertext. The KMS securely re-encrypts the ciphertext with the dApp's public key, ensuring that only the dApp can decrypt and access the plaintext.
Data flow:
Source: Blockchain or dApp (ciphertext).
Processing: KMS performs decryption or re-encryption via the Gateway.
Destination: Plaintext is either sent to the smart contract or re-encrypted and delivered to the dApp.
You can read about the implementation details in our decryption guide.
Re-encryption enables encrypted data to be securely shared or reused under a different encryption key without ever revealing the plaintext. This process is essential for scenarios where data needs to be accessed by another contract, dApp, or user while maintaining confidentiality.
How it work: Re-encryption is facilitated by the Gateway in collaboration with the Key Management System (KMS).
The Gateway receives a re-encryption request, which includes details of the original ciphertext and the target public key.
The KMS securely decrypts the ciphertext using its private key and re-encrypts the data with the recipient's public key.
The re-encrypted ciphertext is then sent to the intended recipient.
Data flow:
Source:
The process starts with an original ciphertext retrieved from the blockchain or a dApp.
Processing:
The Gateway forwards the re-encryption request to the KMS.
The KMS handles decryption and re-encryption using the appropriate keys.
Destination:
The re-encrypted ciphertext is delivered to the target entity, such as a dApp, user, or another contract.
Re-encryption is initiated on the client side via the Gateway service using the fhevmjs
library. Here’s the general workflow:
Retrieve the ciphertext:
The dApp calls a view function (e.g., balanceOf
) on the smart contract to get the handle of the ciphertext to be re-encrypted.
Generate and sign a keypair:
The dApp generates a keypair for the user.
The user signs the public key to ensure authenticity.
Submit re-encryption request:
The dApp calls the Gateway, providing the following information:
The ciphertext handle.
The user’s public key.
The user’s address.
The smart contract address.
The user’s signature.
Decrypt the re-encrypted ciphertext:
The dApp receives the re-encrypted ciphertext from the Gateway.
The dApp decrypts the ciphertext locally using the private key.
You can read our re-encryption guide explaining how to use it.
The flow of information across the fhEVM components during these operations highlights how the system ensures privacy while maintaining usability:
Encryption
Public Key
Frontend encrypts plaintext → Ciphertext submitted to blockchain or coprocessor.
Computation
Evaluation Key
Blockchain initiates computation → Coprocessor processes ciphertext using evaluation key → Updated ciphertext returned to blockchain.
Decryption
Private Key
Blockchain or Gateway sends ciphertext to KMS → KMS decrypts using private key → Plaintext returned to authorized requester (e.g., frontend or specific user).
Re-encryption
Private and Target Keys
Blockchain or Gateway sends ciphertext to KMS → KMS re-encrypts using private key and target key → Updated ciphertext returned to blockchain, frontend, or other contract/user.
This architecture ensures that sensitive data remains encrypted throughout its lifecycle, with decryption or re-encryption only occurring in controlled, secure environments. By separating key roles and processing responsibilities, fhEVM provides a scalable and robust framework for private smart contracts.
This document gives a detailed explanantion of each component of fhEVM and illustrate how they work together to perform computations.
The fhEVM architecture is built around four primary components, each contributing to the system's functionality and performance. These components work together to enable the development and execution of private, composable smart contracts on EVM-compatible blockchains. Below is an overview of these components and their responsibilities:
As a developer working with fhEVM, your workflow typically involves two key elements:
Frontend development: You create a frontend interface for users to interact with your confidential application. This includes encrypting inputs using the public FHE key and submitting them to the blockchain.
Smart contract development:
You write Solidity contracts deployed on the same blockchain as the fhEVM smart contracts. These contracts leverage the TFHE.sol
library to perform operations on encrypted data. Below, we explore the major components involved.
fhEVM smart contracts include the Access Control List (ACL) contract, TFHE.sol
library, and related FHE-enabled contracts.
fhEVM implements symbolic execution to optimize FHE computations:
Handles: Operations on encrypted data return "handles" (references to ciphertexts) instead of immediate results.
Lazy Execution: Actual computations are performed asynchronously, offloading resource-intensive tasks to the coprocessor.
This approach ensures high throughput and flexibility in managing encrypted data.
fhEVM incorporates ZKPoKs to verify the correctness of encrypted inputs and outputs:
Validation: ZKPoKs ensure that inputs are correctly formed and correspond to known plaintexts without revealing sensitive data.
Integrity: They prevent misuse of ciphertexts and ensure the correctness of computations.
By combining symbolic execution and ZKPoKs, fhEVM smart contracts maintain both privacy and verifiability.
The coprocessor is the backbone for handling computationally intensive FHE tasks.
Execution: Performs operations such as addition, multiplication, and comparison on encrypted data.
Ciphertext management: Stores encrypted inputs, states, and outputs securely, either off-chain or in a dedicated on-chain database.
The Gateway acts as the bridge between the blockchain, coprocessor, and KMS.
API for developers: Exposes endpoints for submitting encrypted inputs, retrieving outputs, and managing ciphertexts.
Proof validation: Forwards ZKPoKs to the KMS for verification.
Off-chain coordination: Relays encrypted data and computation results between on-chain and off-chain systems.
The Gateway simplifies the development process by abstracting the complexity of cryptographic operations.
The KMS securely manages the cryptographic backbone of fhEVM by maintaining and distributing the global FHE keys.
Threshold decryption: Uses Multi-Party Computation (MPC) to securely decrypt ciphertexts without exposing the private key to any single entity.
ZKPoK validation: Verifies proofs of plaintext knowledge to ensure that encrypted inputs are valid.
Key distribution: Maintains the global FHE keys, which include:
Public key: Used for encrypting data (accessible to the frontend and smart contracts).
Private key: Stored securely in the KMS and used for decryption.
Evaluation key: Used by the coprocessor to perform FHE computations.
The KMS ensures robust cryptographic security, preventing single points of failure and maintaining public verifiability.
This document provides solutions for common Webpack errors encountered during the development process. Follow the steps below to resolve each issue.
Error message: Module not found: Error: Can't resolve 'tfhe_bg.wasm'
Cause: In the codebase, there is a new URL('tfhe_bg.wasm')
which triggers a resolve by Webpack.
Possible solutions: You can add a fallback for this file by adding a resolve configuration in your webpack.config.js
:
Error message: ReferenceError: Buffer is not defined
Cause: This error occurs when the Node.js Buffer
object is used in a browser environment where it is not natively available.
Possible solutions: To resolve this issue, you need to provide browser-compatible fallbacks for Node.js core modules. Install the necessary browserified npm packages and configure Webpack to use these fallbacks.
Error message: Issues with importing ESM version
Cause: With a bundler such as Webpack or Rollup, imports will be replaced with the version mentioned in the "browser"
field of the package.json
. This can cause issues with typing.
Possible solutions:
If you encounter any other issue, you can force import of the browser package.
Error message: Issues with bundling the library, especially with SSR frameworks.
Cause: The library may not bundle correctly with certain frameworks, leading to errors during the build or runtime process.
Explore our curated list of repositories to jumpstart your FHE development, contribute to the community, and access essential libraries and implementations.
Quickly set up your development environment with these ready-to-use templates:
Contribute to the development of FHE technologies and earn rewards through Zama’s bounty program:
Access the essential libraries for building and integrating FHE-enabled applications:
Explore the foundational implementations enabling FHE integration with blockchain systems:
Use these repositories to accelerate your development, integrate FHE into your applications, or contribute to the growing ecosystem.
This document provides an overview of the functions available in the TFHE
Solidity library. The TFHE library provides functionality for working with encrypted types and performing operations on them. It implements fully homomorphic encryption (FHE) operations in Solidity.
The TFHE
Solidity library provides essential functionality for working with encrypted data types and performing fully homomorphic encryption (FHE) operations in smart contracts. It is designed to streamline the developer experience while maintaining flexibility and performance.
Homomorphic Operations: Enables arithmetic, bitwise, and comparison operations on encrypted values.
Ciphertext-Plaintext Interoperability: Supports operations that mix encrypted and plaintext operands, provided the plaintext operand's size does not exceed the encrypted operand's size.
Example: add(uint8 a, euint8 b)
is valid, but add(uint32 a, euint16 b)
is not.
Ciphertext-plaintext operations are generally faster and consume less gas than ciphertext-ciphertext operations.
Implicit Upcasting: Automatically adjusts operand types when necessary to ensure compatibility during operations on encrypted data.
Flexibility: Handles a wide range of encrypted data types, including booleans, integers, addresses, and byte arrays.
Performance Optimization: Prioritizes efficient computation by supporting optimized operator versions for mixed plaintext and ciphertext inputs.
Ease of Use: Offers consistent APIs across all supported data types, enabling a smooth developer experience.
The library ensures that all operations on encrypted data follow the constraints of FHE while abstracting complexity, allowing developers to focus on building privacy-preserving smart contracts.
ebool
: Encrypted boolean value
euint4
: Encrypted 4-bit unsigned integer
euint8
: Encrypted 8-bit unsigned integer
euint16
: Encrypted 16-bit unsigned integer
euint32
: Encrypted 32-bit unsigned integer
euint64
: Encrypted 64-bit unsigned integer
euint128
: Encrypted 128-bit unsigned integer
euint256
: Encrypted 256-bit unsigned integer
eaddress
: Encrypted Ethereum address
ebytes64
: Encrypted 64-byte value
ebytes128
: Encrypted 128-byte value
ebytes256
: Encrypted 256-byte value
einput
: Input type for encrypted operations (bytes32)
Casting between encrypted types: TFHE.asEbool
converts encrypted integers to encrypted booleans
Casting to encrypted types: TFHE.asEuintX
converts plaintext values to encrypted types
Casting to encrypted addresses: TFHE.asEaddress
converts plaintext addresses to encrypted addresses
Casting to encrypted bytes: TFHE.asEbytesX
converts plaintext bytes to encrypted bytes
asEuint
The asEuint
functions serve three purposes:
verify ciphertext bytes and return a valid handle to the calling smart contract;
cast a euintX
typed ciphertext to a euintY
typed ciphertext, where X != Y
;
trivially encrypt a plaintext value.
The first case is used to process encrypted inputs, e.g. user-provided ciphertexts. Those are generally included in a transaction payload.
The second case is self-explanatory. When X > Y
, the most significant bits are dropped. When X < Y
, the ciphertext is padded to the left with trivial encryptions of 0
.
Examples
asEbool
The asEbool
functions behave similarly to the asEuint
functions, but for encrypted boolean values.
Sets the FHEVM configuration for encrypted operations.
Returns true if the encrypted value is initialized, false otherwise. Supported for all encrypted types (T can be ebool, euintX, eaddress, ebytesX).
Available for euint* types:
Arithmetic: TFHE.add
, TFHE.sub
, TFHE.mul
, TFHE.min
, TFHE.max
, TFHE.neg
, TFHE.div
, TFHE.rem
Note: div
and rem
operations are supported only with plaintext divisors
add
, sub
, mul
, div
, rem
)Performs the operation homomorphically.
Note that division/remainder only support plaintext divisors.
Examples
min
, max
Available for euint* types:
Returns the minimum (resp. maximum) of the two given values.
Examples
neg
, not
)There are two unary operators: neg
(-
) and not
(!
). Note that since we work with unsigned integers, the result of negation is interpreted as the modular opposite. The not
operator returns the value obtained after flipping all the bits of the operand.
Bitwise: TFHE.and
, TFHE.or
, TFHE.xor
, TFHE.not
, TFHE.shl
, TFHE.shr
, TFHE.rotl
, TFHE.rotr
AND
, OR
, XOR
)Unlike other binary operations, bitwise operations do not natively accept a mix of ciphertext and plaintext inputs. To ease developer experience, the TFHE
library adds function overloads for these operations. Such overloads implicitely do a trivial encryption before actually calling the operation function, as shown in the examples below.
Available for euint* types:
Examples
<<
, >>
)Shifts the bits of the base two representation of a
by b
positions.
Examples
Rotates the bits of the base two representation of a
by b
positions.
Examples
eq
, ne
, ge
, gt
, le
, lt
)Note that in the case of ciphertext-plaintext operations, since our backend only accepts plaintext right operands, calling the operation with a plaintext left operand will actually invert the operand order and call the opposite comparison.
The result of comparison operations is an encrypted boolean (ebool
). In the backend, the boolean is represented by an encrypted unsinged integer of bit width 8, but this is abstracted away by the Solidity library.
Available for all encrypted types:
Additional comparisons for euint* types:
select
)If control is true, returns a, otherwise returns b. Available for ebool, eaddress, and ebytes* types.
This operator takes three inputs. The first input b
is of type ebool
and the two others of type euintX
. If b
is an encryption of true
, the first integer parameter is returned. Otherwise, the second integer parameter is returned.
Random encrypted integers can be generated fully on-chain.
That can only be done during transactions and not on an eth_call
RPC method, because PRNG state needs to be mutated on-chain during generation.
The TFHE
library provides a robust set of access control functions for managing permissions on encrypted values. These functions ensure that encrypted data can only be accessed or manipulated by authorized accounts or contracts.
Descriptions
allow
: Grants permanent access to a specific address. Permissions are stored persistently in a dedicated ACL contract.
allowThis
: Grants the current contract access to an encrypted value.
allowTransient
: Grants temporary access to a specific address for the duration of the transaction. Permissions are stored in transient storage for reduced gas costs.
The allow
and allowTransient
functions enable fine-grained control over who can access, decrypt, and reencrypt encrypted values. Temporary permissions (allowTransient
) are ideal for minimizing gas usage in scenarios where access is needed only within a single transaction.
Example: granting access
isAllowed
: Checks whether a specific address has permission to access a ciphertext.
isSenderAllowed
: Similar to isAllowed
, but automatically checks permissions for the msg.sender
.
Both functions return true
if the ciphertext is authorized for the specified address, regardless of whether the allowance is stored in the ACL contract or in transient storage.
These functions help ensure that only authorized accounts or contracts can access encrypted values.
Example: permission verification
cleanTransientStorage
: Removes all temporary permissions from transient storage. Use this function at the end of a transaction to ensure no residual permissions remain.
Underlying implementation:
All encrypted operations and access control functionalities are performed through the underlying Impl
library.
Uninitialized values:
Uninitialized encrypted values are treated as 0
(for integers) or false
(for booleans) in computations.
Implicit casting: Type conversion between encrypted integers of different bit widths is supported through implicit casting, allowing seamless operations without additional developer intervention.
This document provides an overview of the fhevmjs
library, detailing its initialization, instance creation, input handling, encryption, and re-encryption processes.
If you are using fhevmjs
in a web application, you need to initialize it before creating an instance. To do this, you should call initFhevm
and wait for the promise to resolve.
This function returns an instance of fhevmjs, which accepts an object containing:
kmsContractAddress
: the address of the KMSVerifier contract;
aclContractAddress
: the address of the ACL contract;
networkUrl
or network
: the URL or Eip1193 object provided by window.ethereum
- used to fetch chainId and KMS nodes' public key
gatewayUrl
: the URL of the gateway - used to retrieve the public key, ZKPoK public parameters and send inputs and get reencryption
chainId
(optional): the chainId of the network
publicKey
(optional): if the public key has been fetched separately or stored in cache, you can provide it
publicParams
(optional): if the public params has been fetched separately or stored in cache, you can provide it
Using window.ethereum
object:
This method creates an encrypted input and returns an input object. It requires both the user address and the contract address to ensure the encrypted input isn't reused inappropriately in a different context. An input can include multiple values of various types, resulting in a single ciphertext that packs these values.
Input object has different method to add values:
addBool
add4
add8
add16
add32
add64
add128
add256
addBytes64
addBytes128
addBytes256
addAddress
These methods process values and return the necessary data for use on the blockchain. The encrypt
method encrypts these values and provides parameters for use. The send
method encrypts, dispatches the ciphertext and proof to the coprocessor, and returns the required parameters.
A keypair consists of a private key and a public key, both generated by the dApp. These keys are used to reencrypt a blockchain ciphertext, allowing it to be securely transferred to user-specific keypairs.
Verifying that the public key used in the reencryption process belongs to the user requires the user to sign the public key linked to a specific contract address. This signature allows any ciphertext allowed for the user and the contract can be reencrypted using the signed public key. To streamline user interaction during the signature process, we utilize the EIP712 standard as the object to be signed.
This eip712
can be signed using eth_signTypedData_v4
for example in a browser:
Note: it is recommended to store the keypair and the signature in the user's browser to avoid re-requesting signature on every user connection.
Reencrypt method will use the gatewayUrl
to get the reencryption of a ciphertext and decrypt it.
In the next section, we will dive deeper into encryption, re-encryption, and decryption processes, including how they interact with the KMS and Gateway services. For more details, see .
to report bugs and typos, or to suggest new ideas
Request to become an official contributor by emailing .
- Zama's FHE Bounty Program
If you encounter issues with typing, you can use this using TypeScript 5.
Possible solutions: Use the with fhevmjs/bundle
. Embed the library with a <script>
tag and initialize it as shown below:
- Explore open challenges and submit contributions to earn rewards.
The third case is used to "encrypt" a public value so that it can be used as a ciphertext. Note that what we call a trivial encryption is not secure in any sense. When trivially encrypting a plaintext value, this value is still visible in the ciphertext bytes. More information about trivial encryption can be found .
More information about the behavior of these operators can be found at the .
This document gives a preview of the upcoming features of fhEVM. In addition to what's listed here, you can on GitHub.
is designed to assist in creating encrypted inputs and retrieving re-encryption data off-chain through a gateway. The library works with any fhEVM and fhEVM Coprocessors.
Smart contracts deployed on the blockchain to manage encrypted data and interactions.
Includes the Access Control List (ACL) contract, TFHE.sol
Solidity library, Gateway.sol
and other FHE-enabled smart contracts.
An off-chain service that bridges the blockchain with the cryptographic systems like KMS and coprocessor.
Acts as an intermediary to forward the necessary requests and results between the blockchain, the KMS, and users.
An off-chain computational engine designed to execute resource-intensive FHE operations.
Executes symbolic FHE operations, manages ciphertext storage, and ensures efficient computation handling.
A decentralized cryptographic service that securely manages FHE keys and validates operations.
Manages the global FHE key (public, private, evaluation), performs threshold decryption, and validates ZKPoKs.
Example FHE-enabled smart contracts
Hardhat template for FHE smart contract development
React.js template for building FHE dApps
Next.js template for FHE-enabled dApps
Vue.js template for developing FHE dApps
Remix IDE plugin for FHE development
Repository
Description
Solidity library for FHE operations
JavaScript library for client-side FHE
Rust backend and go-ethereum modules for native and coprocessor
Repository
Description
Go implementation of the FHE Virtual Machine
Modified go-ethereum with enhanced FHE support
Foundry template
Q1 '25
Signed Integers
eintX
Coming soon
Add w/ overflow check
TFHE.safeAdd
Binary, Decryption
Coming soon
Sub w/ overflow check
TFHE.safeSub
Binary, Decryption
Coming soon
Mul w/ overflow check
TFHE.safeMul
Binary, Decryption
Coming soon
Random signed int
TFHE.randEintX()
Random
-
Div
TFHE.div
Binary
-
Rem
TFHE.rem
Binary
-
Set inclusion
TFHE.isIn()
Binary
-
Smart contract
Learn core Solidity library.
Frontend
Write a dAPP frontend.
Tutorials
Build quickly with tutorials.
This page gives an overview of Fully Homomorphic Encryption (FHE) and its implementation on the blockchain by fhEVM. It provides the essential architectural concepts needed to start building with fhEVM.
FHE is an advanced cryptographic technique that allows computations to be performed directly on encrypted data, without the need for decryption. This ensures that data remains confidential throughout its entire lifecycle, even during processing.
With FHE:
Sensitive data can be securely encrypted while still being useful for computations.
The results of computations are encrypted, maintaining end-to-end privacy.
FHE operates using three types of keys, each playing a crucial role in its functionality:
Purpose: - for securely decrypting results - Decrypts ciphertexts to recover the original plaintext.
Usage in fhEVM: Managed securely by the Key Management System (KMS) using a threshold MPC protocol. This ensures no single entity ever possesses the full private key.
Purpose: - for encrypting data. - Encrypts plaintexts into ciphertexts.
Usage in fhEVM: Shared globally to allow users and smart contracts to encrypt inputs or states. It ensures that encrypted data can be processed without revealing the underlying information.
Purpose: - for performing encrypted computations - Enables efficient homomorphic operations (e.g., addition, multiplication) on ciphertexts.
Usage in fhEVM: Provided to FHE nodes (on-chain validators or off-chain coprocessors) to perform computations on encrypted data while preserving confidentiality.
These three keys work together to facilitate private and secure computations, forming the foundation of FHE-based systems like fhEVM.
At its core, the fhEVM is built on Zama's high-performance FHE library, TFHE-rs, written in Rust. This library implements the TFHE (Torus Fully Homomorphic Encryption) scheme and is designed to perform secure computations on encrypted data efficiently.
Info: For detailed documentation and implementation examples on the
tfhe-rs
library, visit the TFHE-rs documentation.
However, integrating a standalone FHE library like TFHE-rs into a blockchain environment involves unique challenges. Blockchain systems demand efficient processing, public verifiability, and seamless interoperability, all while preserving their decentralized nature. To address these requirements, Zama designed the fhEVM, a system that bridges the computational power of TFHE-rs with the transparency and scalability of blockchain technology.
Integrating FHE into blockchain systems posed several challenges that needed to be addressed to achieve the goals of confidentiality, composability, and scalability:
Transparency and privacy: Blockchains are inherently transparent, where all on-chain data is publicly visible. FHE solves this by keeping all sensitive data encrypted, ensuring privacy without sacrificing usability.
Public verifiability: On-chain computations need to be verifiable by all participants. This required a mechanism to confirm the correctness of encrypted computations without revealing their inputs or outputs.
Composability: Smart contracts needed to interact seamlessly with each other, even when operating on encrypted data.
Performance and scalability: FHE computations are resource-intensive, and blockchain systems require high throughput to remain practical.
To overcome these challenges, Zama introduced a hybrid architecture for fhEVM that combines:
On-chain functionality for managing state and enforcing access controls.
Off-chain processing via a coprocessor to execute resource-intensive FHE computations.