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 document provides guidance on using the new Zama plugin for the the official Remix IDE, which replaces the deprecated Remix fork. This allows you to develop and manage contracts directly in Remix by simply loading the fhEVM plugin.
Go to the "Plugin Manager" page
Click on "Connect to a Local Plugin"
Fill the name with "Zama" and the "Url" with "https://remix.zama.ai/"
Keep "Iframe" and "Side panel" and validate
After connecting to the Zama plugin, follow the steps to configure it:
Click on the plugin button located on the left of the screen
Use the Zama Coprocessor - Sepolia settings or use a custom gateway and contracts settings.
Afterwards, you will be able to deploy and use any contract that you chose to compile via this plugin interface.
This document introduces the fundamentals of writing confidential smart contracts using the fhEVM. You'll learn how to create contracts that can perform computations on encrypted data while maintaining data privacy.
In this guide, we'll walk through creating a basic smart contract that demonstrates core fhEVM concepts and encrypted operations.
Let’s build a simple Encrypted Counter smart contract to demonstrate the configuration process and the use of encrypted state variables.
Create a new file called EncryptedCounter.sol
in your contracts/
folder and add the following code:
Configuring fhEVM:
The contract inherits from SepoliaZamaFHEVMConfig
which provides the necessary configuration for local development and testing. This configuration includes the addresses of the TFHE library and Gateway contracts.
When deploying to different networks, you can use the appropriate configuration:
The configuration handles setting up:
TFHE library address for encrypted operations
Network-specific parameters
Initializing encrypted variables:
The counter
variable is set to an encrypted 0
using TFHE.asEuint8(0)
.
Permissions are granted to the contract itself for the counter
ciphertext using TFHE.allowThis(counter)
.
A constant CONST_ONE
is initialized as an encrypted value to represent the number 1
.
Encrypted operations:
The increment()
function adds the encrypted constant CONST_ONE
to the counter
using TFHE.add
.
There are two notable issues with this contract:
Counter value visibility: Since the counter is incremented by a fixed value, observers could deduce its value by analyzing blockchain events. To address this, see the documentation on:
Access control for counter
:
The counter is encrypted, but no access is granted to decrypt or view its value. Without proper ACL permissions, the counter remains inaccessible to users. To resolve this, refer to:
With any contracts that you write you will need to write tests as well. You can start by using something like this as a template:
The test file demonstrates key concepts for testing fhEVM smart contracts:
Test setup:
before
: Initializes test signers (users) that will interact with the contract
beforeEach
: Deploys a fresh instance of the contract before each test
Creates FHE instances for each signer to handle encryption/decryption
Test structure:
Key components:
createInstance()
: Sets up FHE instances for each signer to handle encrypted operations
getSigners()
: Provides test accounts to interact with the contract
contractFactory.deploy()
: Creates a new contract instance for testing
tx.wait()
: Ensures transactions are mined before continuing
Deploy fresh contract instances for each test to ensure isolation
Use descriptive test names that explain the expected behavior
Handle asynchronous operations properly with async/await
Set up proper encryption instances for testing encrypted values
Congratulations! You’ve configured and written your first confidential smart contract. Here are some ideas to expand your knowledge:
Explore advanced configurations: Customize the FHEVMConfig
to suit specific encryption requirements.
Add functionalities: Extend the contract by adding decrement functionality or resetting the counter.
Integrate frontend: Learn how to decrypt and display encrypted data in a dApp using the fhevmjs
library.
fhEVM is a protocol enabling confidential smart contracts on EVM-compatible blockchains. By leveraging Fully Homomorphic Encryption (FHE), fhEVM ensures complete data privacy without sacrificing composability or usability.
The design of fhEVM is guided by the following principles:
Preserving security: No impact on the underlying blockchain's security guarantees.
Public verifiability: All computations are publicly verifiable while keeping data confidential.
Developer accessibility: Build confidential smart contracts using familiar Solidity tooling, without requiring cryptographic expertise.
Composability: Confidential smart contracts are fully interoperable with each other and public contracts.
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.
For more information see use of encrypted types.
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
For more information see use of encrypted types.
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.
For more information on operations, see Operations on encrypted types.
For more information on conditional branching, see Conditional logic in FHE.
For more information on random number generation, see Generate Random Encrypted Numbers.
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.
For more information see ACL.
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.
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.
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
This guide walks you through developing secure and efficient confidential smart contracts using fhEVM. Whether you're new to Fully Homomorphic Encryption (FHE) or an experienced blockchain developer, we'll cover everything you need to know - from setting up your development environment to deploying your first encrypted contract.
If you need access to a Sepolia node and aren’t sure how to proceed, consider using a node provider like , Infura, or similar services. These providers offer easy setup and management, allowing you to create an API key to connect to the network seamlessly.
You can get test ETH for Sepolia from these faucets:
Alchemy Sepolia Faucet - https://www.alchemy.com/faucets/ethereum-sepolia
QuickNode Sepolia Faucet - https://faucet.quicknode.com/ethereum/sepolia
Most Ethereum wallets have built-in support for testnets like Sepolia. You can add Sepolia to your wallet in two ways:
Automatic configuration: Many wallets like MetaMask have Sepolia pre-configured. Simply open your network selector and choose "Show/hide test networks" to enable testnet visibility.
Manual configuration: The exact steps for manual configuration will vary by wallet, but generally involve:
Opening network settings
Selecting "Add Network" or "Add Network Manually"
Filling in the above information
Saving the configuration
Before diving into development, we recommend understanding the overall architecture of fhEVM:
This knowledge will help you make better design decisions when building your confidential smart contracts.
Why Hardhat?: It is a powerful Solidity development environment, offering tools for writing, testing, and deploying contracts to the fhEVM
using TypeScript.
Benefit: The template provides a pre-configured setup tailored to confidential smart contracts, saving development time and ensuring compatibility.
Choose and inherit the correct configuration based on the environment:
Mock network: For local testing and development.
Testnets (e.g., Sepolia): For deploying to public test networks.
Mainnet: When deploying to production.
Develop your contract as you would for a traditional EVM chain:
Use cleartext variables and basic logic to simplify debugging and reasoning about the contract’s behavior.
Focus on implementing core functionality without adding encryption initially.
Creating a basic encrypted counter contract
Understanding the configuration process
Working with encrypted state variables
Key resources for working with encrypted types:
Once the logic is stable, integrate the TFHE
Solidity library to enable encryption:
Convert sensitive variables: Replace plaintext types like uintX
with encrypted types such as euintX
.
Enable confidentiality: Encrypted variables and operations ensure sensitive data remains private while still being processed.
Learn how to implement core encryption operations:
Throughout the documentation, you'll find sections marked with 🔧 that highlight important best practices. These include:
Optimized data types: Use appropriately sized encrypted types (euint8
, euint16
, etc.) to minimize gas costs.
Scalar operands: Whenever possible, use scalar operands in operations to reduce computation and gas usage.
Overflow handling: Manage arithmetic overflows in encrypted operations using conditional logic (TFHE.select
).
Secure access control: Use TFHE.allow
and TFHE.isSenderAllowed
to implement robust ACL (Access Control List) mechanisms for encrypted values.
Reencryption patterns: Follow the recommended approaches for reencryption to share or repurpose encrypted data securely.
Why templates?: They demonstrate common patterns and best practices for encrypted operations, such as governance, token standards, and utility contracts.
How to use: Extend or customize these templates to suit your application’s needs.
Throughout the documentation you will encounter many Counter contract examples. These examples are designed to guide you step-by-step through the development of confidential smart contracts, introducing new concepts progressively to deepen your understanding of fhEVM.
The primary example, Counter.sol
, is enhanced in stages to demonstrate a variety of features and best practices for encrypted computations.
Each iteration of the counter will build upon previous concepts while introducing new functionality, helping you understand how to develop robust confidential smart contracts.
Save this in your .env
file:
This document provides guidance on using the fhEVM in other development environments.
The fhEVM does not work with Foundry as Foundry employs its own EVM, preventing us from incorporating a mock for our precompiled contract. An is exploring the possibility of incorporating a plugin system for precompiles, which could potentially pave the way for the utilization of Foundry at a later stage.
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 ). 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.
We want to hear from you! Take 1 minute to share your thoughts and helping us enhance our documentation and libraries. 👉 to participate.
: Learn how fhEVM enables confidential smart contracts
: Understand how data is encrypted, decrypted and computed
: Learn about managing access to encrypted data
Begin with our custom .
Ensure configuration contracts (e.g., SepoliaZamaFHEVMConfig
, SepoliaZamaFHEVMConfig
) are inherited correctly to initialize encryption parameters, cryptographic keys, and Gateway addresses. See for more details.
For a step-by-step guide on developing your first confidential smart contract, see our . This guide covers:
- Learn about euint8, euint16, euint32, euint64, ebool and eaddress
- Understand arithmetic, comparison, and logical operations on encrypted data
- Create and validate encrypted inputs
- Securely decrypt data for authorized users
- Share encrypted data between parties
Use the for pre-built examples:
For more details, explore the .
- Introduction to basic encrypted state variables and simple operations.
- Incorporating encrypted inputs into the contract for enhanced functionality.
- Introduction to decryption and how contracts can interact with the Gateway.
- Introduction to re-encryption, enabling secure sharing of encrypted data.
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 to start with fhEVM by using our Hardhat template.
This template allows you to launch an fhEVM Docker image and run your smart contract on it. For more information, refer to the README.
The Hardhat template comes pre-configured with tools and libraries to streamline the development process:
Default configurations: Includes sensible default configurations for tools like Prettier, Solhint, and ESLint.
GitHub actions: Pre-configured for CI/CD pipelines to lint and test contracts on every push or pull request.
Install pnpm for dependency management.
Set up a mnemonic: Create a .env
file by copying .env.example
:
Generate a mnemonic using this tool if needed.
Install dependencies: Ensure you’re using Node.js v20 or later:
Here’s a list of essential commands to work with the Hardhat template:
Command
Description
pnpm compile
Compiles the smart contracts.
pnpm typechain
Compiles contracts and generates TypeChain bindings.
pnpm test
Runs tests locally in mocked mode, simulating FHE operations.
pnpm lint:sol
Lints Solidity code.
pnpm lint:ts
Lints TypeScript code.
pnpm clean
Cleans contract artifacts, cache, and coverage reports.
pnpm coverage
Analyzes test coverage (mocked mode only).
Mocked mode allows faster testing by simulating FHE operations. This mode runs tests on a local Hardhat network without requiring a real fhEVM node. Use the following commands:
Run tests:
Analyze coverage:
Note: Mocked mode approximates gas consumption for FHE operations but may slightly differ from actual fhEVM behavior.
Non-mocked mode uses a real fhEVM node, such as the coprocessor on the Sepolia test network.
Run tests on Sepolia:
Requirements:
Fund test accounts on Sepolia.
Pass the correct FHEVMConfig
struct in your smart contract’s constructor.
Note: Refer to the fhEVM documentation for configuring
FHEVMConfig
.
For Solidity syntax highlighting, use the Hardhat Solidity extension for VSCode.
Due to limitations in the solidity-coverage
package, coverage computation in fhEVM does not support tests involving the evm_snapshot
Hardhat testing method. However, this method is still supported when running tests in mocked mode. If you are using Hardhat snapshots, we recommend to end your your test description with the [skip-on-coverage]
tag to to avoid coverage issues. Here is an example:
In this snippet, the first test will always run, whether in "real" non-mocked mode (pnpm test
), testing mocked mode (pnpm test
) or coverage (mocked) mode (pnpm coverage
). On the other hand, the second test will be run only in testing mocked mode(pnpm test
), because snapshots only works in that specific case. Actually, the second test will be skipped if run in coverage mode, since its description string ends with [skip-on-coverage]
and similarly, we avoid the test to fail in non-mocked mode since we check that the network name is hardhat
.
Due to intrinsic limitations of the original EVM, the mocked version differ in few corner cases from the real fhEVM, mainly in gas prices for the FHE operations. This means that before deploying to production, developers still need to run the tests with the original fhEVM node, as a final check in non-mocked mode, with pnpm test
or npx hardhat test
.
By using this Hardhat template, you can streamline your fhEVM development workflow and focus on building robust, privacy-preserving smart contracts. For additional details, visit the fhevm-hardhat-template README.
Zama 5-Question Developer Survey
We want to hear from you! Take 1 minute to share your thoughts and helping us enhance our documentation and libraries. 👉 Click here to participate.
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.
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:
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
Contribute to the development of FHE technologies and earn rewards through Zama’s bounty program:
bounty-program - Explore open challenges and submit contributions to earn rewards.
Access the essential libraries for building and integrating FHE-enabled applications:
Repository
Description
Solidity library for FHE operations
JavaScript library for client-side FHE
Rust backend and go-ethereum modules for native and coprocessor
Explore the foundational implementations enabling FHE integration with blockchain systems:
Repository
Description
Go implementation of the FHE Virtual Machine
Fork of go-ethereum v1.10.19 with FHE capabilities
Modified go-ethereum with enhanced FHE support
Use these repositories to accelerate your development, integrate FHE into your applications, or contribute to the growing ecosystem.
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.
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 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 in preformed 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 implemention 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 an detail explanantion of each components of fhEVM and illustrate how they work together to perform compuations.
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:
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.
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.
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 Decrypt and re-encrypt.
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 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 .
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.
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 tokens 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
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
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 .
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.
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
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
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 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 implementations, we suppose here that the 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.
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 prerequisit 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:
encyrptedERC20.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.
Now that we have new knowledge on how to add encrypted inputs, let's upgrade our counter contract.
The EncryptedCounter2
contract builds on the previous example by adding support for encrypted inputs. Here's how it works:
Encrypted state: Like before, the contract maintains an encrypted counter state variable of type euint8
.
Encrypted input handling: The incrementBy
function accepts two parameters:
einput amount
: An encrypted input handle representing the increment value
bytes calldata inputProof
: The zero-knowledge proof validating the encrypted input
Input processing: Inside incrementBy
:
The encrypted input is converted to a euint8
using TFHE.asEuint8()
This conversion validates the proof and creates a usable encrypted value
The value is then added to the counter using homomorphic addition
While we have resolved our problem with the Counter value visibility, there is still the problem with the Access Control for the counter
.
The counter is encrypted, but no access is granted to decrypt or view its value. Without proper ACL permissions, the counter remains inaccessible to users. To resolve this, refer to:
This guide explains how to use the fhEVM Contracts standard library. This library provides secure, extensible, and pre-tested Solidity templates designed for developing smart contracts on fhEVM using the TFHE library.
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:
For a list of all available contracts see the page See all tutorials
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 fulfilment, 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 fulfilment 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 the to 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 fulfilment 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 an 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 fulfilment 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 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.
Here’s an enhanced Encrypted Counter example where each user maintains their own encrypted counter. Re-encryption is used to securely share counter values with individual users.
Here’s a sample test to verify re-encryption functionality:
setupReencryption():
Prepares the re-encryption process by generating keys and a signature for the user.
instance.reencrypt():
Facilitates re-encryption and local decryption of the data for testing purposes.
Validation: Confirms that the decrypted counter matches the expected value.
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.
Remember our Encrypted Counter contract from before? Here’s an improved version of it, upgraded to support decryption:
EncryptedCounter3
Here’s a sample test for the Encrypted Counter contract using Hardhat:
Initialize the Gateway:
Request decryption and wait for results:
Verify the decrypted value:
Explore advanced decryption techniques and learn more about re-encryption:
This guide explains how to use the debug.decrypt[XX]
functions for debugging encrypted data in mocked environments during development with fhEVM.
[!WARNING] 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
[!NOTE] 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.
[!CAUTION] 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.
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]
.
For generating 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 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.
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 sultions: 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 sultions: 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.
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 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:
- Base error handling contract
- 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 botifications:
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 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.
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.
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 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 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
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 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
:
fhevm-contracts
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.
Zama 5-Question Developer Survey
This document provides an overview of the fhevmjs
library, detailing its initialization, instance creation, input handling, encryption, and re-encryption processes.
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.
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.
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:
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 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 .
You can now use your instance to or do a .
For more advanced features and examples, refer to the .
: 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 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
There are two ways to contribute to the Zama fhEVM:
Open issues to report bugs and typos, or to suggest new ideas
Request to become an official contributor by emailing hello@zama.ai.
Becoming an approved contributor involves signing our Contributor License Agreement (CLA)). Only approved contributors can send pull requests, so please make sure to get in touch before you do!
Solve challenges and earn rewards:
bounty-program - Zama's FHE Bounty Program
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 FHEPayment 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 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
)and
/or
/xor
26,000
not
30,000
Gas costs increase with the bit-width of the encrypted integer type. Below are the detailed costs for various operations on encrypted types.
euint4
)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
euint8
)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
euint16
)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
euint32
)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
euint64
)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
euint128
)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
euint256
)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
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 gives an preview of the upcoming features of fhEVM. In addition to what's listed here, you can submit your feature request on GitHub.
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
-
Random encrypted integers that are generated fully on-chain. Currently, implemented as a mockup by using a PRNG in the plain. Not for use in production!
Fundamentals
Explore core features.
Guides
Learn more about fhEVM implementation.
Tutorials
Build quickly with tutorials.