# Table of Contents
- [Welcome | Protocol](#welcome-protocol)
- [Welcome to Zama's documentation | Homepage](#welcome-to-zama-s-documentation-homepage)
- [Initialization | Protocol](#initialization-protocol)
- [Overview | Protocol](#overview-protocol)
- [Decryption | Protocol](#decryption-protocol)
- [Input | Protocol](#input-protocol)
- [Public decryption | Protocol](#public-decryption-protocol)
- [Debugging | Protocol](#debugging-protocol)
- [CLI | Protocol](#cli-protocol)
- [User decryption | Protocol](#user-decryption-protocol)
- [Zama Confidential Blockchain Protocol Litepaper | Protocol](#zama-confidential-blockchain-protocol-litepaper-protocol)
- [Web applications | Protocol](#web-applications-protocol)
- [FHE on blockchain | Protocol](#fhe-on-blockchain-protocol)
- [Host contracts | Protocol](#host-contracts-protocol)
- [Coprocessor | Protocol](#coprocessor-protocol)
- [Overview | Protocol](#overview-protocol)
- [Relayer & Oracle | Protocol](#relayer-oracle-protocol)
- [Decryption | Protocol](#decryption-protocol)
- [Public decryption | Protocol](#public-decryption-protocol)
- [Gateway | Protocol](#gateway-protocol)
- [Contributing | Protocol](#contributing-protocol)
- [KMS | Protocol](#kms-protocol)
- [Initialization | Protocol](#initialization-protocol)
- [FHE library | Protocol](#fhe-library-protocol)
- [Input | Protocol](#input-protocol)
- [Debugging | Protocol](#debugging-protocol)
- [CLI | Protocol](#cli-protocol)
- [Web applications | Protocol](#web-applications-protocol)
- [Roadmap | Protocol](#roadmap-protocol)
- [User decryption | Protocol](#user-decryption-protocol)
- [FHE Operations | Protocol](#fhe-operations-protocol)
- [Decryption | Protocol](#decryption-protocol)
- [Overview | Protocol](#overview-protocol)
- [Encryption | Protocol](#encryption-protocol)
- [FHE counter | Protocol](#fhe-counter-protocol)
- [Logics | Protocol](#logics-protocol)
- [If then else | Protocol](#if-then-else-protocol)
- [Encrypt multiple values | Protocol](#encrypt-multiple-values-protocol)
- [ERC7984 to ERC20 Wrapper | Protocol](#erc7984-to-erc20-wrapper-protocol)
- [Add | Protocol](#add-protocol)
- [Library installation and overview | Protocol](#library-installation-and-overview-protocol)
- [Encrypt single value | Protocol](#encrypt-single-value-protocol)
- [User decrypt multiple values | Protocol](#user-decrypt-multiple-values-protocol)
- [Contract addresses | Protocol](#contract-addresses-protocol)
- [Swap ERC7984 to ERC7984 | Protocol](#swap-erc7984-to-erc7984-protocol)
- [Swap ERC7984 to ERC20 | Protocol](#swap-erc7984-to-erc20-protocol)
- [What is FHEVM Solidity | Protocol](#what-is-fhevm-solidity-protocol)
- [Quick start tutorial | Protocol](#quick-start-tutorial-protocol)
- [Migrate to v0.7 | Protocol](#migrate-to-v0-7-protocol)
- [Generate random numbers | Protocol](#generate-random-numbers-protocol)
- [Hardhat plugin | Protocol](#hardhat-plugin-protocol)
- [Dealing with branches and conditions | Protocol](#dealing-with-branches-and-conditions-protocol)
- [Encrypted inputs | Protocol](#encrypted-inputs-protocol)
- [Set up Hardhat | Protocol](#set-up-hardhat-protocol)
- [Access Control List | Protocol](#access-control-list-protocol)
- [Error handling | Protocol](#error-handling-protocol)
- [Deploy contracts and run tests | Protocol](#deploy-contracts-and-run-tests-protocol)
- [Decryption | Protocol](#decryption-protocol)
- [ERC7984 Standard | Protocol](#erc7984-standard-protocol)
- [Overview | Protocol](#overview-protocol)
- [ACL examples | Protocol](#acl-examples-protocol)
- [Public Decrypt single value | Protocol](#public-decrypt-single-value-protocol)
- [Configuration | Protocol](#configuration-protocol)
- [Logics | Protocol](#logics-protocol)
- [Branching | Protocol](#branching-protocol)
- [Quick start tutorial | Protocol](#quick-start-tutorial-protocol)
- [How to Transform Your Smart Contract into a FHEVM Smart Contract? | Protocol](#how-to-transform-your-smart-contract-into-a-fhevm-smart-contract-protocol)
- [What is FHEVM Solidity | Protocol](#what-is-fhevm-solidity-protocol)
- [Reorgs handling | Protocol](#reorgs-handling-protocol)
- [Foundry | Protocol](#foundry-protocol)
- [Configuration | Protocol](#configuration-protocol)
- [Generate random numbers | Protocol](#generate-random-numbers-protocol)
- [User decrypt single value | Protocol](#user-decrypt-single-value-protocol)
- [AsEbool, asEuintXX, and asEaddress operations | Protocol](#asebool-aseuintxx-and-aseaddress-operations-protocol)
- [Access Control List | Protocol](#access-control-list-protocol)
- [Reorgs handling | Protocol](#reorgs-handling-protocol)
- [Supported types | Protocol](#supported-types-protocol)
- [3. Turn it into FHEVM | Protocol](#3-turn-it-into-fhevm-protocol)
- [Contract addresses | Protocol](#contract-addresses-protocol)
- [ERC7984 Tutorial | Protocol](#erc7984-tutorial-protocol)
- [Casting and trivial encryption | Protocol](#casting-and-trivial-encryption-protocol)
- [Operations on encrypted types | Protocol](#operations-on-encrypted-types-protocol)
- [Dealing with branches and conditions | Protocol](#dealing-with-branches-and-conditions-protocol)
- [Write FHEVM tests in Hardhat | Protocol](#write-fhevm-tests-in-hardhat-protocol)
- [Branching | Protocol](#branching-protocol)
- [Set up Hardhat | Protocol](#set-up-hardhat-protocol)
- [Supported types | Protocol](#supported-types-protocol)
- [How to Transform Your Smart Contract into a FHEVM Smart Contract? | Protocol](#how-to-transform-your-smart-contract-into-a-fhevm-smart-contract-protocol)
- [Migration guide | Protocol](#migration-guide-protocol)
- [Foundry | Protocol](#foundry-protocol)
- [3. Turn it into FHEVM | Protocol](#3-turn-it-into-fhevm-protocol)
- [Sealed-bid auction tutorial | Protocol](#sealed-bid-auction-tutorial-protocol)
- [4. Test the FHEVM contract | Protocol](#4-test-the-fhevm-contract-protocol)
- [Operations on encrypted types | Protocol](#operations-on-encrypted-types-protocol)
- [ACL examples | Protocol](#acl-examples-protocol)
- [2. Write a simple contract | Protocol](#2-write-a-simple-contract-protocol)
- [Public Decrypt multiple values | Protocol](#public-decrypt-multiple-values-protocol)
- [Hardhat plugin | Protocol](#hardhat-plugin-protocol)
- [Decryption | Protocol](#decryption-protocol)
- [2. Write a simple contract | Protocol](#2-write-a-simple-contract-protocol)
- [Deploy contracts and run tests | Protocol](#deploy-contracts-and-run-tests-protocol)
- [Write FHEVM tests in Hardhat | Protocol](#write-fhevm-tests-in-hardhat-protocol)
- [Write FHEVM-enabled Hardhat Tasks | Protocol](#write-fhevm-enabled-hardhat-tasks-protocol)
- [Encrypted inputs | Protocol](#encrypted-inputs-protocol)
- [Error handling | Protocol](#error-handling-protocol)
- [Contract addresses | Protocol](#contract-addresses-protocol)
- [Overview | Protocol](#overview-protocol)
- [Configuration | Protocol](#configuration-protocol)
- [Hardhat plugin | Protocol](#hardhat-plugin-protocol)
- [What is FHEVM Solidity | Protocol](#what-is-fhevm-solidity-protocol)
- [Quick start tutorial | Protocol](#quick-start-tutorial-protocol)
- [Generate random numbers | Protocol](#generate-random-numbers-protocol)
- [Supported types | Protocol](#supported-types-protocol)
- [Reorgs handling | Protocol](#reorgs-handling-protocol)
- [Logics | Protocol](#logics-protocol)
- [Foundry | Protocol](#foundry-protocol)
- [Decryption | Protocol](#decryption-protocol)
- [Migrate to v0.7 | Protocol](#migrate-to-v0-7-protocol)
- [Casting and trivial encryption | Protocol](#casting-and-trivial-encryption-protocol)
- [ACL examples | Protocol](#acl-examples-protocol)
- [How to Transform Your Smart Contract into a FHEVM Smart Contract? | Protocol](#how-to-transform-your-smart-contract-into-a-fhevm-smart-contract-protocol)
- [Encrypted inputs | Protocol](#encrypted-inputs-protocol)
- [Dealing with branches and conditions | Protocol](#dealing-with-branches-and-conditions-protocol)
- [Branching | Protocol](#branching-protocol)
- [Access Control List | Protocol](#access-control-list-protocol)
- [Operations on encrypted types | Protocol](#operations-on-encrypted-types-protocol)
- [Deploy contracts and run tests | Protocol](#deploy-contracts-and-run-tests-protocol)
- [Write FHEVM-enabled Hardhat Tasks | Protocol](#write-fhevm-enabled-hardhat-tasks-protocol)
- [Error handling | Protocol](#error-handling-protocol)
- [HCU | Protocol](#hcu-protocol)
- [3. Turn it into FHEVM | Protocol](#3-turn-it-into-fhevm-protocol)
- [Set up Hardhat | Protocol](#set-up-hardhat-protocol)
- [Write FHEVM tests in Hardhat | Protocol](#write-fhevm-tests-in-hardhat-protocol)
- [2. Write a simple contract | Protocol](#2-write-a-simple-contract-protocol)
- [HCU | Protocol](#hcu-protocol)
- [HCU | Protocol](#hcu-protocol)
- [4. Test the FHEVM contract | Protocol](#4-test-the-fhevm-contract-protocol)
- [Roadmap | Change Log](#roadmap-change-log)
- [Welcome to the Zama Programs Documentations | Community Docs](#welcome-to-the-zama-programs-documentations-community-docs)
- [Welcome to fhEVM | FHEVM](#welcome-to-fhevm-fhevm)
- [Welcome | Concrete ML](#welcome-concrete-ml)
- [Welcome to TFHE-rs | TFHE-rs](#welcome-to-tfhe-rs-tfhe-rs)
- [Welcome | Concrete](#welcome-concrete)
- [FHEVM v0.10 - October 2025 | Change Log](#fhevm-v0-10-october-2025-change-log)
- [TFHE-rs v1.4 - October 2025 | Change Log](#tfhe-rs-v1-4-october-2025-change-log)
- [FHEVM v0.7 - July 2025 | Change Log](#fhevm-v0-7-july-2025-change-log)
- [Zama Protocol Docs | FHEVM](#zama-protocol-docs-fhevm)
- [FHEVM v0.9 - October 2025 | Change Log](#fhevm-v0-9-october-2025-change-log)
- [FHEVM v0.8 - September 2025 | Change Log](#fhevm-v0-8-september-2025-change-log)
- [Overview | FHEVM](#overview-fhevm)
- [Remix | FHEVM](#remix-fhevm)
- [Quick Start | FHEVM](#quick-start-fhevm)
- [Hardhat | FHEVM](#hardhat-fhevm)
- [Frequently Asked Questions | Community Docs](#frequently-asked-questions-community-docs)
- [See all tutorials | FHEVM](#see-all-tutorials-fhevm)
- [Frequently Asked Questions | Community Docs](#frequently-asked-questions-community-docs)
- [Frequently Asked Questions | Community Docs](#frequently-asked-questions-community-docs)
- [Configuration | FHEVM](#configuration-fhevm)
- [Decryption | FHEVM](#decryption-fhevm)
- [ACL examples | FHEVM](#acl-examples-fhevm)
- [FhEVM contracts | FHEVM](#fhevm-contracts-fhevm)
- [Decryption in depth | FHEVM](#decryption-in-depth-fhevm)
- [Supported types | FHEVM](#supported-types-fhevm)
- [Decryption | FHEVM](#decryption-fhevm)
- [Encrypted Inputs | FHEVM](#encrypted-inputs-fhevm)
- [Re-encryption | FHEVM](#re-encryption-fhevm)
- [Key features | FHEVM](#key-features-fhevm)
- [Access Control List | FHEVM](#access-control-list-fhevm)
- [TFHE-rs v1.1 - April 2025 | Change Log](#tfhe-rs-v1-1-april-2025-change-log)
- [TFHE-rs v1.0 January 2025 | Change Log](#tfhe-rs-v1-0-january-2025-change-log)
- [TFHE-rs v1.2 - May 2025 | Change Log](#tfhe-rs-v1-2-may-2025-change-log)
- [TFHE-rs v1.3 - July 2025 | Change Log](#tfhe-rs-v1-3-july-2025-change-log)
- [Email Protection | Cloudflare](#email-protection-cloudflare)
- [Welcome to TFHE-rs | TFHE-rs](#welcome-to-tfhe-rs-tfhe-rs)
- [Welcome to TFHE-rs | TFHE-rs](#welcome-to-tfhe-rs-tfhe-rs)
---
# Welcome | Protocol
**Welcome to the Zama Confidential Blockchain Protocol Docs.** The docs aim to guide you to build confidential dApps on top of any L1 or L2 using Fully Homomorphic Encryption (FHE).
[](https://docs.zama.ai/protocol#where-to-go-next)
Where to go next
------------------------------------------------------------------------
If you're completely new to FHE or the Zama Protocol, we suggest first checking out the [Litepaper](https://docs.zama.ai/protocol/zama-protocol-litepaper)
, which offers a thorough overview of the protocol.
Otherwise:
🟨 Go to [**Quick Start**](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial)
to learn how to write your first confidential smart contract using FHEVM.
🟨 Go to [**Solidity Guides**](https://docs.zama.ai/protocol/solidity-guides)
to explore how encrypted types, operations, ACLs, and other core features work in practice.
🟨 Go to [**Relayer SDK Guides**](https://docs.zama.ai/protocol/relayer-sdk-guides)
to build a frontend that can encrypt, decrypt, and interact securely with the blockchain.
🟨 Go to [**FHE on Blockchain**](https://docs.zama.ai/protocol/protocol/overview)
to learn the architecture in depth and understand how encrypted computation flows through both on-chain and off-chain components.
🟨 Go to [**Examples**](https://docs.zama.ai/protocol/examples)
to find reference and inspiration from smart contract examples and dApp examples.
The Zama Protocol Testnet is not audited and is not intended for production use. **Do not publish any critical or sensitive data**. For production workloads, please wait for the Mainnet release.
[](https://docs.zama.ai/protocol#help-center)
Help center
--------------------------------------------------------------
Ask technical questions and discuss with the community.
* [Community forum](https://community.zama.ai/c/fhevm/15)
* [Discord channel](https://discord.com/invite/zama)
Last updated 21 days ago
---
# Welcome to Zama's documentation | Homepage
[](https://docs.zama.ai/homepage#zama-confidential-blockchain-protocol)
Zama Confidential Blockchain Protocol
------------------------------------------------------------------------------------------------------------------
* * *
A suite of tools and libraries for building confidential smart contracts and dApps on any chains.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial)

**Quick Start**
Build your first contract step by step.
[](https://docs.zama.ai/protocol/solidity-guides)

**Solidity Guides**
Write contracts to compute on encrypted data.
[](https://docs.zama.ai/protocol/relayer-sdk-guides)

**Relayer SDK Guides**
Create frontends that interact with contracts.
[](https://docs.zama.ai/protocol/protocol/overview)

**FHE on Blockchain**
Explore the architecture and core components.
[](https://docs.zama.ai/protocol/examples/)

**Examples**
Review code examples for developers.
[](https://docs.zama.ai/protocol/zama-protocol-litepaper)

**Zama Protocol Litepaper**
Read the litepaper for protocol details.
Read the documentation about the Zama Developer and Creator program [here](https://docs.zama.ai/programs/)
.
[](https://docs.zama.ai/homepage#libraries)
Libraries
----------------------------------------------------------
* * *
Open-source libraries that support FHE computations.
[](https://docs.zama.ai/tfhe-rs)

**TFHE-rs**
Rust implementation of TFHE.
[](https://docs.zama.ai/concrete)

**Concrete**
TFHE Compiler in Python.
[](https://docs.zama.ai/concrete-ml)

**Concrete ML**
Machine learning models in FHE.
[](https://docs.zama.ai/homepage#supports)
Supports
--------------------------------------------------------
* * *
* [Community forum](https://community.zama.ai/)
* [Discord channel](https://discord.com/invite/zama)
* [Telegram](https://t.me/+Ojt5y-I7oR42MTkx)
Last updated 1 month ago
Was this helpful?
---
# Initialization | Protocol
The use of `@zama-fhe/relayer-sdk` requires a setup phase. This consists in the instantiation of the `FhevmInstance`. This object holds all the configuration and methods needed to interact with an FHEVM using a Relayer. It can be created using the following code snippet:
Copy
import { createInstance } from "@zama-fhe/relayer-sdk";
const instance = await createInstance({
// ACL_CONTRACT_ADDRESS (FHEVM Host chain)
aclContractAddress: "0x687820221192C5B662b25367F70076A37bc79b6c",
// KMS_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
kmsContractAddress: "0x1364cBBf2cDF5032C47d8226a6f6FBD2AFCDacAC",
// INPUT_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
inputVerifierContractAddress: "0xbc91f3daD1A5F19F8390c400196e58073B6a0BC4",
// DECRYPTION_ADDRESS (Gateway chain)
verifyingContractAddressDecryption: "0xb6E160B1ff80D67Bfe90A85eE06Ce0A2613607D1",
// INPUT_VERIFICATION_ADDRESS (Gateway chain)
verifyingContractAddressInputVerification: "0x7048C39f048125eDa9d678AEbaDfB22F7900a29F",
// FHEVM Host chain id
chainId: 11155111,
// Gateway chain id
gatewayChainId: 55815,
// Optional RPC provider to host chain
network: "https://eth-sepolia.public.blastapi.io",
// Relayer URL
relayerUrl: "https://relayer.testnet.zama.cloud",
});
or the even simpler:
Copy
import { createInstance, SepoliaConfig } from "@zama-fhe/relayer-sdk";
const instance = await createInstance(SepoliaConfig);
The information regarding the configuration of Sepolia's FHEVM and associated Relayer maintained by Zama can be found in the `SepoliaConfig` object or in the [contract addresses page](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure/contract_addresses)
. The `gatewayChainId` is `55815`. The `chainId` is the chain-id of the FHEVM chain, so for Sepolia it would be `11155111`.
For more information on the Relayer's part in the overall architecture please refer to [the Relayer's page in the architecture documentation](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle)
.
[PreviousOverview](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1)
[NextInput](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/input)
Last updated 1 month ago
---
# Overview | Protocol
**Welcome to the Relayer SDK Docs.**
This section provides an overview of the key features of Zama’s FHEVM Relayer JavaScript SDK. The SDK lets you interact with FHEVM smart contracts without dealing directly with the [Gateway Chain](https://docs.zama.ai/protocol/protocol/overview/gateway)
.
With the Relayer, FHEVM clients only need a wallet on the FHEVM host chain. All interactions with the Gateway chain are handled through HTTP calls to Zama's Relayer, which pays for it on the Gateway chain.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1#where-to-go-next)
Where to go next
------------------------------------------------------------------------------------------------
If you’re new to the Zama Protocol, start with the [Litepaper](https://docs.zama.ai/protocol/zama-protocol-litepaper)
or the [Protocol Overview](https://docs.zama.ai/protocol)
to understand the foundations.
Otherwise:
🟨 Go to [**Setup guide**](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/initialization)
to learn how to configure the Relayer SDK for your project.
🟨 Go to [**Input registration**](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/input)
to see how to register new encrypted inputs for your smart contracts.
🟨 Go to [**User decryption**](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/user-decryption)
to enable users to decrypt data with their own keys, once permissions have been granted via Access Control List(ACL).
🟨 Go to [**Public decryption**](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/public-decryption)
to learn how to decrypt outputs that are publicly accessible, either via HTTP or onchain Oracle.
🟨 Go to [**Solidity ACL Guide**](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl)
for more detailed instructions about access control.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1#help-center)
Help center
--------------------------------------------------------------------------------------
Ask technical questions and discuss with the community.
* [Community forum](https://community.zama.ai/c/fhevm/15)
* [Discord channel](https://discord.com/invite/zama)
[NextInitialization](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/initialization)
Last updated 1 month ago
---
# Decryption | Protocol
[User decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/user-decryption)
[Public decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/public-decryption)
[PreviousInput](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/input)
[NextUser decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/user-decryption)
Last updated 1 month ago
---
# Input | Protocol
This document explains how to register ciphertexts to the FHEVM. Registering ciphertexts to the FHEVM allows for future use on-chain using the `FHE.fromExternal` solidity function. All values encrypted for use with the FHEVM are encrypted under a public key of the protocol.
Copy
// We first create a buffer for values to encrypt and register to the fhevm
const buffer = instance.createEncryptedInput(
// The address of the contract allowed to interact with the "fresh" ciphertexts
contractAddress,
// The address of the entity allowed to import ciphertexts to the contract at `contractAddress`
userAddress,
);
// We add the values with associated data-type method
buffer.add64(BigInt(23393893233));
buffer.add64(BigInt(1));
// buffer.addBool(false);
// buffer.add8(BigInt(43));
// buffer.add16(BigInt(87));
// buffer.add32(BigInt(2339389323));
// buffer.add128(BigInt(233938932390));
// buffer.addAddress('0xa5e1defb98EFe38EBb2D958CEe052410247F4c80');
// buffer.add256(BigInt('2339389323922393930'));
// This will encrypt the values, generate a proof of knowledge for it, and then upload the ciphertexts using the relayer.
// This action will return the list of ciphertext handles.
const ciphertexts = await buffer.encrypt();
With a contract `MyContract` that implements the following it is possible to add two "fresh" ciphertexts.
Copy
contract MyContract {
...
function add(
externalEuint64 a,
externalEuint64 b,
bytes calldata proof
) public virtual returns (euint64) {
return FHE.add(FHE.fromExternal(a, proof), FHE.fromExternal(b, proof))
}
}
With `my_contract` the contract in question using `ethers` it is possible to call the add function as following.
Copy
my_contract.add(ciphertexts.handles[0], ciphertexts.handles[1], ciphertexts.inputProof);
[PreviousInitialization](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/initialization)
[NextDecryption](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption)
Last updated 1 month ago
---
# Public decryption | Protocol
This document explains how to perform public decryption of FHEVM ciphertexts. Public decryption is required when you want everyone to see the value in a ciphertext, for example the result of private auction. Public decryption can be done with either the Relayer HTTP endpoint or calling the on-chain decryption oracle.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/public-decryption#http-public-decrypt)
HTTP Public Decrypt
-------------------------------------------------------------------------------------------------------------------------------------------------
Calling the public decryption endpoint of the Relayer can be done easily using the following code snippet.
Copy
// A list of ciphertexts handles to decrypt
const handles = [\
"0x830a61b343d2f3de67ec59cb18961fd086085c1c73ff0000000000aa36a70000",\
"0x98ee526413903d4613feedb9c8fa44fe3f4ed0dd00ff0000000000aa36a70400",\
"0xb837a645c9672e7588d49c5c43f4759a63447ea581ff0000000000aa36a70700",\
];
// The list of decrypted values
// {
// '0x830a61b343d2f3de67ec59cb18961fd086085c1c73ff0000000000aa36a70000': true,
// '0x98ee526413903d4613feedb9c8fa44fe3f4ed0dd00ff0000000000aa36a70400': 242n,
// '0xb837a645c9672e7588d49c5c43f4759a63447ea581ff0000000000aa36a70700': '0xfC4382C084fCA3f4fB07c3BCDA906C01797595a8'
// }
const values = instance.publicDecrypt(handles);
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/public-decryption#onchain-public-decrypt)
Onchain Public Decrypt
-------------------------------------------------------------------------------------------------------------------------------------------------------
For more details please refer to the on [onchain Oracle public decryption page](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle)
.
[PreviousUser decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/user-decryption)
[NextWeb applications](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webapp)
Last updated 1 month ago
---
# Debugging | Protocol
This document provides solutions for common Webpack errors encountered during the development process. Follow the steps below to resolve each issue.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webpack#cant-resolve-tfhe_bg.wasm)
Can't resolve 'tfhe\_bg.wasm'
------------------------------------------------------------------------------------------------------------------------------------------------
**Error message:** `Module not found: Error: Can't resolve 'tfhe_bg.wasm'`
**Cause:** In the codebase, there is a `new URL('tfhe_bg.wasm')` which triggers a resolve by Webpack.
**Possible solutions:** You can add a fallback for this file by adding a resolve configuration in your `webpack.config.js`:
Copy
resolve: {
fallback: {
'tfhe_bg.wasm': require.resolve('tfhe/tfhe_bg.wasm'),
},
},
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webpack#buffer-not-defined)
Buffer not defined
------------------------------------------------------------------------------------------------------------------------------
**Error message:** `ReferenceError: Buffer is not defined`
**Cause:** This error occurs when the Node.js `Buffer` object is used in a browser environment where it is not natively available.
**Possible solutions:** To resolve this issue, you need to provide browser-compatible fallbacks for Node.js core modules. Install the necessary browserified npm packages and configure Webpack to use these fallbacks.
Copy
resolve: {
fallback: {
buffer: require.resolve('buffer/'),
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
path: require.resolve('path-browserify'),
},
},
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webpack#issue-with-importing-esm-version)
Issue with importing ESM version
----------------------------------------------------------------------------------------------------------------------------------------------------------
**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 issues with typing, you can use this [tsconfig.json](https://github.com/zama-ai/fhevm-react-template/blob/main/tsconfig.json)
using TypeScript 5.
* If you encounter any other issue, you can force import of the browser package.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webpack#use-bundled-version)
Use bundled version
--------------------------------------------------------------------------------------------------------------------------------
**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.
**Possible solutions:** Use the [prebundled version available](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webapp)
with `@zama-fhe/relayer-sdk/bundle`. Embed the library with a `
In your project, you can use the bundle import if you install `@zama-fhe/relayer-sdk` package:
Copy
import { initSDK, createInstance, SepoliaConfig } from "@zama-fhe/relayer-sdk/bundle";
####
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webapp#using-esm-cdn)
Using ESM CDN
If you prefer You can also use the `@zama-fhe/relayer-sdk` as a ES module:
Copy
####
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webapp#using-npm-package)
Using npm package
Install the `@zama-fhe/relayer-sdk` library to your project:
Copy
# Using npm
npm install @zama-fhe/relayer-sdk
# Using Yarn
yarn add @zama-fhe/relayer-sdk
# Using pnpm
pnpm add @zama-fhe/relayer-sdk
`@zama-fhe/relayer-sdk` uses ESM format. You need to set the [type to "module" in your package.json](https://nodejs.org/api/packages.html#type)
. If your node project use `"type": "commonjs"` or no type, you can force the loading of the web version by using `import { createInstance } from '@zama-fhe/relayer-sdk/web';`
Copy
import { initSDK, createInstance, SepoliaConfig } from "@zama-fhe/relayer-sdk";
###
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webapp#step-2-initialize-your-project)
Step 2: Initialize your project
To use the library in your project, you need to load the WASM of [TFHE](https://www.npmjs.com/package/tfhe)
first with `initSDK`.
Copy
import { initSDK } from "@zama-fhe/relayer-sdk/bundle";
const init = async () => {
await initSDK(); // Load needed WASM
};
###
[](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webapp#step-3-create-an-instance)
Step 3: Create an instance
Once the WASM is loaded, you can now create an instance.
Copy
import { initSDK, createInstance, SepoliaConfig } from "@zama-fhe/relayer-sdk/bundle";
const init = async () => {
await initSDK(); // Load FHE
const config = { ...SepoliaConfig, network: window.ethereum };
return createInstance(config);
};
init().then((instance) => {
console.log(instance);
});
You can now use your instance to [encrypt parameters](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/input)
, perform [user decryptions](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/user-decryption)
or [public decryptions](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/public-decryption)
.
[PreviousPublic decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/fhevm-relayer/decryption/public-decryption)
[NextDebugging](https://docs.zama.ai/protocol/relayer-sdk-guides/v0.1/development-guide/webpack)
Last updated 1 month ago
---
# FHE on blockchain | Protocol
This section explains in depth the Zama Confidential Blockchain Protocol (Zama Protocol) and demonstrates how it can bring encrypted computation to smart contracts using Fully Homomorphic Encryption (FHE).
FHEVM is the core technology that powers the Zama Protocol. It is composed of the following key components.

* [**FHEVM Solidity library**](https://docs.zama.ai/protocol/protocol/overview/library)
: Enables developers to write confidential smart contracts in plain Solidity using encrypted data types and operations.
* [**Host contracts**](https://docs.zama.ai/protocol/protocol/overview/hostchain)
: Trusted on-chain contracts deployed on EVM-compatible blockchains. They manage access control and trigger off-chain encrypted computation.
* [**Coprocessors**](https://docs.zama.ai/protocol/protocol/overview/coprocessor)
– Decentralized services that verify encrypted inputs, run FHE computations, and commit results.
* [**Gateway**](https://docs.zama.ai/protocol/protocol/overview/gateway)
**–** The central orchestrator of the protocol. It validates encrypted inputs, manages access control lists (ACLs), bridges ciphertexts across chains, and coordinates coprocessors and the KMS.
* [**Key Management Service (KMS)**](https://docs.zama.ai/protocol/protocol/overview/kms)
– A threshold MPC network that generates and rotates FHE keys, and handles secure, verifiable decryption.
* [**Relayer & oracle**](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle)
– A lightweight off-chain service that helps users interact with the Gateway by forwarding encryption or decryption requests.
[PreviousWelcome](https://docs.zama.ai/protocol)
[NextFHE library](https://docs.zama.ai/protocol/protocol/overview/library)
Last updated 3 months ago
---
# Host contracts | Protocol
This document explains one of the key components of the Zama Protocol - Host contracts.
[](https://docs.zama.ai/protocol/protocol/overview/hostchain#what-are-host-contracts)
What are host contracts?
-------------------------------------------------------------------------------------------------------------------
Host contracts are smart contracts deployed on any supported blockchain (EVM or non-EVM) that act as trusted bridges between on-chain applications and the FHEVM protocol. They serve as the minimal and foundational interface that confidential smart contracts use to:
* Interact with encrypted data (handles)
* Perform access control operations
* Emit events for the off-chain components (coprocessors, Gateway)
These host contracts are used indirectly by developers via the FHEVM Solidity library, abstracting away complexity and integrating smoothly into existing workflows.
[](https://docs.zama.ai/protocol/protocol/overview/hostchain#responsibilities-of-host-contracts)
Responsibilities of host contracts
----------------------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/protocol/overview/hostchain#trusted-interface-layer)
Trusted interface layer
Host contracts are the only on-chain components that:
* Maintain and enforce Access Control Lists (ACLs) for ciphertexts.
* Emit events that trigger coprocessor execution.
* Validate access permissions (persistent, transient, or decryption-related).
They are effectively the on-chain authority for:
* Who is allowed to access a ciphertext
* When and how they can use it
* These ACLs are mirrored on the Gateway for off-chain enforcement and bridging.
###
[](https://docs.zama.ai/protocol/protocol/overview/hostchain#access-control-api)
Access Control API
Host contracts expose access control logic via standardized function calls (wrapped by the FHEVM library):
* `allow(handle, address)`: Grants persistent access.
* `allowTransient(handle, address)`: Grants temporary access for a single transaction.
* `allowForDecryption(handle)`: Marks a handle as publicly decryptable.
* `isAllowed(handle, address)`: Returns whether a given address has access.
* `isSenderAllowed(handle)`: Checks if msg.sender is allowed to use a handle.
They also emit:
* `Allowed(handle, address)`
* `AllowedForDecryption(handle)`
These events are crucial for triggering coprocessor state updates and ensuring proper ACL replication to the Gateway.
→ See the full guide of [ACL](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl)
.
###
[](https://docs.zama.ai/protocol/protocol/overview/hostchain#security-role)
Security role
Although the FHE computation happens off-chain, host contracts play a critical role in protocol security by:
* Enforcing ACL-based gating
* Ensuring only authorized contracts and users can decrypt or use a handle
* Preventing misuse of encrypted data (e.g., computation without access)
Access attempts without proper authorization are rejected at the smart contract level, protecting both the integrity of confidential operations and user privacy.
[PreviousFHE library](https://docs.zama.ai/protocol/protocol/overview/library)
[NextCoprocessor](https://docs.zama.ai/protocol/protocol/overview/coprocessor)
Last updated 3 months ago
---
# Coprocessor | Protocol
This document explains one of the key components of the Zama Protocol - Coprocessor, the Zama Protocol’s off-chain computation engine.
[](https://docs.zama.ai/protocol/protocol/overview/coprocessor#what-is-the-coprocessor)
What is the Coprocessor?
---------------------------------------------------------------------------------------------------------------------
Coprocessor performs the heavy cryptographic operations—specifically, fully homomorphic encryption (FHE) computations—on behalf of smart contracts that operate on encrypted data. Acting as a decentralized compute layer, the coprocessor bridges symbolic on-chain logic with real-world encrypted execution.
Coprocessor works together with the Gateway, verifying encrypted inputs, executing FHE instructions, and maintaining synchronization of access permissions, in particula
* Listens to events emitted by host chains and the Gateway.
* Executes FHE computations (`add`, `mul`, `div`, `cmp`, etc.) on ciphertexts.
* Validates encrypted inputs and ZK proofs of correctness.
* Maintains and updates a replica of the host chain’s Access Control Lists (ACLs).
* Stores and serves encrypted data for decryption or bridging.
Each coprocessor independently executes tasks and publishes verifiable results, enabling a publicly auditable and horizontally scalable confidential compute infrastructure .
[](https://docs.zama.ai/protocol/protocol/overview/coprocessor#responsibilities-of-the-coprocessor)
Responsibilities of the Coprocessor
--------------------------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/protocol/overview/coprocessor#encrypted-input-verification)
Encrypted input verification
When users submit encrypted values to the Gateway, each coprocessor:
* Verifies the associated Zero-Knowledge Proof of Knowledge (ZKPoK).
* Extracts and unpacks individual ciphertexts from a packed submission.
* Stores the ciphertexts under derived handles.
* Signs the verified handles, embedding user and contract metadata.
* Sends the signed data back to the Gateway for consensus.
This ensures only valid, well-formed encrypted values enter the system .
###
[](https://docs.zama.ai/protocol/protocol/overview/coprocessor#fhe-computation-execution)
FHE computation execution
When a smart contract executes a function over encrypted values, the on-chain logic emits symbolic computation events. Each coprocessor:
* Reads these events from the host chain node it runs.
* Fetches associated ciphertexts from its storage.
* Executes the required FHE operations using the TFHE-rs library (e.g., add, mul, select).
* Stores the resulting ciphertext under a deterministically derived handle.
* Optionally publishes a commitment (digest) of the ciphertext to the Gateway for verifiability.
This offloads expensive computation from the host chain while maintaining full determinism and auditability .
###
[](https://docs.zama.ai/protocol/protocol/overview/coprocessor#acl-replication)
ACL replication
Coprocessors replicate the Access Control List (ACL) logic from host contracts. They:
* Listen to Allowed and AllowedForDecryption events.
* Push updates to the Gateway.
This ensures decentralized enforcement of access rights, enabling proper handling of decryptions, bridges, and contract interactions .
###
[](https://docs.zama.ai/protocol/protocol/overview/coprocessor#ciphertext-commitment)
Ciphertext commitment
To ensure verifiability and mitigate misbehavior, each coprocessor:
* Commits to ciphertext digests (via hash) when processing Allowed events.
* Publishes these commitments to the Gateway.
* Enables external verification of FHE computations.
This is essential for fraud-proof mechanisms and eventual slashing of malicious or faulty operators .
###
[](https://docs.zama.ai/protocol/protocol/overview/coprocessor#bridging-and-decryption-support)
Bridging & decryption support
Coprocessors assist in:
* Bridging encrypted values between host chains by generating new handles and signatures.
* Preparing ciphertexts for public and user decryption using operations like Switch-n-Squash to normalize ciphertexts for the KMS.
These roles help maintain cross-chain interoperability and enable privacy-preserving data access for users and smart contracts .
[](https://docs.zama.ai/protocol/protocol/overview/coprocessor#security-and-trust-assumptions)
Security and trust assumptions
----------------------------------------------------------------------------------------------------------------------------------
Coprocessors are designed to be minimally trusted and publicly verifiable. Every FHE computation or input verification they perform is accompanied by a cryptographic commitment (hash digest) and a signature, allowing anyone to independently verify correctness.
The protocol relies on a majority-honest assumption: as long as more than 50% of coprocessors are honest, results are valid. The Gateway aggregates responses and accepts outputs only when a majority consensus is reached.
To enforce honest behavior, coprocessors must stake $ZAMA tokens and are subject to slashing if caught misbehaving—either through automated checks or governance-based fraud proofs.
This model ensures correctness through transparency, resilience through decentralization, and integrity through economic incentives.
[](https://docs.zama.ai/protocol/protocol/overview/coprocessor#architecture-and-scalability)
Architecture & Scalability
----------------------------------------------------------------------------------------------------------------------------
The coprocessor architecture includes:
* Event listeners for host chains and the Gateway
* A task queue for FHE and ACL update jobs
* Worker threads that process tasks in parallel
* A public storage layer (e.g., S3) for ciphertext availability
This modular setup supports horizontal scaling: adding more workers or machines increases throughput. Symbolic computation and delayed execution also ensure low gas costs on-chain .
[PreviousHost contracts](https://docs.zama.ai/protocol/protocol/overview/hostchain)
[NextGateway](https://docs.zama.ai/protocol/protocol/overview/gateway)
Last updated 3 months ago
---
# Overview | Protocol
**Welcome to the Relayer SDK Docs.**
This section provides an overview of the key features of Zama’s FHEVM Relayer JavaScript SDK. The SDK lets you interact with FHEVM smart contracts without dealing directly with the [Gateway Chain](https://docs.zama.ai/protocol/protocol/overview/gateway)
.
With the Relayer, FHEVM clients only need a wallet on the FHEVM host chain. All interactions with the Gateway chain are handled through HTTP calls to Zama's Relayer, which pays for it on the Gateway chain.
[](https://docs.zama.ai/protocol/relayer-sdk-guides#where-to-go-next)
Where to go next
-------------------------------------------------------------------------------------------
If you’re new to the Zama Protocol, start with the [Litepaper](https://docs.zama.ai/protocol/zama-protocol-litepaper)
or the [Protocol Overview](https://docs.zama.ai/protocol)
to understand the foundations.
Otherwise:
🟨 Go to [**Setup guide**](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/initialization)
to learn how to configure the Relayer SDK for your project.
🟨 Go to [**Input registration**](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/input)
to see how to register new encrypted inputs for your smart contracts.
🟨 Go to [**User decryption**](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/user-decryption)
to enable users to decrypt data with their own keys, once permissions have been granted via Access Control List(ACL).
🟨 Go to [**Public decryption**](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/public-decryption)
to learn how to decrypt outputs that are publicly accessible, either via HTTP or onchain Oracle.
🟨 Go to [**Solidity ACL Guide**](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl)
for more detailed instructions about access control.
[](https://docs.zama.ai/protocol/relayer-sdk-guides#help-center)
Help center
---------------------------------------------------------------------------------
Ask technical questions and discuss with the community.
* [Community forum](https://community.zama.ai/c/fhevm/15)
* [Discord channel](https://discord.com/invite/zama)
[NextInitialization](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/initialization)
Last updated 1 month ago
---
# Relayer & Oracle | Protocol
This document explains the service interface of the Zama Protocol - Relayer & Oracle.
[](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle#what-is-the-oracle)
What is the Oracle?
--------------------------------------------------------------------------------------------------------------
The Oracle is an off-chain service that acts on behalf of smart contracts to retrieve decrypted values from the FHEVM protocol.
While the FHEVM protocol’s core components handle encryption, computation, and key management, Oracles and Relayers provide the necessary connectivity between users, smart contracts, and the off-chain infrastructure. They act as lightweight services that interface with the Gateway, enabling smooth interaction with encrypted values—without requiring users or contracts to handle complex integration logic.
These components are not part of the trusted base of the protocol; their actions are fully verifiable, and their misbehavior does not compromise confidentiality or correctness.
[](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle#responsibilities-of-the-oracle)
Responsibilities of the Oracle
-------------------------------------------------------------------------------------------------------------------------------------
* Listen for on-chain decryption requests from contracts.
* Forward decryption requests to the Gateway on behalf of the contract.
* Wait for the KMS to produce signed plaintexts via the Gateway.
* Call back the contract on the host chain, passing the decrypted result.
Since the decrypted values are signed by the KMS, the receiving smart contract can verify the result, removing any need to trust the oracle itself.
[](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle#security-model-of-the-oracle)
Security model of the Oracle
---------------------------------------------------------------------------------------------------------------------------------
* Oracles are **untrusted**: they can only delay a request, not falsify it.
* All results are signed and verifiable on-chain.
If one oracle fails to respond, another can take over.
Goal: Enable contracts to access decrypted values asynchronously and securely, without embedding decryption logic.
[](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle#what-is-the-relayer)
What is the Relayer?
----------------------------------------------------------------------------------------------------------------
The Relayer is a user-facing service that simplifies interaction with the Gateway, particularly for encryption and decryption operations that need to happen off-chain.
[](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle#responsibilities-of-the-relayer)
Responsibilities of the Relayer
---------------------------------------------------------------------------------------------------------------------------------------
* Send encrypted inputs from the user to the Gateway for registration.
* Initiate user-side decryption requests, including EIP-712 authentication.
* Collect the response from the KMS, re-encrypted under the user’s public key.
* Deliver the ciphertext back to the user, who decrypts it locally in their browser/app.
This allows users to interact with encrypted smart contracts without having to run their own Gateway interface, validator, or FHE tooling.
[](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle#security-model-of-the-relayer)
Security model of the Relayer
-----------------------------------------------------------------------------------------------------------------------------------
* Relayers are stateless and **untrusted**.
* All data flows are signed and auditable by the user.
* Users can always run their own relayer or interact with the Gateway directly if needed.
Goal: Make it easy for users to submit encrypted inputs and retrieve private decrypted results without managing infrastructure.
[](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle#how-they-fit-in)
How they fit in
-------------------------------------------------------------------------------------------------------
* Smart contracts use the Oracle to receive plaintext results of encrypted computations via callbacks.
* Users rely on the Relayer to push encrypted values into the system and fetch personal decrypted results, all backed by EIP-712 signatures and FHE key re-encryption.
Together, Oracles and Relayers help bridge the gap between encrypted execution and application usability—without compromising security or decentralization.
[PreviousKMS](https://docs.zama.ai/protocol/protocol/overview/kms)
[NextRoadmap](https://docs.zama.ai/protocol/protocol/roadmap)
Last updated 3 months ago
---
# Decryption | Protocol
[User decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/user-decryption)
[Public decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/public-decryption)
[PreviousInput](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/input)
[NextUser decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/user-decryption)
Last updated 1 month ago
---
# Public decryption | Protocol
This document explains how to perform public decryption of FHEVM ciphertexts. Public decryption is required when you want everyone to see the value in a ciphertext, for example the result of private auction. Public decryption can be done with either the Relayer HTTP endpoint or calling the on-chain decryption oracle.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/public-decryption#http-public-decrypt)
HTTP Public Decrypt
--------------------------------------------------------------------------------------------------------------------------------------------
Calling the public decryption endpoint of the Relayer can be done easily using the following code snippet.
Copy
// A list of ciphertexts handles to decrypt
const handles = [\
'0x830a61b343d2f3de67ec59cb18961fd086085c1c73ff0000000000aa36a70000',\
'0x98ee526413903d4613feedb9c8fa44fe3f4ed0dd00ff0000000000aa36a70400',\
'0xb837a645c9672e7588d49c5c43f4759a63447ea581ff0000000000aa36a70700',\
];
// The list of decrypted values
// {
// '0x830a61b343d2f3de67ec59cb18961fd086085c1c73ff0000000000aa36a70000': true,
// '0x98ee526413903d4613feedb9c8fa44fe3f4ed0dd00ff0000000000aa36a70400': 242n,
// '0xb837a645c9672e7588d49c5c43f4759a63447ea581ff0000000000aa36a70700': '0xfC4382C084fCA3f4fB07c3BCDA906C01797595a8'
// }
const values = instance.publicDecrypt(handles);
[](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/public-decryption#onchain-public-decrypt)
Onchain Public Decrypt
--------------------------------------------------------------------------------------------------------------------------------------------------
For more details please refer to the on [onchain Oracle public decryption page](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle)
.
[PreviousUser decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/user-decryption)
[NextWeb applications](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webapp)
Last updated 1 month ago
---
# Gateway | Protocol
This document explains one of the key components of the Zama Protocol - Gateway, the central orchestrator within Zama’s FHEVM protocol, coordinates interactions between users, host chains, coprocessors, and the Key Management Service (KMS), ensuring that encrypted data flows securely and correctly through the system.
[](https://docs.zama.ai/protocol/protocol/overview/gateway#what-is-the-gateway)
What is the Gateway?
---------------------------------------------------------------------------------------------------------
The Gateway is a specialized blockchain component (implemented as an Arbitrum rollup) responsible for managing:
* Validation of encrypted inputs from users and applications.
* Bridging of encrypted ciphertexts across different blockchains.
* Decryption orchestration via KMS nodes.
* Consensus enforcement among decentralized coprocessors.
* Staking and reward distribution to operators participating in FHE computations.
It is designed to be trust-minimized: computations are independently verifiable, and no sensitive data or decryption keys are stored on the Gateway itself.
[](https://docs.zama.ai/protocol/protocol/overview/gateway#responsibilities-of-the-gateway)
Responsibilities of the Gateway
--------------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/protocol/overview/gateway#encrypted-input-validation)
Encrypted input validation
The Gateway ensures that encrypted values provided by users are well-formed and valid. It does this by:
* Accepting encrypted inputs along with Zero-Knowledge Proofs of Knowledge (ZKPoKs).
* Emitting verification events for coprocessors to validate.
* Aggregating signatures from a majority of coprocessors to generate attestations, which can then be used on-chain as trusted external values.
###
[](https://docs.zama.ai/protocol/protocol/overview/gateway#access-control-coordination)
Access Control coordination
The Gateway maintains a synchronized copy of Access Control Lists (ACLs) from host chains, enabling it to independently determine if decryption or computation rights should be granted for a ciphertext. This helps enforce:
* Access permissions (allow)
* Public decryption permissions (allowForDecryption)
These ACL updates are replicated by coprocessors and pushed to the Gateway for verification and enforcement.
###
[](https://docs.zama.ai/protocol/protocol/overview/gateway#decryption-orchestration)
Decryption orchestration
When a smart contract or user requests the decryption of an encrypted value:
1. The Gateway verifies ACL permissions.
2. It then triggers the KMS to decrypt (either publicly or privately).
3. Once the KMS returns signed results, the Gateway emits events that can be picked up by an oracle (for smart contract decryption) or returned to the user (for private decryption).
This ensures asynchronous, secure, and auditable decryption without the Gateway itself knowing the plaintext.
###
[](https://docs.zama.ai/protocol/protocol/overview/gateway#cross-chain-bridging)
Cross-chain bridging
The Gateway also handles bridging of encrypted handles between host chains. It:
* Verifies access rights on the source chain using its ACL copy.
* Requests the coprocessors to compute new handles for the target chain.
* Collects signatures from coprocessors.
Issues attestations allowing these handles to be used on the destination chain.
###
[](https://docs.zama.ai/protocol/protocol/overview/gateway#consensus-and-slashing-enforcement)
Consensus and slashing enforcement
The Gateway enforces consensus across decentralized coprocessors and KMS nodes. If discrepancies occur:
* Coprocessors must provide commitments to ciphertexts.
* Fraudulent or incorrect behavior can be challenged and slashed.
* Governance mechanisms can be triggered for off-chain verification when necessary.
###
[](https://docs.zama.ai/protocol/protocol/overview/gateway#protocol-administration)
Protocol administration
The Gateway runs smart contracts that administer:
* Operator and participant registration (coprocessors, KMS nodes, host chains)
* Key management and rotation
* Bridging logic
* Input validation and decryption workflows
[](https://docs.zama.ai/protocol/protocol/overview/gateway#security-and-trust-assumptions)
Security and trust assumptions
------------------------------------------------------------------------------------------------------------------------------
The Gateway is designed to operate without requiring trust:
* It does not perform any computation itself—it merely orchestrates and validates.
* All actions are signed, and cryptographic verification is built into every step.
The protocol assumes no trust in the Gateway for security guarantees—it can be fully audited and replaced if necessary.
[PreviousCoprocessor](https://docs.zama.ai/protocol/protocol/overview/coprocessor)
[NextKMS](https://docs.zama.ai/protocol/protocol/overview/kms)
Last updated 3 months ago
---
# Contributing | Protocol
There are two ways to contribute to FHEVM:
* [Open issues](https://github.com/zama-ai/fhevm/issues/new/choose)
to report bugs and typos, or to suggest new ideas
* Request to become an official contributor by emailing [\[email protected\]](https://docs.zama.ai/cdn-cgi/l/email-protection#4e262b2222210e342f232f602f27)
.
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!
[](https://docs.zama.ai/protocol/developer/contribute#zama-bounty-program)
Zama Bounty Program
---------------------------------------------------------------------------------------------------
Solve challenges and earn rewards:
* [bounty-program](https://github.com/zama-ai/bounty-program)
- Zama's FHE Bounty Program
[PreviousRoadmap](https://docs.zama.ai/protocol/protocol/roadmap)
Last updated 3 months ago
---
# KMS | Protocol
This document explains one of the key components of the Zama Protocol - The Key Management Service (KMS), responsible for the secure generation, management, and usage of FHE keys needed to enable confidential smart contracts.
[](https://docs.zama.ai/protocol/protocol/overview/kms#what-is-the-kms)
What is the KMS?
---------------------------------------------------------------------------------------------
The KMS is a decentralized network of several nodes (also called "parties") that run an MPC (Multi-Party Computation) protocol:
* Securely generate global FHE keys
* Decrypt ciphertexts securely for public and user-targeted decryptions
* Support zero-knowledge proof infrastructure
* Manage key lifecycles with NIST compliance
It works entirely off-chain, but is orchestrated through the Gateway, which initiates and tracks all key-related operations. This separation of powers ensures strong decentralization and auditability.
[](https://docs.zama.ai/protocol/protocol/overview/kms#key-responsibilities)
Key responsibilities
------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/protocol/overview/kms#fhe-threshold-key-generation)
FHE threshold key generation
* The KMS securely generates a global public/private key pair used across all host chains.
* This key enables composability — encrypted data can be shared between contracts and chains.
* The private FHE key is never directly accessible by a single party; instead, it is secret-shared among the MPC nodes.
The system follows the NIST SP 800-57 key lifecycle model, managing key states such as Active, Suspended, Deactivated,and Destroyed to ensure proper rotation and forward security.
###
[](https://docs.zama.ai/protocol/protocol/overview/kms#threshold-decryption-via-mpc)
Threshold Decryption via MPC
The KMS performs decryption using a threshold decryption protocol — at least a minimum number of MPC parties (e.g., 9 out of 13) must participate in the protocol to robustly decrypt a value.
* This protects against compromise: no individual party has access to the full key. And adversary would need to control more than the threshold of KMS nodes to influence the system.
* The protocol supports both:
* Public decryption (e.g., for smart contracts)
* User decryption (privately returned, re-encrypted only for the user to access)
All decryption operation outputs are signed by each node and the output can be verified on-chain for full auditability.
###
[](https://docs.zama.ai/protocol/protocol/overview/kms#zk-proof-support)
ZK Proof support
The KMS generates Common Reference Strings (CRS) needed to validate Zero-Knowledge Proofs of Knowledge (ZKPoK) when users submit encrypted values.
This ensures encrypted inputs are valid and well-formed, and that a user has knowledge of the plaintext contained in the submitted input ciphertext.
[](https://docs.zama.ai/protocol/protocol/overview/kms#security-architecture)
Security architecture
--------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/protocol/overview/kms#mpc-based-key-sharing)
MPC-based key sharing
* The KMS currently uses 13 MPC nodes, operated by different reputable organizations.
* Private keys are split using threshold secret sharing.
* Communication between nodes are secured using mTLS with gRPC.
###
[](https://docs.zama.ai/protocol/protocol/overview/kms#honest-majority-assumption)
Honest majority assumption
* The protocol is robust against malicious actors as long as at most 1/3 of the nodes act maliciously.
* It supports guaranteed output delivery even if some nodes are offline or misbehaving.
###
[](https://docs.zama.ai/protocol/protocol/overview/kms#secure-execution-environments)
Secure execution environments
Each MPC node runs by default inside an AWS Nitro Enclave, a secure execution environment that prevents even node operators from accessing their own key shares. This design mitigates insider risks, such as unauthorized key reconstruction or selling of shares.
###
[](https://docs.zama.ai/protocol/protocol/overview/kms#auditable-via-gateway)
Auditable via gateway
* All operations are broadcast through the Gateway and recorded as blockchain events.
* KMS responses are signed, allowing smart contracts and users to verify results cryptographically.
###
[](https://docs.zama.ai/protocol/protocol/overview/kms#key-lifecycle-management)
Key lifecycle management
The KMS adheres to a formal key lifecycle, as per NIST SP 800-57:
State
Description
Pre-activation
Key is created but not in use.
Active
Key is used for encryption and decryption.
Suspended
Temporarily replaced during rotation. Still usable for decryption.
Deactivated
Archived; only used for decryption.
Compromised
Flagged for misuse; only decryption allowed.
Destroyed
Key material is deleted permanently.
The KMS supports key switching using FHE, allowing ciphertexts to be securely transferred between keys during rotation. This maintains interoperability across key updates.
###
[](https://docs.zama.ai/protocol/protocol/overview/kms#backup-and-recovery)
Backup & recovery
In addition to robustness through MPC, the KMS also offers a custodial backup system:
* Each MPC node splits its key share into encrypted fragments, distributing them to independent custodians.
* If a share is lost, a quorum of custodians can collaboratively restore it, ensuring recovery even if several MPC nodes are offline.
* This approach guarantees business continuity and resilience against outages.
* All recovery operations require a quorum of operators and are fully auditable on-chain.
###
[](https://docs.zama.ai/protocol/protocol/overview/kms#workflow-example-public-decryption)
Workflow example: Public decryption
1. A smart contract requests decryption via an oracle.
2. The Gateway verifies permissions (i.e. that the contract is allowed to decrypt the ciphertext) and emits an event.
3. KMS parties retrieve the ciphertext, verify it, and run the MPC decryption protocol to jointly compute the plaintext and sign their result.
4. Once a quorum agrees on the plaintext result, it is published (with signatures).
5. The oracle posts the plaintext back on-chain and contracts can verify the authenticity using the KMS signatures.
[PreviousGateway](https://docs.zama.ai/protocol/protocol/overview/gateway)
[NextRelayer & Oracle](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle)
Last updated 3 months ago
---
# Initialization | Protocol
The use of `@zama-fhe/relayer-sdk` requires a setup phase. This consists of the instantiation of the `FhevmInstance`. This object holds all the configuration and methods needed to interact with an FHEVM using a Relayer. It can be created using the following code snippet:
Copy
import { createInstance } from '@zama-fhe/relayer-sdk';
const instance = await createInstance({
// ACL_CONTRACT_ADDRESS (FHEVM Host chain)
aclContractAddress: '0x687820221192C5B662b25367F70076A37bc79b6c',
// KMS_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
kmsContractAddress: '0x1364cBBf2cDF5032C47d8226a6f6FBD2AFCDacAC',
// INPUT_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
inputVerifierContractAddress: '0xbc91f3daD1A5F19F8390c400196e58073B6a0BC4',
// DECRYPTION_ADDRESS (Gateway chain)
verifyingContractAddressDecryption:
'0xb6E160B1ff80D67Bfe90A85eE06Ce0A2613607D1',
// INPUT_VERIFICATION_ADDRESS (Gateway chain)
verifyingContractAddressInputVerification:
'0x7048C39f048125eDa9d678AEbaDfB22F7900a29F',
// FHEVM Host chain id
chainId: 11155111,
// Gateway chain id
gatewayChainId: 55815,
// Optional RPC provider to host chain
network: 'https://eth-sepolia.public.blastapi.io',
// Relayer URL
relayerUrl: 'https://relayer.testnet.zama.cloud',
});
or the even simpler:
Copy
import { createInstance, SepoliaConfig } from '@zama-fhe/relayer-sdk';
const instance = await createInstance(SepoliaConfig);
The information regarding the configuration of Sepolia's FHEVM and associated Relayer maintained by Zama can be found in the `SepoliaConfig` object or in the [contract addresses page](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure/contract_addresses)
. The `gatewayChainId` is `55815`. The `chainId` is the chain-id of the FHEVM chain, so for Sepolia it would be `11155111`.
For more information on the Relayer's part in the overall architecture please refer to [the Relayer's page in the architecture documentation](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle)
.
[PreviousOverview](https://docs.zama.ai/protocol/relayer-sdk-guides)
[NextInput](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/input)
Last updated 1 month ago
---
# FHE library | Protocol
This document offers a high-level overview of the **FHEVM library**, helping you understand how it fits into the broader Zama Protocol. To learn how to use it in practice, see the [Solidity Guides](https://docs.zama.ai/protocol/solidity-guides)
.
[](https://docs.zama.ai/protocol/protocol/overview/library#what-is-fhevm-library)
What is FHEVM library?
-------------------------------------------------------------------------------------------------------------
The FHEVM library enables developers to build smart contracts that operate on encrypted data—without requiring any knowledge of cryptography.
It extends the standard Solidity development flow with:
* Encrypted data types
* Arithmetic, logical, and conditional operations on encrypted values
* Fine-grained access control
* Secure input handling and attestation support
This library serves as an **abstraction layer** over Fully Homomorphic Encryption (FHE) and interacts seamlessly with off-chain components such as the **Coprocessors** and the **Gateway**.
[](https://docs.zama.ai/protocol/protocol/overview/library#key-features)
Key features
------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/protocol/overview/library#encrypted-data-types)
Encrypted data types
The library introduces encrypted variants of common Solidity types, implemented as user-defined value types. Internally, these are represented as `bytes32` handles that point to encrypted values stored off-chain.
Category
Types
Booleans
`ebool`
Unsigned integers
`euint8`, `euint16`, ..., `euint256`
Signed integers
`eint8`, `eint16,` ..., `eint256`
Addresses
`eaddress`
→ See the full guide of [Encrypted data types](https://docs.zama.ai/protocol/solidity-guides/smart-contract/types)
.
###
[](https://docs.zama.ai/protocol/protocol/overview/library#fhe-operations)
FHE operations
Each encrypted type supports operations similar to its plaintext counterpart:
* Arithmetic: `add`, `sub`, `mul`, `div`, `rem`, `neg`
* Logic: `and`, `or`, `xor`, `not`
* Comparison: `lt`, `gt`, `le`, `ge`, `eq`, `ne`, `min`, `max`
* Bit manipulation: `shl`, `shr`, `rotl`, `rotr`
These operations are symbolically executed on-chain by generating new handles and emitting events for coprocessors to process the actual FHE computation off-chain.
Example:
Copy
function compute(euint64 x, euint64 y, euint64 z) public returns (euint64) {
euint64 result = FHE.mul(FHE.add(x, y), z);
return result;
}
→ See the full guide of [Operations on encrypted types](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations)
.
###
[](https://docs.zama.ai/protocol/protocol/overview/library#branching-with-encrypted-conditions)
Branching with encrypted Conditions
Direct if or require statements are not compatible with encrypted booleans. Instead, the library provides a `select`operator to emulate conditional logic without revealing which branch was taken:
Copy
ebool condition = FHE.lte(x, y);
euint64 result = FHE.select(condition, valueIfTrue, valueIfFalse);
This preserves confidentiality even in conditional logic.
→ See the full guide of [Branching](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions)
.
###
[](https://docs.zama.ai/protocol/protocol/overview/library#handling-external-encrypted-inputs)
Handling external encrypted inputs
When users want to pass encrypted inputs (e.g., values they’ve encrypted off-chain or bridged from another chain), they provide:
* external values
* A list of coprocessor signatures (attestation)
The function `fromExternal` is used to validate the attestation and extract a usable encrypted handle:
Copy
function handleInput(externalEuint64 param1, externalEbool param2, bytes calldata attestation) public {
euint64 val = FHE.fromExternal(param1, attestation);
ebool flag = FHE.fromExternal(param2, attestation);
}
This ensures that only authorized, well-formed ciphertexts are accepted by smart contracts.
→ See the full guide of [Encrypted input](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs)
.
###
[](https://docs.zama.ai/protocol/protocol/overview/library#access-control)
Access control
The FHE library also exposes methods for managing access to encrypted values using the ACL maintained by host contracts:
* `allow(handle, address)`: Grant persistent access
* `allowTransient(handle, address)`: Grant access for the current transaction only
* `allowForDecryption(handle)`: Make handle publicly decryptable
* `isAllowed(handle, address)`: Check if address has access
* `isSenderAllowed(handle)`: Shortcut for checking msg.sender permissions
These `allow` methods emit events consumed by the coprocessors to replicate the ACL state in the Gateway.
→ See the full guide of [ACL](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl)
.
###
[](https://docs.zama.ai/protocol/protocol/overview/library#pseudo-random-encrypted-values)
Pseudo-random encrypted values
The library allows generation of pseudo-random encrypted integers, useful for games, lotteries, or randomized logic:
* `randEuintXX()`
* `randEuintXXBounded`(uint bound)
These are deterministic across coprocessors and indistinguishable to external observers.
→ See the full guide of [Generate random number](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random)
.
[PreviousFHE on blockchain](https://docs.zama.ai/protocol/protocol/overview)
[NextHost contracts](https://docs.zama.ai/protocol/protocol/overview/hostchain)
Last updated 3 months ago
---
# Input | Protocol
This document explains how to register ciphertexts to the FHEVM. Registering ciphertexts to the FHEVM allows for future use on-chain using the `FHE.fromExternal` solidity function. All values encrypted for use with the FHEVM are encrypted under a public key of the protocol.
Copy
// We first create a buffer for values to encrypt and register to the fhevm
const buffer = instance.createEncryptedInput(
// The address of the contract allowed to interact with the "fresh" ciphertexts
contractAddress,
// The address of the entity allowed to import ciphertexts to the contract at `contractAddress`
userAddress,
);
// We add the values with associated data-type method
buffer.add64(BigInt(23393893233));
buffer.add64(BigInt(1));
// buffer.addBool(false);
// buffer.add8(BigInt(43));
// buffer.add16(BigInt(87));
// buffer.add32(BigInt(2339389323));
// buffer.add128(BigInt(233938932390));
// buffer.addAddress('0xa5e1defb98EFe38EBb2D958CEe052410247F4c80');
// buffer.add256(BigInt('2339389323922393930'));
// This will encrypt the values, generate a proof of knowledge for it, and then upload the ciphertexts using the relayer.
// This action will return the list of ciphertext handles.
const ciphertexts = await buffer.encrypt();
With a contract `MyContract` that implements the following it is possible to add two "fresh" ciphertexts.
Copy
contract MyContract {
...
function add(
externalEuint64 a,
externalEuint64 b,
bytes calldata proof
) public virtual returns (euint64) {
return FHE.add(FHE.fromExternal(a, proof), FHE.fromExternal(b, proof))
}
}
With `my_contract` the contract in question using `ethers` it is possible to call the add function as following.
Copy
my_contract.add(
ciphertexts.handles[0],
ciphertexts.handles[1],
ciphertexts.inputProof,
);
[PreviousInitialization](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/initialization)
[NextDecryption](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption)
Last updated 1 month ago
---
# Debugging | Protocol
This document provides solutions for common Webpack errors encountered during the development process. Follow the steps below to resolve each issue.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webpack#cant-resolve-tfhe_bg.wasm)
Can't resolve 'tfhe\_bg.wasm'
-------------------------------------------------------------------------------------------------------------------------------------------
**Error message:** `Module not found: Error: Can't resolve 'tfhe_bg.wasm'`
**Cause:** In the codebase, there is a `new URL('tfhe_bg.wasm')` which triggers a resolve by Webpack.
**Possible solutions:** You can add a fallback for this file by adding a resolve configuration in your `webpack.config.js`:
Copy
resolve: {
fallback: {
'tfhe_bg.wasm': require.resolve('tfhe/tfhe_bg.wasm'),
},
},
[](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webpack#buffer-not-defined)
Buffer not defined
-------------------------------------------------------------------------------------------------------------------------
**Error message:** `ReferenceError: Buffer is not defined`
**Cause:** This error occurs when the Node.js `Buffer` object is used in a browser environment where it is not natively available.
**Possible solutions:** To resolve this issue, you need to provide browser-compatible fallbacks for Node.js core modules. Install the necessary browserified npm packages and configure Webpack to use these fallbacks.
Copy
resolve: {
fallback: {
buffer: require.resolve('buffer/'),
crypto: require.resolve('crypto-browserify'),
stream: require.resolve('stream-browserify'),
path: require.resolve('path-browserify'),
},
},
[](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webpack#issue-with-importing-esm-version)
Issue with importing ESM version
-----------------------------------------------------------------------------------------------------------------------------------------------------
**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 issues with typing, you can use this [tsconfig.json](https://github.com/zama-ai/fhevm-react-template/blob/main/packages/site/tsconfig.json)
using TypeScript 5.
* If you encounter any other issue, you can force import of the browser package.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webpack#use-bundled-version)
Use bundled version
---------------------------------------------------------------------------------------------------------------------------
**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.
**Possible solutions:** Use the [prebundled version available](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webapp)
with `@zama-fhe/relayer-sdk/bundle`. Embed the library with a `
In your project, you can use the bundle import if you install `@zama-fhe/relayer-sdk` package:
Copy
import {
initSDK,
createInstance,
SepoliaConfig,
} from '@zama-fhe/relayer-sdk/bundle';
####
[](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webapp#using-esm-cdn)
Using ESM CDN
If you prefer You can also use the `@zama-fhe/relayer-sdk` as a ES module:
Copy
####
[](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webapp#using-npm-package)
Using npm package
Install the `@zama-fhe/relayer-sdk` library to your project:
Copy
# Using npm
npm install @zama-fhe/relayer-sdk
# Using Yarn
yarn add @zama-fhe/relayer-sdk
# Using pnpm
pnpm add @zama-fhe/relayer-sdk
`@zama-fhe/relayer-sdk` uses ESM format. You need to set the [type to "module" in your package.json](https://nodejs.org/api/packages.html#type)
. If your node project use `"type": "commonjs"` or no type, you can force the loading of the web version by using `import { createInstance } from '@zama-fhe/relayer-sdk/web';`
Copy
import { initSDK, createInstance, SepoliaConfig } from '@zama-fhe/relayer-sdk';
###
[](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webapp#step-2-initialize-your-project)
Step 2: Initialize your project
To use the library in your project, you need to load the WASM of [TFHE](https://www.npmjs.com/package/tfhe)
first with `initSDK`.
Copy
import { initSDK } from '@zama-fhe/relayer-sdk/bundle';
const init = async () => {
await initSDK(); // Load needed WASM
};
###
[](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webapp#step-3-create-an-instance)
Step 3: Create an instance
Once the WASM is loaded, you can now create an instance.
Copy
import {
initSDK,
createInstance,
SepoliaConfig,
} from '@zama-fhe/relayer-sdk/bundle';
const init = async () => {
await initSDK(); // Load FHE
const config = { ...SepoliaConfig, network: window.ethereum };
return createInstance(config);
};
init().then((instance) => {
console.log(instance);
});
You can now use your instance to [encrypt parameters](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/input)
, perform [user decryptions](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/user-decryption)
or [public decryptions](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/public-decryption)
.
[PreviousPublic decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/public-decryption)
[NextDebugging](https://docs.zama.ai/protocol/relayer-sdk-guides/development-guide/webpack)
Last updated 1 month ago
---
# Roadmap | Protocol
This document gives a preview of the upcoming features of FHEVM. In addition to what's listed here, you can [submit your feature request](https://github.com/zama-ai/fhevm/issues/new)
on GitHub.
[](https://docs.zama.ai/protocol/protocol/roadmap#features)
Features
-------------------------------------------------------------------------
Name
Description
ETA
Foundry template
[Forge](https://book.getfoundry.sh/reference/forge/forge)
Q1 '25
[](https://docs.zama.ai/protocol/protocol/roadmap#operations)
Operations
-----------------------------------------------------------------------------
Name
Function name
Type
ETA
Signed Integers
`eintX`
Coming soon
Add w/ overflow check
`FHE.safeAdd`
Binary, Decryption
Coming soon
Sub w/ overflow check
`FHE.safeSub`
Binary, Decryption
Coming soon
Mul w/ overflow check
`FHE.safeMul`
Binary, Decryption
Coming soon
Random signed int
`FHE.randEintX()`
Random
\-
Div
`FHE.div`
Binary
\-
Rem
`FHE.rem`
Binary
\-
Set inclusion
`FHE.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!
[PreviousRelayer & Oracle](https://docs.zama.ai/protocol/protocol/overview/relayer_oracle)
[NextContributing](https://docs.zama.ai/protocol/developer/contribute)
Last updated 3 months ago
---
# User decryption | Protocol
This document explains how to perform user decryption. User decryption is required when you want a user to access their private data without it being exposed to the blockchain.
User decryption 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.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/user-decryption#when-to-use-user-decryption)
When to use user decryption
----------------------------------------------------------------------------------------------------------------------------------------------------------
User decryption is particularly useful for **allowing individual users to securely access and decrypt their private data**, such as balances or counters, while maintaining data confidentiality.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/user-decryption#overview)
Overview
--------------------------------------------------------------------------------------------------------------------
The user decryption process involves retrieving ciphertext from the blockchain and performing user-decryption 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 user's 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.
User decryption is facilitated by the **Relayer** and the **Key Management System (KMS)**. The workflow consists of the following:
1. Retrieving the ciphertext from the blockchain using a contract’s view function.
2. Re-encrypting the ciphertext client-side with the user’s public key, ensuring only the user can decrypt it.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/user-decryption#step-1-retrieve-the-ciphertext)
Step 1: retrieve the ciphertext
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
To retrieve the ciphertext that needs to be decrypted, you can implement a view function in your smart contract. Below is an example implementation:
Copy
import "@fhevm/solidity/lib/FHE.sol";
contract ConfidentialERC20 {
...
function balanceOf(account address) public view returns (euint64) {
return balances[msg.sender];
}
...
}
Here, `balanceOf` allows retrieval of the user’s encrypted balance handle stored on the blockchain. Doing this will return the ciphertext handle, an identifier for the underlying ciphertext.
For the user to be able to user decrypt (also called re-encrypt) the ciphertext value the access control (ACL) needs to be set properly using the `FHE.allow(ciphertext, address)` function in the solidity contract holding the ciphertext.
For more details on the topic please refer to [the ACL documentation](https://docs.zama.ai/protocol/solidity-guides/solidity-guides/smart-contract/acl)
.
[](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/user-decryption#step-2-decrypt-the-ciphertext)
Step 2: decrypt the ciphertext
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Using that ciphertext handle user decryption is performed client-side using the `@zama-fhe/relayer-sdk` library. The user needs to have created an instance object prior to that (for more context see [the relayer-sdk setup page](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/initialization)
).
Copy
// instance: [`FhevmInstance`] from `zama-fhe/relayer-sdk`
// signer: [`Signer`] from ethers (could a [`Wallet`])
// ciphertextHandle: [`string`]
// contractAddress: [`string`]
const keypair = instance.generateKeypair();
const handleContractPairs = [\
{\
handle: ciphertextHandle,\
contractAddress: contractAddress,\
},\
];
const startTimeStamp = Math.floor(Date.now() / 1000).toString();
const durationDays = '10'; // String for consistency
const contractAddresses = [contractAddress];
const eip712 = instance.createEIP712(
keypair.publicKey,
contractAddresses,
startTimeStamp,
durationDays,
);
const signature = await signer.signTypedData(
eip712.domain,
{
UserDecryptRequestVerification: eip712.types.UserDecryptRequestVerification,
},
eip712.message,
);
const result = await instance.userDecrypt(
handleContractPairs,
keypair.privateKey,
keypair.publicKey,
signature.replace('0x', ''),
contractAddresses,
signer.address,
startTimeStamp,
durationDays,
);
const decryptedValue = result[ciphertextHandle];
[PreviousDecryption](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption)
[NextPublic decryption](https://docs.zama.ai/protocol/relayer-sdk-guides/fhevm-relayer/decryption/public-decryption)
Last updated 1 month ago
---
# FHE Operations | Protocol
[Add](https://docs.zama.ai/protocol/examples/basic/fhe-operations/fheadd)
[If then else](https://docs.zama.ai/protocol/examples/basic/fhe-operations/fheifthenelse)
[PreviousFHE counter](https://docs.zama.ai/protocol/examples)
[NextAdd](https://docs.zama.ai/protocol/examples/basic/fhe-operations/fheadd)
Last updated 3 months ago
---
# Decryption | Protocol
[User decrypt single value](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-single-value)
[User decrypt multiple values](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-multiple-values)
[Public Decrypt single value](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-single-value)
[Public Decrypt multiple values](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-multiple-values)
[PreviousEncrypt multiple values](https://docs.zama.ai/protocol/examples/basic/encryption/fhe-encrypt-multiple-values)
[NextUser decrypt single value](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-single-value)
Last updated 3 months ago
---
# Overview | Protocol
**Welcome to Solidity Guides!**
This section will guide you through writing confidential smart contracts in Solidity using the FHEVM library. With Fully Homomorphic Encryption(FHE), your contracts can operate directly on encrypted data without ever decrypting it onchain.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7#where-to-go-next)
Where to go next
---------------------------------------------------------------------------------------------
If you’re new to the Zama Protocol, start with the [Litepaper](https://docs.zama.ai/protocol/zama-protocol-litepaper)
or the [Protocol Overview](https://docs.zama.ai/protocol)
to understand the foundations.
Otherwise:
🟨 Go to [**What is FHEVM**](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/overview)
to learn about the core concepts and features.
🟨 Go to [**Quick Start Tutorial**](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial)
to build and test your first confidential smart contract.
🟨 Go to [**Smart Contract Guides**](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure)
for details on encrypted types, supported operations, inputs, ACL, and decryption flows.
🟨 Go to [**Development Guides**](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat)
to set up your local environment with Hardhat or Foundry and deploy FHEVM contracts.
🟨 Go to [**Migration Guide**](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/migration)
if you're upgrading from a previous version to v0.7.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7#help-center)
Help center
-----------------------------------------------------------------------------------
Ask technical questions and discuss with the community.
* [Community forum](https://community.zama.ai/c/fhevm/15)
* [Discord channel](https://discord.com/invite/zama)
[NextWhat is FHEVM Solidity](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/overview)
Last updated 1 month ago
---
# Encryption | Protocol
[Encrypt single value](https://docs.zama.ai/protocol/examples/basic/encryption/fhe-encrypt-single-value)
[Encrypt multiple values](https://docs.zama.ai/protocol/examples/basic/encryption/fhe-encrypt-multiple-values)
[PreviousIf then else](https://docs.zama.ai/protocol/examples/basic/fhe-operations/fheifthenelse)
[NextEncrypt single value](https://docs.zama.ai/protocol/examples/basic/encryption/fhe-encrypt-single-value)
Last updated 3 months ago
---
# FHE counter | Protocol
This example demonstrates how to build an confidential counter using FHEVM, in comparison to a simple counter.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
###
[](https://docs.zama.ai/protocol/examples#a-simple-counter)
A simple counter
counter.sol
[](https://docs.zama.ai/protocol/examples#tab-counter.sol)
counter.ts
[](https://docs.zama.ai/protocol/examples#tab-counter.ts)
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title A simple counter contract
contract Counter {
uint32 private _count;
/// @notice Returns the current count
function getCount() external view returns (uint32) {
return _count;
}
/// @notice Increments the counter by a specific value
function increment(uint32 value) external {
_count += value;
}
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
}
Copy
import { Counter, Counter__factory } from "../types";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
type Signers = {
deployer: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
async function deployFixture() {
const factory = (await ethers.getContractFactory("Counter")) as Counter__factory;
const counterContract = (await factory.deploy()) as Counter;
const counterContractAddress = await counterContract.getAddress();
return { counterContract, counterContractAddress };
}
describe("Counter", function () {
let signers: Signers;
let counterContract: Counter;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { deployer: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
beforeEach(async () => {
({ counterContract } = await deployFixture());
});
it("count should be zero after deployment", async function () {
const count = await counterContract.getCount();
console.log(`Counter.getCount() === ${count}`);
// Expect initial count to be 0 after deployment
expect(count).to.eq(0);
});
it("increment the counter by 1", async function () {
const countBeforeInc = await counterContract.getCount();
const tx = await counterContract.connect(signers.alice).increment(1);
await tx.wait();
const countAfterInc = await counterContract.getCount();
expect(countAfterInc).to.eq(countBeforeInc + 1n);
});
it("decrement the counter by 1", async function () {
// First increment, count becomes 1
let tx = await counterContract.connect(signers.alice).increment(1);
await tx.wait();
// Then decrement, count goes back to 0
tx = await counterContract.connect(signers.alice).decrement(1);
await tx.wait();
const count = await counterContract.getCount();
expect(count).to.eq(0);
});
});
###
[](https://docs.zama.ai/protocol/examples#an-fhe-counter)
An FHE counter
FHECounter.sol
[](https://docs.zama.ai/protocol/examples#tab-fhecounter.sol)
FHECounter.ts
[](https://docs.zama.ai/protocol/examples#tab-fhecounter.ts)
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { FHE, euint32, externalEuint32 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
/// @title A simple FHE counter contract
contract FHECounter is SepoliaConfig {
euint32 private _count;
/// @notice Returns the current count
function getCount() external view returns (euint32) {
return _count;
}
/// @notice Increments the counter by a specified encrypted value.
/// @dev This example omits overflow/underflow checks for simplicity and readability.
/// In a production contract, proper range checks should be implemented.
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, encryptedEuint32);
FHE.allowThis(_count);
FHE.allow(_count, msg.sender);
}
/// @notice Decrements the counter by a specified encrypted value.
/// @dev This example omits overflow/underflow checks for simplicity and readability.
/// In a production contract, proper range checks should be implemented.
function decrement(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.sub(_count, encryptedEuint32);
FHE.allowThis(_count);
FHE.allow(_count, msg.sender);
}
}
Copy
import { FHECounter, FHECounter__factory } from "../types";
import { FhevmType } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers, fhevm } from "hardhat";
type Signers = {
deployer: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
async function deployFixture() {
const factory = (await ethers.getContractFactory("FHECounter")) as FHECounter__factory;
const fheCounterContract = (await factory.deploy()) as FHECounter;
const fheCounterContractAddress = await fheCounterContract.getAddress();
return { fheCounterContract, fheCounterContractAddress };
}
describe("FHECounter", function () {
let signers: Signers;
let fheCounterContract: FHECounter;
let fheCounterContractAddress: string;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { deployer: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
beforeEach(async () => {
({ fheCounterContract, fheCounterContractAddress } = await deployFixture());
});
it("encrypted count should be uninitialized after deployment", async function () {
const encryptedCount = await fheCounterContract.getCount();
// Expect initial count to be bytes32(0) after deployment,
// (meaning the encrypted count value is uninitialized)
expect(encryptedCount).to.eq(ethers.ZeroHash);
});
it("increment the counter by 1", async function () {
const encryptedCountBeforeInc = await fheCounterContract.getCount();
expect(encryptedCountBeforeInc).to.eq(ethers.ZeroHash);
const clearCountBeforeInc = 0;
// Encrypt constant 1 as a euint32
const clearOne = 1;
const encryptedOne = await fhevm
.createEncryptedInput(fheCounterContractAddress, signers.alice.address)
.add32(clearOne)
.encrypt();
const tx = await fheCounterContract
.connect(signers.alice)
.increment(encryptedOne.handles[0], encryptedOne.inputProof);
await tx.wait();
const encryptedCountAfterInc = await fheCounterContract.getCount();
const clearCountAfterInc = await fhevm.userDecryptEuint(
FhevmType.euint32,
encryptedCountAfterInc,
fheCounterContractAddress,
signers.alice,
);
expect(clearCountAfterInc).to.eq(clearCountBeforeInc + clearOne);
});
it("decrement the counter by 1", async function () {
// Encrypt constant 1 as a euint32
const clearOne = 1;
const encryptedOne = await fhevm
.createEncryptedInput(fheCounterContractAddress, signers.alice.address)
.add32(clearOne)
.encrypt();
// First increment by 1, count becomes 1
let tx = await fheCounterContract
.connect(signers.alice)
.increment(encryptedOne.handles[0], encryptedOne.inputProof);
await tx.wait();
// Then decrement by 1, count goes back to 0
tx = await fheCounterContract.connect(signers.alice).decrement(encryptedOne.handles[0], encryptedOne.inputProof);
await tx.wait();
const encryptedCountAfterDec = await fheCounterContract.getCount();
const clearCountAfterDec = await fhevm.userDecryptEuint(
FhevmType.euint32,
encryptedCountAfterDec,
fheCounterContractAddress,
signers.alice,
);
expect(clearCountAfterDec).to.eq(0);
});
});
[NextFHE Operations](https://docs.zama.ai/protocol/examples/basic/fhe-operations)
Last updated 16 days ago
---
# Logics | Protocol
[Branching](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions)
[Dealing with branches and conditions](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/loop)
[Error handling](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/error_handling)
[PreviousReorgs handling](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/reorgs_handling)
[NextBranching](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions)
Last updated 3 months ago
---
# If then else | Protocol
This example demonstrates how to write a simple contract with conditions using FHEVM, in comparison to a simple counter.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
FHEIfThenElse.sol
[](https://docs.zama.ai/protocol/examples/basic/fhe-operations/fheifthenelse#tab-fheifthenelse.sol)
FHEIfThenElse.ts
[](https://docs.zama.ai/protocol/examples/basic/fhe-operations/fheifthenelse#tab-fheifthenelse.ts)
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { FHE, ebool, euint8, externalEuint8 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract FHEIfThenElse is SepoliaConfig {
euint8 private _a;
euint8 private _b;
euint8 private _max;
// solhint-disable-next-line no-empty-blocks
constructor() {}
function setA(externalEuint8 inputA, bytes calldata inputProof) external {
_a = FHE.fromExternal(inputA, inputProof);
FHE.allowThis(_a);
}
function setB(externalEuint8 inputB, bytes calldata inputProof) external {
_b = FHE.fromExternal(inputB, inputProof);
FHE.allowThis(_b);
}
function computeMax() external {
// a >= b
// solhint-disable-next-line var-name-mixedcase
ebool _a_ge_b = FHE.ge(_a, _b);
// a >= b ? a : b
_max = FHE.select(_a_ge_b, _a, _b);
// For more information about FHE permissions in this case,
// read the `computeAPlusB()` commentaries in `FHEAdd.sol`.
FHE.allowThis(_max);
FHE.allow(_max, msg.sender);
}
function result() public view returns (euint8) {
return _max;
}
}
Copy
import { FHEIfThenElse, FHEIfThenElse__factory } from "../../../types";
import type { Signers } from "../../types";
import { FhevmType, HardhatFhevmRuntimeEnvironment } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
import * as hre from "hardhat";
async function deployFixture() {
// Contracts are deployed using the first signer/account by default
const factory = (await ethers.getContractFactory("FHEIfThenElse")) as FHEIfThenElse__factory;
const fheIfThenElse = (await factory.deploy()) as FHEIfThenElse;
const fheIfThenElse_address = await fheIfThenElse.getAddress();
return { fheIfThenElse, fheIfThenElse_address };
}
/**
* This trivial example demonstrates the FHE encryption mechanism
* and highlights a common pitfall developers may encounter.
*/
describe("FHEIfThenElse", function () {
let contract: FHEIfThenElse;
let contractAddress: string;
let signers: Signers;
let bob: HardhatEthersSigner;
before(async function () {
// Check whether the tests are running against an FHEVM mock environment
if (!hre.fhevm.isMock) {
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
}
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1] };
bob = ethSigners[2];
});
beforeEach(async function () {
// Deploy a new contract each time we run a new test
const deployment = await deployFixture();
contractAddress = deployment.fheIfThenElse_address;
contract = deployment.fheIfThenElse;
});
it("a >= b ? a : b should succeed", async function () {
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
let tx;
// Let's compute `a >= b ? a : b`
const a = 80;
const b = 123;
// Alice encrypts and sets `a` as 80
const inputA = await fhevm.createEncryptedInput(contractAddress, signers.alice.address).add8(a).encrypt();
tx = await contract.connect(signers.alice).setA(inputA.handles[0], inputA.inputProof);
await tx.wait();
// Alice encrypts and sets `b` as 203
const inputB = await fhevm.createEncryptedInput(contractAddress, signers.alice.address).add8(b).encrypt();
tx = await contract.connect(signers.alice).setB(inputB.handles[0], inputB.inputProof);
await tx.wait();
// Why Bob has FHE permissions to execute the operation in this case ?
// See `computeAPlusB()` in `FHEAdd.sol` for a detailed answer
tx = await contract.connect(bob).computeMax();
await tx.wait();
const encryptedMax = await contract.result();
const clearMax = await fhevm.userDecryptEuint(
FhevmType.euint8, // Specify the encrypted type
encryptedMax,
contractAddress, // The contract address
bob, // The user wallet
);
expect(clearMax).to.equal(a >= b ? a : b);
});
});
[PreviousAdd](https://docs.zama.ai/protocol/examples/basic/fhe-operations/fheadd)
[NextEncryption](https://docs.zama.ai/protocol/examples/basic/encryption)
Last updated 21 days ago
---
# Encrypt multiple values | Protocol
This example demonstrates the FHE encryption mechanism with multiple values.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
EncryptMultipleValues.sol
[](https://docs.zama.ai/protocol/examples/basic/encryption/fhe-encrypt-multiple-values#tab-encryptmultiplevalues.sol)
EncryptMultipleValues.ts
[](https://docs.zama.ai/protocol/examples/basic/encryption/fhe-encrypt-multiple-values#tab-encryptmultiplevalues.ts)
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import {
FHE,
externalEbool,
externalEuint32,
externalEaddress,
ebool,
euint32,
eaddress
} from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
/**
* This trivial example demonstrates the FHE encryption mechanism.
*/
contract EncryptMultipleValues is SepoliaConfig {
ebool private _encryptedEbool;
euint32 private _encryptedEuint32;
eaddress private _encryptedEaddress;
// solhint-disable-next-line no-empty-blocks
constructor() {}
function initialize(
externalEbool inputEbool,
externalEuint32 inputEuint32,
externalEaddress inputEaddress,
bytes calldata inputProof
) external {
_encryptedEbool = FHE.fromExternal(inputEbool, inputProof);
_encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
_encryptedEaddress = FHE.fromExternal(inputEaddress, inputProof);
// For each of the 3 values:
// Grant FHE permission to both the contract itself (`address(this)`) and the caller (`msg.sender`),
// to allow future decryption by the caller (`msg.sender`).
FHE.allowThis(_encryptedEbool);
FHE.allow(_encryptedEbool, msg.sender);
FHE.allowThis(_encryptedEuint32);
FHE.allow(_encryptedEuint32, msg.sender);
FHE.allowThis(_encryptedEaddress);
FHE.allow(_encryptedEaddress, msg.sender);
}
function encryptedBool() public view returns (ebool) {
return _encryptedEbool;
}
function encryptedUint32() public view returns (euint32) {
return _encryptedEuint32;
}
function encryptedAddress() public view returns (eaddress) {
return _encryptedEaddress;
}
}
Copy
//TODO;
import { EncryptMultipleValues, EncryptMultipleValues__factory } from "../../../types";
import type { Signers } from "../../types";
import { FhevmType, HardhatFhevmRuntimeEnvironment } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
import * as hre from "hardhat";
async function deployFixture() {
// Contracts are deployed using the first signer/account by default
const factory = (await ethers.getContractFactory("EncryptMultipleValues")) as EncryptMultipleValues__factory;
const encryptMultipleValues = (await factory.deploy()) as EncryptMultipleValues;
const encryptMultipleValues_address = await encryptMultipleValues.getAddress();
return { encryptMultipleValues, encryptMultipleValues_address };
}
/**
* This trivial example demonstrates the FHE encryption mechanism
* and highlights a common pitfall developers may encounter.
*/
describe("EncryptMultipleValues", function () {
let contract: EncryptMultipleValues;
let contractAddress: string;
let signers: Signers;
before(async function () {
// Check whether the tests are running against an FHEVM mock environment
if (!hre.fhevm.isMock) {
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
}
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1] };
});
beforeEach(async function () {
// Deploy a new contract each time we run a new test
const deployment = await deployFixture();
contractAddress = deployment.encryptMultipleValues_address;
contract = deployment.encryptMultipleValues;
});
// ✅ Test should succeed
it("encryption should succeed", async function () {
// Use the FHEVM Hardhat plugin runtime environment
// to perform FHEVM input encryptions.
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
const input = fhevm.createEncryptedInput(contractAddress, signers.alice.address);
input.addBool(true);
input.add32(123456);
input.addAddress(signers.owner.address);
const enc = await input.encrypt();
const inputEbool = enc.handles[0];
const inputEuint32 = enc.handles[1];
const inputEaddress = enc.handles[2];
const inputProof = enc.inputProof;
// Don't forget to call `connect(signers.alice)` to make sure
// the Solidity `msg.sender` is `signers.alice.address`.
const tx = await contract.connect(signers.alice).initialize(inputEbool, inputEuint32, inputEaddress, inputProof);
await tx.wait();
const encryptedBool = await contract.encryptedBool();
const encryptedUint32 = await contract.encryptedUint32();
const encryptedAddress = await contract.encryptedAddress();
const clearBool = await fhevm.userDecryptEbool(
encryptedBool,
contractAddress, // The contract address
signers.alice, // The user wallet
);
const clearUint32 = await fhevm.userDecryptEuint(
FhevmType.euint32, // Specify the encrypted type
encryptedUint32,
contractAddress, // The contract address
signers.alice, // The user wallet
);
const clearAddress = await fhevm.userDecryptEaddress(
encryptedAddress,
contractAddress, // The contract address
signers.alice, // The user wallet
);
expect(clearBool).to.equal(true);
expect(clearUint32).to.equal(123456);
expect(clearAddress).to.equal(signers.owner.address);
});
});
[PreviousEncrypt single value](https://docs.zama.ai/protocol/examples/basic/encryption/fhe-encrypt-single-value)
[NextDecryption](https://docs.zama.ai/protocol/examples/basic/decryption)
Last updated 3 months ago
---
# ERC7984 to ERC20 Wrapper | Protocol
This example demonstrates how to wrap between the ERC20 token into a ERC7984 token using OpenZeppelin's smart contract library powered by ZAMA's FHEVM.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
ERC7984ERC20WrapperExample.sol
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984erc20wrappermock#tab-erc7984erc20wrapperexample.sol)
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984erc20wrappermock#tab-undefined-1)
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {ERC7984ERC20Wrapper, ERC7984} from "@openzeppelin/confidential-contracts/token/ERC7984/extensions/ERC7984ERC20Wrapper.sol";
contract ERC7984ERC20WrapperExample is ERC7984ERC20Wrapper, SepoliaConfig {
constructor(
IERC20 token,
string memory name,
string memory symbol,
string memory uri
) ERC7984ERC20Wrapper(token) ERC7984(name, symbol, uri) {}
}
[PreviousERC7984 Tutorial](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial)
[NextSwap ERC7984 to ERC20](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/swaperc7984toerc20)
Last updated 21 days ago
---
# Add | Protocol
This example demonstrates how to write a simple "a + b" contract using FHEVM.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
FHEAdd.sol
[](https://docs.zama.ai/protocol/examples/basic/fhe-operations/fheadd#tab-fheadd.sol)
FHEAdd.ts
[](https://docs.zama.ai/protocol/examples/basic/fhe-operations/fheadd#tab-fheadd.ts)
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { FHE, euint8, externalEuint8 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract FHEAdd is SepoliaConfig {
euint8 private _a;
euint8 private _b;
// solhint-disable-next-line var-name-mixedcase
euint8 private _a_plus_b;
// solhint-disable-next-line no-empty-blocks
constructor() {}
function setA(externalEuint8 inputA, bytes calldata inputProof) external {
_a = FHE.fromExternal(inputA, inputProof);
FHE.allowThis(_a);
}
function setB(externalEuint8 inputB, bytes calldata inputProof) external {
_b = FHE.fromExternal(inputB, inputProof);
FHE.allowThis(_b);
}
function computeAPlusB() external {
// The sum `a + b` is computed by the contract itself (`address(this)`).
// Since the contract has FHE permissions over both `a` and `b`,
// it is authorized to perform the `FHE.add` operation on these values.
// It does not matter if the contract caller (`msg.sender`) has FHE permission or not.
_a_plus_b = FHE.add(_a, _b);
// At this point the contract ifself (`address(this)`) has been granted ephemeral FHE permission
// over `_a_plus_b`. This FHE permission will be revoked when the function exits.
//
// Now, to make sure `_a_plus_b` can be decrypted by the contract caller (`msg.sender`),
// we need to grant permanent FHE permissions to both the contract ifself (`address(this)`)
// and the contract caller (`msg.sender`)
FHE.allowThis(_a_plus_b);
FHE.allow(_a_plus_b, msg.sender);
}
function result() public view returns (euint8) {
return _a_plus_b;
}
}
Copy
import { FHEAdd, FHEAdd__factory } from "../../../types";
import type { Signers } from "../../types";
import { FhevmType, HardhatFhevmRuntimeEnvironment } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
import * as hre from "hardhat";
async function deployFixture() {
// Contracts are deployed using the first signer/account by default
const factory = (await ethers.getContractFactory("FHEAdd")) as FHEAdd__factory;
const fheAdd = (await factory.deploy()) as FHEAdd;
const fheAdd_address = await fheAdd.getAddress();
return { fheAdd, fheAdd_address };
}
/**
* This trivial example demonstrates the FHE encryption mechanism
* and highlights a common pitfall developers may encounter.
*/
describe("FHEAdd", function () {
let contract: FHEAdd;
let contractAddress: string;
let signers: Signers;
let bob: HardhatEthersSigner;
before(async function () {
// Check whether the tests are running against an FHEVM mock environment
if (!hre.fhevm.isMock) {
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
}
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1] };
bob = ethSigners[2];
});
beforeEach(async function () {
// Deploy a new contract each time we run a new test
const deployment = await deployFixture();
contractAddress = deployment.fheAdd_address;
contract = deployment.fheAdd;
});
it("a + b should succeed", async function () {
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
let tx;
// Let's compute 80 + 123 = 203
const a = 80;
const b = 123;
// Alice encrypts and sets `a` as 80
const inputA = await fhevm.createEncryptedInput(contractAddress, signers.alice.address).add8(a).encrypt();
tx = await contract.connect(signers.alice).setA(inputA.handles[0], inputA.inputProof);
await tx.wait();
// Alice encrypts and sets `b` as 203
const inputB = await fhevm.createEncryptedInput(contractAddress, signers.alice.address).add8(b).encrypt();
tx = await contract.connect(signers.alice).setB(inputB.handles[0], inputB.inputProof);
await tx.wait();
// Why Bob has FHE permissions to execute the operation in this case ?
// See `computeAPlusB()` in `FHEAdd.sol` for a detailed answer
tx = await contract.connect(bob).computeAPlusB();
await tx.wait();
const encryptedAplusB = await contract.result();
const clearAplusB = await fhevm.userDecryptEuint(
FhevmType.euint8, // Specify the encrypted type
encryptedAplusB,
contractAddress, // The contract address
bob, // The user wallet
);
expect(clearAplusB).to.equal(a + b);
});
});
[PreviousFHE Operations](https://docs.zama.ai/protocol/examples/basic/fhe-operations)
[NextIf then else](https://docs.zama.ai/protocol/examples/basic/fhe-operations/fheifthenelse)
Last updated 3 months ago
---
# Library installation and overview | Protocol
This section contains comprehensive guides and examples for using [OpenZeppelin's confidential smart contracts library](https://github.com/OpenZeppelin/openzeppelin-confidential-contracts)
with FHEVM. OpenZeppelin's confidential contracts library provides a secure, audited foundation for building privacy-preserving applications on fully homomorphic encryption (FHE) enabled blockchains.
The library includes implementations of popular standards like ERC20, ERC721, and ERC1155, adapted for confidential computing with FHEVM, ensuring your applications maintain privacy while leveraging battle-tested security patterns.
###
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/openzeppelin#getting-started)
Getting Started
This guide will help you set up a development environment for working with OpenZeppelin's confidential contracts and FHEVM.
####
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/openzeppelin#prerequisites)
Prerequisites
Before you begin, ensure you have the following installed:
* **Node.js** >= 20
* **Hardhat** ^2.24
* **Access to an FHEVM-enabled network** and the Zama gateway/relayer
####
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/openzeppelin#project-setup)
Project Setup
1. **Clone the FHEVM Hardhat template repository:**
Copy
git clone https://github.com/zama-ai/fhevm-hardhat-template conf-token
cd conf-token
2. **Install project dependencies:**
Copy
npm ci
3. **Install OpenZeppelin's confidential contracts library:**
Copy
npm i @openzeppelin/confidential-contracts
4. **Compile the contracts:**
Copy
npm run compile
5. **Run the test suite:**
Copy
npm test
###
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/openzeppelin#available-guides)
Available Guides
Explore the following guides to learn how to implement confidential contracts using OpenZeppelin's library:
* [**ERC7984 Standard**](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984)
- Learn about the ERC7984 standard for confidential tokens
* [**ERC7984 Tutorial**](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial)
- Step-by-step tutorial for implementing ERC7984 tokens
* [**ERC7984 to ERC20 Wrapper**](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984erc20wrappermock)
- Convert between confidential and public token standards
* [**Swap ERC7984 to ERC20**](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/swaperc7984toerc20)
- Implement cross-standard token swapping
* [**Swap ERC7984 to ERC7984**](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/swaperc7984toerc7984)
- Confidential token-to-token swapping
* [**Vesting Wallet**](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/vesting-wallet)
- Implement confidential token vesting mechanisms
[PreviousPublic Decrypt multiple values](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-multiple-values)
[NextERC7984 Standard](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984)
Last updated 21 days ago
---
# Encrypt single value | Protocol
This example demonstrates the FHE encryption mechanism and highlights a common pitfall developers may encounter.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
EncryptSingleValue.sol
[](https://docs.zama.ai/protocol/examples/basic/encryption/fhe-encrypt-single-value#tab-encryptsinglevalue.sol)
EncryptSingleValue.ts
[](https://docs.zama.ai/protocol/examples/basic/encryption/fhe-encrypt-single-value#tab-encryptsinglevalue.ts)
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { FHE, externalEuint32, euint32 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
/**
* This trivial example demonstrates the FHE encryption mechanism.
*/
contract EncryptSingleValue is SepoliaConfig {
euint32 private _encryptedEuint32;
// solhint-disable-next-line no-empty-blocks
constructor() {}
function initialize(externalEuint32 inputEuint32, bytes calldata inputProof) external {
_encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
// Grant FHE permission to both the contract itself (`address(this)`) and the caller (`msg.sender`),
// to allow future decryption by the caller (`msg.sender`).
FHE.allowThis(_encryptedEuint32);
FHE.allow(_encryptedEuint32, msg.sender);
}
function encryptedUint32() public view returns (euint32) {
return _encryptedEuint32;
}
}
Copy
import { EncryptSingleValue, EncryptSingleValue__factory } from "../../../types";
import type { Signers } from "../../types";
import { FhevmType, HardhatFhevmRuntimeEnvironment } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
import * as hre from "hardhat";
async function deployFixture() {
// Contracts are deployed using the first signer/account by default
const factory = (await ethers.getContractFactory("EncryptSingleValue")) as EncryptSingleValue__factory;
const encryptSingleValue = (await factory.deploy()) as EncryptSingleValue;
const encryptSingleValue_address = await encryptSingleValue.getAddress();
return { encryptSingleValue, encryptSingleValue_address };
}
/**
* This trivial example demonstrates the FHE encryption mechanism
* and highlights a common pitfall developers may encounter.
*/
describe("EncryptSingleValue", function () {
let contract: EncryptSingleValue;
let contractAddress: string;
let signers: Signers;
before(async function () {
// Check whether the tests are running against an FHEVM mock environment
if (!hre.fhevm.isMock) {
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
}
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1] };
});
beforeEach(async function () {
// Deploy a new contract each time we run a new test
const deployment = await deployFixture();
contractAddress = deployment.encryptSingleValue_address;
contract = deployment.encryptSingleValue;
});
// ✅ Test should succeed
it("encryption should succeed", async function () {
// Use the FHEVM Hardhat plugin runtime environment
// to perform FHEVM input encryptions.
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
// 🔐 Encryption Process:
// Values are encrypted locally and bound to a specific contract/user pair.
// This grants the bound contract FHE permissions to receive and process the encrypted value,
// but only when it is sent by the bound user.
const input = fhevm.createEncryptedInput(contractAddress, signers.alice.address);
// Add a uint32 value to the list of values to encrypt locally.
input.add32(123456);
// Perform the local encryption. This operation produces two components:
// 1. `handles`: an array of FHEVM handles. In this case, a single handle associated with the
// locally encrypted uint32 value `123456`.
// 2. `inputProof`: a zero-knowledge proof that attests the `handles` are cryptographically
// bound to the pair `[contractAddress, signers.alice.address]`.
const enc = await input.encrypt();
// a 32-bytes FHEVM handle that represents a future Solidity `euint32` value.
const inputEuint32 = enc.handles[0];
const inputProof = enc.inputProof;
// Now `signers.alice.address` can send the encrypted value and its associated zero-knowledge proof
// to the smart contract deployed at `contractAddress`.
const tx = await contract.connect(signers.alice).initialize(inputEuint32, inputProof);
await tx.wait();
// Let's try to decrypt it to check that everything is ok!
const encryptedUint32 = await contract.encryptedUint32();
const clearUint32 = await fhevm.userDecryptEuint(
FhevmType.euint32, // Specify the encrypted type
encryptedUint32,
contractAddress, // The contract address
signers.alice, // The user wallet
);
expect(clearUint32).to.equal(123456);
});
// ❌ This test illustrates a very common pitfall
it("encryption should fail", async function () {
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
const enc = await fhevm.createEncryptedInput(contractAddress, signers.alice.address).add32(123456).encrypt();
const inputEuint32 = enc.handles[0];
const inputProof = enc.inputProof;
try {
// Here is a very common error !
// `contract.initialize` will sign the Ethereum transaction using user `signers.owner`
// instead of `signers.alice`.
//
// In the Solidity contract the following is checked:
// - Is the contract allowed to manipulate `inputEuint32`? Answer is: ✅ yes!
// - Is the sender allowed to manipulate `inputEuint32`? Answer is: ❌ no! Only `signers.alice` is!
const tx = await contract.initialize(inputEuint32, inputProof);
await tx.wait();
} catch {
//console.log(e);
}
});
});
[PreviousEncryption](https://docs.zama.ai/protocol/examples/basic/encryption)
[NextEncrypt multiple values](https://docs.zama.ai/protocol/examples/basic/encryption/fhe-encrypt-multiple-values)
Last updated 3 months ago
---
# User decrypt multiple values | Protocol
This example demonstrates the FHE user decryption mechanism with multiple values.
User decryption is a mechanism that allows specific users to decrypt encrypted values while keeping them hidden from others. Unlike public decryption where decrypted values become visible to everyone, user decryption maintains privacy by only allowing authorized users with the proper permissions to view the data. While permissions are granted onchain through smart contracts, the actual **decryption call occurs off-chain in the frontend application**.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
UserDecryptMultipleValues.sol
[](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-multiple-values#tab-userdecryptmultiplevalues.sol)
UserDecryptMultipleValues.ts
[](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-multiple-values#tab-userdecryptmultiplevalues.ts)
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { FHE, ebool, euint32, euint64 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract UserDecryptMultipleValues is SepoliaConfig {
ebool private _encryptedBool; // = 0 (uninitizalized)
euint32 private _encryptedUint32; // = 0 (uninitizalized)
euint64 private _encryptedUint64; // = 0 (uninitizalized)
// solhint-disable-next-line no-empty-blocks
constructor() {}
function initialize(bool a, uint32 b, uint64 c) external {
// Compute 3 trivial FHE formulas
// _encryptedBool = a ^ false
_encryptedBool = FHE.xor(FHE.asEbool(a), FHE.asEbool(false));
// _encryptedUint32 = b + 1
_encryptedUint32 = FHE.add(FHE.asEuint32(b), FHE.asEuint32(1));
// _encryptedUint64 = c + 1
_encryptedUint64 = FHE.add(FHE.asEuint64(c), FHE.asEuint64(1));
// see `DecryptSingleValue.sol` for more detailed explanations
// about FHE permissions and asynchronous user decryption requests.
FHE.allowThis(_encryptedBool);
FHE.allowThis(_encryptedUint32);
FHE.allowThis(_encryptedUint64);
FHE.allow(_encryptedBool, msg.sender);
FHE.allow(_encryptedUint32, msg.sender);
FHE.allow(_encryptedUint64, msg.sender);
}
function encryptedBool() public view returns (ebool) {
return _encryptedBool;
}
function encryptedUint32() public view returns (euint32) {
return _encryptedUint32;
}
function encryptedUint64() public view returns (euint64) {
return _encryptedUint64;
}
}
Copy
import { UserDecryptMultipleValues, UserDecryptMultipleValues__factory } from "../../../types";
import type { Signers } from "../../types";
import { HardhatFhevmRuntimeEnvironment } from "@fhevm/hardhat-plugin";
import { utils as fhevm_utils } from "@fhevm/mock-utils";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { DecryptedResults } from "@zama-fhe/relayer-sdk";
import { expect } from "chai";
import { ethers } from "hardhat";
import * as hre from "hardhat";
async function deployFixture() {
// Contracts are deployed using the first signer/account by default
const factory = (await ethers.getContractFactory("UserDecryptMultipleValues")) as UserDecryptMultipleValues__factory;
const userDecryptMultipleValues = (await factory.deploy()) as UserDecryptMultipleValues;
const userDecryptMultipleValues_address = await userDecryptMultipleValues.getAddress();
return { userDecryptMultipleValues, userDecryptMultipleValues_address };
}
/**
* This trivial example demonstrates the FHE user decryption mechanism
* and highlights a common pitfall developers may encounter.
*/
describe("UserDecryptMultipleValues", function () {
let contract: UserDecryptMultipleValues;
let contractAddress: string;
let signers: Signers;
before(async function () {
// Check whether the tests are running against an FHEVM mock environment
if (!hre.fhevm.isMock) {
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
}
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1] };
});
beforeEach(async function () {
// Deploy a new contract each time we run a new test
const deployment = await deployFixture();
contractAddress = deployment.userDecryptMultipleValues_address;
contract = deployment.userDecryptMultipleValues;
});
// ✅ Test should succeed
it("user decryption should succeed", async function () {
const tx = await contract.connect(signers.alice).initialize(true, 123456, 78901234567);
await tx.wait();
const encryptedBool = await contract.encryptedBool();
const encryptedUint32 = await contract.encryptedUint32();
const encryptedUint64 = await contract.encryptedUint64();
// The FHEVM Hardhat plugin provides a set of convenient helper functions
// that make it easy to perform FHEVM operations within your Hardhat environment.
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
const aliceKeypair = fhevm.generateKeypair();
const startTimestamp = fhevm_utils.timestampNow();
const durationDays = 365;
const aliceEip712 = fhevm.createEIP712(aliceKeypair.publicKey, [contractAddress], startTimestamp, durationDays);
const aliceSignature = await signers.alice.signTypedData(
aliceEip712.domain,
{ UserDecryptRequestVerification: aliceEip712.types.UserDecryptRequestVerification },
aliceEip712.message,
);
const decrytepResults: DecryptedResults = await fhevm.userDecrypt(
[\
{ handle: encryptedBool, contractAddress: contractAddress },\
{ handle: encryptedUint32, contractAddress: contractAddress },\
{ handle: encryptedUint64, contractAddress: contractAddress },\
],
aliceKeypair.privateKey,
aliceKeypair.publicKey,
aliceSignature,
[contractAddress],
signers.alice.address,
startTimestamp,
durationDays,
);
expect(decrytepResults[encryptedBool]).to.equal(true);
expect(decrytepResults[encryptedUint32]).to.equal(123456 + 1);
expect(decrytepResults[encryptedUint64]).to.equal(78901234567 + 1);
});
});
[PreviousUser decrypt single value](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-single-value)
[NextPublic Decrypt single value](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-single-value)
Last updated 21 days ago
---
# Contract addresses | Protocol
Save this in your `.env` file.
These are Sepolia addresses.
Contract/Service
Address/Value
FHEVM\_EXECUTOR\_CONTRACT
0x848B0066793BcC60346Da1F49049357399B8D595
ACL\_CONTRACT
0x687820221192C5B662b25367F70076A37bc79b6c
HCU\_LIMIT\_CONTRACT
0x594BB474275918AF9609814E68C61B1587c5F838
KMS\_VERIFIER\_CONTRACT
0x1364cBBf2cDF5032C47d8226a6f6FBD2AFCDacAC
INPUT\_VERIFIER\_CONTRACT
0xbc91f3daD1A5F19F8390c400196e58073B6a0BC4
DECRYPTION\_ORACLE\_CONTRACT
0xa02Cda4Ca3a71D7C46997716F4283aa851C28812
DECRYPTION\_ADDRESS
0xb6E160B1ff80D67Bfe90A85eE06Ce0A2613607D1
INPUT\_VERIFICATION\_ADDRESS
0x7048C39f048125eDa9d678AEbaDfB22F7900a29F
RELAYER\_URL
`https://relayer.testnet.zama.cloud`
[PreviousConfiguration](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure)
[NextSupported types](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/types)
Last updated 3 months ago
---
# Swap ERC7984 to ERC7984 | Protocol
This example demonstrates how to swap between a confidential token - the ERC7984 and the ERC20 tokens using OpenZeppelin's smart contract library powered by ZAMA's FHEVM.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
SwapERC7984ToERC20.sol
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/swaperc7984toerc7984#tab-swaperc7984toerc20.sol)
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/swaperc7984toerc7984#tab-undefined-1)
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {FHE, externalEuint64, euint64} from "@fhevm/solidity/lib/FHE.sol";
import {IERC7984} from "@openzeppelin/confidential-contracts/interfaces/IERC7984.sol";
contract SwapERC7984ToERC7984 {
function swapConfidentialForConfidential(
IERC7984 fromToken,
IERC7984 toToken,
externalEuint64 amountInput,
bytes calldata inputProof
) public virtual {
require(fromToken.isOperator(msg.sender, address(this)));
euint64 amount = FHE.fromExternal(amountInput, inputProof);
FHE.allowTransient(amount, address(fromToken));
euint64 amountTransferred = fromToken.confidentialTransferFrom(msg.sender, address(this), amount);
FHE.allowTransient(amountTransferred, address(toToken));
toToken.confidentialTransfer(msg.sender, amountTransferred);
}
}
[PreviousSwap ERC7984 to ERC20](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/swaperc7984toerc20)
[NextVesting Wallet](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/vesting-wallet)
Last updated 21 days ago
---
# Swap ERC7984 to ERC20 | Protocol
This example demonstrates how to swap between a confidential token - the ERC7984 and the ERC20 tokens using OpenZeppelin's smart contract library powered by ZAMA's FHEVM.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
SwapERC7984ToERC20.sol
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/swaperc7984toerc20#tab-swaperc7984toerc20.sol)
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/swaperc7984toerc20#tab-undefined-1)
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {FHE, externalEuint64, euint64} from "@fhevm/solidity/lib/FHE.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC7984} from "@openzeppelin/confidential-contracts/interfaces/IERC7984.sol";
contract SwapERC7984ToERC20 {
error SwapERC7984ToERC20InvalidGatewayRequest(uint256 requestId);
mapping(uint256 requestId => address) private _receivers;
IERC7984 private _fromToken;
IERC20 private _toToken;
constructor(IERC7984 fromToken, IERC20 toToken) {
_fromToken = fromToken;
_toToken = toToken;
}
function SwapERC7984ToERC20(externalEuint64 encryptedInput, bytes memory inputProof) public {
euint64 amount = FHE.fromExternal(encryptedInput, inputProof);
FHE.allowTransient(amount, address(_fromToken));
euint64 amountTransferred = _fromToken.confidentialTransferFrom(msg.sender, address(this), amount);
bytes32[] memory cts = new bytes32[](1);
cts[0] = euint64.unwrap(amountTransferred);
uint256 requestID = FHE.requestDecryption(cts, this.finalizeSwap.selector);
// register who is getting the tokens
_receivers[requestID] = msg.sender;
}
function finalizeSwap(uint256 requestID, uint64 amount, bytes[] memory signatures) public virtual {
FHE.checkSignatures(requestID, signatures);
address to = _receivers[requestID];
require(to != address(0), SwapERC7984ToERC20InvalidGatewayRequest(requestID));
delete _receivers[requestID];
if (amount != 0) {
SafeERC20.safeTransfer(_toToken, to, amount);
}
}
}
[PreviousERC7984 to ERC20 Wrapper](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984erc20wrappermock)
[NextSwap ERC7984 to ERC7984](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/swaperc7984toerc7984)
Last updated 21 days ago
---
# What is FHEVM Solidity | Protocol
This document provides an overview of key features of the FHEVM smart contract library.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/overview#configuration-and-initialization)
Configuration and initialization
Smart contracts using FHEVM require proper configuration and initialization:
* **Environment setup**: Import and inherit from environment-specific configuration contracts
* **Relayer configuration**: Configure secure relayer access for cryptographic operations
* **Initialization checks**: Validate encrypted variables are properly initialized before use
For more information see [Configuration](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/overview#encrypted-data-types)
Encrypted data types
FHEVM introduces encrypted data types compatible with Solidity:
* **Booleans**: `ebool`
* **Unsigned Integers**: `euint8`, `euint16`, `euint32`, `euint64`, `euint128`, `euint256`
* **Addresses**: `eaddress`
* **Input**: `externalEbool`, `externalEaddress`, `externalEuintXX` 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](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/types)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/overview#casting-types)
Casting types
fhevm provides functions to cast between encrypted types:
* **Casting between encrypted types**: `FHE.asEbool` converts encrypted integers to encrypted booleans
* **Casting to encrypted types**: `FHE.asEuintX` converts plaintext values to encrypted types
* **Casting to encrypted addresses**: `FHE.asEaddress` converts plaintext addresses to encrypted addresses
For more information see [use of encrypted types](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/types)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/overview#confidential-computation)
Confidential computation
fhevm enables symbolic execution of encrypted operations, supporting:
* **Arithmetic:** `FHE.add`, `FHE.sub`, `FHE.mul`, `FHE.min`, `FHE.max`, `FHE.neg`, `FHE.div`, `FHE.rem`
* Note: `div` and `rem` operations are supported only with plaintext divisors
* **Bitwise:** `FHE.and`, `FHE.or`, `FHE.xor`, `FHE.not`, `FHE.shl`, `FHE.shr`, `FHE.rotl`, `FHE.rotr`
* **Comparison:** `FHE.eq`, `FHE.ne`, `FHE.lt`, `FHE.le`, `FHE.gt`, `FHE.ge`
* **Advanced:** `FHE.select` for branching on encrypted conditions, `FHE.randEuintX` for on-chain randomness.
For more information on operations, see [Operations on encrypted types](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations)
.
For more information on conditional branching, see [Conditional logic in FHE](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions)
.
For more information on random number generation, see [Generate Random Encrypted Numbers](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/random)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/overview#access-control-mechanism)
Access control mechanism
fhevm enforces access control with a blockchain-based Access Control List (ACL):
* **Persistent access**: `FHE.allow`, `FHE.allowThis` grants permanent permissions for ciphertexts.
* **Transient access**: `FHE.allowTransient` provides temporary access for specific transactions.
* **Validation**: `FHE.isSenderAllowed` ensures that only authorized entities can interact with ciphertexts.
For more information see [ACL](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl)
.
[PreviousOverview](https://docs.zama.ai/protocol/solidity-guides/v0.7)
[NextSet up Hardhat](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup)
Last updated 3 months ago
---
# Quick start tutorial | Protocol
This tutorial guides you to start quickly with Zama’s **Fully Homomorphic Encryption (FHE)** technology for building confidential smart contracts.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial#what-youll-learn)
What You’ll Learn
-----------------------------------------------------------------------------------------------------------------------------------
In **about 30 minutes**, you'll go from a basic Solidity contract to a fully confidential one using **FHEVM**. Here's what you'll do:
1. Set up your development environment
2. Write a simple Solidity smart contract
3. Convert it into an FHEVM-compatible confidential contract
4. Test your FHEVM-compatible confidential contract
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial#prerequisite)
Prerequisite
--------------------------------------------------------------------------------------------------------------------------
* A basic understanding of **Solidity** library and **Ethereum**.
* Some familiarity with **Hardhat.**
**About Hardhat**
[**Hardhat**](https://hardhat.org/)
is a development environment for compiling, deploying, testing, and debugging Ethereum smart contracts. It’s widely used in the Ethereum ecosystem.
In this tutorial, we'll introduce the FHEVM hardhat template that provides an easy way to use FHEVM.
[PreviousSet up Hardhat](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup)
[Next2\. Write a simple contract](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract)
Last updated 3 months ago
---
# Migrate to v0.7 | Protocol
This document provides instructions on migrating from FHEVM v0.6 to v0.7.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/migration#from-0.6.x)
From 0.6.x
-------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/migration#package-and-library)
Package and library
The package is now `@fhevm/solidity` instead of `fhevm` and the library name has changed from `TFHE` to `FHE`
Copy
import { FHE } from "@fhevm/solidity";
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/migration#configuration)
Configuration
Configuration has been renamed from `SepoliaZamaConfig` to `SepoliaConfig`.
Copy
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
Also, the function to define manually the Coprocessor has been renamed from `setFHEVM` to `setCoprocessor`, and the function to define the oracle has been changed to `setDecryptionOracle`.
Copy
constructor () {
FHE.setCoprocessor(0x848B0066793BcC60346Da1F49049357399B8D595);
FHE.setDecryptionOracle(0xa02Cda4Ca3a71D7C46997716F4283aa851C28812);
}
You can read more about [Configuration on the dedicated page](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/migration#decryption-oracle)
Decryption Oracle
Previously, an abstract contract `GatewayCaller` was used to request decryption. It has been replaced by `FHE.requestDecryption`:
Copy
function requestBoolInfinite() public {
bytes32[] memory cts = new bytes32[](1);
cts[0] = FHE.toBytes32(myEncryptedValue);
FHE.requestDecryption(cts, this.myCallback.selector);
}
You can read more about [Decryption Oracle on the dedicated page](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/migration#deprecation-of-ebytes)
Deprecation of ebytes
`ebytes` has been deprecated and removed from FHEVM.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/migration#block-gas-limit)
Block gas limit
Block gas limit has been removed in favor of HCU (Homomorphic Complexity Unit) limit. FHEVM 0.7.0 includes two limits:
* **Sequential homomorphic operations depth limit per transaction**: Controls HCU usage for operations that must be processed in order. This limit is set to **5,000,000** HCU.
* **Global homomorphic operations complexity per transaction**: Controls HCU usage for operations that can be processed in parallel. This limit is set to **20,000,000** HCU.
You can read more about [HCU on the dedicated page](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu)
.
[PreviousHCU](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu)
[NextHow to Transform Your Smart Contract into a FHEVM Smart Contract?](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/transform_smart_contract_with_fhevm)
Last updated 3 months ago
---
# Generate random numbers | Protocol
This document explains how to generate cryptographically secure random encrypted numbers fully on-chain using the `FHE` library in fhevm. These numbers are encrypted and remain confidential, enabling privacy-preserving smart contract logic.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/random#key-notes-on-random-number-generation)
**Key notes on random number generation**
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* **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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/random#basic-usage)
**Basic usage**
------------------------------------------------------------------------------------------------------------------------
The `FHE` library allows you to generate random encrypted numbers of various bit sizes. Below is a list of supported types and their usage:
Copy
// Generate random encrypted numbers
ebool rb = FHE.randEbool(); // Random encrypted boolean
euint8 r8 = FHE.randEuint8(); // Random 8-bit number
euint16 r16 = FHE.randEuint16(); // Random 16-bit number
euint32 r32 = FHE.randEuint32(); // Random 32-bit number
euint64 r64 = FHE.randEuint64(); // Random 64-bit number
euint128 r128 = FHE.randEuint128(); // Random 128-bit number
euint256 r256 = FHE.randEuint256(); // Random 256-bit number
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/random#example-random-boolean)
**Example: Random Boolean**
Copy
function randomBoolean() public returns (ebool) {
return FHE.randEbool();
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/random#bounded-random-numbers)
**Bounded random numbers**
----------------------------------------------------------------------------------------------------------------------------------------------
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]`.
Copy
// Generate random numbers with upper bounds
euint8 r8 = FHE.randEuint8(100); // Random number between 0-99
euint16 r16 = FHE.randEuint16(1000); // Random number between 0-999
euint32 r32 = FHE.randEuint32(1000000); // Random number between 0-999999
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/random#example-random-bumber-with-upper-bound)
**Example: Random bumber with upper bound**
Copy
function randomBoundedNumber(uint16 upperBound) public returns (euint16) {
return FHE.randEuint16(upperBound);
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/random#security-considerations)
**Security Considerations**
------------------------------------------------------------------------------------------------------------------------------------------------
* **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.
[PreviousAsEbool, asEuintXX, and asEaddress operations](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators)
[NextEncrypted inputs](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs)
Last updated 3 months ago
---
# Hardhat plugin | Protocol
This section will guide you through writing and testing FHEVM smart contracts in Solidity using [Hardhat](https://hardhat.org/)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat#the-fhevm-hardhat-plugin)
The FHEVM Hardhat Plugin
To write FHEVM smart contracts using Hardhat, you need to install the [FHEVM Hardhat Plugin](https://www.npmjs.com/package/@fhevm/hardhat-plugin)
in your Hardhat project.
This plugin enables you to develop, test, and interact with FHEVM contracts right out of the box.
It extends Hardhat’s functionality with a complete FHEVM API that allows you:
* Encrypt data
* Decrypt data
* Run tests using various FHEVM execution modes
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat#where-to-go-next)
Where to go next
🟨 Go to [**Setup Hardhat**](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup)
to initialize your FHEVM Hardhat project.
🟨 Go to [**Write FHEVM Tests in Hardhat**](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/write_test)
for details on writing tests of FHEVM smart contracts using Hardhat.
🟨 Go to [**Run FHEVM Tests in Hardhat**](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/run_test)
to learn how to execute those tests in different FHEVM environments.
[PreviousDecryption](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle)
[NextWrite FHEVM tests in Hardhat](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/write_test)
Last updated 3 months ago
---
# Dealing with branches and conditions | Protocol
This document explains how to handle branches, loops or conditions when working with Fully Homomorphic Encryption (FHE), specifically when the condition / index is encrypted.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/loop#breaking-a-loop)
Breaking a loop
----------------------------------------------------------------------------------------------------------------------
❌ In FHE, it is not possible to break a loop based on an encrypted condition. For example, this would not work:
Copy
euint8 maxValue = FHE.asEuint(6); // Could be a value between 0 and 10
euint8 x = FHE.asEuint(0);
// some code
while(FHE.lt(x, maxValue)){
x = FHE.add(x, 2);
}
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 `FHE.select` inside the loop.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/loop#suggested-approach)
Suggested approach
----------------------------------------------------------------------------------------------------------------------------
✅ For example, the previous code could maybe be replaced by the following snippet:
Copy
euint8 maxValue = FHE.asEuint(6); // Could be a value between 0 and 10
euint8 x;
// some code
for (uint32 i = 0; i < 10; i++) {
euint8 toAdd = FHE.select(FHE.lt(x, maxValue), 2, 0);
x = FHE.add(x, toAdd);
}
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/loop#best-practices)
Best practices
--------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/loop#obfuscate-branching)
Obfuscate branching
The previous paragraph emphasized that branch logic should rely as much as possible on `FHE.select` instead of decryptions. It hides effectively which branch has been executed.
However, this is sometimes not enough. Enhancing the privacy of smart contracts often requires revisiting your application's logic.
For example, if implementing a simple AMM for two encrypted ERC20 tokens based on a linear constant function, it is recommended to not only hide the amounts being swapped, but also the token which is swapped in a pair.
✅ Here is a very simplified example implementation, we suppose here that the rate between tokenA and tokenB is constant and equals to 1:
Copy
// typically either encryptedAmountAIn or encryptedAmountBIn is an encrypted null value
// ideally, the user already owns some amounts of both tokens and has pre-approved the AMM on both tokens
function swapTokensForTokens(
externalEuint32 encryptedAmountAIn,
externalEuint32 encryptedAmountBIn,
bytes calldata inputProof
) external {
euint32 encryptedAmountA = FHE.asEuint32(encryptedAmountAIn, inputProof); // even if amount is null, do a transfer to obfuscate trade direction
euint32 encryptedAmountB = FHE.asEuint32(encryptedAmountBIn, inputProof); // even if amount is null, do a transfer to obfuscate trade direction
// send tokens from user to AMM contract
FHE.allowTransient(encryptedAmountA, tokenA);
IConfidentialERC20(tokenA).transferFrom(msg.sender, address(this), encryptedAmountA);
FHE.allowTransient(encryptedAmountB, tokenB);
IConfidentialERC20(tokenB).transferFrom(msg.sender, address(this), encryptedAmountB);
// send tokens from AMM contract to user
// Price of tokenA in tokenB is constant and equal to 1, so we just swap the encrypted amounts here
FHE.allowTransient(encryptedAmountB, tokenA);
IConfidentialERC20(tokenA).transfer(msg.sender, encryptedAmountB);
FHE.allowTransient(encryptedAmountA, tokenB);
IConfidentialERC20(tokenB).transferFrom(msg.sender, address(this), encryptedAmountA);
}
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/loop#avoid-using-encrypted-indexes)
Avoid using encrypted indexes
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.
Copy
euint32 x;
euint32[] encArray;
function setXwithEncryptedIndex(externalEuint32 encryptedIndex, bytes calldata inputProof) public {
euint32 index = FHE.asEuint32(encryptedIndex, inputProof);
for (uint32 i = 0; i < encArray.length; i++) {
ebool isEqual = FHE.eq(index, i);
x = FHE.select(isEqual, encArray[i], x);
}
FHE.allowThis(x);
}
[PreviousBranching](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions)
[NextError handling](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/error_handling)
Last updated 3 months ago
---
# Encrypted inputs | Protocol
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.
Encrypted inputs are a core feature of FHEVM, enabling users to push encrypted data onto the blockchain while ensuring data confidentiality and integrity.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs#what-are-encrypted-inputs)
What are encrypted inputs?
--------------------------------------------------------------------------------------------------------------------------------------
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs#key-characteristics-of-encrypted-inputs)
Key characteristics of encrypted inputs:
1. **Confidentiality**: Data is encrypted using the public FHE key, ensuring that only authorized parties can decrypt or process the values.
2. **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.
3. **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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs#parameters-in-encrypted-functions)
Parameters in encrypted functions
-----------------------------------------------------------------------------------------------------------------------------------------------------
When a function in a smart contract is called, it may accept two types of parameters for encrypted inputs:
1. `**externalEbool**`**,** `**externalEaddress**`**,**`**externalEuintXX**`: Refers to the index of the encrypted parameter within the proof, representing a specific encrypted input handle.
2. `**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:
Copy
function exampleFunction(
externalEbool param1,
externalEuint64 param2,
externalEuint8 param3,
bytes calldata inputProof
) public {
// Function logic here
}
In this example, `param1`, `param2`, and `param3` are encrypted inputs for `ebool`, `euint64`, and `euint8` while `inputProof` contains the corresponding ZKPoK to validate their authenticity.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs#input-generation-using-hardhat)
Input Generation using Hardhat
In the below example, we use Alice's address to create the encrypted inputs and submits the transaction.
Copy
import { fhevm } from "hardhat";
const input = fhevm.createEncryptedInput(contract.address, signers.alice.address);
input.addBool(canTransfer); // at index 0
input.add64(transferAmount); // at index 1
input.add8(transferType); // at index 2
const encryptedInput = await input.encrypt();
const externalEboolParam1 = encryptedInput.handles[0];
const externalEuint64Param2 = encryptedInput.handles[1];
const externalEuint8Param3 = encryptedInput.handles[2];
const inputProof = encryptedInput.inputProof;
tx = await myContract
.connect(signers.alice)
[\
"exampleFunction(bytes32,bytes32,bytes32,bytes)"\
](signers.bob.address, externalEboolParam1, externalEuint64Param2, externalEuint8Param3, inputProof);
await tx.wait();
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs#input-order)
Input Order
Developers are free to design the function parameters in any order. There is no required correspondence between the order in which encrypted inputs are constructed in TypeScript and the order of arguments in the Solidity function.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs#validating-encrypted-inputs)
Validating encrypted inputs
-----------------------------------------------------------------------------------------------------------------------------------------
Smart contracts process encrypted inputs by verifying them against the associated zero-knowledge proof. This is done using the `FHE.asEuintXX`, `FHE.asEbool`, or `FHE.asEaddress` functions, which validate the input and convert it into the appropriate encrypted type.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs#example-validation)
Example validation
This example demonstrates a function that performs multiple encrypted operations, such as updating a user's encrypted balance and toggling an encrypted boolean flag:
Copy
function myExample(externalEuint64 encryptedAmount, externalEbool encryptedToggle, bytes calldata inputProof) public {
// Validate and convert the encrypted inputs
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
ebool toggleFlag = FHE.fromExternal(encryptedToggle, inputProof);
// Update the user's encrypted balance
balances[msg.sender] = FHE.add(balances[msg.sender], amount);
// Toggle the user's encrypted flag
userFlags[msg.sender] = FHE.not(toggleFlag);
// FHE permissions and function logic here
...
}
// Function to retrieve a user's encrypted balance
function getEncryptedBalance() public view returns (euint64) {
return balances[msg.sender];
}
// Function to retrieve a user's encrypted flag
function getEncryptedFlag() public view returns (ebool) {
return userFlags[msg.sender];
}
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs#example-validation-in-the-confidentialerc20.sol-smart-contract)
Example validation in the `ConfidentialERC20.sol` smart contract
Here’s an example of a smart contract function that verifies an encrypted input before proceeding:
Copy
function transfer(
address to,
externalEuint64 encryptedAmount,
bytes calldata inputProof
) public {
// Verify the provided encrypted amount and convert it into an encrypted uint64
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
// Function logic here, such as transferring funds
...
}
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs#how-validation-works)
How validation works
1. **Input verification**: The `FHE.fromExternal` function ensures that the input is a valid ciphertext with a corresponding ZKPoK.
2. **Type conversion**: The function transforms `externalEbool`, `externalEaddress`, `externalEuintXX` into the appropriate encrypted type (`ebool`, `eaddress`, `euintXX`) for further operations within the contract.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs#best-practices)
Best Practices
---------------------------------------------------------------------------------------------------------------
* **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.
[PreviousGenerate random numbers](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/random)
[NextAccess Control List](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl)
Last updated 3 months ago
---
# Set up Hardhat | Protocol
In this section, you’ll learn how to set up a FHEVM Hardhat development environment using the **FHEVM Hardhat template** as a starting point for building and testing fully homomorphic encrypted smart contracts.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#create-a-local-hardhat-project)
Create a local Hardhat Project
-----------------------------------------------------------------------------------------------------------------------------------------------
1
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#install-a-node.js-tls-version)
Install a Node.js TLS version
Ensure that Node.js is installed on your machine.
* Download and install the recommended LTS (Long-Term Support) version from the [official website](https://nodejs.org/en)
.
* Use an **even-numbered** version (e.g., `v18.x`, `v20.x`)
**Hardhat** does not support odd-numbered Node.js versions. If you’re using one (e.g., v21.x, v23.x), Hardhat will display a persistent warning message and may behave unexpectedly.
To verify your installation:
Copy
node -v
npm -v
2
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#create-a-new-github-repository-from-the-fhevm-hardhat-template)
Create a new GitHub repository from the FHEVM Hardhat template.
1. On GitHub, navigate to the main page of the [FHEVM Hardhat template](https://github.com/zama-ai/fhevm-hardhat-template)
repository.
2. Above the file list, click the green **Use this template** button.
3. Follow the instructions to create a new repository from the FHEVM Hardhat template.
See Github doc: [Creating a repository from a template](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template#creating-a-repository-from-a-template)
3
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#clone-your-newly-created-github-repository-locally)
Clone your newly created GitHub repository locally
Now that your GitHub repository has been created, you can clone it to your local machine:
Copy
cd
git clone
# Navigate to the root of your new FHEVM Hardhat project
cd
Next, let’s install your local Hardhat development environment.
4
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#install-your-fhevm-hardhat-project-dependencies)
Install your FHEVM Hardhat project dependencies
From the project root directory, run:
Copy
npm install
This will install all required dependencies defined in your `package.json`, setting up your local FHEVM Hardhat development environment.
5
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#set-up-the-hardhat-configuration-variables-optional)
Set up the Hardhat configuration variables (optional)
If you do plan to deploy to the Sepolia Ethereum Testnet, you'll need to set up the following [Hardhat Configuration variables](https://hardhat.org/hardhat-runner/docs/guides/configuration-variables)
.
`MNEMONIC`
A mnemonic is a 12-word seed phrase used to generate your Ethereum wallet keys.
1. Get one by creating a wallet with [MetaMask](https://metamask.io/)
, or using any trusted mnemonic generator.
2. Set it up in your Hardhat project:
Copy
npx hardhat vars set MNEMONIC
`INFURA_API_KEY`
The INFURA project key allows you to connect to Ethereum testnets like Sepolia.
1. Obtain one by following the [Infura + MetaMask](https://docs.metamask.io/services/get-started/infura/)
setup guide.
2. Configure it in your project:
Copy
npx hardhat vars set INFURA_API_KEY
**Default Values**
If you skip this step, Hardhat will fall back to these defaults:
* `MNEMONIC` = "test test test test test test test test test test test junk"
* `INFURA_API_KEY` = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
These defaults are not suitable for real deployments.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#missing-variable-error)
Missing variable error:
If any of the requested Hardhat Configuration Variables is missing, you'll get an error message like this one:`Error HH1201: Cannot find a value for the configuration variable 'MNEMONIC'. Use 'npx hardhat vars set MNEMONIC' to set it or 'npx hardhat var setup' to list all the configuration variables used by this project.`
Congratulations! You're all set to start building your confidential dApp.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#optional-settings)
Optional settings
---------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#install-vscode-extensions)
Install VSCode extensions
If you're using Visual Studio Code, there are some extensions available to improve you your development experience:
* [Prettier - Code formatter by prettier.io](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
— ID:`esbenp.prettier-vscode`,
* [ESLint by Microsoft](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
— ID:`dbaeumer.vscode-eslint`
Solidity support (pick one only):
* [Solidity by Juan Blanco](https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity)
— ID:`juanblanco.solidity`
* [Solidity by Nomic Foundation](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity)
— ID:`nomicfoundation.hardhat-solidity`
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#rest-set-the-hardhat-project)
Rest set the Hardhat project
If you'd like to start from a clean slate, you can reset your FHEVM Hardhat project by removing all example code and generated files.
Copy
# Navigate to the root of your new FHEVM Hardhat project
cd
Then run:
Copy
rm -rf test/* src/* tasks/* deploy ./fhevmTemp ./artifacts ./cache ./coverage ./types ./coverage.json ./dist
[PreviousWhat is FHEVM Solidity](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/overview)
[NextQuick start tutorial](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial)
Last updated 3 months ago
---
# Access Control List | Protocol
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl#what-is-the-acl)
What is the ACL?
---------------------------------------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl#why-is-the-acl-important)
Why is the ACL important?
---------------------------------------------------------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl#how-does-the-acl-work)
How does the ACL work?
---------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl#types-of-access)
Types of access
* **Permanent allowance**:
* Configured using `FHE.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 `FHE.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.
* **Permanent public allowance**:
* Configured using `FHE.makePubliclyDecryptable(ciphertext)`.
* Grants long-term access to the ciphertext for any user.
* Stored in a dedicated contract for persistent storage.
**Syntactic sugar**:
* `FHE.allowThis(ciphertext)` is shorthand for `FHE.allow(ciphertext, address(this))`. It authorizes the current contract to reuse a ciphertext handle in future transactions.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl#transient-vs.-permanent-allowance)
Transient vs. permanent allowance
Allowance type
Purpose
Storage type
Use case
**Transient**
Temporary access during a transaction.
[Transient storage](https://eips.ethereum.org/EIPS/eip-1153)
(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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl#granting-and-verifying-access)
Granting and verifying access
------------------------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl#granting-access)
Granting access
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl#verifying-access)
Verifying access
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl#practical-uses-of-the-acl)
Practical uses of the ACL
----------------------------------------------------------------------------------------------------------------------------------
* **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.
* * *
For a detailed explanation of the ACL's functionality, including code examples and advanced configurations, see [ACL examples](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples)
.
[PreviousEncrypted inputs](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs)
[NextACL examples](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples)
Last updated 3 months ago
---
# Error handling | Protocol
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/error_handling#challenges-in-error-handling)
**Challenges in error handling**
--------------------------------------------------------------------------------------------------------------------------------------------------------------
In the context of encrypted data:
1. **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.
2. **Limited feedback**: Encrypted computations lack direct mechanisms for exposing failure reasons while maintaining confidentiality.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/error_handling#recommended-approach-error-logging-with-a-handler)
**Recommended approach: Error logging with a handler**
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/error_handling#example-implementation)
**Example implementation**
The following contract snippet demonstrates how to implement and use an error handler:
Copy
struct LastError {
euint8 error; // Encrypted error code
uint timestamp; // Timestamp of the error
}
// Define error codes
euint8 internal NO_ERROR;
euint8 internal NOT_ENOUGH_FUNDS;
constructor() {
NO_ERROR = FHE.asEuint8(0); // Code 0: No error
NOT_ENOUGH_FUNDS = FHE.asEuint8(1); // Code 1: Insufficient funds
}
// Store the last error for each address
mapping(address => LastError) private _lastErrors;
// Event to notify about an error state change
event ErrorChanged(address indexed user);
/**
* @dev Set the last error for a specific address.
* @param error Encrypted error code.
* @param addr Address of the user.
*/
function setLastError(euint8 error, address addr) private {
_lastErrors[addr] = LastError(error, block.timestamp);
emit ErrorChanged(addr);
}
/**
* @dev Internal transfer function with error handling.
* @param from Sender's address.
* @param to Recipient's address.
* @param amount Encrypted transfer amount.
*/
function _transfer(address from, address to, euint32 amount) internal {
// Check if the sender has enough balance to transfer
ebool canTransfer = FHE.le(amount, balances[from]);
// Log the error state: NO_ERROR or NOT_ENOUGH_FUNDS
setLastError(FHE.select(canTransfer, NO_ERROR, NOT_ENOUGH_FUNDS), msg.sender);
// Perform the transfer operation conditionally
balances[to] = FHE.add(balances[to], FHE.select(canTransfer, amount, FHE.asEuint32(0)));
FHE.allowThis(balances[to]);
FHE.allow(balances[to], to);
balances[from] = FHE.sub(balances[from], FHE.select(canTransfer, amount, FHE.asEuint32(0)));
FHE.allowThis(balances[from]);
FHE.allow(balances[from], from);
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/error_handling#how-it-works)
**How It Works**
------------------------------------------------------------------------------------------------------------------------------
1. **Define error codes**:
* `NO_ERROR`: Indicates a successful operation.
* `NOT_ENOUGH_FUNDS`: Indicates insufficient balance for a transfer.
2. **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.
3. **Conditional updates**:
* Use the `FHE.select` function to update balances and log errors based on the transfer condition (`canTransfer`).
4. **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."
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/error_handling#example-error-query)
**Example error query**
--------------------------------------------------------------------------------------------------------------------------------------------
The frontend or another contract can query the `_lastErrors` mapping to retrieve error details:
Copy
/**
* @dev Get the last error for a specific address.
* @param user Address of the user.
* @return error Encrypted error code.
* @return timestamp Timestamp of the error.
*/
function getLastError(address user) public view returns (euint8 error, uint timestamp) {
LastError memory lastError = _lastErrors[user];
return (lastError.error, lastError.timestamp);
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/error_handling#benefits-of-this-approach)
**Benefits of this approach**
--------------------------------------------------------------------------------------------------------------------------------------------------------
1. **User feedback**:
* Provides actionable error messages without compromising the confidentiality of encrypted computations.
2. **Scalable error tracking**:
* Logs errors per user, making it easy to identify and debug specific issues.
3. **Event-driven notifications**:
* Enables frontends to react to errors in real time via the `ErrorChanged` event.
By implementing error handlers as demonstrated, developers can ensure a seamless user experience while maintaining the privacy and integrity of encrypted data operations.
[PreviousDealing with branches and conditions](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/loop)
[NextDecryption](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle)
Last updated 3 months ago
---
# Deploy contracts and run tests | Protocol
In this section, you'll find everything you need to test your FHEVM smart contracts in your [Hardhat](https://hardhat.org/)
project.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/run_test#fhevm-runtime-modes)
FHEVM Runtime Modes
The FHEVM Hardhat plugin provides three **FHEVM runtime modes** tailored for different stages of contract development and testing. Each mode offers a trade-off between speed, encryption, and persistence.
1. The **Hardhat (In-Memory)** default network: 🧪 _Uses mock encryption._ Ideal for regular tests, CI test coverage, and fast feedback during early contract development. No real encryption is used.
2. The **Hardhat Node (Local Server)** network: 🧪 _Uses mock encryption._ Ideal when you need persistent state - for example, when testing frontend interactions, simulating user flows, or validating deployments in a realistic local environment. Still uses mock encryption.
3. The **Sepolia Testnet** network: 🔐 _Uses real encryption._ Use this mode once your contract logic is stable and validated locally. This is the only mode that runs on the full FHEVM stack with **real encrypted values**. It simulates real-world production conditions but is slower and requires Sepolia ETH.
**Zama Testnet** is not a blockchain itself. It is a protocol that enables you to run confidential smart contracts on existing blockchains (such as Ethereum, Base, and others) with the support of encrypted types. See the [FHE on blockchain](https://docs.zama.ai/protocol/protocol/overview)
guide to learn more about the protocol architecture.
Currently, **Zama Protocol** is available on the **Sepolia Testnet**. Support for additional chains will be added in the future. [See the roadmap↗](https://docs.zama.ai/protocol/zama-protocol-litepaper#roadmap)
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/run_test#summary)
Summary
Mode
Encryption
Persistent
Chain
Speed
Usage
Hardhat (default)
🧪 Mock
❌ No
In-Memory
⚡⚡ Very Fast
Fast local testing and coverage
Hardhat Node
🧪 Mock
✅ Yes
Server
⚡ Fast
Frontend integration and local persistent testing
Sepolia Testnet
🔐 Real Encryption
✅ Yes
Server
🐢 Slow
Full-stack validation with real encrypted data
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/run_test#the-fhevm-hardhat-template)
The FHEVM Hardhat Template
To demonstrate the three available testing modes, we'll use the [fhevm-hardhat-template](https://github.com/zama-ai/fhevm-hardhat-template)
, which comes with the FHEVM Hardhat Plugin pre-installed, a basic `FHECounter` smart contract, and ready-to-use tasks for interacting with a deployed instance of this contract.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/run_test#run-on-hardhat-default)
Run on Hardhat (default)
To run your tests in-memory using FHEVM mock values, simply run the following:
Copy
npx hardhat test --network hardhat
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/run_test#run-on-hardhat-node)
Run on Hardhat Node
You can also run your tests against a local Hardhat node, allowing you to deploy contract instances and interact with them in a persistent environment.
1
**Launch the Hardhat Node server:**
* Open a new terminal window.
* From the root project directory, run the following:
Copy
npx hardhat node
2
**Run your test suite (optional):**
From the root project directory:
Copy
npx hardhat test --network localhost
3
**Deploy the** `**FHECounter**` **smart contract on Hardhat Node**
From the root project directory:
Copy
npx hardhat deploy --network localhost
Check the deployed contract FHEVM configuration:
Copy
npx hardhat fhevm check-fhevm-compatibility --network localhost --address
4
**Interact with the deployed** `**FHECounter**` **smart contract**
From the root project directory:
1. Decrypt the current counter value:
Copy
npx hardhat --network localhost task:decrypt-count
1. Increment the counter by 1:
Copy
npx hardhat --network localhost task:increment --value 1
1. Decrypt the new counter value:
Copy
npx hardhat --network localhost task:decrypt-count
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/run_test#run-on-sepolia-ethereum-testnet)
Run on Sepolia Ethereum Testnet
To test your FHEVM smart contract using real encrypted values, you can run your tests on the Sepolia Testnet.
1
**Rebuild the project for Sepolia**
From the root project directory:
Copy
npx hardhat clean
npx hardhat compile --network sepolia
2
**Deploy the** `**FHECounter**` **smart contract on Sepolia**
Copy
npx hardhat deploy --network sepolia
3
**Check the deployed** `**FHECounter**` **contract FHEVM configuration**
From the root project directory:
Copy
npx hardhat fhevm check-fhevm-compatibility --network sepolia --address
If an internal exception is raised, it likely means the contract was not properly compiled for the Sepolia network.
4
**Interact with the deployed** `**FHECounter**` **contract**
From the root project directory:
1. Decrypt the current counter value (⏳ wait...):
Copy
npx hardhat --network sepolia task:decrypt-count
1. Increment the counter by 1 (⏳ wait...):
Copy
npx hardhat --network sepolia task:increment --value 1
1. Decrypt the new counter value (⏳ wait...):
Copy
npx hardhat --network sepolia task:decrypt-count
[PreviousWrite FHEVM tests in Hardhat](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/write_test)
[NextFoundry](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/foundry)
Last updated 3 months ago
---
# Decryption | Protocol
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.
Decryption is essential in two primary cases:
1. **Smart contract logic**: A contract requires plaintext values for computations or decision-making.
2. **User interaction**: Plaintext data needs to be revealed to all users, such as revealing the decision of the vote.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle#overview)
Overview
---------------------------------------------------------------------------------------------------
Decryption in FHEVM is an asynchronous process that involves the Relayer and Key Management System (KMS). Here’s an example of how to safely request decryption in a contract.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle#example-asynchronous-decryption-in-a-contract)
Example: asynchronous decryption in a contract
Copy
pragma solidity ^0.8.24;
import "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract TestAsyncDecrypt is SepoliaConfig {
ebool xBool;
bool public yBool;
bool isDecryptionPending;
uint256 latestRequestId;
constructor() {
xBool = FHE.asEbool(true);
FHE.allowThis(xBool);
}
function requestBool() public {
require(!isDecryptionPending, "Decryption is in progress");
bytes32[] memory cts = new bytes32[](1);
cts[0] = FHE.toBytes32(xBool);
uint256 latestRequestId = FHE.requestDecryption(cts, this.myCustomCallback.selector);
/// @dev This prevents sending multiple requests before the first callback was sent.
isDecryptionPending = true;
}
function myCustomCallback(uint256 requestId, bool decryptedInput, bytes[] memory signatures) public returns (bool) {
/// @dev This check is used to verify that the request id is the expected one.
require(requestId == latestRequestId, "Invalid requestId");
FHE.checkSignatures(requestId, signatures);
yBool = decryptedInput;
isDecryptionPending = false;
return yBool;
}
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle#decryption-in-depth)
Decryption in depth
-------------------------------------------------------------------------------------------------------------------------
This document provides a detailed guide on implementing decryption in your smart contracts using the `DecryptionOracle` in fhevm. It covers the setup, usage of the `FHE.requestDecryption` function, and testing with Hardhat.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle#decryptionoracle-setup)
`DecryptionOracle` setup
---------------------------------------------------------------------------------------------------------------------------------
The `DecryptionOracle` is pre-deployed on the FHEVM testnet. It uses a default relayer account specified in the `.env` file.
Anyone can fulfill decryption requests but it is essential to add signature verification (and to include a logic to invalidate the replay of decryption requests). The role of the `DecryptionOracle` contract is to independently verify the KMS signature during execution. This ensures that the relayers cannot manipulate or send fraudulent decryption results, even if compromised.
There are two functions to consider: `requestDecryption` and `checkSignatures`.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle#fhe.requestdecryption-function)
`FHE.requestDecryption` function
You can call the function `FHE.requestDecryption` as such:
Copy
function requestDecryption(
bytes32[] calldata ctsHandles,
bytes4 callbackSelector
) external payable returns (uint256 requestId);
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle#function-arguments)
Function arguments
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`, `euint8`, `euint16`, `euint32`, `euint64` or `eaddress`.
`ctsHandles` is the array of ciphertexts that are requested to be decrypted. Tthe 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 once the relayer fulfils the decryption request.
Copy
function [callbackName](uint256 requestID, XXX x_0, XXX x_1, ..., XXX x_N-1, bytes[] memory signatures) external;
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:
Ciphertext type
Decrypted type
ebool
bool
euint8
uint8
euint16
uint16
euint32
uint32
euint64
uint64
euint128
uint128
euint256
uint256
eaddress
address
Here `callbackName` is a custom name given by the developer to the callback function, `requestID` will be the request id of the decryption (could be commented if not needed in the logic, but must be present) and `x_0`, `x_1`, ... `x_N-1` are the results of the decryption of the `ct` array values, i.e their number should be the size of the `ct` array.
`msgValue` is the value in native tokens to be sent to the calling contract during fulfillment, i.e when the callback will be called with the results of decryption.
Notice that the callback should always verify the signatures and implement a replay protection mechanism (see below).
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle#fhe.checksignatures-function)
`FHE.checkSignatures` function
You can call the function `FHE.checkSignatures` as such:
Copy
function checkSignatures(uint256 requestId, bytes[] memory signatures);
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/oracle#function-arguments-1)
Function arguments
The first argument, `requestID`, is the value that was returned in the `requestDecryption`function. The second argument, `signatures`, is an array of signatures from the KMS signers.
This function reverts if the signatures are invalid.
[PreviousError handling](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/error_handling)
[NextHardhat plugin](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat)
Last updated 3 months ago
---
# ERC7984 Standard | Protocol
This example demonstrates how to create a confidential token using OpenZeppelin's smart contract library powered by ZAMA's FHEVM.
To run this example correctly, make sure you clone the [fhevm-hardhat-template](https://github.com/zama-ai/fhevm-hardhat-template)
and that the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
ERC7984Example.sol
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984#tab-erc7984example.sol)
ERC7984Example.test.ts
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984#tab-erc7984example.test.ts)
ERC7984Example.fixture.ts
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984#tab-erc7984example.fixture.ts)
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {FHE, externalEuint64, euint64} from "@fhevm/solidity/lib/FHE.sol";
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
import {ERC7984} from "@openzeppelin/confidential-contracts/token/ERC7984.sol";
contract ERC7984Example is SepoliaConfig, ERC7984, Ownable2Step {
constructor(
address owner,
uint64 amount,
string memory name_,
string memory symbol_,
string memory tokenURI_
) ERC7984(name_, symbol_, tokenURI_) Ownable(owner) {
euint64 encryptedAmount = FHE.asEuint64(amount);
_mint(owner, encryptedAmount);
}
}
Copy
import { expect } from 'chai';
import { ethers, fhevm } from 'hardhat';
describe('ERC7984Example', function () {
let token: any;
let owner: any;
let recipient: any;
let other: any;
const INITIAL_AMOUNT = 1000;
const TRANSFER_AMOUNT = 100;
beforeEach(async function () {
[owner, recipient, other] = await ethers.getSigners();
// Deploy ERC7984Example contract
token = await ethers.deployContract('ERC7984Example', [\
owner.address,\
INITIAL_AMOUNT,\
'Confidential Token',\
'CTKN',\
'https://example.com/token'\
]);
});
describe('Initialization', function () {
it('should set the correct name', async function () {
expect(await token.name()).to.equal('Confidential Token');
});
it('should set the correct symbol', async function () {
expect(await token.symbol()).to.equal('CTKN');
});
it('should set the correct token URI', async function () {
expect(await token.tokenURI()).to.equal('https://example.com/token');
});
it('should mint initial amount to owner', async function () {
// Verify that the owner has a balance (without decryption for now)
const balanceHandle = await token.confidentialBalanceOf(owner.address);
expect(balanceHandle).to.not.be.undefined;
});
});
describe('Transfer Process', function () {
it('should transfer tokens from owner to recipient', async function () {
// Create encrypted input for transfer amount
const encryptedInput = await fhevm
.createEncryptedInput(await token.getAddress(), owner.address)
.add64(TRANSFER_AMOUNT)
.encrypt();
// Perform the transfer
await expect(token
.connect(owner)
['confidentialTransfer(address,bytes32,bytes)'](
recipient.address,
encryptedInput.handles[0],
encryptedInput.inputProof
)).to.not.be.reverted;
// Check that both addresses have balance handles (without decryption for now)
const recipientBalanceHandle = await token.confidentialBalanceOf(recipient.address);
const ownerBalanceHandle = await token.confidentialBalanceOf(owner.address);
expect(recipientBalanceHandle).to.not.be.undefined;
expect(ownerBalanceHandle).to.not.be.undefined;
});
it('should allow recipient to transfer received tokens', async function () {
// First transfer from owner to recipient
const encryptedInput1 = await fhevm
.createEncryptedInput(await token.getAddress(), owner.address)
.add64(TRANSFER_AMOUNT)
.encrypt();
await expect(token
.connect(owner)
['confidentialTransfer(address,bytes32,bytes)'](
recipient.address,
encryptedInput1.handles[0],
encryptedInput1.inputProof
)).to.not.be.reverted;
// Second transfer from recipient to other
const encryptedInput2 = await fhevm
.createEncryptedInput(await token.getAddress(), recipient.address)
.add64(50) // Transfer half of what recipient received
.encrypt();
await expect(token
.connect(recipient)
['confidentialTransfer(address,bytes32,bytes)'](
other.address,
encryptedInput2.handles[0],
encryptedInput2.inputProof
)).to.not.be.reverted;
// Check that all addresses have balance handles (without decryption for now)
const otherBalanceHandle = await token.confidentialBalanceOf(other.address);
const recipientBalanceHandle = await token.confidentialBalanceOf(recipient.address);
expect(otherBalanceHandle).to.not.be.undefined;
expect(recipientBalanceHandle).to.not.be.undefined;
});
it('should revert when trying to transfer more than balance', async function () {
const excessiveAmount = INITIAL_AMOUNT + 100;
const encryptedInput = await fhevm
.createEncryptedInput(await token.getAddress(), recipient.address)
.add64(excessiveAmount)
.encrypt();
await expect(
token
.connect(recipient)
['confidentialTransfer(address,bytes32,bytes)'](
other.address,
encryptedInput.handles[0],
encryptedInput.inputProof
)
).to.be.revertedWithCustomError(token, 'ERC7984ZeroBalance')
.withArgs(recipient.address);
});
it('should revert when transferring to zero address', async function () {
const encryptedInput = await fhevm
.createEncryptedInput(await token.getAddress(), owner.address)
.add64(TRANSFER_AMOUNT)
.encrypt();
await expect(
token
.connect(owner)
['confidentialTransfer(address,bytes32,bytes)'](
ethers.ZeroAddress,
encryptedInput.handles[0],
encryptedInput.inputProof
)
).to.be.revertedWithCustomError(token, 'ERC7984InvalidReceiver')
.withArgs(ethers.ZeroAddress);
});
});
});
Copy
import { ethers } from "hardhat";
import type { ERC7984Example } from "../../types";
import type { ERC7984Example__factory } from "../../types";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
export async function deployERC7984ExampleFixture(owner: HardhatEthersSigner) {
// Deploy ERC7984Example with initial supply
const ERC7984ExampleFactory = (await ethers.getContractFactory(
"ERC7984Example",
)) as ERC7984Example__factory;
const ERC7984Example = (await ERC7984ExampleFactory.deploy(
owner.address, // Owner address
1000, // Initial amount
"Confidential Token",
"CTKN",
"https://example.com/token",
)) as ERC7984Example;
const ERC7984ExampleAddress = await ERC7984Example.getAddress();
return {
ERC7984Example,
ERC7984ExampleAddress,
};
}
[PreviousLibrary installation and overview](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/openzeppelin)
[NextERC7984 Tutorial](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial)
Last updated 21 days ago
---
# Overview | Protocol
**Welcome to Solidity Guides!**
This section will guide you through writing confidential smart contracts in Solidity using the FHEVM library. With Fully Homomorphic Encryption(FHE), your contracts can operate directly on encrypted data without ever decrypting it onchain.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8#where-to-go-next)
Where to go next
---------------------------------------------------------------------------------------------
If you’re new to the Zama Protocol, start with the [Litepaper](https://docs.zama.ai/protocol/zama-protocol-litepaper)
or the [Protocol Overview](https://docs.zama.ai/protocol)
to understand the foundations.
Otherwise:
🟨 Go to [**What is FHEVM**](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/overview)
to learn about the core concepts and features.
🟨 Go to [**Quick Start Tutorial**](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial)
to build and test your first confidential smart contract.
🟨 Go to [**Smart Contract Guides**](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure)
for details on encrypted types, supported operations, inputs, ACL, and decryption flows.
🟨 Go to [**Development Guides**](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat)
to set up your local environment with Hardhat or Foundry and deploy FHEVM contracts.
🟨 Go to [**Migration Guide**](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration)
if you're upgrading from a previous version to v0.7.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8#help-center)
Help center
-----------------------------------------------------------------------------------
Ask technical questions and discuss with the community.
* [Community forum](https://community.zama.ai/c/fhevm/15)
* [Discord channel](https://discord.com/invite/zama)
[NextWhat is FHEVM Solidity](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/overview)
Last updated 1 month ago
---
# ACL examples | Protocol
This page provides detailed instructions and examples on how to use and implement the ACL (Access Control List) in fhevm. For an overview of ACL concepts and their importance, refer to the [access control list (ACL) overview](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl)
.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#controlling-access-permanent-and-transient-allowances)
Controlling access: permanent and transient allowances
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The ACL system allows you to define two types of permissions for accessing ciphertexts:
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#permanent-allowance)
Permanent allowance
* **Function**: `FHE.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.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#alternative-solidity-syntax)
Alternative Solidity syntax
You can also use method-chaining syntax for granting allowances since FHE is a Solidity library.
Copy
using FHE for *;
ciphertext.allow(address1).allow(address2);
This is equivalent to calling `FHE.allow(ciphertext, address1)` followed by `FHE.allow(ciphertext, address2)`.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#transient-allowance)
Transient allowance
* **Function**: `FHE.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.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#alternative-solidity-syntax-1)
Alternative Solidity syntax
Method chaining is also available for transient allowances since FHE is a Solidity library.
Copy
using FHE for *;
ciphertext.allowTransient(address1).allowTransient(address2);
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#syntactic-sugar)
Syntactic sugar
* **Function**: `FHE.allowThis(ciphertext)`
* **Equivalent To**: `FHE.allow(ciphertext, address(this))`
* **Purpose**: Simplifies granting permanent access to the current contract for managing ciphertexts.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#alternative-solidity-syntax-2)
Alternative Solidity syntax
You can also use method-chaining syntax for allowThis since FHE is a Solidity library.
Copy
using FHE for *;
ciphertext.allowThis();
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#make-publicly-decryptable)
Make publicly decryptable
To make a ciphertext publicly decryptable, you can use the `FHE.makePubliclyDecryptable(ciphertext)` function. This grants decryption rights to anyone, which is useful for scenarios where the encrypted value should be accessible by all.
Copy
// Grant public decryption right to a ciphertext
FHE.makePubliclyDecryptable(ciphertext);
// Or using method syntax:
ciphertext.makePubliclyDecryptable();
* **Function**: `FHE.makePubliclyDecryptable(ciphertext)`
* **Purpose**: Makes the ciphertext decryptable by anyone.
* **Use Case**: When you want to publish encrypted results or data.
> You can combine multiple allowance methods (such as `.allow()`, `.allowThis()`, `.allowTransient()`) directly on ciphertext objects to grant access to several addresses or contracts in a single, fluent statement.
>
> **Example**
>
> Copy
>
> // Grant transient access to one address and permanent access to another address
> ciphertext.allowTransient(address1).allow(address2);
>
> // Grant permanent access to the current contract and another address
> ciphertext.allowThis().allow(address1);
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#best-practices)
Best practices
-------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#verifying-sender-access)
Verifying sender access
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.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#example-scenario-confidential-erc20-attack)
Example scenario: Confidential ERC20 attack
Consider an **Confidential ERC20 token**. An attacker controlling two accounts, **Account A** and **Account B**, with 100 tokens in Account A, could exploit the system as follows:
1. The attacker attempts to send the target user's encrypted balance from **Account A** to **Account B**.
2. 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 `FHE.isSenderAllowed()` function to verify that the sender has legitimate access to the encrypted amount being transferred.
* * *
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#example-secure-verification)
Example: secure verification
Copy
function transfer(address to, euint64 encryptedAmount, bytes calldata inputProof) public {
// Ensure the sender is authorized to access the encrypted amount
require(FHE.isSenderAllowed(encryptedAmount), "Unauthorized access to encrypted amount.");
// Proceed with further logic
euint64 amount = FHE.asEuint64(encryptedAmount);
...
}
By enforcing this check, you can safeguard against inference attacks and ensure that encrypted values are only manipulated by authorized entities.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#acl-for-user-decryption)
ACL for user decryption
-------------------------------------------------------------------------------------------------------------------------------------------
If a ciphertext can be decrypt by a user, explicit access must be granted to them. Additionally, the user decryption mechanism requires the signature of a public key associated with the contract address. Therefore, a value that needs to be decrypted must be explicitly authorized for both the user and the contract.
Due to the user decryption mechanism, a user signs a public key associated with a specific contract; therefore, the ciphertext also needs to be allowed for the contract.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples#example-secure-transfer-in-confidentialerc20)
Example: Secure Transfer in ConfidentialERC20
Copy
function transfer(address to, euint64 encryptedAmount) public {
require(FHE.isSenderAllowed(encryptedAmount), "The caller is not authorized to access this encrypted amount.");
euint64 amount = FHE.asEuint64(encryptedAmount);
ebool canTransfer = FHE.le(amount, balances[msg.sender]);
euint64 newBalanceTo = FHE.add(balances[to], FHE.select(canTransfer, amount, FHE.asEuint64(0)));
balances[to] = newBalanceTo;
// Allow this new balance for both the contract and the owner.
FHE.allowThis(newBalanceTo);
FHE.allow(newBalanceTo, to);
euint64 newBalanceFrom = FHE.sub(balances[from], FHE.select(canTransfer, amount, FHE.asEuint64(0)));
balances[from] = newBalanceFrom;
// Allow this new balance for both the contract and the owner.
FHE.allowThis(newBalanceFrom);
FHE.allow(newBalanceFrom, from);
}
By understanding how to grant and verify permissions, you can effectively manage access to encrypted data in your FHEVM smart contracts. For additional context, see the [ACL overview](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl)
.
[PreviousAccess Control List](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl)
[NextReorgs handling](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/reorgs_handling)
Last updated 3 months ago
---
# Public Decrypt single value | Protocol
This example demonstrates the FHE public decryption mechanism with a single value.
Public decryption is a mechanism that makes encrypted values visible to everyone once decrypted. Unlike user decryption where values remain private to authorized users, public decryption makes the data permanently visible to all participants. The public decryption call occurs onchain through smart contracts, making the decrypted value part of the blockchain's public state.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
PublicDecryptSingleValue.sol
[](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-single-value#tab-publicdecryptsinglevalue.sol)
PublicDecryptSingleValue.ts
[](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-single-value#tab-publicdecryptsinglevalue.ts)
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { FHE, euint32 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract PublicDecryptSingleValue is SepoliaConfig {
euint32 private _encryptedUint32; // = 0 (uninitizalized)
uint32 private _clearUint32; // = 0 (uninitizalized)
// solhint-disable-next-line no-empty-blocks
constructor() {}
function initializeUint32(uint32 value) external {
// Compute a trivial FHE formula _trivialEuint32 = value + 1
_encryptedUint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
// Grant FHE permissions to:
// ✅ The contract itself (`address(this)`): allows it to request async public decryption to the FHEVM backend
//
// Note: If you forget to call `FHE.allowThis(_trivialEuint32)`,
// any async public decryption request of `_trivialEuint32`
// by the contract itself (`address(this)`) will fail!
FHE.allowThis(_encryptedUint32);
}
function initializeUint32Wrong(uint32 value) external {
// Compute a trivial FHE formula _trivialEuint32 = value + 1
_encryptedUint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
}
function requestDecryptSingleUint32() external {
bytes32[] memory cypherTexts = new bytes32[](1);
cypherTexts[0] = FHE.toBytes32(_encryptedUint32);
// Two possible outcomes:
// ✅ If `initializeUint32` was called, the public decryption request will succeed.
// ❌ If `initializeUint32Wrong` was called, the public decryption request will fail 💥
//
// Explanation:
// The request succeeds only if the contract itself (`address(this)`) was granted
// the necessary FHE permissions. Missing `FHE.allowThis(...)` will cause failure.
FHE.requestDecryption(
// the list of encrypte values we want to publc decrypt
cypherTexts,
// the function selector the FHEVM backend will callback with the clear values as arguments
this.callbackDecryptSingleUint32.selector
);
}
function callbackDecryptSingleUint32(uint256 requestID, bytes memory cleartexts, bytes memory decryptionProof) external {
// The `cleartexts` argument is an ABI encoding of the decrypted values associated to the
// handles (using `abi.encode`).
//
// ===============================
// ☠️🔒 SECURITY WARNING! 🔒☠️
// ===============================
//
// Must call `FHE.checkSignatures(...)` here!
// ------------------------
//
// This callback must only be called by the authorized FHEVM backend.
// To enforce this, the contract author MUST verify the authenticity of the caller
// by using the `FHE.checkSignatures` helper. This ensures that the provided signatures
// match the expected FHEVM backend and prevents unauthorized or malicious calls.
//
// Failing to perform this verification allows anyone to invoke this function with
// forged values, potentially compromising contract integrity.
//
// The responsibility for signature validation lies entirely with the contract author.
//
// The signatures are included in the `decryptionProof` parameter.
//
FHE.checkSignatures(requestID, cleartexts, decryptionProof);
(uint32 decryptedInput) = abi.decode(cleartexts, (uint32));
_clearUint32 = decryptedInput;
}
function clearUint32() public view returns (uint32) {
return _clearUint32;
}
}
Copy
import { PublicDecryptSingleValue, PublicDecryptSingleValue__factory } from "../../../types";
import type { Signers } from "../../types";
import { HardhatFhevmRuntimeEnvironment } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
import * as hre from "hardhat";
async function deployFixture() {
// Contracts are deployed using the first signer/account by default
const factory = (await ethers.getContractFactory(
"PublicDecryptSingleValue",
)) as PublicDecryptSingleValue__factory;
const publicDecryptSingleValue = (await factory.deploy()) as PublicDecryptSingleValue;
const publicDecryptSingleValue_address = await publicDecryptSingleValue.getAddress();
return { publicDecryptSingleValue, publicDecryptSingleValue_address };
}
/**
* This trivial example demonstrates the FHE public decryption mechanism
* and highlights a common pitfall developers may encounter.
*/
describe("PublicDecryptSingleValue", function () {
let contract: PublicDecryptSingleValue;
let signers: Signers;
before(async function () {
// Check whether the tests are running against an FHEVM mock environment
if (!hre.fhevm.isMock) {
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
}
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1] };
});
beforeEach(async function () {
// Deploy a new contract each time we run a new test
const deployment = await deployFixture();
contract = deployment.publicDecryptSingleValue;
});
// ✅ Test should succeed
it("public decryption should succeed", async function () {
let tx = await contract.connect(signers.alice).initializeUint32(123456);
await tx.wait();
tx = await contract.requestDecryptSingleUint32();
await tx.wait();
// We use the FHEVM Hardhat plugin to simulate the asynchronous onchain
// public decryption
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
// Use the built-in `awaitDecryptionOracle` helper to wait for the FHEVM public decryption oracle
// to complete all pending Solidity public decryption requests.
await fhevm.awaitDecryptionOracle();
// At this point, the Solidity callback should have been invoked by the FHEVM backend.
// We can now retrieve the decrypted (clear) value.
const clearUint32 = await contract.clearUint32();
expect(clearUint32).to.equal(123456 + 1);
});
// ❌ Test should fail
it("decryption should fail", async function () {
const tx = await contract.connect(signers.alice).initializeUint32Wrong(123456);
await tx.wait();
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
const senderNotAllowedError = fhevm.revertedWithCustomErrorArgs("ACL", "SenderNotAllowed");
await expect(contract.connect(signers.alice).requestDecryptSingleUint32()).to.be.revertedWithCustomError(
...senderNotAllowedError,
);
});
});
[PreviousUser decrypt multiple values](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-multiple-values)
[NextPublic Decrypt multiple values](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-multiple-values)
Last updated 21 days ago
---
# Configuration | Protocol
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure#core-configuration-setup)
Core configuration setup
--------------------------------------------------------------------------------------------------------------------------------------
To utilize encrypted computations in Solidity contracts, you must configure the **FHE library** and **Oracle 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.
This library and its associated contracts provide a standardized way to configure and interact with Zama's FHEVM (Fully Homomorphic Encryption Virtual Machine) infrastructure on different Ethereum networks. It supplies the necessary contract addresses for Zama's FHEVM components (`ACL`, `FHEVMExecutor`, `KMSVerifier`, `InputVerifier`) and the decryption oracle, enabling seamless integration for Solidity contracts that require FHEVM support.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure#key-components-configured-automatically)
Key components configured automatically
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
1. **FHE library**: Sets up encryption parameters and cryptographic keys.
2. **Oracle**: Manages secure cryptographic operations such as public decryption.
3. **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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure#zamaconfig.sol)
ZamaConfig.sol
------------------------------------------------------------------------------------------------------------------
The `ZamaConfig` library exposes functions to retrieve FHEVM configuration structs and oracle addresses for supported networks (currently only the Sepolia testnet).
Under the hood, this library encapsulates the network-specific addresses of Zama's FHEVM infrastructure into a single struct (`FHEVMConfigStruct`).
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure#sepoliaconfig)
SepoliaConfig
----------------------------------------------------------------------------------------------------------------
The `SepoliaConfig` contract is designed to be inherited by a user contract. The constructor automatically sets up the FHEVM coprocessor and decryption oracle using the configuration provided by the library for the respective network. When a contract inherits from `SepoliaConfig`, the constructor calls `FHE.setCoprocessor` and `FHE.setDecryptionOracle` with the appropriate addresses. This ensures that the inheriting contract is automatically wired to the correct FHEVM contracts and oracle for the target network, abstracting away manual address management and reducing the risk of misconfiguration.
**Example: using Sepolia configuration**
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract MyERC20 is SepoliaConfig {
constructor() {
// Additional initialization logic if needed
}
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure#using-isinitialized)
Using `isInitialized`
------------------------------------------------------------------------------------------------------------------------------
The `isInitialized` utility function checks whether an encrypted variable has been properly initialized, preventing unexpected behavior due to uninitialized values.
**Function signature**
Copy
function isInitialized(T v) internal pure returns (bool)
**Purpose**
* Ensures encrypted variables are initialized before use.
* Prevents potential logic errors in contract execution.
**Example: Initialization Check for Encrypted Counter**
Copy
require(FHE.isInitialized(counter), "Counter not initialized!");
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure#summary)
Summary
----------------------------------------------------------------------------------------------------
By leveraging prebuilt a configuration contract like `SepoliaConfig` in `ZamaConfig.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.
[Previous4\. Test the FHEVM contract](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract)
[NextContract addresses](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure/contract_addresses)
Last updated 1 month ago
---
# Logics | Protocol
[Branching](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions)
[Dealing with branches and conditions](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/loop)
[Error handling](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/error_handling)
[PreviousReorgs handling](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/reorgs_handling)
[NextBranching](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions)
---
# Branching | Protocol
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.
This document covers encrypted branching and how to move from an encrypted condition to a non-encrypted business logic in your smart contract.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions#what-is-confidential-branching)
What is confidential branching?
-----------------------------------------------------------------------------------------------------------------------------------------------------------
In FHEVM, when you perform [comparison operations](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations#comparison-operations)
, 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 `FHE.select` function, which acts as a ternary operator for encrypted values.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions#using-fhe.select-for-conditional-logic)
**Using** `**FHE.select**` **for conditional logic**
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The `FHE.select` function enables branching logic by selecting one of two encrypted values based on an encrypted condition (`ebool`). It works as follows:
Copy
FHE.select(condition, valueIfTrue, valueIfFalse);
* `**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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions#example-auction-bidding-logic)
**Example: Auction Bidding Logic**
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Here's an example of using conditional logic to update the highest winning number in a guessing game:
Copy
function bid(externalEuint64 encryptedValue, bytes calldata inputProof) external onlyBeforeEnd {
// Convert the encrypted input to an encrypted 64-bit integer
euint64 bid = FHE.asEuint64(encryptedValue, inputProof);
// Compare the current highest bid with the new bid
ebool isAbove = FHE.lt(highestBid, bid);
// Update the highest bid if the new bid is greater
highestBid = FHE.select(isAbove, bid, highestBid);
// Allow the contract to use the updated highest bid ciphertext
FHE.allowThis(highestBid);
}
This is a simplified example to demonstrate the functionality.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions#how-does-it-work)
How Does It Work?
* **Comparison**:
* The `FHE.lt` function compares `highestBid` and `bid`, returning an `ebool` (`isAbove`) that indicates whether the new bid is higher.
* **Selection**:
* The `FHE.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 `FHE.allowThis`.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions#key-considerations)
Key Considerations
----------------------------------------------------------------------------------------------------------------------------------
* **Value change behavior:** Each time `FHE.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 `FHE.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., `FHE.allowThis`, `FHE.allow`) to ensure the updated ciphertexts are authorized for use in future computations or transactions.
* * *
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions#how-to-branch-to-a-non-confidential-path)
How to branch to a non-confidential path?
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
So far, this section only covered how to do branching using encrypted variables. However, there may be many cases where the "public" contract logic will depend on the outcome from a encrypted path.
To do so, there are only one way to branch from an encrypted path to a non-encrypted path: it requires a public decryption using the oracle. Hence, any contract logic that requires moving from an encrypted input to a non-encrypted path always requires an async contract logic.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions#example-auction-bidding-logic-item-release)
**Example: Auction Bidding Logic: Item Release**
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Going back to our previous example with the auction bidding logic. Let's assume that the winner of the auction can receive some prize, which is not confidential.
Copy
bool public isPrizeDistributed;
eaddress internal highestBidder;
euint64 internal highestBid;
function bid(externalEuint64 encryptedValue, bytes calldata inputProof) external onlyBeforeEnd {
// Convert the encrypted input to an encrypted 64-bit integer
euint64 bid = FHE.asEuint64(encryptedValue, inputProof);
// Compare the current highest bid with the new bid
ebool isAbove = FHE.lt(highestBid, bid);
// Update the highest bid if the new bid is greater
highestBid = FHE.select(isAbove, bid, highestBid);
// Update the highest bidder address if the new bid is greater
highestBidder = FHE.select(isAbove, FHE.asEaddress(msg.sender), currentBidder));
// Allow the contract to use the highest bidder address
FHE.allowThis(highestBidder);
// Allow the contract to use the updated highest bid ciphertext
FHE.allowThis(highestBid);
}
function revealWinner() external onlyAfterEnd {
bytes32[] memory cts = new bytes32[](2);
cts[0] = FHE.toBytes32(highestBidder);
uint256 requestId = FHE.requestDecryption(cts, this.transferPrize.selector);
}
function transferPrize(uint256 requestId, address auctionWinner, bytes memory signatures) external {
require(!isPrizeDistributed, "Prize has already been distributed");
FHE.verifySignatures(requestId, signatures)
isPrizeDistributed = true;
// Business logic to transfer the prize to the auction winner
}
This is a simplified example to demonstrate the functionality.
As you can see the in the above example, the path to move from an encrypted condition to a decrypted business logic must be async and requires calling the decryption oracle contract to reveal the result of the logic using encrypted variables.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/conditions#summary)
Summary
------------------------------------------------------------------------------------------------------------
* `**FHE.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.
[PreviousLogics](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics)
[NextDealing with branches and conditions](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics/loop)
Last updated 3 months ago
---
# Quick start tutorial | Protocol
This tutorial guides you to start quickly with Zama’s **Fully Homomorphic Encryption (FHE)** technology for building confidential smart contracts.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial#what-youll-learn)
What You’ll Learn
-----------------------------------------------------------------------------------------------------------------------------------
In **about 30 minutes**, you'll go from a basic Solidity contract to a fully confidential one using **FHEVM**. Here's what you'll do:
1. Set up your development environment
2. Write a simple Solidity smart contract
3. Convert it into an FHEVM-compatible confidential contract
4. Test your FHEVM-compatible confidential contract
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial#prerequisite)
Prerequisite
--------------------------------------------------------------------------------------------------------------------------
* A basic understanding of **Solidity** library and **Ethereum**.
* Some familiarity with **Hardhat.**
**About Hardhat**
[**Hardhat**](https://hardhat.org/)
is a development environment for compiling, deploying, testing, and debugging Ethereum smart contracts. It’s widely used in the Ethereum ecosystem.
In this tutorial, we'll introduce the FHEVM hardhat template that provides an easy way to use FHEVM.
[PreviousSet up Hardhat](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup)
[Next2\. Write a simple contract](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract)
Last updated 1 month ago
---
# How to Transform Your Smart Contract into a FHEVM Smart Contract? | Protocol
This short guide will walk you through converting a standard Solidity contract into one that leverages Fully Homomorphic Encryption (FHE) using FHEVM. This approach lets you develop your contract logic as usual, then adapt it to support encrypted computation for privacy.
For this guide, we will focus on a voting contract example.
* * *
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/transform_smart_contract_with_fhevm#id-1.-start-with-a-standard-solidity-contract)
1\. Start with a Standard Solidity Contract
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Begin by writing your voting contract in Solidity as you normally would. Focus on implementing the core logic and functionality.
Copy
// Standard Solidity voting contract example
pragma solidity ^0.8.0;
contract SimpleVoting {
mapping(address => bool) public hasVoted;
uint64 public yesVotes;
uint64 public noVotes;
uint256 public voteDeadline;
function vote(bool support) public {
require(block.timestamp <= voteDeadline, "Too late to vote");
require(!hasVoted[msg.sender], "Already voted");
hasVoted[msg.sender] = true;
if (support) {
yesVotes += 1;
} else {
noVotes += 1;
}
}
function getResults() public view returns (uint64, uint64) {
return (yesVotes, noVotes);
}
}
* * *
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/transform_smart_contract_with_fhevm#id-2.-identify-sensitive-data-and-operations)
2\. Identify Sensitive Data and Operations
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Review your contract and determine which variables, functions, or computations require privacy. In this example, the vote counts (`yesVotes`, `noVotes`) and individual votes should be encrypted.
* * *
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/transform_smart_contract_with_fhevm#id-3.-integrate-fhevm-and-update-your-business-logic-accordingly)
3\. Integrate FHEVM and update your business logic accordingly.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Replace standard data types and operations with their FHEVM equivalents for the identified sensitive parts. Use encrypted types and FHEVM library functions to perform computations on encrypted data.
Copy
pragma solidity ^0.8.0;
import "@fhevm/solidity/lib/FHE.sol";
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
contract EncryptedSimpleVoting is SepoliaConfig {
enum VotingStatus {
Open,
DecryptionInProgress,
ResultsDecrypted
}
mapping(address => bool) public hasVoted;
VotingStatus public status;
uint64 public decryptedYesVotes;
uint64 public decryptedNoVotes;
uint256 public voteDeadline;
euint64 private encryptedYesVotes;
euint64 private encryptedNoVotes;
constructor() {
encryptedYesVotes = FHE.asEuint64(0);
encryptedNoVotes = FHE.asEuint64(0);
FHE.allowThis(encryptedYesVotes);
FHE.allowThis(encryptedNoVotes);
}
function vote(externalEbool support, bytes memory inputProof) public {
require(block.timestamp <= voteDeadline, "Too late to vote");
require(!hasVoted[msg.sender], "Already voted");
hasVoted[msg.sender] = true;
ebool isSupport = FHE.fromExternal(support, inputProof);
encryptedYesVotes = FHE.select(isSupport, FHE.add(encryptedYesVotes, 1), encryptedYesVotes);
encryptedNoVotes = FHE.select(isSupport, encryptedNoVotes, FHE.add(encryptedNoVotes, 1));
FHE.allowThis(encryptedYesVotes);
FHE.allowThis(encryptedNoVotes);
}
function requestVoteDecryption() public {
require(block.timestamp > voteDeadline, "Voting is not finished");
bytes32[] memory cts = new bytes32[](2);
cts[0] = FHE.toBytes32(encryptedYesVotes);
cts[1] = FHE.toBytes32(encryptedNoVotes);
uint256 requestId = FHE.requestDecryption(cts, this.callbackDecryptVotes.selector);
status = VotingStatus.DecryptionInProgress;
}
function callbackDecryptVotes(uint256 requestId, uint64 yesVotes, uint64 noVotes, bytes[] memory signatures) public {
FHE.checkSignatures(requestId, signatures);
decryptedYesVotes = yesVotes;
decryptedNoVotes = noVotes;
status = VotingStatus.ResultsDecrypted;
}
function getResults() public view returns (uint64, uint64) {
require(status == VotingStatus.ResultsDecrypted, "Results were not decrypted");
return (
decryptedYesVotes,
decryptedNoVotes
);
}
}
Adjust your contract’s code to accept and return encrypted data where necessary. This may involve changing function parameters and return types to work with ciphertexts instead of plaintext values, as shown above.
* The `vote` function now has two parameters: `support` and `inputProof`.
* The `getResults` can only be called after the decryption occurred. Otherwise, the decrypted results are not visible to anyone.
However, it is far from being the main change. As this example illustrates, working with FHEVM often requires re-architecting the original logic to support privacy.
In the updated code, the logic becomes async; results are hidden until a request (to the oracle) explicitely has to be made to decrypt publically the vote results.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/transform_smart_contract_with_fhevm#conclusion)
Conclusion
---------------------------------------------------------------------------------------------------------------------------------------
As this short guide showed, integrating with FHEVM not only requires integration with the FHEVM stack, it also requires refactoring your business logic to support mechanism to swift between encrypted and non-encrypted components of the logic.
[PreviousMigrate to v0.7](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/migration)
Last updated 3 months ago
---
# What is FHEVM Solidity | Protocol
This document provides an overview of key features of the FHEVM smart contract library.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/overview#configuration-and-initialization)
Configuration and initialization
Smart contracts using FHEVM require proper configuration and initialization:
* **Environment setup**: Import and inherit from environment-specific configuration contracts
* **Relayer configuration**: Configure secure relayer access for cryptographic operations
* **Initialization checks**: Validate encrypted variables are properly initialized before use
For more information see [Configuration](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/overview#encrypted-data-types)
Encrypted data types
FHEVM introduces encrypted data types compatible with Solidity:
* **Booleans**: `ebool`
* **Unsigned Integers**: `euint8`, `euint16`, `euint32`, `euint64`, `euint128`, `euint256`
* **Addresses**: `eaddress`
* **Input**: `externalEbool`, `externalEaddress`, `externalEuintXX` 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](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/types)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/overview#casting-types)
Casting types
fhevm provides functions to cast between encrypted types:
* **Casting between encrypted types**: `FHE.asEbool` converts encrypted integers to encrypted booleans
* **Casting to encrypted types**: `FHE.asEuintX` converts plaintext values to encrypted types
* **Casting to encrypted addresses**: `FHE.asEaddress` converts plaintext addresses to encrypted addresses
For more information see [use of encrypted types](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/types)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/overview#confidential-computation)
Confidential computation
fhevm enables symbolic execution of encrypted operations, supporting:
* **Arithmetic:** `FHE.add`, `FHE.sub`, `FHE.mul`, `FHE.min`, `FHE.max`, `FHE.neg`, `FHE.div`, `FHE.rem`
* Note: `div` and `rem` operations are supported only with plaintext divisors
* **Bitwise:** `FHE.and`, `FHE.or`, `FHE.xor`, `FHE.not`, `FHE.shl`, `FHE.shr`, `FHE.rotl`, `FHE.rotr`
* **Comparison:** `FHE.eq`, `FHE.ne`, `FHE.lt`, `FHE.le`, `FHE.gt`, `FHE.ge`
* **Advanced:** `FHE.select` for branching on encrypted conditions, `FHE.randEuintX` for on-chain randomness.
For more information on operations, see [Operations on encrypted types](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations)
.
For more information on conditional branching, see [Conditional logic in FHE](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions)
.
For more information on random number generation, see [Generate Random Encrypted Numbers](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/random)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/overview#access-control-mechanism)
Access control mechanism
fhevm enforces access control with a blockchain-based Access Control List (ACL):
* **Persistent access**: `FHE.allow`, `FHE.allowThis` grants permanent permissions for ciphertexts.
* **Transient access**: `FHE.allowTransient` provides temporary access for specific transactions.
* **Validation**: `FHE.isSenderAllowed` ensures that only authorized entities can interact with ciphertexts.
For more information see [ACL](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl)
.
[PreviousOverview](https://docs.zama.ai/protocol/solidity-guides/v0.8)
[NextSet up Hardhat](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup)
Last updated 1 month ago
---
# Reorgs handling | Protocol
This page provides detailed instructions on how to handle reorg risks on Ethereum when using FHEVM.
Since ACL events are propagated from the FHEVM host chain to the [Gateway](https://docs.zama.ai/protocol/protocol/overview/gateway)
immediately after being included in a block, dApp developers must take special care when encrypted information is critically important. For example, if an encrypted handle conceals the private key of a Bitcoin wallet holding significant funds, we need to ensure that this information cannot inadvertently leak to the wrong person due to a reorg on the FHEVM host chain. Therefore, it's the responsibility of dApp developers to prevent such scenarios by implementing a two-step ACL authorization process with a timelock between the request and the ACL call.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/reorgs_handling#simple-example-handling-reorg-risk-on-ethereum)
Simple example: Handling reorg risk on Ethereum
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
On Ethereum, a reorg can be up to 95 slots deep in the worst case, so waiting for more than 95 blocks should ensure that a previously sent transaction has been finalized—unless more than 1/3 of the nodes are malicious and willing to lose their stake, which is highly improbable.
❌ **Instead of writing this contract:**
Copy
contract PrivateKeySale {
euint256 privateKey;
bool isAlreadyBought = false;
constructor(externalEuint256 _privateKey, bytes inputProof) {
privateKey = FHE.fromExternal(_privateKey, inputProof);
FHE.allowThis(privateKey);
}
function buyPrivateKey() external payable {
require(msg.value == 1 ether, "Must pay 1 ETH");
require(!isBought, "Private key already bought");
isBought = true;
FHE.allow(encryptedPrivateKey, msg.sender);
}
}
Since the \`privateKey\`\` encrypted variable contains critical information, we don't want to mistakenly leak it for free if a reorg occurs. This could happen in the previous example because we immediately grant authorization to the buyer in the same transaction that processes the sale.
✅ **We recommend writing something like this instead:**
Copy
contract PrivateKeySale {
euint256 privateKey;
bool isAlreadyBought = false;
uint256 blockWhenBought = 0;
address buyer;
constructor(externalEuint256 _privateKey, bytes inputProof) {
privateKey = FHE.fromExternal(_privateKey, inputProof);
FHE.allowThis(privateKey);
}
function buyPrivateKey() external payable {
require(msg.value == 1 ether, "Must pay 1 ETH");
require(!isBought, "Private key already bought");
isBought = true;
blockWhenBought = block.number;
buyer = msg.sender;
}
function requestACL() external {
require(isBought, "Private key has not been bought yet");
require(block.number > blockWhenBought + 95, "Too early to request ACL, risk of reorg");
FHE.allow(privateKey, buyer);
}
}
This approach ensures that at least 96 blocks have elapsed between the transaction that purchases the private key and the transaction that authorizes the buyer to decrypt it.
This type of contract worsens the user experience by adding a timelock before users can decrypt data, so it should be used sparingly: only when leaked information could be critically important and high-value.
[PreviousACL examples](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/acl/acl_examples)
[NextLogics](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/logics)
Last updated 3 months ago
---
# Foundry | Protocol
This guide explains how to use Foundry with FHEVM for developing smart contracts.
While a Foundry template is currently in development, we strongly recommend using the [Hardhat template](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup)
) for now, as it provides a fully tested and supported development environment for FHEVM smart contracts.
However, you could still use Foundry with the mocked version of the fhevm, but please be aware that this approach is **NOT** recommended, since the mocked version is not fully equivalent to the real FHEVM node's implementation (see warning in hardhat). In order to do this, you will need to rename your `FHE.sol` imports from `@fhevm/solidity/lib/FHE.sol` to `fhevm/mocks/FHE.sol` in your solidity source files.
[PreviousDeploy contracts and run tests](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/run_test)
[NextHCU](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu)
Last updated 3 months ago
---
# Configuration | Protocol
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure#core-configuration-setup)
Core configuration setup
--------------------------------------------------------------------------------------------------------------------------------------
To utilize encrypted computations in Solidity contracts, you must configure the **FHE library** and **Oracle 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.
This library and its associated contracts provide a standardized way to configure and interact with Zama's FHEVM (Fully Homomorphic Encryption Virtual Machine) infrastructure on different Ethereum networks. It supplies the necessary contract addresses for Zama's FHEVM components (`ACL`, `FHEVMExecutor`, `KMSVerifier`, `InputVerifier`) and the decryption oracle, enabling seamless integration for Solidity contracts that require FHEVM support.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure#key-components-configured-automatically)
Key components configured automatically
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
1. **FHE library**: Sets up encryption parameters and cryptographic keys.
2. **Oracle**: Manages secure cryptographic operations such as public decryption.
3. **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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure#zamaconfig.sol)
ZamaConfig.sol
------------------------------------------------------------------------------------------------------------------
The `ZamaConfig` library exposes functions to retrieve FHEVM configuration structs and oracle addresses for supported networks (currently only the Sepolia testnet).
Under the hood, this library encapsulates the network-specific addresses of Zama's FHEVM infrastructure into a single struct (`FHEVMConfigStruct`).
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure#sepoliaconfig)
SepoliaConfig
----------------------------------------------------------------------------------------------------------------
The `SepoliaConfig` contract is designed to be inherited by a user contract. The constructor automatically sets up the FHEVM coprocessor and decryption oracle using the configuration provided by the library for the respective network. When a contract inherits from `SepoliaConfig`, the constructor calls `FHE.setCoprocessor` and `FHE.setDecryptionOracle` with the appropriate addresses. This ensures that the inheriting contract is automatically wired to the correct FHEVM contracts and oracle for the target network, abstracting away manual address management and reducing the risk of misconfiguration.
**Example: using Sepolia configuration**
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract MyERC20 is SepoliaConfig {
constructor() {
// Additional initialization logic if needed
}
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure#using-isinitialized)
Using `isInitialized`
------------------------------------------------------------------------------------------------------------------------------
The `isInitialized` utility function checks whether an encrypted variable has been properly initialized, preventing unexpected behavior due to uninitialized values.
**Function signature**
Copy
function isInitialized(T v) internal pure returns (bool)
**Purpose**
* Ensures encrypted variables are initialized before use.
* Prevents potential logic errors in contract execution.
**Example: Initialization Check for Encrypted Counter**
Copy
require(FHE.isInitialized(counter), "Counter not initialized!");
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure#summary)
Summary
----------------------------------------------------------------------------------------------------
By leveraging prebuilt a configuration contract like `SepoliaConfig` in `ZamaConfig.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.
[Previous4\. Test the FHEVM contract](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/test_the_fhevm_contract)
[NextContract addresses](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure/contract_addresses)
Last updated 3 months ago
---
# Generate random numbers | Protocol
This document explains how to generate cryptographically secure random encrypted numbers fully on-chain using the `FHE` library in fhevm. These numbers are encrypted and remain confidential, enabling privacy-preserving smart contract logic.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/random#key-notes-on-random-number-generation)
**Key notes on random number generation**
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* **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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/random#basic-usage)
**Basic usage**
------------------------------------------------------------------------------------------------------------------------
The `FHE` library allows you to generate random encrypted numbers of various bit sizes. Below is a list of supported types and their usage:
Copy
// Generate random encrypted numbers
ebool rb = FHE.randEbool(); // Random encrypted boolean
euint8 r8 = FHE.randEuint8(); // Random 8-bit number
euint16 r16 = FHE.randEuint16(); // Random 16-bit number
euint32 r32 = FHE.randEuint32(); // Random 32-bit number
euint64 r64 = FHE.randEuint64(); // Random 64-bit number
euint128 r128 = FHE.randEuint128(); // Random 128-bit number
euint256 r256 = FHE.randEuint256(); // Random 256-bit number
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/random#example-random-boolean)
**Example: Random Boolean**
Copy
function randomBoolean() public returns (ebool) {
return FHE.randEbool();
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/random#bounded-random-numbers)
**Bounded random numbers**
----------------------------------------------------------------------------------------------------------------------------------------------
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]`.
Copy
// Generate random numbers with upper bounds
euint8 r8 = FHE.randEuint8(100); // Random number between 0-99
euint16 r16 = FHE.randEuint16(1000); // Random number between 0-999
euint32 r32 = FHE.randEuint32(1000000); // Random number between 0-999999
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/random#example-random-number-with-upper-bound)
**Example: Random number with upper bound**
Copy
function randomBoundedNumber(uint16 upperBound) public returns (euint16) {
return FHE.randEuint16(upperBound);
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/random#security-considerations)
**Security Considerations**
------------------------------------------------------------------------------------------------------------------------------------------------
* **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.
[PreviousCasting and trivial encryption](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/casting)
[NextEncrypted inputs](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs)
Last updated 1 month ago
---
# User decrypt single value | Protocol
This example demonstrates the FHE user decryption mechanism with a single value.
User decryption is a mechanism that allows specific users to decrypt encrypted values while keeping them hidden from others. Unlike public decryption where decrypted values become visible to everyone, user decryption maintains privacy by only allowing authorized users with the proper permissions to view the data. While permissions are granted onchain through smart contracts, the actual **decryption call occurs off-chain in the frontend application**.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
UserDecryptSingleValue.sol
[](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-single-value#tab-userdecryptsinglevalue.sol)
UserDecryptSingleValue.ts
[](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-single-value#tab-userdecryptsinglevalue.ts)
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { FHE, euint32 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
/**
* This trivial example demonstrates the FHE decryption mechanism
* and highlights common pitfalls developers may encounter.
*/
contract UserDecryptSingleValue is SepoliaConfig {
euint32 private _trivialEuint32;
// solhint-disable-next-line no-empty-blocks
constructor() {}
function initializeUint32(uint32 value) external {
// Compute a trivial FHE formula _trivialEuint32 = value + 1
_trivialEuint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
// Grant FHE permissions to:
// ✅ The contract caller (`msg.sender`): allows them to decrypt `_trivialEuint32`.
// ✅ The contract itself (`address(this)`): allows it to operate on `_trivialEuint32` and
// also enables the caller to perform user decryption.
//
// Note: If you forget to call `FHE.allowThis(_trivialEuint32)`, the user will NOT be able
// to user decrypt the value! Both the contract and the caller must have FHE permissions
// for user decryption to succeed.
FHE.allowThis(_trivialEuint32);
FHE.allow(_trivialEuint32, msg.sender);
}
function initializeUint32Wrong(uint32 value) external {
// Compute a trivial FHE formula _trivialEuint32 = value + 1
_trivialEuint32 = FHE.add(FHE.asEuint32(value), FHE.asEuint32(1));
// ❌ Common FHE permission mistake:
// ================================================================
// We grant FHE permissions to the contract caller (`msg.sender`),
// expecting they will be able to user decrypt the encrypted value later.
//
// However, this will fail! 💥
// The contract itself (`address(this)`) also needs FHE permissions to allow user decryption.
// Without granting the contract access using `FHE.allowThis(...)`,
// the user decryption attempt by the user will not succeed.
FHE.allow(_trivialEuint32, msg.sender);
}
function encryptedUint32() public view returns (euint32) {
return _trivialEuint32;
}
}
Copy
import { UserDecryptSingleValue, UserDecryptSingleValue__factory } from "../../../types";
import type { Signers } from "../../types";
import { FhevmType, HardhatFhevmRuntimeEnvironment } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
import * as hre from "hardhat";
async function deployFixture() {
// Contracts are deployed using the first signer/account by default
const factory = (await ethers.getContractFactory("UserDecryptSingleValue")) as UserDecryptSingleValue__factory;
const userUserDecryptSingleValue = (await factory.deploy()) as UserDecryptSingleValue;
const userUserDecryptSingleValue_address = await userUserDecryptSingleValue.getAddress();
return { userUserDecryptSingleValue, userUserDecryptSingleValue_address };
}
/**
* This trivial example demonstrates the FHE user decryption mechanism
* and highlights a common pitfall developers may encounter.
*/
describe("UserDecryptSingleValue", function () {
let contract: UserDecryptSingleValue;
let contractAddress: string;
let signers: Signers;
before(async function () {
// Check whether the tests are running against an FHEVM mock environment
if (!hre.fhevm.isMock) {
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
}
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1] };
});
beforeEach(async function () {
// Deploy a new contract each time we run a new test
const deployment = await deployFixture();
contractAddress = deployment.userUserDecryptSingleValue_address;
contract = deployment.userUserDecryptSingleValue;
});
// ✅ Test should succeed
it("user decryption should succeed", async function () {
const tx = await contract.connect(signers.alice).initializeUint32(123456);
await tx.wait();
const encryptedUint32 = await contract.encryptedUint32();
// The FHEVM Hardhat plugin provides a set of convenient helper functions
// that make it easy to perform FHEVM operations within your Hardhat environment.
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
const clearUint32 = await fhevm.userDecryptEuint(
FhevmType.euint32, // Specify the encrypted type
encryptedUint32,
contractAddress, // The contract address
signers.alice, // The user wallet
);
expect(clearUint32).to.equal(123456 + 1);
});
// ❌ Test should fail
it("user decryption should fail", async function () {
const tx = await contract.connect(signers.alice).initializeUint32Wrong(123456);
await tx.wait();
const encryptedUint32 = await contract.encryptedUint32();
await expect(
hre.fhevm.userDecryptEuint(FhevmType.euint32, encryptedUint32, contractAddress, signers.alice),
).to.be.rejectedWith(new RegExp("^dapp contract (.+) is not authorized to user decrypt handle (.+)."));
});
});
[PreviousDecryption](https://docs.zama.ai/protocol/examples/basic/decryption)
[NextUser decrypt multiple values](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-user-decrypt-multiple-values)
Last updated 21 days ago
---
# AsEbool, asEuintXX, and asEaddress operations | Protocol
This documentation covers the `asEbool`, `asEuintXX`, and `asEaddress` operations provided by the FHE 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:
1. **Trivial encryption**: Converting plaintext values to encrypted types
2. **Type casting**: Converting between different encrypted types
3. **Input handling**: Processing encrypted inputs with proofs
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators#id-1.-trivial-encryption)
1\. Trivial encryption
----------------------------------------------------------------------------------------------------------------------------------------------------
Trivial encryption simply put is a plain text in a format of a ciphertext.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators#overview)
Overview
Trivial encryption is the process of converting plaintext values into encrypted types (ciphertexts) compatible with FHE 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`
* `address` → `eaddress`
When doing trivial encryption, the data is made compatible with FHE operations but remains publicly visible on-chain unless explicitly encrypted.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators#example)
**Example**
Copy
euint64 value64 = FHE.asEuint64(7262); // Trivial encrypt a uint64
ebool valueBool = FHE.asEbool(true); // Trivial encrypt a boolean
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators#id-2.-casting-between-encrypted-types)
2\. Casting between encrypted types
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
This type of casting is used to reinterpret or convert one encrypted type into another. For example:
* `euint32` → `euint64`
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 information
>
> * Casting from larger types to smaller types (e.g. `euint64` → `euint32`) will truncate and lose information
>
The table below summarizes the available casting functions:
From type
To type
Function
`euintX`
`euintX`
`FHE.asEuintXX`
`ebool`
`euintX`
`FHE.asEuintXX`
`euintX`
`ebool`
`FHE.asEboolXX`
Casting between encrypted types is efficient and often necessary when handling data with differing precision requirements.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators#workflow-for-encrypted-types)
**Workflow for encrypted types**
Copy
// Casting between encrypted types
euint32 value32 = FHE.asEuint32(value64); // Cast to euint32
ebool valueBool = FHE.asEbool(value32); // Cast to ebool
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators#id-3.-encrypted-input)
3\. Encrypted input
----------------------------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators#overview-1)
Overview
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 inputs is in depth explained in the following section: [encrypted inputs](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs)
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators#example-1)
Example
Copy
euint64 encryptedValue = FHE.asEuint64(einputHandle, inputProof); // einputHandle as of type externalEuint64
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators#details)
Details
Encrypted input casting validates:
1. The input handle references a valid ciphertext.
2. The accompanying proof matches the expected type.
For more information, see the [Encrypetd inputs documentation](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/inputs)
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators#overall-operation-summary)
Overall operation summary
--------------------------------------------------------------------------------------------------------------------------------------------------------
Casting Type
Function
Input Type
Output Type
Trivial encryption
`FHE.asEuintXX(x)`
`uintX`
`euintX`
`FHE.asEbool(x)`
`bool`
`ebool`
`FHE.asEaddress(x)`
`address`
`eaddress`
Conversion between types
`FHE.asEuintXX(x)`
`euintXX`/`ebool`
`euintYY`
`FHE.asEbool(x)`
`euintXX`
`ebool`
Encrypted input
`FHE.asEuintXX(x, y)`
`externalEuintXX`, `bytes` proof
`euintX`
`FHE.asEbool(x, y)`
`externalEbool`,`bytes` proof
`ebool`
`FHE.asEaddress(x, y)`
`externalEaddress`, `bytes` proof
`eaddress`
[PreviousOperations on encrypted types](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations)
[NextGenerate random numbers](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/random)
Last updated 3 months ago
---
# Access Control List | Protocol
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl#what-is-the-acl)
What is the ACL?
---------------------------------------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl#why-is-the-acl-important)
Why is the ACL important?
---------------------------------------------------------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl#how-does-the-acl-work)
How does the ACL work?
---------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl#types-of-access)
Types of access
* **Permanent allowance**:
* Configured using `FHE.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 `FHE.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.
* **Permanent public allowance**:
* Configured using `FHE.makePubliclyDecryptable(ciphertext)`.
* Grants long-term access to the ciphertext for any user.
* Stored in a dedicated contract for persistent storage.
**Syntactic sugar**:
* `FHE.allowThis(ciphertext)` is shorthand for `FHE.allow(ciphertext, address(this))`. It authorizes the current contract to reuse a ciphertext handle in future transactions.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl#transient-vs.-permanent-allowance)
Transient vs. permanent allowance
Allowance type
Purpose
Storage type
Use case
**Transient**
Temporary access during a transaction.
[Transient storage](https://eips.ethereum.org/EIPS/eip-1153)
(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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl#granting-and-verifying-access)
Granting and verifying access
------------------------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl#granting-access)
Granting access
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl#verifying-access)
Verifying access
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl#practical-uses-of-the-acl)
Practical uses of the ACL
----------------------------------------------------------------------------------------------------------------------------------
* **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.
* * *
For a detailed explanation of the ACL's functionality, including code examples and advanced configurations, see [ACL examples](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples)
.
[PreviousEncrypted inputs](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs)
[NextACL examples](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples)
Last updated 1 month ago
---
# Reorgs handling | Protocol
This page provides detailed instructions on how to handle reorg risks on Ethereum when using FHEVM.
Since ACL events are propagated from the FHEVM host chain to the [Gateway](https://docs.zama.ai/protocol/protocol/overview/gateway)
immediately after being included in a block, dApp developers must take special care when encrypted information is critically important. For example, if an encrypted handle conceals the private key of a Bitcoin wallet holding significant funds, we need to ensure that this information cannot inadvertently leak to the wrong person due to a reorg on the FHEVM host chain. Therefore, it's the responsibility of dApp developers to prevent such scenarios by implementing a two-step ACL authorization process with a timelock between the request and the ACL call.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/reorgs_handling#simple-example-handling-reorg-risk-on-ethereum)
Simple example: Handling reorg risk on Ethereum
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
On Ethereum, a reorg can be up to 95 slots deep in the worst case, so waiting for more than 95 blocks should ensure that a previously sent transaction has been finalized—unless more than 1/3 of the nodes are malicious and willing to lose their stake, which is highly improbable.
❌ **Instead of writing this contract:**
Copy
contract PrivateKeySale {
euint256 privateKey;
bool isAlreadyBought = false;
constructor(externalEuint256 _privateKey, bytes inputProof) {
privateKey = FHE.fromExternal(_privateKey, inputProof);
FHE.allowThis(privateKey);
}
function buyPrivateKey() external payable {
require(msg.value == 1 ether, "Must pay 1 ETH");
require(!isBought, "Private key already bought");
isBought = true;
FHE.allow(encryptedPrivateKey, msg.sender);
}
}
Since the \`privateKey\`\` encrypted variable contains critical information, we don't want to mistakenly leak it for free if a reorg occurs. This could happen in the previous example because we immediately grant authorization to the buyer in the same transaction that processes the sale.
✅ **We recommend writing something like this instead:**
Copy
contract PrivateKeySale {
euint256 privateKey;
bool isAlreadyBought = false;
uint256 blockWhenBought = 0;
address buyer;
constructor(externalEuint256 _privateKey, bytes inputProof) {
privateKey = FHE.fromExternal(_privateKey, inputProof);
FHE.allowThis(privateKey);
}
function buyPrivateKey() external payable {
require(msg.value == 1 ether, "Must pay 1 ETH");
require(!isBought, "Private key already bought");
isBought = true;
blockWhenBought = block.number;
buyer = msg.sender;
}
function requestACL() external {
require(isBought, "Private key has not been bought yet");
require(block.number > blockWhenBought + 95, "Too early to request ACL, risk of reorg");
FHE.allow(privateKey, buyer);
}
}
This approach ensures that at least 96 blocks have elapsed between the transaction that purchases the private key and the transaction that authorizes the buyer to decrypt it.
This type of contract worsens the user experience by adding a timelock before users can decrypt data, so it should be used sparingly: only when leaked information could be critically important and high-value.
[PreviousACL examples](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples)
[NextLogics](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics)
Last updated 1 month ago
---
# Supported types | Protocol
This document introduces the encrypted integer types provided by the `FHE` library in FHEVM and explains their usage, including casting, state variable declarations, and type-specific considerations.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/types#introduction)
Introduction
----------------------------------------------------------------------------------------------------------
The `FHE` 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.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/types#key-features-of-encrypted-types)
Key features of encrypted types
* 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 `FHE` 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 `FHE` 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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/types#list-of-encrypted-types)
List of encrypted types
--------------------------------------------------------------------------------------------------------------------------------
The `FHE` library currently supports the following encrypted types:
Type
Bit Length
Supported Operators
Aliases (with supported operators)
Ebool
2
and, or, xor, eq, ne, not, select, rand
Euint8
8
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint16
16
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint32
32
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint64
64
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint128
128
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint160
160
Eaddress (eq, ne, select)
Euint256
256
and, or, xor, shl, shr, rotl, rotr, eq, ne, neg, not, select, rand, randBounded
Division (`div`) and remainder (`rem`) operations are only supported when the right-hand side (`rhs`) operand is a plaintext (non-encrypted) value. Attempting to use an encrypted value as `rhs` will result in a panic. This restriction ensures correct and secure computation within the current framework.
Higher-precision integer types are available in the `TFHE-rs` library and can be added to `fhevm` as needed.
[PreviousContract addresses](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/configure/contract_addresses)
[NextOperations on encrypted types](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations)
Last updated 3 months ago
---
# 3. Turn it into FHEVM | Protocol
In this tutorial, you'll learn how to take a basic Solidity smart contract and progressively upgrade it to support Fully Homomorphic Encryption using the FHEVM library by Zama.
Starting with the plain `Counter.sol` contract that you build from the ["Write a simple contract" tutorial](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract)
, and step-by-step, you’ll learn how to:
* Replace standard types with encrypted equivalents
* Integrate zero-knowledge proof validation
* Enable encrypted on-chain computation
* Grant permissions for secure off-chain decryption
By the end, you'll have a fully functional smart contract that supports FHE computation.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#initiate-the-contract)
Initiate the contract
---------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#create-the-fhecounter.sol-file)
Create the `FHECounter.sol` file
Navigate to your project’s `contracts` directory:
Copy
cd /contracts
From there, create a new file named `FHECounter.sol`, and copy the following Solidity code into it:
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title A simple counter contract
contract Counter {
uint32 private _count;
/// @notice Returns the current count
function getCount() external view returns (uint32) {
return _count;
}
/// @notice Increments the counter by a specific value
function increment(uint32 value) external {
_count += value;
}
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
}
This is a plain `Counter` contract that we’ll use as the starting point for adding FHEVM functionality. We will modify this contract step-by-step to progressively integrate FHEVM capabilities.
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#turn-counter-into-fhecounter)
Turn `Counter` into `FHECounter`
To begin integrating FHEVM features into your contract, we first need to import the required FHEVM libraries.
**Replace the current header**
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
**With this updated header:**
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { FHE, euint32, externalEuint32 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
This imports:
* **FHE** — the core library to work with FHEVM encrypted types
* **euint32** and **externalEuint32** — encrypted uint32 types used in FHEVM
* **SepoliaConfig** — provides the FHEVM configuration for the Sepolia network. Inheriting from it enables your contract to use the FHE library
**Replace the current contract declaration:**
Copy
/// @title A simple counter contract
contract Counter {
**With the updated declaration :**
Copy
/// @title A simple FHE counter contract
contract FHECounter is SepoliaConfig {
This change:
* Renames the contract to `FHECounter`
* Inherits from `SepoliaConfig` to enable FHEVM support
This contract must inherit from the `SepoliaConfig` abstract contract; otherwise, it will not be able to execute any FHEVM-related functionality on Sepolia or Hardhat.
From your project's root directory, run:
Copy
npx hardhat compile
Great! Your smart contract is now compiled and ready to use **FHEVM features.**
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#apply-fhe-functions-and-types)
Apply FHE functions and types
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#comment-out-the-increment-and-decrement-functions)
Comment out the `increment()` and `decrement()` Functions
Before we move forward, let’s comment out the `increment()` and `decrement()` functions in `FHECounter`. We'll replace them later with updated versions that support FHE-encrypted operations.
Copy
/// @notice Increments the counter by a specific value
// function increment(uint32 value) external {
// _count += value;
// }
/// @notice Decrements the counter by a specific value
// function decrement(uint32 value) external {
// require(_count >= value, "Counter: cannot decrement below zero");
// _count -= value;
// }
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#replace-uint32-with-the-fhevm-euint32-type)
Replace `uint32` with the FHEVM `euint32` Type
We’ll now switch from the standard Solidity `uint32` type to the encrypted FHEVM type `euint32`.
This enables private, homomorphic computation on encrypted integers.
**Replace**
Copy
uint32 _count;
and
Copy
function getCount() external view returns (uint32) {
**With :**
Copy
euint32 _count;
and
Copy
function getCount() external view returns (euint32) {
3
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#replace-increment-uint32-value-with-the-fhevm-version-increment-externaleuint32-value)
Replace `increment(uint32 value)` with the FHEVM version `increment(externalEuint32 value)`
To support encrypted input, we will update the increment function to accept a value encrypted off-chain.
Instead of using a `uint32`, the new version will accept an `externalEuint32`, which is an encrypted integer produced off-chain and sent to the smart contract.
To ensure the validity of this encrypted value, we also include a second argument:`inputProof`, a bytes array containing a Zero-Knowledge Proof of Knowledge (ZKPoK) that proves two things:
1. The `externalEuint32` was encrypted off-chain by the function caller (`msg.sender`)
2. The `externalEuint32` is bound to the contract (`address(this)`) and can only be processed by it.
**Replace**
Copy
/// @notice Increments the counter by a specific value
// function increment(uint32 value) external {
// _count += value;
// }
**With :**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
// _count += value;
}
4
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#convert-externaleuint32-to-euint32)
Convert `externalEuint32` to `euint32`
You cannot directly use `externalEuint32` in FHE operations. To manipulate it with the FHEVM library, you first need to convert it into the native FHE type `euint32`.
This conversion is done using:
Copy
FHE.fromExternal(inputEuint32, inputProof);
This method verifies the zero-knowledge proof and returns a usable encrypted value within the contract.
**Replace**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
// _count += value;
}
**With :**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
// _count += value;
}
5
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#convert-_count--value-into-its-fhevm-equivalent)
Convert `_count += value` into its FHEVM equivalent
To perform the update `_count += value` in a Fully Homomorphic way, we use the `FHE.add()` operator. This function allows us to compute the FHE sum of 2 encrypted integers.
**Replace**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
// _count += value;
}
**With :**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, evalue);
}
This FHE operation allows the smart contract to process encrypted values without ever decrypting them — a core feature of FHEVM that enables on-chain privacy.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#grant-fhe-permissions)
Grant FHE Permissions
---------------------------------------------------------------------------------------------------------------------------------------------------------------
This step is critical! You must grant FHE permissions to both the contract and the caller to ensure the encrypted `_count` value can be decrypted off-chain by the caller. Without these 2 permissions, the caller will not be able to compute the clear result.
To grant FHE permission we will call the `FHE.allow()` function.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#replace)
Replace
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, evalue);
}
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#with)
With :
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, evalue);
FHE.allowThis(_count);
FHE.allow(_count, msg.sender);
}
We grant **two** FHE permissions here — not just one. In the next part of the tutorial, you'll learn why **both** are necessary.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#convert-decrement-to-its-fhevm-equivalent)
Convert `decrement()` to its FHEVM equivalent
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Just like with the `increment()` migration, we’ll now convert the `decrement()` function to its FHEVM-compatible version.
Replace :
Copy
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
with the following :
Copy
/// @notice Decrements the counter by a specific value
/// @dev This example omits overflow/underflow checks for simplicity and readability.
/// In a production contract, proper range checks should be implemented.
function decrement(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.sub(_count, encryptedEuint32);
FHE.allowThis(_count);
FHE.allow(_count, msg.sender);
}
The `increment()` and `decrement()` functions do not perform any overflow or underflow checks.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm#compile-fhecounter.sol)
Compile `FHECounter.sol`
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
From your project's root directory, run:
Copy
npx hardhat compile
Congratulations! Your smart contract is now fully **FHEVM-compatible**.
Now you should have the following files in your project:
* [`contracts/FHECounter.sol`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#fhecounter.sol)
— your Solidity smart FHEVM contract
* [`test/FHECounter.ts`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#fhecounter.ts)
— your FHEVM Hardhat test suite written in TypeScript
In the [next tutorial](https://github.com/zama-ai/fhevm/blob/release/0.7.x/docs/solidity-guides/getting-started/quick-start-tutorial/test_fhevm_contract.md)
, we’ll move on to the **TypeScript integration**, where you’ll learn how to interact with your newly upgraded FHEVM contract in a test suite.
[Previous2\. Write a simple contract](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract)
[Next4\. Test the FHEVM contract](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/test_the_fhevm_contract)
Last updated 3 months ago
---
# Contract addresses | Protocol
Save this in your `.env` file.
These are Sepolia addresses.
Contract/Service
Address/Value
FHEVM\_EXECUTOR\_CONTRACT
0x848B0066793BcC60346Da1F49049357399B8D595
ACL\_CONTRACT
0x687820221192C5B662b25367F70076A37bc79b6c
HCU\_LIMIT\_CONTRACT
0x594BB474275918AF9609814E68C61B1587c5F838
KMS\_VERIFIER\_CONTRACT
0x1364cBBf2cDF5032C47d8226a6f6FBD2AFCDacAC
INPUT\_VERIFIER\_CONTRACT
0xbc91f3daD1A5F19F8390c400196e58073B6a0BC4
DECRYPTION\_ORACLE\_CONTRACT
0xa02Cda4Ca3a71D7C46997716F4283aa851C28812
DECRYPTION\_ADDRESS
0xb6E160B1ff80D67Bfe90A85eE06Ce0A2613607D1
INPUT\_VERIFICATION\_ADDRESS
0x7048C39f048125eDa9d678AEbaDfB22F7900a29F
RELAYER\_URL
`https://relayer.testnet.zama.cloud`
[PreviousConfiguration](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure)
[NextSupported types](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/types)
Last updated 1 month ago
---
# ERC7984 Tutorial | Protocol
This tutorial explains how to create a confidential fungible token using Fully Homomorphic Encryption (FHE) and the OpenZeppelin smart contract library. By following this guide, you will learn how to build a token where balances and transactions remain encrypted while maintaining full functionality.
###
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial#why-fhe-for-confidential-tokens)
Why FHE for confidential tokens?
Confidential tokens make sense in many real-world scenarios:
* **Privacy**: Users can transact without revealing their exact balances or transaction amounts
* **Regulatory Compliance**: Maintains privacy while allowing for selective disclosure when needed
* **Business Intelligence**: Companies can keep their token holdings private from competitors
* **Personal Privacy**: Individuals can participate in DeFi without exposing their financial position
* **Audit Trail**: All transactions are still recorded on-chain, just in encrypted form
FHE enables these benefits by allowing computations on encrypted data without decryption, ensuring privacy while maintaining the security and transparency of blockchain.
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial#project-setup)
Project Setup
----------------------------------------------------------------------------------------------------------------------------------------
Before starting this tutorial, ensure you have:
1. Installed the FHEVM hardhat template
2. Set up the OpenZeppelin confidential contracts library
For help with these steps, refer to the following tutorial:
* [Setting up OpenZeppelin confidential contracts](https://github.com/zama-ai/fhevm/blob/main/docs/examples/openzeppelin/openzeppelin/README.md)
###
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial#understanding-the-architecture)
Understanding the architecture
Our confidential token will inherit from several key contracts:
1. `**ERC7984**` - OpenZeppelin's base for confidential tokens
2. `**Ownable2Step**` - Access control for minting and administrative functions
3. `**SepoliaConfig**` - FHE configuration for the Sepolia testnet
###
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial#the-base-smart-contract)
The base smart contract
Let's create our confidential token contract in `contracts/ERC7984Example.sol`. This contract will demonstrate the core functionality of ERC7984 tokens.
A few key points about this implementation:
* The contract mints an initial supply with a clear (non-encrypted) amount during deployment
* The initial mint is done once during construction, establishing the token's total supply
* All subsequent transfers will be fully encrypted, preserving privacy
* The contract inherits from ERC7984 for confidential token functionality and Ownable2Step for secure access control
While this example uses a clear initial mint for simplicity, in production you may want to consider:
* Using encrypted minting for complete privacy from genesis
* Implementing a more sophisticated minting schedule
* Overriding some privacy assumptions
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {FHE, externalEuint64, euint64} from "@fhevm/solidity/lib/FHE.sol";
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
import {ERC7984} from "@openzeppelin/confidential-contracts/token/ERC7984.sol";
contract ERC7984Example is SepoliaConfig, ERC7984, Ownable2Step {
constructor(
address owner,
uint64 amount,
string memory name_,
string memory symbol_,
string memory tokenURI_
) ERC7984(name_, symbol_, tokenURI_) Ownable(owner) {
euint64 encryptedAmount = FHE.asEuint64(amount);
_mint(owner, encryptedAmount);
}
}
###
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial#test-workflow)
Test workflow
Now let's test the token transfer process. We'll create a test that:
1. Encrypts a transfer amount
2. Sends tokens from owner to recipient
3. Verifies the transfer was successful by checking balance handles
Create a new file `test/ERC7984Example.test.ts` with the following test:
Copy
import { expect } from 'chai';
import { ethers, fhevm } from 'hardhat';
describe('ERC7984Example', function () {
let token: any;
let owner: any;
let recipient: any;
let other: any;
const INITIAL_AMOUNT = 1000;
const TRANSFER_AMOUNT = 100;
beforeEach(async function () {
[owner, recipient, other] = await ethers.getSigners();
// Deploy ERC7984Example contract
token = await ethers.deployContract('ERC7984Example', [\
owner.address,\
INITIAL_AMOUNT,\
'Confidential Token',\
'CTKN',\
'https://example.com/token'\
]);
});
describe('Confidential Transfer Process', function () {
it('should transfer tokens from owner to recipient', async function () {
// Create encrypted input for transfer amount
const encryptedInput = await fhevm
.createEncryptedInput(await token.getAddress(), owner.address)
.add64(TRANSFER_AMOUNT)
.encrypt();
// Perform the confidential transfer
await expect(token
.connect(owner)
['confidentialTransfer(address,bytes32,bytes)'](
recipient.address,
encryptedInput.handles[0],
encryptedInput.inputProof
)).to.not.be.reverted;
// Check that both addresses have balance handles (without decryption for now)
const recipientBalanceHandle = await token.confidentialBalanceOf(recipient.address);
const ownerBalanceHandle = await token.confidentialBalanceOf(owner.address);
expect(recipientBalanceHandle).to.not.be.undefined;
expect(ownerBalanceHandle).to.not.be.undefined;
});
});
});
To run the tests, use:
Copy
npx hardhat test test/ERC7984Example.test.ts
###
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial#advanced-features-and-extensions)
Advanced features and extensions
The basic ERC7984Example contract provides core functionality, but you can extend it with additional features. For example:
####
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial#minting-functions)
Minting functions
**Visible Mint** - Allows the owner to mint tokens with a clear amount:
Copy
function mint(address to, uint64 amount) external onlyOwner {
_mint(to, FHE.asEuint64(amount));
}
* **When to use**: Prefer this for public/tokenomics-driven mints where transparency is desired (e.g., scheduled emissions).
* **Privacy caveat**: The minted amount is visible in calldata and events; use `confidentialMint` for privacy.
* **Access control**: Consider replacing `onlyOwner` with role-based access via `AccessControl` (e.g., `MINTER_ROLE`) for multi-signer workflows.
* **Supply caps**: If you need a hard cap, add a check before `_mint` and enforce it consistently for both visible and confidential flows.
**Confidential Mint** - Allows minting with encrypted amounts for enhanced privacy:
Copy
function confidentialMint(
address to,
externalEuint64 encryptedAmount,
bytes calldata inputProof
) external onlyOwner returns (euint64 transferred) {
return _mint(to, FHE.fromExternal(encryptedAmount, inputProof));
}
* **Inputs**: `encryptedAmount` and `inputProof` are produced off-chain with the SDK. Always validate and revert on malformed inputs.
* **Gas considerations**: Confidential operations cost more gas; batch mints sparingly and prefer fewer larger mints to reduce overhead.
* **Auditing**: While amounts stay private, you still get a verifiable audit trail of mints (timestamps, sender, recipient).
* **Example (Hardhat SDK)**:
Copy
const enc = await fhevm
.createEncryptedInput(await token.getAddress(), owner.address)
.add64(1_000)
.encrypt();
await token.confidentialMint(recipient.address, enc.handles[0], enc.inputProof);
####
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial#burning-functions)
Burning functions
**Visible Burn** - Allows the owner to burn tokens with a clear amount:
Copy
function burn(address from, uint64 amount) external onlyOwner {
_burn(from, FHE.asEuint64(amount));
}
**Confidential Burn** - Allows burning with encrypted amounts:
Copy
function confidentialBurn(
address from,
externalEuint64 encryptedAmount,
bytes calldata inputProof
) external onlyOwner returns (euint64 transferred) {
return _burn(from, FHE.fromExternal(encryptedAmount, inputProof));
}
* **Authorization**: Burning from arbitrary accounts is powerful; consider stronger controls (roles, multisig, timelocks) or user-consented burns.
* **Event strategy**: Decide whether to emit custom events revealing intent (not amounts) for better observability and offchain indexing.
* **Error surfaces**: Expect balance/allowance-like failures if encrypted amount exceeds balance; test both success and revert paths.
* **Example (Hardhat SDK)**:
Copy
const enc = await fhevm
.createEncryptedInput(await token.getAddress(), owner.address)
.add64(250)
.encrypt();
await token.confidentialBurn(holder.address, enc.handles[0], enc.inputProof);
####
[](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial#total-supply-visibility)
Total supply visibility
If you want the owner to be able to view the total supply (useful for administrative purposes):
Copy
function _update(address from, address to, euint64 amount) internal virtual override returns (euint64 transferred) {
transferred = super._update(from, to, amount);
FHE.allow(confidentialTotalSupply(), owner());
}
* **What this does**: Grants the `owner` permission to decrypt the latest total supply handle after every state-changing update.
* **Operational model**: The owner can call `confidentialTotalSupply()` and use their off-chain key material to decrypt the returned handle.
* **Security considerations**:
* If ownership changes, ensure only the new owner can decrypt going forward. With `Ownable2Step`, this function will automatically allow the current `owner()`.
* Be mindful of compliance: granting supply visibility may be considered privileged access; document who holds the key and why.
* **Alternatives**: If you want organization-wide access, grant via a dedicated admin contract that holds decryption authority instead of a single EOA.
[PreviousERC7984 Standard](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984)
[NextERC7984 to ERC20 Wrapper](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984erc20wrappermock)
Last updated 21 days ago
---
# Casting and trivial encryption | Protocol
This documentation covers the `asEbool`, `asEuintXX`, and `asEaddress` operations provided by the FHE 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 two main use cases:
1. **Trivial encryption**: Converting plaintext values to encrypted types
2. **Type casting**: Converting between different encrypted types
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/casting#id-1.-trivial-encryption)
1\. Trivial encryption
---------------------------------------------------------------------------------------------------------------------------------------------
Trivial encryption simply put is a plain text in a format of a ciphertext.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/casting#overview)
Overview
Trivial encryption is the process of converting plaintext values into encrypted types (ciphertexts) compatible with FHE 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`
* `address` → `eaddress`
When doing trivial encryption, the data is made compatible with FHE operations but remains publicly visible on-chain unless explicitly encrypted.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/casting#example)
**Example**
Copy
euint64 value64 = FHE.asEuint64(7262); // Trivial encrypt a uint64
ebool valueBool = FHE.asEbool(true); // Trivial encrypt a boolean
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/casting#id-2.-casting-between-encrypted-types)
2\. Casting between encrypted types
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
This type of casting is used to reinterpret or convert one encrypted type into another. For example:
* `euint32` → `euint64`
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 information
>
> * Casting from larger types to smaller types (e.g. `euint64` → `euint32`) will truncate and lose information
>
The table below summarizes the available casting functions:
From type
To type
Function
`euintX`
`euintX`
`FHE.asEuintXX`
`ebool`
`euintX`
`FHE.asEuintXX`
`euintX`
`ebool`
`FHE.asEboolXX`
Casting between encrypted types is efficient and often necessary when handling data with differing precision requirements.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/casting#workflow-for-encrypted-types)
**Workflow for encrypted types**
Copy
// Casting between encrypted types
euint32 value32 = FHE.asEuint32(value64); // Cast to euint32
ebool valueBool = FHE.asEbool(value32); // Cast to ebool
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/casting#overall-operation-summary)
Overall operation summary
-------------------------------------------------------------------------------------------------------------------------------------------------
Casting Type
Function
Input Type
Output Type
Trivial encryption
`FHE.asEuintXX(x)`
`uintX`
`euintX`
`FHE.asEbool(x)`
`bool`
`ebool`
`FHE.asEaddress(x)`
`address`
`eaddress`
Conversion between types
`FHE.asEuintXX(x)`
`euintXX`/`ebool`
`euintYY`
`FHE.asEbool(x)`
`euintXX`
`ebool`
[PreviousOperations on encrypted types](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations)
[NextGenerate random numbers](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/random)
Last updated 1 month ago
---
# Operations on encrypted types | Protocol
This document outlines the operations supported on encrypted types in the `FHE` library, enabling arithmetic, bitwise, comparison, and more on Fully Homomorphic Encryption (FHE) ciphertexts.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations#arithmetic-operations)
Arithmetic operations
---------------------------------------------------------------------------------------------------------------------------------
The following arithmetic operations are supported for encrypted integers (`euintX`):
Name
Function name
Symbol
Type
Add
`FHE.add`
`+`
Binary
Subtract
`FHE.sub`
`-`
Binary
Multiply
`FHE.mul`
`*`
Binary
Divide (plaintext divisor)
`FHE.div`
Binary
Reminder (plaintext divisor)
`FHE.rem`
Binary
Negation
`FHE.neg`
`-`
Unary
Min
`FHE.min`
Binary
Max
`FHE.max`
Binary
Division (FHE.div) and remainder (FHE.rem) operations are currently supported only with plaintext divisors.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations#bitwise-operations)
Bitwise operations
---------------------------------------------------------------------------------------------------------------------------
The FHE library also supports bitwise operations, including shifts and rotations:
Name
Function name
Symbol
Type
Bitwise AND
`FHE.and`
`&`
Binary
Bitwise OR
`FHE.or`
`|`
Binary
Bitwise XOR
`FHE.xor`
`^`
Binary
Bitwise NOT
`FHE.not`
`~`
Unary
Shift Right
`FHE.shr`
Binary
Shift Left
`FHE.shl`
Binary
Rotate Right
`FHE.rotr`
Binary
Rotate Left
`FHE.rotl`
Binary
The shift operators `FHE.shr` and `FHE.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, `FHE.shr(euint64 x, 70)` is equivalent to `FHE.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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations#comparison-operations)
Comparison operations
---------------------------------------------------------------------------------------------------------------------------------
Encrypted integers can be compared using the following functions:
Name
Function name
Symbol
Type
Equal
`FHE.eq`
Binary
Not equal
`FHE.ne`
Binary
Greater than or equal
`FHE.ge`
Binary
Greater than
`FHE.gt`
Binary
Less than or equal
`FHE.le`
Binary
Less than
`FHE.lt`
Binary
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations#ternary-operation)
Ternary operation
-------------------------------------------------------------------------------------------------------------------------
The `FHE.select` function is a ternary operation that selects one of two encrypted values based on an encrypted condition:
Name
Function name
Symbol
Type
Select
`FHE.select`
Ternary
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations#random-operations)
Random operations
-------------------------------------------------------------------------------------------------------------------------
You can generate cryptographically secure random numbers fully on-chain:
**Name**
**Function Name**
**Symbol**
**Type**
Random Unsigned Integer
`FHE.randEuintX()`
Random
For more details, refer to the [Random Encrypted Numbers](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/random)
document.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations#best-practices)
Best Practices
-------------------------------------------------------------------------------------------------------------------
Here are some best practices to follow when using encrypted operations in your smart contracts:
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations#use-the-appropriate-encrypted-type-size)
Use the appropriate encrypted type size
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:
Copy
// Bad: Using euint256 for small numbers wastes gas
euint64 age = FHE.asEuint128(25); // age will never exceed 255
euint64 percentage = FHE.asEuint128(75); // percentage is 0-100
✅ Instead, use the smallest appropriate type:
Copy
// Good: Using appropriate sized types
euint8 age = FHE.asEuint8(25); // age fits in 8 bits
euint8 percentage = FHE.asEuint8(75); // percentage fits in 8 bits
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations#use-scalar-operands-when-possible-to-save-gas)
Use scalar operands when possible to save gas
Some FHE 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:
Copy
euint32 x;
...
x = FHE.add(x,FHE.asEuint(42));
✅ Than this one:
Copy
euint32 x;
// ...
x = FHE.add(x,42);
Despite both leading to the same encrypted result!
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations#beware-of-overflows-of-fhe-arithmetic-operators)
Beware of overflows of FHE arithmetic operators
FHE arithmetic operators can overflow. Do not forget to take into account such a possibility when implementing FHEVM smart contracts.
❌ For example, if you wanted to create a mint function for an encrypted ERC20 token with an encrypted `totalSupply` state variable, this code is vulnerable to overflows:
Copy
function mint(externalEuint32 encryptedAmount, bytes calldata inputProof) public {
euint32 mintedAmount = FHE.asEuint32(encryptedAmount, inputProof);
totalSupply = FHE.add(totalSupply, mintedAmount);
balances[msg.sender] = FHE.add(balances[msg.sender], mintedAmount);
FHE.allowThis(balances[msg.sender]);
FHE.allow(balances[msg.sender], msg.sender);
}
✅ But you can fix this issue by using `FHE.select` to cancel the mint in case of an overflow:
Copy
function mint(externalEuint32 encryptedAmount, bytes calldata inputProof) public {
euint32 mintedAmount = FHE.asEuint32(encryptedAmount, inputProof);
euint32 tempTotalSupply = FHE.add(totalSupply, mintedAmount);
ebool isOverflow = FHE.lt(tempTotalSupply, totalSupply);
totalSupply = FHE.select(isOverflow, totalSupply, tempTotalSupply);
euint32 tempBalanceOf = FHE.add(balances[msg.sender], mintedAmount);
balances[msg.sender] = FHE.select(isOverflow, balances[msg.sender], tempBalanceOf);
FHE.allowThis(balances[msg.sender]);
FHE.allow(balances[msg.sender], msg.sender);
}
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.
[PreviousSupported types](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/types)
[NextAsEbool, asEuintXX, and asEaddress operations](https://docs.zama.ai/protocol/solidity-guides/v0.7/smart-contract/operations/asexxoperators)
Last updated 3 months ago
---
# Dealing with branches and conditions | Protocol
This document explains how to handle branches, loops or conditions when working with Fully Homomorphic Encryption (FHE), specifically when the condition / index is encrypted.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/loop#breaking-a-loop)
Breaking a loop
----------------------------------------------------------------------------------------------------------------------
❌ In FHE, it is not possible to break a loop based on an encrypted condition. For example, this would not work:
Copy
euint8 maxValue = FHE.asEuint(6); // Could be a value between 0 and 10
euint8 x = FHE.asEuint(0);
// some code
while(FHE.lt(x, maxValue)){
x = FHE.add(x, 2);
}
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 `FHE.select` inside the loop.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/loop#suggested-approach)
Suggested approach
----------------------------------------------------------------------------------------------------------------------------
✅ For example, the previous code could maybe be replaced by the following snippet:
Copy
euint8 maxValue = FHE.asEuint(6); // Could be a value between 0 and 10
euint8 x;
// some code
for (uint32 i = 0; i < 10; i++) {
euint8 toAdd = FHE.select(FHE.lt(x, maxValue), 2, 0);
x = FHE.add(x, toAdd);
}
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/loop#best-practices)
Best practices
--------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/loop#obfuscate-branching)
Obfuscate branching
The previous paragraph emphasized that branch logic should rely as much as possible on `FHE.select` instead of decryptions. It hides effectively which branch has been executed.
However, this is sometimes not enough. Enhancing the privacy of smart contracts often requires revisiting your application's logic.
For example, if implementing a simple AMM for two encrypted ERC20 tokens based on a linear constant function, it is recommended to not only hide the amounts being swapped, but also the token which is swapped in a pair.
✅ Here is a very simplified example implementation, we suppose here that the rate between tokenA and tokenB is constant and equals to 1:
Copy
// typically either encryptedAmountAIn or encryptedAmountBIn is an encrypted null value
// ideally, the user already owns some amounts of both tokens and has pre-approved the AMM on both tokens
function swapTokensForTokens(
externalEuint32 encryptedAmountAIn,
externalEuint32 encryptedAmountBIn,
bytes calldata inputProof
) external {
euint32 encryptedAmountA = FHE.asEuint32(encryptedAmountAIn, inputProof); // even if amount is null, do a transfer to obfuscate trade direction
euint32 encryptedAmountB = FHE.asEuint32(encryptedAmountBIn, inputProof); // even if amount is null, do a transfer to obfuscate trade direction
// send tokens from user to AMM contract
FHE.allowTransient(encryptedAmountA, tokenA);
IConfidentialERC20(tokenA).transferFrom(msg.sender, address(this), encryptedAmountA);
FHE.allowTransient(encryptedAmountB, tokenB);
IConfidentialERC20(tokenB).transferFrom(msg.sender, address(this), encryptedAmountB);
// send tokens from AMM contract to user
// Price of tokenA in tokenB is constant and equal to 1, so we just swap the encrypted amounts here
FHE.allowTransient(encryptedAmountB, tokenA);
IConfidentialERC20(tokenA).transfer(msg.sender, encryptedAmountB);
FHE.allowTransient(encryptedAmountA, tokenB);
IConfidentialERC20(tokenB).transferFrom(msg.sender, address(this), encryptedAmountA);
}
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/loop#avoid-using-encrypted-indexes)
Avoid using encrypted indexes
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.
Copy
euint32 x;
euint32[] encArray;
function setXwithEncryptedIndex(externalEuint32 encryptedIndex, bytes calldata inputProof) public {
euint32 index = FHE.asEuint32(encryptedIndex, inputProof);
for (uint32 i = 0; i < encArray.length; i++) {
ebool isEqual = FHE.eq(index, i);
x = FHE.select(isEqual, encArray[i], x);
}
FHE.allowThis(x);
}
[PreviousBranching](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions)
[NextError handling](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/error_handling)
Last updated 1 month ago
---
# Write FHEVM tests in Hardhat | Protocol
In this section, you'll find everything you need to set up a new [Hardhat](https://hardhat.org/)
project and start developing FHEVM smart contracts from scratch using the [FHEVM Hardhat Plugin](https://www.npmjs.com/package/@fhevm/hardhat-plugin)
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/write_test#enabling-the-fhevm-hardhat-plugin-in-your-hardhat-project)
Enabling the FHEVM Hardhat Plugin in your Hardhat project
Like any Hardhat plugin, the [FHEVM Hardhat Plugin](https://www.npmjs.com/package/@fhevm/hardhat-plugin)
must be enabled by adding the following `import` statement to your `hardhat.config.ts` file:
Copy
import "@fhevm/hardhat-plugin";
Without this import, the Hardhat FHEVM API will **not** be available in your Hardhat runtime environment (HRE).
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/write_test#accessing-the-hardhat-fhevm-api)
Accessing the Hardhat FHEVM API
The plugin extends the standard [Hardhat Runtime Environment](https://hardhat.org/hardhat-runner/docs/advanced/hardhat-runtime-environment)
(or `hre` in short) with the new `fhevm` Hardhat module.
You can access it in either of the following ways:
Copy
import { fhevm } from "hardhat";
or
Copy
import * as hre from "hardhat";
// Then access: hre.fhevm
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/write_test#encrypting-values-using-the-hardhat-fhevm-api)
Encrypting Values Using the Hardhat FHEVM API
Suppose the FHEVM smart contract you want to test has a function called `foo` that takes an encrypted `uint32` value as input. The Solidity function `foo` should be declared as follows:
Copy
function foo(externalEunit32 value, bytes calldata memory inputProof);
Where:
* `externalEunit32 value` : is a `bytes32` representing the encrypted `uint32`
* `bytes calldata memory inputProof` : is a `bytes` array representing the zero-knowledge proof of knowledge that validates the encryption
To compute these arguments in TypeScript, you need:
* The **address of the target smart contract**
* The **signer’s address** (i.e., the account sending the transaction)
1
**Create a new encryted input**
Copy
// use the `fhevm` API module from the Hardhat Runtime Environment
const input = fhevm.createEncryptedInput(contractAddress, signers.alice.address);
2
**Add the value you want to encrypt.**
Copy
input.add32(12345);
3
**Perform local encryption.**
Copy
const encryptedInputs = await input.encrypt();
4
**Call the Solidity function**
Copy
const externalUint32Value = encryptedInputs.handles[0];
const inputProof = encryptedInputs.proof;
const tx = await input.foo(externalUint32Value, inputProof);
await tx.wait();
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/write_test#encryption-examples)
Encryption examples
* [Basic encryption examples](https://docs.zama.ai/protocol/examples/basic/encryption)
* [FHECounter](https://docs.zama.ai/protocol/examples#an-fhe-counter)
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/write_test#decrypting-values-using-the-hardhat-fhevm-api)
Decrypting values using the Hardhat FHEVM API
Suppose user **Alice** wants to decrypt a `euint32` value that is stored in a smart contract exposing the following Solidity `view` function:
Copy
function getEncryptedUint32Value() public view returns (euint32) { returns _encryptedUint32Value; }
For simplicity, we assume that both Alice’s account and the target smart contract already have the necessary FHE permissions to decrypt this value. For a detailed explanation of how FHE permissions work, see the [`initializeUint32()`](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-decrypt-single-value#tab-decryptsinglevalue.sol)
function in [DecryptSingleValue.sol](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-decrypt-single-value#tab-decryptsinglevalue.sol)
.
1
**Retrieve the encrypted value (a** `**bytes32**` **handle) from the smart contract:**
Copy
const encryptedUint32Value = await contract.getEncryptedUint32Value();
2
**Perform the decryption using the FHEVM API:**
Copy
const clearUint32Value = await fhevm.userDecryptEuint(
FhevmType.euint32, // Encrypted type (must match the Solidity type)
encryptedUint32Value, // bytes32 handle Alice wants to decrypt
contractAddress, // Target contract address
signers.alice, // Alice’s wallet
);
If either the target smart contract or the user does **NOT** have FHE permissions, then the decryption call will fail!
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/write_test#supported-decryption-types)
Supported Decryption Types
Use the appropriate function for each encrypted data type:
Type
Function
`euintXXX`
`fhevm.userDecryptEuint(...)`
`ebool`
`fhevm.userDecryptEbool(...)`
`eaddress`
`fhevm.userDecryptEaddress(...)`
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/write_test#decryption-examples)
Decryption examples
* [Basic decryption examples](https://docs.zama.ai/protocol/examples/basic/decryption)
* [FHECounter](https://docs.zama.ai/protocol/examples#an-fhe-counter)
[PreviousHardhat plugin](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat)
[NextDeploy contracts and run tests](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hardhat/run_test)
Last updated 3 months ago
---
# Branching | Protocol
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.
This document covers encrypted branching and how to move from an encrypted condition to a non-encrypted business logic in your smart contract.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions#what-is-confidential-branching)
What is confidential branching?
-----------------------------------------------------------------------------------------------------------------------------------------------------------
In FHEVM, when you perform [comparison operations](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations#comparison-operations)
, 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 `FHE.select` function, which acts as a ternary operator for encrypted values.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions#using-fhe.select-for-conditional-logic)
**Using** `**FHE.select**` **for conditional logic**
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The `FHE.select` function enables branching logic by selecting one of two encrypted values based on an encrypted condition (`ebool`). It works as follows:
Copy
FHE.select(condition, valueIfTrue, valueIfFalse);
* `**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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions#example-auction-bidding-logic)
**Example: Auction Bidding Logic**
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Here's an example of using conditional logic to update the highest winning number in a guessing game:
Copy
function bid(externalEuint64 encryptedValue, bytes calldata inputProof) external onlyBeforeEnd {
// Convert the encrypted input to an encrypted 64-bit integer
euint64 bid = FHE.asEuint64(encryptedValue, inputProof);
// Compare the current highest bid with the new bid
ebool isAbove = FHE.lt(highestBid, bid);
// Update the highest bid if the new bid is greater
highestBid = FHE.select(isAbove, bid, highestBid);
// Allow the contract to use the updated highest bid ciphertext
FHE.allowThis(highestBid);
}
This is a simplified example to demonstrate the functionality.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions#how-does-it-work)
How Does It Work?
* **Comparison**:
* The `FHE.lt` function compares `highestBid` and `bid`, returning an `ebool` (`isAbove`) that indicates whether the new bid is higher.
* **Selection**:
* The `FHE.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 `FHE.allowThis`.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions#key-considerations)
Key Considerations
----------------------------------------------------------------------------------------------------------------------------------
* **Value change behavior:** Each time `FHE.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 `FHE.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., `FHE.allowThis`, `FHE.allow`) to ensure the updated ciphertexts are authorized for use in future computations or transactions.
* * *
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions#how-to-branch-to-a-non-confidential-path)
How to branch to a non-confidential path?
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
So far, this section only covered how to do branching using encrypted variables. However, there may be many cases where the "public" contract logic will depend on the outcome from a encrypted path.
To do so, there are only one way to branch from an encrypted path to a non-encrypted path: it requires a public decryption using the oracle. Hence, any contract logic that requires moving from an encrypted input to a non-encrypted path always requires an async contract logic.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions#example-auction-bidding-logic-item-release)
**Example: Auction Bidding Logic: Item Release**
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Going back to our previous example with the auction bidding logic. Let's assume that the winner of the auction can receive some prize, which is not confidential.
Copy
bool public isPrizeDistributed;
eaddress internal highestBidder;
euint64 internal highestBid;
function bid(externalEuint64 encryptedValue, bytes calldata inputProof) external onlyBeforeEnd {
// Convert the encrypted input to an encrypted 64-bit integer
euint64 bid = FHE.asEuint64(encryptedValue, inputProof);
// Compare the current highest bid with the new bid
ebool isAbove = FHE.lt(highestBid, bid);
// Update the highest bid if the new bid is greater
highestBid = FHE.select(isAbove, bid, highestBid);
// Update the highest bidder address if the new bid is greater
highestBidder = FHE.select(isAbove, FHE.asEaddress(msg.sender), currentBidder));
// Allow the contract to use the highest bidder address
FHE.allowThis(highestBidder);
// Allow the contract to use the updated highest bid ciphertext
FHE.allowThis(highestBid);
}
function revealWinner() external onlyAfterEnd {
bytes32[] memory cts = new bytes32[](2);
cts[0] = FHE.toBytes32(highestBidder);
uint256 requestId = FHE.requestDecryption(cts, this.transferPrize.selector);
}
function transferPrize(uint256 requestId, address auctionWinner, bytes memory signatures) external {
require(!isPrizeDistributed, "Prize has already been distributed");
FHE.verifySignatures(requestId, signatures)
isPrizeDistributed = true;
// Business logic to transfer the prize to the auction winner
}
This is a simplified example to demonstrate the functionality.
As you can see the in the above example, the path to move from an encrypted condition to a decrypted business logic must be async and requires calling the decryption oracle contract to reveal the result of the logic using encrypted variables.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/conditions#summary)
Summary
------------------------------------------------------------------------------------------------------------
* `**FHE.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.
[PreviousLogics](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics)
[NextDealing with branches and conditions](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/loop)
Last updated 1 month ago
---
# Set up Hardhat | Protocol
In this section, you’ll learn how to set up a FHEVM Hardhat development environment using the **FHEVM Hardhat template** as a starting point for building and testing fully homomorphic encrypted smart contracts.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#create-a-local-hardhat-project)
Create a local Hardhat Project
-----------------------------------------------------------------------------------------------------------------------------------------------
1
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#install-a-node.js-tls-version)
Install a Node.js TLS version
Ensure that Node.js is installed on your machine.
* Download and install the recommended LTS (Long-Term Support) version from the [official website](https://nodejs.org/en)
.
* Use an **even-numbered** version (e.g., `v18.x`, `v20.x`)
**Hardhat** does not support odd-numbered Node.js versions. If you’re using one (e.g., v21.x, v23.x), Hardhat will display a persistent warning message and may behave unexpectedly.
To verify your installation:
Copy
node -v
npm -v
2
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#create-a-new-github-repository-from-the-fhevm-hardhat-template)
Create a new GitHub repository from the FHEVM Hardhat template.
1. On GitHub, navigate to the main page of the [FHEVM Hardhat template](https://github.com/zama-ai/fhevm-hardhat-template)
repository.
2. Above the file list, click the green **Use this template** button.
3. Follow the instructions to create a new repository from the FHEVM Hardhat template.
See Github doc: [Creating a repository from a template](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template#creating-a-repository-from-a-template)
3
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#clone-your-newly-created-github-repository-locally)
Clone your newly created GitHub repository locally
Now that your GitHub repository has been created, you can clone it to your local machine:
Copy
cd
git clone
# Navigate to the root of your new FHEVM Hardhat project
cd
Next, let’s install your local Hardhat development environment.
4
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#install-your-fhevm-hardhat-project-dependencies)
Install your FHEVM Hardhat project dependencies
From the project root directory, run:
Copy
npm install
This will install all required dependencies defined in your `package.json`, setting up your local FHEVM Hardhat development environment.
5
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#set-up-the-hardhat-configuration-variables-optional)
Set up the Hardhat configuration variables (optional)
If you do plan to deploy to the Sepolia Ethereum Testnet, you'll need to set up the following [Hardhat Configuration variables](https://hardhat.org/hardhat-runner/docs/guides/configuration-variables)
.
`MNEMONIC`
A mnemonic is a 12-word seed phrase used to generate your Ethereum wallet keys.
1. Get one by creating a wallet with [MetaMask](https://metamask.io/)
, or using any trusted mnemonic generator.
2. Set it up in your Hardhat project:
Copy
npx hardhat vars set MNEMONIC
`INFURA_API_KEY`
The INFURA project key allows you to connect to Ethereum testnets like Sepolia.
1. Obtain one by following the [Infura + MetaMask](https://docs.metamask.io/services/get-started/infura/)
setup guide.
2. Configure it in your project:
Copy
npx hardhat vars set INFURA_API_KEY
**Default Values**
If you skip this step, Hardhat will fall back to these defaults:
* `MNEMONIC` = "test test test test test test test test test test test junk"
* `INFURA_API_KEY` = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
These defaults are not suitable for real deployments.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#missing-variable-error)
Missing variable error:
If any of the requested Hardhat Configuration Variables is missing, you'll get an error message like this one:`Error HH1201: Cannot find a value for the configuration variable 'MNEMONIC'. Use 'npx hardhat vars set MNEMONIC' to set it or 'npx hardhat var setup' to list all the configuration variables used by this project.`
Congratulations! You're all set to start building your confidential dApp.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#optional-settings)
Optional settings
---------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#install-vscode-extensions)
Install VSCode extensions
If you're using Visual Studio Code, there are some extensions available to improve you your development experience:
* [Prettier - Code formatter by prettier.io](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
— ID:`esbenp.prettier-vscode`,
* [ESLint by Microsoft](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
— ID:`dbaeumer.vscode-eslint`
Solidity support (pick one only):
* [Solidity by Juan Blanco](https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity)
— ID:`juanblanco.solidity`
* [Solidity by Nomic Foundation](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity)
— ID:`nomicfoundation.hardhat-solidity`
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#reset-the-hardhat-project)
Reset the Hardhat project
If you'd like to start from a clean slate, you can reset your FHEVM Hardhat project by removing all example code and generated files.
Copy
# Navigate to the root of your new FHEVM Hardhat project
cd
Then run:
Copy
rm -rf test/* src/* tasks/* deploy ./fhevmTemp ./artifacts ./cache ./coverage ./types ./coverage.json ./dist
[PreviousWhat is FHEVM Solidity](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/overview)
[NextQuick start tutorial](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial)
Last updated 1 month ago
---
# Supported types | Protocol
This document introduces the encrypted integer types provided by the `FHE` library in FHEVM and explains their usage, including casting, state variable declarations, and type-specific considerations.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/types#introduction)
Introduction
----------------------------------------------------------------------------------------------------------
The `FHE` 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.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/types#key-features-of-encrypted-types)
Key features of encrypted types
* 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 `FHE` 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 `FHE` 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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/types#list-of-encrypted-types)
List of encrypted types
--------------------------------------------------------------------------------------------------------------------------------
The `FHE` library currently supports the following encrypted types:
Type
Bit Length
Supported Operators
Aliases (with supported operators)
Ebool
2
and, or, xor, eq, ne, not, select, rand
Euint8
8
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint16
16
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint32
32
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint64
64
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint128
128
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint160
160
Eaddress (eq, ne, select)
Euint256
256
and, or, xor, shl, shr, rotl, rotr, eq, ne, neg, not, select, rand, randBounded
Division (`div`) and remainder (`rem`) operations are only supported when the right-hand side (`rhs`) operand is a plaintext (non-encrypted) value. Attempting to use an encrypted value as `rhs` will result in a panic. This restriction ensures correct and secure computation within the current framework.
Higher-precision integer types are available in the `TFHE-rs` library and can be added to `fhevm` as needed.
[PreviousContract addresses](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure/contract_addresses)
[NextOperations on encrypted types](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations)
Last updated 1 month ago
---
# How to Transform Your Smart Contract into a FHEVM Smart Contract? | Protocol
This short guide will walk you through converting a standard Solidity contract into one that leverages Fully Homomorphic Encryption (FHE) using FHEVM. This approach lets you develop your contract logic as usual, then adapt it to support encrypted computation for privacy.
For this guide, we will focus on a voting contract example.
* * *
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/transform_smart_contract_with_fhevm#id-1.-start-with-a-standard-solidity-contract)
1\. Start with a Standard Solidity Contract
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Begin by writing your voting contract in Solidity as you normally would. Focus on implementing the core logic and functionality.
Copy
// Standard Solidity voting contract example
pragma solidity ^0.8.0;
contract SimpleVoting {
mapping(address => bool) public hasVoted;
uint64 public yesVotes;
uint64 public noVotes;
uint256 public voteDeadline;
function vote(bool support) public {
require(block.timestamp <= voteDeadline, "Too late to vote");
require(!hasVoted[msg.sender], "Already voted");
hasVoted[msg.sender] = true;
if (support) {
yesVotes += 1;
} else {
noVotes += 1;
}
}
function getResults() public view returns (uint64, uint64) {
return (yesVotes, noVotes);
}
}
* * *
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/transform_smart_contract_with_fhevm#id-2.-identify-sensitive-data-and-operations)
2\. Identify Sensitive Data and Operations
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Review your contract and determine which variables, functions, or computations require privacy. In this example, the vote counts (`yesVotes`, `noVotes`) and individual votes should be encrypted.
* * *
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/transform_smart_contract_with_fhevm#id-3.-integrate-fhevm-and-update-your-business-logic-accordingly)
3\. Integrate FHEVM and update your business logic accordingly.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Replace standard data types and operations with their FHEVM equivalents for the identified sensitive parts. Use encrypted types and FHEVM library functions to perform computations on encrypted data.
Copy
pragma solidity ^0.8.0;
import "@fhevm/solidity/lib/FHE.sol";
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
contract EncryptedSimpleVoting is SepoliaConfig {
enum VotingStatus {
Open,
DecryptionInProgress,
ResultsDecrypted
}
mapping(address => bool) public hasVoted;
VotingStatus public status;
uint64 public decryptedYesVotes;
uint64 public decryptedNoVotes;
uint256 public voteDeadline;
euint64 private encryptedYesVotes;
euint64 private encryptedNoVotes;
constructor() {
encryptedYesVotes = FHE.asEuint64(0);
encryptedNoVotes = FHE.asEuint64(0);
FHE.allowThis(encryptedYesVotes);
FHE.allowThis(encryptedNoVotes);
}
function vote(externalEbool support, bytes memory inputProof) public {
require(block.timestamp <= voteDeadline, "Too late to vote");
require(!hasVoted[msg.sender], "Already voted");
hasVoted[msg.sender] = true;
ebool isSupport = FHE.fromExternal(support, inputProof);
encryptedYesVotes = FHE.select(isSupport, FHE.add(encryptedYesVotes, 1), encryptedYesVotes);
encryptedNoVotes = FHE.select(isSupport, encryptedNoVotes, FHE.add(encryptedNoVotes, 1));
FHE.allowThis(encryptedYesVotes);
FHE.allowThis(encryptedNoVotes);
}
function requestVoteDecryption() public {
require(block.timestamp > voteDeadline, "Voting is not finished");
bytes32[] memory cts = new bytes32[](2);
cts[0] = FHE.toBytes32(encryptedYesVotes);
cts[1] = FHE.toBytes32(encryptedNoVotes);
uint256 requestId = FHE.requestDecryption(cts, this.callbackDecryptVotes.selector);
status = VotingStatus.DecryptionInProgress;
}
function callbackDecryptVotes(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) public {
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
(uint64 yesVotes, uint64 noVotes) = abi.decode(cleartexts, (uint64, uint64));
decryptedYesVotes = yesVotes;
decryptedNoVotes = noVotes;
status = VotingStatus.ResultsDecrypted;
}
function getResults() public view returns (uint64, uint64) {
require(status == VotingStatus.ResultsDecrypted, "Results were not decrypted");
return (
decryptedYesVotes,
decryptedNoVotes
);
}
}
Adjust your contract’s code to accept and return encrypted data where necessary. This may involve changing function parameters and return types to work with ciphertexts instead of plaintext values, as shown above.
* The `vote` function now has two parameters: `support` and `inputProof`.
* The `getResults` can only be called after the decryption occurred. Otherwise, the decrypted results are not visible to anyone.
However, it is far from being the main change. As this example illustrates, working with FHEVM often requires re-architecting the original logic to support privacy.
In the updated code, the logic becomes async; results are hidden until a request (to the oracle) explicitely has to be made to decrypt publically the vote results.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/transform_smart_contract_with_fhevm#conclusion)
Conclusion
---------------------------------------------------------------------------------------------------------------------------------------
As this short guide showed, integrating with FHEVM not only requires integration with the FHEVM stack, it also requires refactoring your business logic to support mechanism to swift between encrypted and non-encrypted components of the logic.
[PreviousMigration guide](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration)
Last updated 1 month ago
---
# Migration guide | Protocol
This document provides instructions on migrating from a previous version of FHEVM.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration#from-0.7.x)
From 0.7.x
-------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration#decryption-oracle)
Decryption Oracle
Callbacks are now implemented using an ABI-encoded bytes value, which includes all cleartexts.
Copy
function myCustomCallback(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) public returns (bool) {
/// @dev This check is used to verify that the request id is the expected one.
require(requestId == latestRequestId, "Invalid requestId");
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
(bool decryptedInput) = abi.decode(cleartexts, (bool));
yBool = decryptedInput;
isDecryptionPending = false;
return yBool;
}
}
`function setDecryptionOracle(address decryptionOracle)` is now deprecated. The decryption oracle address is now configured through function `setCoprocessor(CoprocessorConfig memory coprocessorConfig)`. For example:
Copy
CoprocessorConfig({
ACLAddress: 0x687820221192C5B662b25367F70076A37bc79b6c,
CoprocessorAddress: 0x848B0066793BcC60346Da1F49049357399B8D595,
DecryptionOracleAddress: 0xa02Cda4Ca3a71D7C46997716F4283aa851C28812,
KMSVerifierAddress: 0x1364cBBf2cDF5032C47d8226a6f6FBD2AFCDacAC
});
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration#from-0.6.x)
From 0.6.x
-------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration#package-and-library)
Package and library
The package is now `@fhevm/solidity` instead of `FHEVM` and the library name has changed from `TFHE` to `FHE`
Copy
import { FHE } from "@fhevm/solidity";
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration#configuration)
Configuration
Configuration has been renamed from `SepoliaZamaConfig` to `SepoliaConfig`.
Copy
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
Also, the function to define manually the Coprocessor has been renamed from `setFHEVM` to `setCoprocessor`, and the function to define the oracle is now integrated into `setCoprocessor`.
Copy
import { ZamaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
constructor () {
FHE.setCoprocessor(ZamaConfig.getSepoliaConfig());
}
You can read more about [Configuration on the dedicated page](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration#decryption-oracle-1)
Decryption Oracle
Previously, an abstract contract `GatewayCaller` was used to request decryption. It has been replaced by `FHE.requestDecryption`:
Copy
function requestBoolInfinite() public {
bytes32[] memory cts = new bytes32[](1);
cts[0] = FHE.toBytes32(myEncryptedValue);
FHE.requestDecryption(cts, this.myCallback.selector);
}
Callbacks are now implemented using an ABI-encoded bytes value, which includes all cleartexts.
Copy
function myCustomCallback(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) public returns (bool) {
/// @dev This check is used to verify that the request id is the expected one.
require(requestId == latestRequestId, "Invalid requestId");
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
(bool decryptedInput) = abi.decode(cleartexts, (bool));
yBool = decryptedInput;
isDecryptionPending = false;
return yBool;
}
}
You can read more about [Decryption Oracle on the dedicated page](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration#deprecation-of-ebytes)
Deprecation of ebytes
`ebytes` has been deprecated and removed from FHEVM.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration#block-gas-limit)
Block gas limit
Block gas limit has been removed in favor of HCU (Homomorphic Complexity Unit) limit. FHEVM 0.7.0 includes two limits:
* **Sequential homomorphic operations depth limit per transaction**: Controls HCU usage for operations that must be processed in order. This limit is set to **5,000,000** HCU.
* **Global homomorphic operations complexity per transaction**: Controls HCU usage for operations that can be processed in parallel. This limit is set to **20,000,000** HCU.
You can read more about [HCU on the dedicated page](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu)
.
[PreviousHCU](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu)
[NextHow to Transform Your Smart Contract into a FHEVM Smart Contract?](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/transform_smart_contract_with_fhevm)
Last updated 1 month ago
---
# Foundry | Protocol
This guide explains how to use Foundry with FHEVM for developing smart contracts.
While a Foundry template is currently in development, we strongly recommend using the [Hardhat template](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup)
) for now, as it provides a fully tested and supported development environment for FHEVM smart contracts.
However, you could still use Foundry with the mocked version of the FHEVM, but please be aware that this approach is **NOT** recommended, since the mocked version is not fully equivalent to the real FHEVM node's implementation (see warning in hardhat). In order to do this, you will need to rename your `FHE.sol` imports from `@fhevm/solidity/lib/FHE.sol` to `fhevm/mocks/FHE.sol` in your solidity source files.
[PreviousWrite FHEVM-enabled Hardhat Tasks](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task)
[NextHCU](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu)
Last updated 1 month ago
---
# 3. Turn it into FHEVM | Protocol
In this tutorial, you'll learn how to take a basic Solidity smart contract and progressively upgrade it to support Fully Homomorphic Encryption using the FHEVM library by Zama.
Starting with the plain `Counter.sol` contract that you built from the ["Write a simple contract" tutorial](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract)
, and step-by-step, you’ll learn how to:
* Replace standard types with encrypted equivalents
* Integrate zero-knowledge proof validation
* Enable encrypted on-chain computation
* Grant permissions for secure off-chain decryption
By the end, you'll have a fully functional smart contract that supports FHE computation.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#initiate-the-contract)
Initiate the contract
---------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#create-the-fhecounter.sol-file)
Create the `FHECounter.sol` file
Navigate to your project’s `contracts` directory:
Copy
cd /contracts
From there, create a new file named `FHECounter.sol`, and copy the following Solidity code into it:
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title A simple counter contract
contract Counter {
uint32 private _count;
/// @notice Returns the current count
function getCount() external view returns (uint32) {
return _count;
}
/// @notice Increments the counter by a specific value
function increment(uint32 value) external {
_count += value;
}
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
}
This is a plain `Counter` contract that we’ll use as the starting point for adding FHEVM functionality. We will modify this contract step-by-step to progressively integrate FHEVM capabilities.
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#turn-counter-into-fhecounter)
Turn `Counter` into `FHECounter`
To begin integrating FHEVM features into your contract, we first need to import the required FHEVM libraries.
**Replace the current header**
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
**With this updated header:**
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { FHE, euint32, externalEuint32 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
These imports:
* **FHE** — the core library to work with FHEVM encrypted types
* **euint32** and **externalEuint32** — encrypted uint32 types used in FHEVM
* **SepoliaConfig** — provides the FHEVM configuration for the Sepolia network. Inheriting from it enables your contract to use the FHE library
**Replace the current contract declaration:**
Copy
/// @title A simple counter contract
contract Counter {
**With the updated declaration :**
Copy
/// @title A simple FHE counter contract
contract FHECounter is SepoliaConfig {
This change:
* Renames the contract to `FHECounter`
* Inherits from `SepoliaConfig` to enable FHEVM support
This contract must inherit from the `SepoliaConfig` abstract contract; otherwise, it will not be able to execute any FHEVM-related functionality on Sepolia or Hardhat.
From your project's root directory, run:
Copy
npx hardhat compile
Great! Your smart contract is now compiled and ready to use **FHEVM features.**
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#apply-fhe-functions-and-types)
Apply FHE functions and types
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#comment-out-the-increment-and-decrement-functions)
Comment out the `increment()` and `decrement()` Functions
Before we move forward, let’s comment out the `increment()` and `decrement()` functions in `FHECounter`. We'll replace them later with updated versions that support FHE-encrypted operations.
Copy
/// @notice Increments the counter by a specific value
// function increment(uint32 value) external {
// _count += value;
// }
/// @notice Decrements the counter by a specific value
// function decrement(uint32 value) external {
// require(_count >= value, "Counter: cannot decrement below zero");
// _count -= value;
// }
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#replace-uint32-with-the-fhevm-euint32-type)
Replace `uint32` with the FHEVM `euint32` Type
We’ll now switch from the standard Solidity `uint32` type to the encrypted FHEVM type `euint32`.
This enables private, homomorphic computation on encrypted integers.
**Replace**
Copy
uint32 _count;
and
Copy
function getCount() external view returns (uint32) {
**With :**
Copy
euint32 _count;
and
Copy
function getCount() external view returns (euint32) {
3
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#replace-increment-uint32-value-with-the-fhevm-version-increment-externaleuint32-value)
Replace `increment(uint32 value)` with the FHEVM version `increment(externalEuint32 value)`
To support encrypted input, we will update the increment function to accept a value encrypted off-chain.
Instead of using a `uint32`, the new version will accept an `externalEuint32`, which is an encrypted integer produced off-chain and sent to the smart contract.
To ensure the validity of this encrypted value, we also include a second argument:`inputProof`, a bytes array containing a Zero-Knowledge Proof of Knowledge (ZKPoK) that proves two things:
1. The `externalEuint32` was encrypted off-chain by the function caller (`msg.sender`)
2. The `externalEuint32` is bound to the contract (`address(this)`) and can only be processed by it.
**Replace**
Copy
/// @notice Increments the counter by a specific value
// function increment(uint32 value) external {
// _count += value;
// }
**With :**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
// _count += value;
}
4
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#convert-externaleuint32-to-euint32)
Convert `externalEuint32` to `euint32`
You cannot directly use `externalEuint32` in FHE operations. To manipulate it with the FHEVM library, you first need to convert it into the native FHE type `euint32`.
This conversion is done using:
Copy
FHE.fromExternal(inputEuint32, inputProof);
This method verifies the zero-knowledge proof and returns a usable encrypted value within the contract.
**Replace**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
// _count += value;
}
**With :**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
// _count += value;
}
5
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#convert-_count--value-into-its-fhevm-equivalent)
Convert `_count += value` into its FHEVM equivalent
To perform the update `_count += value` in a Fully Homomorphic way, we use the `FHE.add()` operator. This function allows us to compute the FHE sum of 2 encrypted integers.
**Replace**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
// _count += value;
}
**With :**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, evalue);
}
This FHE operation allows the smart contract to process encrypted values without ever decrypting them — a core feature of FHEVM that enables on-chain privacy.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#grant-fhe-permissions)
Grant FHE Permissions
---------------------------------------------------------------------------------------------------------------------------------------------------------------
This step is critical! You must grant FHE permissions to both the contract and the caller to ensure the encrypted `_count` value can be decrypted off-chain by the caller. Without these 2 permissions, the caller will not be able to compute the clear result.
To grant FHE permission we will call the `FHE.allow()` function.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#replace)
Replace
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, evalue);
}
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#with)
With :
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, evalue);
FHE.allowThis(_count);
FHE.allow(_count, msg.sender);
}
We grant **two** FHE permissions here — not just one. In the next part of the tutorial, you'll learn why **both** are necessary.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#convert-decrement-to-its-fhevm-equivalent)
Convert `decrement()` to its FHEVM equivalent
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Just like with the `increment()` migration, we’ll now convert the `decrement()` function to its FHEVM-compatible version.
Replace :
Copy
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
with the following :
Copy
/// @notice Decrements the counter by a specific value
/// @dev This example omits overflow/underflow checks for simplicity and readability.
/// In a production contract, proper range checks should be implemented.
function decrement(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.sub(_count, encryptedEuint32);
FHE.allowThis(_count);
FHE.allow(_count, msg.sender);
}
The `increment()` and `decrement()` functions do not perform any overflow or underflow checks.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm#compile-fhecounter.sol)
Compile `FHECounter.sol`
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
From your project's root directory, run:
Copy
npx hardhat compile
Congratulations! Your smart contract is now fully **FHEVM-compatible**.
Now you should have the following files in your project:
* [`contracts/FHECounter.sol`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#fhecounter.sol)
— your Solidity smart FHEVM contract
* [`test/FHECounter.ts`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#fhecounter.ts)
— your FHEVM Hardhat test suite written in TypeScript
In the [next tutorial](https://github.com/zama-ai/fhevm/blob/release/0.8.x/docs/solidity-guides/getting-started/quick-start-tutorial/test_fhevm_contract.md)
, we’ll move on to the **TypeScript integration**, where you’ll learn how to interact with your newly upgraded FHEVM contract in a test suite.
[Previous2\. Write a simple contract](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract)
[Next4\. Test the FHEVM contract](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract)
Last updated 1 month ago
---
# Sealed-bid auction tutorial | Protocol
This tutorial explains how to build a sealed-bid NFT auction using Fully Homomorphic Encryption (FHE). In this system, participants submit encrypted bids for a single NFT. Bids remain confidential during the auction, and only the winner’s information is revealed at the end.
By following this guide, you will learn how to:
* Accept and process encrypted bids
* Compare bids securely without revealing their values
* Reveal the winner after the auction concludes
* Design an auction that is private, fair, and transparent
[](https://docs.zama.ai/protocol/examples/advanced/sealed-bid-auction/sealed-bid-auction-tutorial#why-fhe)
Why FHE
-----------------------------------------------------------------------------------------------------------------------
In most onchain auctions, **bids are fully public**. Anyone can inspect the blockchain or monitor pending transactions to see how much each participant has bid. This breaks fairness as all it takes to win is to send a new bid with just one wei higher than the current highest.
Existing solutions like commit-reveal schemes attempt to hide bids during a preliminary commit phase. However, they come with several drawbacks: increased transaction overhead, poor user experience (e.g., requiring users to send funds to EOA via `CREATE2`), and delays caused by the need for multiple auction phases.
Fully Homomorphic Encryption (FHE) to enable participants to submit encrypted bids directly to a smart contract in a single step, eliminating multi-phase complexity, improving user experience, and preserving bid secrecy without ever revealing or decrypting them.
[](https://docs.zama.ai/protocol/examples/advanced/sealed-bid-auction/sealed-bid-auction-tutorial#project-setup)
Project Setup
-----------------------------------------------------------------------------------------------------------------------------------
Before starting this tutorial, ensure you have:
1. Installed the FHEVM hardhat template
2. Set up the OpenZeppelin confidential contracts library
3. Deployed your confidential token
For help with these steps, refer to these tutorials:
* [Setting up OpenZeppelin confidential contracts](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/openzeppelin)
* [Deploying a Confidential Token](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/erc7984/erc7984-tutorial)
[](https://docs.zama.ai/protocol/examples/advanced/sealed-bid-auction/sealed-bid-auction-tutorial#create-the-smart-contracts)
Create the smart contracts
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Let’s now create a new contract called `BlindAuction.sol` in the `./contracts/` folder. To enable FHE operations in our contract, we will need to inherit our contract from `SepoliaConfig`. This configuration provides the necessary parameters and network-specific settings required to interact with Zama’s FHEVM.
Let’s also create some state variable that is going to be used in our auction. For the payment, we will rely on a `ConfidentialFungibleToken`. Indeed, we cannot use traditional ERC20, because even if the state in our auction is private, anyone can still monitor blockchain transactions and guess the bid value. By using a `ConfidentialFungibleToken` we ensure the amount stays hidden. This `ConfidentialFungibleToken` can be used with any ERC20, you will only need to wrap your token to hide future transfers.
Our contract will also include an `ERC721` token representing the NFT being auctioned and the address of the auction’s beneficiary. Finally, we’ll define some time-related parameters to control the auction’s duration.
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { FHE, externalEuint64, euint64, ebool } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
import {ConfidentialFungibleToken} from "@openzeppelin/confidential-contracts/token/ConfidentialFungibleToken.sol";
// ...
contract BlindAuction is SepoliaConfig {
/// @notice The recipient of the highest bid once the auction ends
address public beneficiary;
/// @notice Confidenctial Payment Token
ConfidentialFungibleToken public confidentialFungibleToken;
/// @notice Token for the auction
IERC721 public nftContract;
uint256 public tokenId;
/// @notice Auction duration
uint256 public auctionStartTime;
uint256 public auctionEndTime;
// ...
constructor(
address _nftContractAddress,
address _confidentialFungibleTokenAddress,
uint256 _tokenId,
uint256 _auctionStartTime,
uint256 _auctionEndTime
) {
beneficiary = msg.sender;
confidentialFungibleToken = ConfidentialFungibleToken(_confidentialFungibleTokenAddress);
nftContract = IERC721(_nftContractAddress);
// Transfer the NFT to the contract for the auction
nftContract.safeTransferFrom(msg.sender, address(this), _tokenId);
require(_auctionStartTime < _auctionEndTime, "INVALID_TIME");
auctionStartTime = _auctionStartTime;
auctionEndTime = _auctionEndTime;
}
// ...
}
Now, we need a way to store the highest bid and the potential winner. To store that information privately, we will use some tools provided by the FHE library. For storing an encrypted address, we can use `eaddress` type and for the highest bid, we can store the amount with `euint64`. Additionally, we can create a mapping to track the user bids.
Copy
/// @notice Encrypted auction info
euint64 private highestBid;
eaddress private winningAddress;
/// @notice Mapping from bidder to their bid value
mapping(address account => euint64 bidAmount) private bids;
As you may notice, in our code we are using euint64, which represents an encrypted 64-bit unsigned integer. Unlike standard Solidity type, where there is not that much difference between uint64 and uint256, in FHE the size of your data has a significant effect on performance. The larger the representation, the more expensive the computation becomes. That is for this reason, we recommend you to choose wisely your number representation based on your use case. Here for instance, euint64 is more than enough to handle token balance.
###
[](https://docs.zama.ai/protocol/examples/advanced/sealed-bid-auction/sealed-bid-auction-tutorial#create-our-bid-function)
Create our bid function
Let’s now create our bid function, where the user will transfer a confidential amount and send it to the auction smart contract. Since we want bids to remain private, users must first encrypt their bid amount locally. This encrypted value will then be used to securely transfer funds from the `ConfidentialFungibleToken` token that we’ve set as the payment method. We can create our function as follows:
Copy
function bid(
externalEuint64 encryptedAmount,
bytes calldata inputProof
) public onlyDuringAuction nonReentrant {
// Get and verify the amount from the user
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
// ...
Here, we accept two parameters:
* Encrypted Amount: The user’s bid amount, encrypted using FHE.
* Input Proof: A Zero-Knowledge Proof ensuring the validity of the encrypted data.
We can verify those parameters by using our helper function `FHE.fromExternal()` which gives us the reference to our encrypted amount.
Then, we need to transfer the confidential token to the contract.
Copy
euint64 balanceBefore = confidentialFungibleToken.confidentialBalanceOf(address(this));
confidentialFungibleToken.confidentialTransferFrom(msg.sender, address(this), amount);
euint64 balanceAfter = confidentialFungibleToken.confidentialBalanceOf(address(this));
euint64 sentBalance = FHE.sub(balanceAfter, balanceBefore);
Notice that here, we are not using the amount provided by the user as a source of trust. Indeed, in case the user does not have enough funds, when calling the `confidentialTransferFrom()`, **the transaction will not be reverted, but instead transfer silently a** `**0**` **value**. This design choice protects eventual leaks as reverted transactions can unintentionally reveal some information on the data.
> Note: To dive deeper into how FHE works, each FHE operation done on chain will emit an event used to construct a computation graph. This graph is then executed by the Zama FHEVM. Thus, the FHE operation is not directly done on the smart contract side, but rather follows the source graph generated by it.
Once the payment is done, we need to update the bid balance of the user. Notice here that the user can increase his previous bid if he wants:
Copy
euint64 previousBid = bids[msg.sender];
if (FHE.isInitialized(previousBid)) { // The user increase his bid
euint64 newBid = FHE.add(previousBid, sentBalance);
bids[msg.sender] = newBid;
} else {
// First bid for the user
bids[msg.sender] = sentBalance;
}
And finally we can check if we need to update the encrypted winner:
Copy
// Compare the total value of the user from the highest bid
euint64 currentBid = bids[msg.sender];
FHE.allowThis(currentBid);
FHE.allow(currentBid, msg.sender);
if (FHE.isInitialized(highestBid)) {
ebool isNewWinner = FHE.lt(highestBid, currentBid);
highestBid = FHE.select(isNewWinner, currentBid, highestBid);
winningAddress = FHE.select(isNewWinner, FHE.asEaddress(msg.sender), winningAddress);
} else {
highestBid = currentBid;
winningAddress = FHE.asEaddress(msg.sender);
}
FHE.allowThis(highestBid);
FHE.allowThis(winningAddress);
As you can see here, we are using some FHE functions. Let’s talk a bit about the `FHE.allow()` and `FHE.allowThis()`. Each encrypted value has a restriction on who can read this value. To be able to access this value or even do some computation on it, we need to explicitly request access. This is the reason why we need to explicitly request the access. Here for instance, we want the contract and the user to have access to the bid value. However, only the contract can have access to the highest bid value and winner address that will be revealed at the end of the auction.
Another point that we want to mention is the `FHE.select()` function. As mentioned previously, when using FHE, we do not want transactions to be reverted. Instead, when building our graph of FHE operation, we want to create two paths depending on an encrypted value. This is the reason we are using **branching** allowing us to define the type of process we want. Here for instance, if the bid value of the user is higher than the current one, we are going to change the amount and the address. However, if it is not the case, we are keeping the old one. This branching method is particularly useful, as on chain you cannot have access directly to encrypted data, but you still want to adapt your contract logic based on them.
Alright, it seems our bidding function is ready. Here is the full code we have seen so far:
Copy
function bid(externalEuint64 encryptedAmount, bytes calldata inputProof) public onlyDuringAuction nonReentrant {
// Get and verify the amount from the user
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
// Transfer the confidential token as payment
euint64 balanceBefore = confidentialFungibleToken.confidentialBalanceOf(address(this));
FHE.allowTransient(amount, address(confidentialFungibleToken));
confidentialFungibleToken.confidentialTransferFrom(msg.sender, address(this), amount);
euint64 balanceAfter = confidentialFungibleToken.confidentialBalanceOf(address(this));
euint64 sentBalance = FHE.sub(balanceAfter, balanceBefore);
// Need to update the bid balance
euint64 previousBid = bids[msg.sender];
if (FHE.isInitialized(previousBid)) {
// The user increase his bid
euint64 newBid = FHE.add(previousBid, sentBalance);
bids[msg.sender] = newBid;
} else {
// First bid for the user
bids[msg.sender] = sentBalance;
}
// Compare the total value of the user from the highest bid
euint64 currentBid = bids[msg.sender];
FHE.allowThis(currentBid);
FHE.allow(currentBid, msg.sender);
if (FHE.isInitialized(highestBid)) {
ebool isNewWinner = FHE.lt(highestBid, currentBid);
highestBid = FHE.select(isNewWinner, currentBid, highestBid);
winningAddress = FHE.select(isNewWinner, FHE.asEaddress(msg.sender), winningAddress);
} else {
highestBid = currentBid;
winningAddress = FHE.asEaddress(msg.sender);
}
FHE.allowThis(highestBid);
FHE.allowThis(winningAddress);
}
###
[](https://docs.zama.ai/protocol/examples/advanced/sealed-bid-auction/sealed-bid-auction-tutorial#auction-resolution-phase)
Auction resolution phase
Once all participants have placed their bids, it’s time to move to the resolution phase, where we will need to reveal the winner address. First, we will need to decrypt the winner’s address as it is currently encrypted. To do so, we can use the `DecryptionOracle` provided by Zama. This oracle will be in charge of handling securely the decryption of an encrypted value and will return the result via a callback. To implement this, let's create a function that will call the `DecryptionOracle`:
Copy
function decryptWinningAddress() public onlyAfterEnd {
bytes32[] memory cts = new bytes32[](1);
cts[0] = FHE.toBytes32(winningAddress);
_latestRequestId = FHE.requestDecryption(cts, this.resolveAuctionCallback.selector);
}
Here, we are requesting to decrypt a single parameter for the `winningAddress`. However, you can request multiple ones by increasing the `cts` array and adding other parameters.
Notice also that when calling the `FHE.requestDecryption()`, we are passing a selector in the parameter. This selector will be the one called back by the oracle.
Notice also that we have restricted this function to be called only when the auction has ended. We must not be able to call it while the auction is still running, else it will leak some information.
We can now write our `resolveAuctionCallback` callback function:
Copy
function resolveAuctionCallback(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) public {
require(requestId == _latestRequestId, "Invalid requestId");
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
(address resultWinnerAddress) = abi.decode(cleartexts, (address));
winnerAddress = resultWinnerAddress;
}
`cleartexts` is the bytes array corresponding to the ABI encoding of all requested decrypted values, in this case `abi.encode(winningAddress)`.
To ensure that it is the expected data we are waiting for, we need to verify the `requestId` parameter and the signatures (included in the `decryptionProof` parameter), which verify the computation logic done. Once verified, we can update the winner’s address.
###
[](https://docs.zama.ai/protocol/examples/advanced/sealed-bid-auction/sealed-bid-auction-tutorial#claiming-rewards-and-refunds)
Claiming rewards & refunds
Alright, once the winner is revealed, we can now allow the winner to claim his reward and the other one to get refunded.
Copy
function winnerClaimPrize() public onlyAfterWinnerRevealed {
require(winnerAddress == msg.sender, "Only winner can claim item");
require(!isNftClaimed, "NFT has already been claimed");
isNftClaimed = true;
// Reset bid value
bids[msg.sender] = FHE.asEuint64(0);
FHE.allowThis(bids[msg.sender]);
FHE.allow(bids[msg.sender], msg.sender);
// Transfer the highest bid to the beneficiary
FHE.allowTransient(highestBid, address(confidentialFungibleToken));
confidentialFungibleToken.confidentialTransfer(beneficiary, highestBid);
// Send the NFT to the winner
nftContract.safeTransferFrom(address(this), msg.sender, tokenId);
}
Copy
function withdraw(address bidder) public onlyAfterWinnerRevealed {
if (bidder == winnerAddress) revert TooLateError(auctionEndTime);
// Get the user bid value
euint64 amount = bids[bidder];
FHE.allowTransient(amount, address(confidentialFungibleToken));
// Reset user bid value
euint64 newBid = FHE.asEuint64(0);
bids[bidder] = newBid;
FHE.allowThis(newBid);
FHE.allow(newBid, bidder);
// Refund the user with his bid amount
confidentialFungibleToken.confidentialTransfer(bidder, amount);
}
[](https://docs.zama.ai/protocol/examples/advanced/sealed-bid-auction/sealed-bid-auction-tutorial#conclusion)
Conclusion
-----------------------------------------------------------------------------------------------------------------------------
In this guide, we have walked through how to build a sealed-bid NFT auction using Fully Homomorphic Encryption (FHE) onchain.
We demonstrated how FHE can be used to design a private and fair auction mechanism, keeping all bids encrypted and only revealing information when necessary.
Now it’s your turn. Feel free to build on this code, extend it with more complex logic, or create your own decentralized application powered by FHE.
[PreviousSealed-bid auction](https://docs.zama.ai/protocol/examples/advanced/sealed-bid-auction)
Last updated 10 days ago
---
# 4. Test the FHEVM contract | Protocol
In this tutorial, you’ll learn how to migrate a standard Hardhat test suite - from `Counter.ts` to its FHEVM-compatible version `FHECounter.ts` — and progressively enhance it to support Fully Homomorphic Encryption using Zama’s FHEVM library.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#set-up-the-fhevm-testing-environment)
Set up the FHEVM testing environment
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#create-a-test-script-test-fhecounter.ts)
Create a test script `test/FHECounter.ts`
Go to your project's `test` directory
Copy
cd /test
From there, create a new file named `FHECounter.ts` and copy/paste the following Typescript skeleton code in it.
Copy
import { FHECounter, FHECounter__factory } from "../types";
import { FhevmType } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers, fhevm } from "hardhat";
type Signers = {
deployer: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
async function deployFixture() {
const factory = (await ethers.getContractFactory("FHECounter")) as FHECounter__factory;
const fheCounterContract = (await factory.deploy()) as FHECounter;
const fheCounterContractAddress = await fheCounterContract.getAddress();
return { fheCounterContract, fheCounterContractAddress };
}
describe("FHECounter", function () {
let signers: Signers;
let fheCounterContract: FHECounter;
let fheCounterContractAddress: string;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { deployer: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
beforeEach(async () => {
({ fheCounterContract, fheCounterContractAddress } = await deployFixture());
});
it("should be deployed", async function () {
console.log(`FHECounter has been deployed at address ${fheCounterContractAddress}`);
// Test the deployed address is valid
expect(ethers.isAddress(fheCounterContractAddress)).to.eq(true);
});
// it("count should be zero after deployment", async function () {
// const count = await counterContract.getCount();
// console.log(`Counter.getCount() === ${count}`);
// // Expect initial count to be 0 after deployment
// expect(count).to.eq(0);
// });
// it("increment the counter by 1", async function () {
// const countBeforeInc = await counterContract.getCount();
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
// });
// it("decrement the counter by 1", async function () {
// // First increment, count becomes 1
// let tx = await counterContract.connect(signers.alice).increment();
// await tx.wait();
// // Then decrement, count goes back to 0
// tx = await counterContract.connect(signers.alice).decrement(1);
// await tx.wait();
// const count = await counterContract.getCount();
// expect(count).to.eq(0);
// });
});
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#whats-different-from-counter.ts)
What’s Different from `Counter.ts`?
* This test file is structurally similar to the original `Counter.ts`, but it uses the FHEVM-compatible smart contract `FHECounter` instead of the regular `Counter`.
– For clarity, the `Counter` unit tests are included as comments, allowing you to better understand how each part is adapted during the migration to FHEVM.
* While the test logic remains the same, this version is now set up to support encrypted computations via the FHEVM library — enabling tests that manipulate confidential values directly on-chain.
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#run-the-test-test-fhecounter.ts)
Run the test `test/FHECounter.ts`
From your project's root directory, run:
Copy
npx hardhat test
Output:
Copy
FHECounter
FHECounter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
1 passing (1ms)
Great! Your Hardhat FHEVM test environment is properly setup.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#test-functions)
Test functions
------------------------------------------------------------------------------------------------------------------------------------------------------
Now everything is up and running, you can start testing your contract functions.
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#call-the-contract-getcount-view-function)
Call the contract `getCount()` view function
Replace the commented‐out test for the legacy `Counter` contract:
Copy
// it("count should be zero after deployment", async function () {
// const count = await counterContract.getCount();
// console.log(`Counter.getCount() === ${count}`);
// // Expect initial count to be 0 after deployment
// expect(count).to.eq(0);
// });
with its FHEVM equivalent:
Copy
it("encrypted count should be uninitialized after deployment", async function () {
const encryptedCount = await fheCounterContract.getCount();
// Expect initial count to be bytes32(0) after deployment,
// (meaning the encrypted count value is uninitialized)
expect(encryptedCount).to.eq(ethers.ZeroHash);
});
**What’s different?**
– `encryptedCount` is no longer a plain TypeScript number. It is now a hexadecimal string representing a Solidity `bytes32` value, known as an **FHEVM handle**. This handle points to an encrypted FHEVM primitive of type `euint32`, which internally represents an encrypted Solidity `uint32` primitive type.
* `encryptedCount` is equal to `0x0000000000000000000000000000000000000000000000000000000000000000` which means that `encryptedCount` is uninitialized, and does not reference to any encrypted value at this point.
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
✔ encrypted count should be uninitialized after deployment
2 passing (7ms)
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#setup-the-increment-function-unit-test)
Setup the `increment()` function unit test
We’ll migrate the `increment()` unit test to FHEVM step by step. To start, let’s handle the value of the counter before the first increment. As explained above, the counter is initially a `bytes32` value equal to zero, meaning the FHEVM `euint32` variable is uninitialized.
We’ll interpret this as if the underlying clear value is 0.
Replace the commented‐out test for the legacy `Counter` contract:
Copy
// it("increment the counter by 1", async function () {
// const countBeforeInc = await counterContract.getCount();
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
// });
with the following:
Copy
it("increment the counter by 1", async function () {
const encryptedCountBeforeInc = await fheCounterContract.getCount();
expect(encryptedCountBeforeInc).to.eq(ethers.ZeroHash);
const clearCountBeforeInc = 0;
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
});
3
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#encrypt-the-increment-function-argument)
Encrypt the `increment()` function argument
The `increment()` function takes a single argument: the value by which the counter should be incremented. In the initial version of `Counter.sol`, this value is a clear `uint32`.
We’ll switch to passing an encrypted value instead, using FHEVM `externalEuint32` primitive type. This allows us to securely increment the counter without revealing the input value on-chain.
We are using an `externalEuint32` instead of a regular `euint32`. This tells the FHEVM that the encrypted `uint32` was provided externally (e.g., by a user) and must be verified for integrity and authenticity before it can be used within the contract.
Replace :
Copy
it("increment the counter by 1", async function () {
const encryptedCountBeforeInc = await fheCounterContract.getCount();
expect(encryptedCountBeforeInc).to.eq(ethers.ZeroHash);
const clearCountBeforeInc = 0;
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
});
with the following:
Copy
it("increment the counter by 1", async function () {
const encryptedCountBeforeInc = await fheCounterContract.getCount();
expect(encryptedCountBeforeInc).to.eq(ethers.ZeroHash);
const clearCountBeforeInc = 0;
// Encrypt constant 1 as a euint32
const clearOne = 1;
const encryptedOne = await fhevm
.createEncryptedInput(fheCounterContractAddress, signers.alice.address)
.add32(clearOne)
.encrypt();
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
});
`fhevm.createEncryptedInput(fheCounterContractAddress, signers.alice.address)` creates an encrypted value that is bound to both the contract (`fheCounterContractAddress`) and the user (`signers.alice.address`). This means only Alice can use this encrypted value, and only within the `FHECounter.sol` contract at that specific address. **It cannot be reused by another user or in a different contract, ensuring data confidentiality and binding context-specific encryption.**
4
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#call-the-increment-function-with-the-encrypted-argument)
Call the `increment()` function with the encrypted argument
Now that we have an encrypted argument, we can call the `increment()` function with it.
Below, you’ll notice that the updated `increment()` function now takes **two arguments instead of one.**
This is because the FHEVM requires both:
1. The `externalEuint32` — the encrypted value itself
2. An accompanying **Zero-Knowledge Proof of Knowledge** (`inputProof`) — which verifies that the encrypted input is securely bound to:
* the caller (Alice, the transaction signer), and
* the target smart contract (where `increment()` is being executed)
This ensures that the encrypted value cannot be reused in a different context or by a different user, preserving **confidentiality and integrity.**
Replace :
Copy
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
with the following:
Copy
const tx = await fheCounterContract.connect(signers.alice).increment(encryptedOne.handles[0], encryptedOne.inputProof);
await tx.wait();
At this point the counter has been successfully incremented by 1 using a **Fully Homomorphic Encryption (FHE)**. In the next step, we will retrieve the updated encrypted counter value and decrypt it locally. But before we move on, let’s quickly run the tests to make sure everything is working correctly.
* * *
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
FHECounter
FHECounter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
✔ encrypted count should be uninitialized after deployment
✔ increment the counter by 1
3 passing (7ms)
5
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#call-the-getcount-function-and-decrypt-the-value)
Call the `getCount()` function and Decrypt the value
Now that the counter has been incremented using an encrypted input, it's time to **read the updated encrypted value** from the smart contract and **decrypt it** using the `userDecryptEuint` function provided by the FHEVM Hardhat Plugin.
The `userDecryptEuint` function takes four parameters:
1. **FhevmType**: The integer type of the FHE-encrypted value. In this case, we're using `FhevmType.euint32` because the counter is a `uint32`.
2. **Encrypted handle**: A 32-byte FHEVM handle representing the encrypted value you want to decrypt.
3. **Smart contract address**: The address of the contract that has permission to access the encrypted handle.
4. **User signer**: The signer (e.g., signers.alice) who has permission to access the handle.
Note: Permissions to access the FHEVM handle are set on-chain using the `FHE.allow()` Solidity function (see FHECounter.sol).
Replace :
Copy
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
with the following:
Copy
const encryptedCountAfterInc = await fheCounterContract.getCount();
const clearCountAfterInc = await fhevm.userDecryptEuint(
FhevmType.euint32,
encryptedCountAfterInc,
fheCounterContractAddress,
signers.alice,
);
expect(clearCountAfterInc).to.eq(clearCountBeforeInc + clearOne);
* * *
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
FHECounter
FHECounter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
✔ encrypted count should be uninitialized after deployment
✔ increment the counter by 1
3 passing (7ms)
6
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#call-the-contract-decrement-function)
Call the contract `decrement()` function
Similarly to the previous test, we’ll now call the `decrement()` function using an encrypted input.
Replace :
Copy
// it("decrement the counter by 1", async function () {
// // First increment, count becomes 1
// let tx = await counterContract.connect(signers.alice).increment();
// await tx.wait();
// // Then decrement, count goes back to 0
// tx = await counterContract.connect(signers.alice).decrement(1);
// await tx.wait();
// const count = await counterContract.getCount();
// expect(count).to.eq(0);
// });
with the following:
Copy
it("decrement the counter by 1", async function () {
// Encrypt constant 1 as a euint32
const clearOne = 1;
const encryptedOne = await fhevm
.createEncryptedInput(fheCounterContractAddress, signers.alice.address)
.add32(clearOne)
.encrypt();
// First increment by 1, count becomes 1
let tx = await fheCounterContract.connect(signers.alice).increment(encryptedOne.handles[0], encryptedOne.inputProof);
await tx.wait();
// Then decrement by 1, count goes back to 0
tx = await fheCounterContract.connect(signers.alice).decrement(encryptedOne.handles[0], encryptedOne.inputProof);
await tx.wait();
const encryptedCountAfterDec = await fheCounterContract.getCount();
const clearCountAfterDec = await fhevm.userDecryptEuint(
FhevmType.euint32,
encryptedCountAfterDec,
fheCounterContractAddress,
signers.alice,
);
expect(clearCountAfterDec).to.eq(0);
});
* * *
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
FHECounter
FHECounter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
✔ encrypted count should be uninitialized after deployment
✔ increment the counter by 1
✔ decrement the counter by 1
4 passing (7ms)
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#congratulations-youve-completed-the-full-tutorial)
Congratulations! You've completed the full tutorial.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
You have successfully written and tested your FHEVM-based counter smart contract. By now, your project should include the following files:
* [`contracts/FHECounter.sol`](https://docs.zama.ai/protocol/examples#tab-fhecounter.sol)
— your Solidity smart contract
* [`test/FHECounter.ts`](https://docs.zama.ai/protocol/examples#tab-fhecounter.ts)
— your Hardhat test suite written in TypeScript
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/test_the_fhevm_contract#next-step)
Next step
--------------------------------------------------------------------------------------------------------------------------------------------
If you would like to deploy your project on the testnet, or learn more about using FHEVM Hardhat Plugin, head to [Deploy contracts and run tests](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/run_test)
.
[Previous3\. Turn it into FHEVM](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm)
[NextConfiguration](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/configure)
Last updated 1 month ago
---
# Operations on encrypted types | Protocol
This document outlines the operations supported on encrypted types in the `FHE` library, enabling arithmetic, bitwise, comparison, and more on Fully Homomorphic Encryption (FHE) ciphertexts.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations#arithmetic-operations)
Arithmetic operations
---------------------------------------------------------------------------------------------------------------------------------
The following arithmetic operations are supported for encrypted integers (`euintX`):
Name
Function name
Symbol
Type
Add
`FHE.add`
`+`
Binary
Subtract
`FHE.sub`
`-`
Binary
Multiply
`FHE.mul`
`*`
Binary
Divide (plaintext divisor)
`FHE.div`
Binary
Reminder (plaintext divisor)
`FHE.rem`
Binary
Negation
`FHE.neg`
`-`
Unary
Min
`FHE.min`
Binary
Max
`FHE.max`
Binary
Division (FHE.div) and remainder (FHE.rem) operations are currently supported only with plaintext divisors.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations#bitwise-operations)
Bitwise operations
---------------------------------------------------------------------------------------------------------------------------
The FHE library also supports bitwise operations, including shifts and rotations:
Name
Function name
Symbol
Type
Bitwise AND
`FHE.and`
`&`
Binary
Bitwise OR
`FHE.or`
`|`
Binary
Bitwise XOR
`FHE.xor`
`^`
Binary
Bitwise NOT
`FHE.not`
`~`
Unary
Shift Right
`FHE.shr`
Binary
Shift Left
`FHE.shl`
Binary
Rotate Right
`FHE.rotr`
Binary
Rotate Left
`FHE.rotl`
Binary
The shift operators `FHE.shr` and `FHE.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, `FHE.shr(euint64 x, 70)` is equivalent to `FHE.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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations#comparison-operations)
Comparison operations
---------------------------------------------------------------------------------------------------------------------------------
Encrypted integers can be compared using the following functions:
Name
Function name
Symbol
Type
Equal
`FHE.eq`
Binary
Not equal
`FHE.ne`
Binary
Greater than or equal
`FHE.ge`
Binary
Greater than
`FHE.gt`
Binary
Less than or equal
`FHE.le`
Binary
Less than
`FHE.lt`
Binary
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations#ternary-operation)
Ternary operation
-------------------------------------------------------------------------------------------------------------------------
The `FHE.select` function is a ternary operation that selects one of two encrypted values based on an encrypted condition:
Name
Function name
Symbol
Type
Select
`FHE.select`
Ternary
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations#random-operations)
Random operations
-------------------------------------------------------------------------------------------------------------------------
You can generate cryptographically secure random numbers fully on-chain:
**Name**
**Function Name**
**Symbol**
**Type**
Random Unsigned Integer
`FHE.randEuintX()`
Random
For more details, refer to the [Random Encrypted Numbers](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/random)
document.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations#best-practices)
Best Practices
-------------------------------------------------------------------------------------------------------------------
Here are some best practices to follow when using encrypted operations in your smart contracts:
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations#use-the-appropriate-encrypted-type-size)
Use the appropriate encrypted type size
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:
Copy
// Bad: Using euint256 for small numbers wastes gas
euint64 age = FHE.asEuint128(25); // age will never exceed 255
euint64 percentage = FHE.asEuint128(75); // percentage is 0-100
✅ Instead, use the smallest appropriate type:
Copy
// Good: Using appropriate sized types
euint8 age = FHE.asEuint8(25); // age fits in 8 bits
euint8 percentage = FHE.asEuint8(75); // percentage fits in 8 bits
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations#use-scalar-operands-when-possible-to-save-gas)
Use scalar operands when possible to save gas
Some FHE 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:
Copy
euint32 x;
...
x = FHE.add(x,FHE.asEuint(42));
✅ Than this one:
Copy
euint32 x;
// ...
x = FHE.add(x,42);
Despite both leading to the same encrypted result!
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations#beware-of-overflows-of-fhe-arithmetic-operators)
Beware of overflows of FHE arithmetic operators
FHE arithmetic operators can overflow. Do not forget to take into account such a possibility when implementing FHEVM smart contracts.
❌ For example, if you wanted to create a mint function for an encrypted ERC20 token with an encrypted `totalSupply` state variable, this code is vulnerable to overflows:
Copy
function mint(externalEuint32 encryptedAmount, bytes calldata inputProof) public {
euint32 mintedAmount = FHE.asEuint32(encryptedAmount, inputProof);
totalSupply = FHE.add(totalSupply, mintedAmount);
balances[msg.sender] = FHE.add(balances[msg.sender], mintedAmount);
FHE.allowThis(balances[msg.sender]);
FHE.allow(balances[msg.sender], msg.sender);
}
✅ But you can fix this issue by using `FHE.select` to cancel the mint in case of an overflow:
Copy
function mint(externalEuint32 encryptedAmount, bytes calldata inputProof) public {
euint32 mintedAmount = FHE.asEuint32(encryptedAmount, inputProof);
euint32 tempTotalSupply = FHE.add(totalSupply, mintedAmount);
ebool isOverflow = FHE.lt(tempTotalSupply, totalSupply);
totalSupply = FHE.select(isOverflow, totalSupply, tempTotalSupply);
euint32 tempBalanceOf = FHE.add(balances[msg.sender], mintedAmount);
balances[msg.sender] = FHE.select(isOverflow, balances[msg.sender], tempBalanceOf);
FHE.allowThis(balances[msg.sender]);
FHE.allow(balances[msg.sender], msg.sender);
}
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.
[PreviousSupported types](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/types)
[NextCasting and trivial encryption](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/casting)
Last updated 1 month ago
---
# ACL examples | Protocol
This page provides detailed instructions and examples on how to use and implement the ACL (Access Control List) in FHEVM. For an overview of ACL concepts and their importance, refer to the [access control list (ACL) overview](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl)
.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#controlling-access-permanent-and-transient-allowances)
Controlling access: permanent and transient allowances
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The ACL system allows you to define two types of permissions for accessing ciphertexts:
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#permanent-allowance)
Permanent allowance
* **Function**: `FHE.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.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#alternative-solidity-syntax)
Alternative Solidity syntax
You can also use method-chaining syntax for granting allowances since FHE is a Solidity library.
Copy
using FHE for *;
ciphertext.allow(address1).allow(address2);
This is equivalent to calling `FHE.allow(ciphertext, address1)` followed by `FHE.allow(ciphertext, address2)`.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#transient-allowance)
Transient allowance
* **Function**: `FHE.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.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#alternative-solidity-syntax-1)
Alternative Solidity syntax
Method chaining is also available for transient allowances since FHE is a Solidity library.
Copy
using FHE for *;
ciphertext.allowTransient(address1).allowTransient(address2);
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#syntactic-sugar)
Syntactic sugar
* **Function**: `FHE.allowThis(ciphertext)`
* **Equivalent To**: `FHE.allow(ciphertext, address(this))`
* **Purpose**: Simplifies granting permanent access to the current contract for managing ciphertexts.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#alternative-solidity-syntax-2)
Alternative Solidity syntax
You can also use method-chaining syntax for allowThis since FHE is a Solidity library.
Copy
using FHE for *;
ciphertext.allowThis();
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#make-publicly-decryptable)
Make publicly decryptable
To make a ciphertext publicly decryptable, you can use the `FHE.makePubliclyDecryptable(ciphertext)` function. This grants decryption rights to anyone, which is useful for scenarios where the encrypted value should be accessible by all.
Copy
// Grant public decryption right to a ciphertext
FHE.makePubliclyDecryptable(ciphertext);
// Or using method syntax:
ciphertext.makePubliclyDecryptable();
* **Function**: `FHE.makePubliclyDecryptable(ciphertext)`
* **Purpose**: Makes the ciphertext decryptable by anyone.
* **Use Case**: When you want to publish encrypted results or data.
> You can combine multiple allowance methods (such as `.allow()`, `.allowThis()`, `.allowTransient()`) directly on ciphertext objects to grant access to several addresses or contracts in a single, fluent statement.
>
> **Example**
>
> Copy
>
> // Grant transient access to one address and permanent access to another address
> ciphertext.allowTransient(address1).allow(address2);
>
> // Grant permanent access to the current contract and another address
> ciphertext.allowThis().allow(address1);
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#best-practices)
Best practices
-------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#verifying-sender-access)
Verifying sender access
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.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#example-scenario-confidential-erc20-attack)
Example scenario: Confidential ERC20 attack
Consider an **Confidential ERC20 token**. An attacker controlling two accounts, **Account A** and **Account B**, with 100 tokens in Account A, could exploit the system as follows:
1. The attacker attempts to send the target user's encrypted balance from **Account A** to **Account B**.
2. 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 `FHE.isSenderAllowed()` function to verify that the sender has legitimate access to the encrypted amount being transferred.
* * *
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#example-secure-verification)
Example: secure verification
Copy
function transfer(address to, euint64 encryptedAmount, bytes calldata inputProof) public {
// Ensure the sender is authorized to access the encrypted amount
require(FHE.isSenderAllowed(encryptedAmount), "Unauthorized access to encrypted amount.");
// Proceed with further logic
euint64 amount = FHE.asEuint64(encryptedAmount);
...
}
By enforcing this check, you can safeguard against inference attacks and ensure that encrypted values are only manipulated by authorized entities.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#acl-for-user-decryption)
ACL for user decryption
-------------------------------------------------------------------------------------------------------------------------------------------
If a ciphertext can be decrypt by a user, explicit access must be granted to them. Additionally, the user decryption mechanism requires the signature of a public key associated with the contract address. Therefore, a value that needs to be decrypted must be explicitly authorized for both the user and the contract.
Due to the user decryption mechanism, a user signs a public key associated with a specific contract; therefore, the ciphertext also needs to be allowed for the contract.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/acl_examples#example-secure-transfer-in-confidentialerc20)
Example: Secure Transfer in ConfidentialERC20
Copy
function transfer(address to, euint64 encryptedAmount) public {
require(FHE.isSenderAllowed(encryptedAmount), "The caller is not authorized to access this encrypted amount.");
euint64 amount = FHE.asEuint64(encryptedAmount);
ebool canTransfer = FHE.le(amount, balances[msg.sender]);
euint64 newBalanceTo = FHE.add(balances[to], FHE.select(canTransfer, amount, FHE.asEuint64(0)));
balances[to] = newBalanceTo;
// Allow this new balance for both the contract and the owner.
FHE.allowThis(newBalanceTo);
FHE.allow(newBalanceTo, to);
euint64 newBalanceFrom = FHE.sub(balances[from], FHE.select(canTransfer, amount, FHE.asEuint64(0)));
balances[from] = newBalanceFrom;
// Allow this new balance for both the contract and the owner.
FHE.allowThis(newBalanceFrom);
FHE.allow(newBalanceFrom, from);
}
By understanding how to grant and verify permissions, you can effectively manage access to encrypted data in your FHEVM smart contracts. For additional context, see the [ACL overview](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl)
.
[PreviousAccess Control List](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl)
[NextReorgs handling](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl/reorgs_handling)
Last updated 1 month ago
---
# 2. Write a simple contract | Protocol
In this tutorial, you'll write and test a simple regular Solidity smart contract within the FHEVM Hardhat template to get familiar with Hardhat workflow.
In the [next tutorial](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm)
, you'll learn how to convert this contract into an FHEVM contract.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#prerequiste)
Prerequiste
------------------------------------------------------------------------------------------------------------------------------------------------
* [Set up your Hardhat envrionment](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup)
.
* Make sure that you Hardhat project is clean and ready to start. See the instructions [here](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/setup#rest-set-the-hardhat-envrionment)
.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#what-youll-learn)
What you'll learn
-----------------------------------------------------------------------------------------------------------------------------------------------------------
By the end of this tutorial, you will learn to:
* Write a minimal Solidity contract using Hardhat.
* Test the contract using TypeScript and Hardhat’s testing framework.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#write-a-simple-contract)
Write a simple contract
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#create-counter.sol)
Create `Counter.sol`
Go to your project's `contracts` directory:
Copy
cd /contracts
From there, create a new file named `Counter.sol` and copy/paste the following Solidity code in it.
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title A simple counter contract
contract Counter {
uint32 private _count;
/// @notice Returns the current count
function getCount() external view returns (uint32) {
return _count;
}
/// @notice Increments the counter by a specific value
function increment(uint32 value) external {
_count += value;
}
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
}
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#compile-counter.sol)
Compile `Counter.sol`
From your project's root directory, run:
Copy
npx hardhat compile
Great! Your Smart Contract is now compiled.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#set-up-the-testing-environment)
Set up the testing environment
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#create-a-test-script-test-counter.ts)
Create a test script `test/Counter.ts`
Go to your project's `test` directory
Copy
cd /test
From there, create a new file named `Counter.ts` and copy/paste the following Typescript skeleton code in it.
Copy
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { ethers } from "hardhat";
describe("Counter", function () {
it("empty test", async function () {
console.log("Cool! The test basic skeleton is running!");
});
});
The file contains the following:
* all the required `import` statements we will need during the various tests
* The `chai` basic statements to run a first empty test named `empty test`
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#run-the-test-test-counter.ts)
Run the test `test/Counter.ts`
From your project's root directory, run:
Copy
npx hardhat test
Output:
Copy
Counter
Cool! The test basic skeleton is running!
✔ empty test
1 passing (1ms)
Great! Your Hardhat test environment is properly setup.
3
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#set-up-the-test-signers)
Set up the test signers
Before interacting with smart contracts in Hardhat tests, we need to initialize signers.
In the context of Ethereum development, a signer represents an entity (usually a wallet) that can send transactions and sign messages. In Hardhat, `ethers.getSigners()` returns a list of pre-funded test accounts.
We’ll define three named signers for convenience:
* `owner` — the deployer of the contract
* `alice` and `bob` — additional simulated users
**Replace the contents of** `**test/Counter.ts**` **with the following:**
Copy
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { ethers } from "hardhat";
type Signers = {
owner: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
describe("Counter", function () {
let signers: Signers;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
it("should work", async function () {
console.log(`address of user owner is ${signers.owner.address}`);
console.log(`address of user alice is ${signers.alice.address}`);
console.log(`address of user bob is ${signers.bob.address}`);
});
});
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
address of user owner is 0x37AC010c1c566696326813b840319B58Bb5840E4
address of user alice is 0xD9F9298BbcD72843586e7E08DAe577E3a0aC8866
address of user bob is 0x3f0CdAe6ebd93F9F776BCBB7da1D42180cC8fcC1
✔ should work
1 passing (2ms)
4
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#set-up-testing-instance)
Set up testing instance
Now that we have our signers set up, we can deploy the smart contract.
To ensure isolated and deterministic tests, we should deploy a fresh instance of `Counter.sol` before each test. This avoids any side effects from previous tests.
The standard approach is to define a `deployFixture()` function that handles contract deployment.
Copy
async function deployFixture() {
const factory = (await ethers.getContractFactory("Counter")) as Counter__factory;
const counterContract = (await factory.deploy()) as Counter;
const counterContractAddress = await counterContract.getAddress();
return { counterContract, counterContractAddress };
}
To run this setup before each test case, call `deployFixture()` inside a `beforeEach` block:
Copy
beforeEach(async () => {
({ counterContract, counterContractAddress } = await deployFixture());
});
This ensures each test runs with a clean, independent contract instance.
Let's put it together. Now your`test/Counter.ts` should look like the following:
Copy
import { Counter, Counter__factory } from "../types";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
type Signers = {
deployer: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
async function deployFixture() {
const factory = (await ethers.getContractFactory("Counter")) as Counter__factory;
const counterContract = (await factory.deploy()) as Counter;
const counterContractAddress = await counterContract.getAddress();
return { counterContract, counterContractAddress };
}
describe("Counter", function () {
let signers: Signers;
let counterContract: Counter;
let counterContractAddress: Counter;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { deployer: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
beforeEach(async () => {
// Deploy a new instance of the contract before each test
({ counterContract, counterContractAddress } = await deployFixture());
});
it("should be deployed", async function () {
console.log(`Counter has been deployed at address ${counterContractAddress}`);
// Test the deployed address is valid
expect(ethers.isAddress(counterContractAddress)).to.eq(true);
});
});
**Run the test:**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output:**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
1 passing (7ms)
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#test-functions)
Test functions
------------------------------------------------------------------------------------------------------------------------------------------------------
Now everything is up and running, you can start testing your contract functions.
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#call-the-contract-getcount-view-function)
Call the contract `getCount()` view function
Everything is up and running, we can now call the `Counter.sol` view function `getCount()` !
Just below the test block `it("should be deployed", async function () {...}`,
add the following unit test:
Copy
it("count should be zero after deployment", async function () {
const count = await counterContract.getCount();
console.log(`Counter.getCount() === ${count}`);
// Expect initial count to be 0 after deployment
expect(count).to.eq(0);
});
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
Counter.getCount() === 0
✔ count should be zero after deployment
1 passing (7ms)
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#call-the-contract-increment-transaction-function)
Call the contract `increment()` transaction function
Just below the test block `it("count should be zero after deployment", async function () {...}`, add the following test block:
Copy
it("increment the counter by 1", async function () {
const countBeforeInc = await counterContract.getCount();
const tx = await counterContract.connect(signers.alice).increment(1);
await tx.wait();
const countAfterInc = await counterContract.getCount();
expect(countAfterInc).to.eq(countBeforeInc + 1n);
});
**Remarks:**
* `increment()` is a transactional function that modifies the blockchain state.
* It must be signed by a user — here we use `alice`.
* `await wait()` to wait for the transaction to mined.
* The test compares the counter before and after the transaction to ensure it incremented as expected.
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
Counter.getCount() === 0
✔ count should be zero after deployment
✔ increment the counter by 1
2 passing (12ms)
3
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#call-the-contract-decrement-transaction-function)
Call the contract `decrement()` transaction function
Just below the test block `it("increment the counter by 1", async function () {...}`,
add the following test block:
Copy
it("decrement the counter by 1", async function () {
// First increment, count becomes 1
let tx = await counterContract.connect(signers.alice).increment(1);
await tx.wait();
// Then decrement, count goes back to 0
tx = await counterContract.connect(signers.alice).decrement(1);
await tx.wait();
const count = await counterContract.getCount();
expect(count).to.eq(0);
});
* * *
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
Counter.getCount() === 0
✔ count should be zero after deployment
✔ increment the counter by 1
✔ decrement the counter by 1
2 passing (12ms)
Now you have succesefully write and test your counter contract. You should have the following files in your project:
* [`contracts/Counter.sol`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#counter.sol)
— your Solidity smart contract
* [`test/Counter.ts`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#counter.ts)
— your Hardhat test suite written in TypeScript
These files form the foundation of a basic Hardhat-based smart contract project.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/write_a_simple_contract#next-step)
Next step
--------------------------------------------------------------------------------------------------------------------------------------------
Now that you've written and tested a basic Solidity smart contract, you're ready to take the next step.
In the [next tutorial](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm)
, we’ll transform this standard `Counter.sol` contract into `FHECounter.sol`, a trivial FHEVM-compatible version — allowing the counter value to be stored and updated using trivial fully homomorphic encryption.
[PreviousQuick start tutorial](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial)
[Next3\. Turn it into FHEVM](https://docs.zama.ai/protocol/solidity-guides/v0.7/getting-started/quick-start-tutorial/turn_it_into_fhevm)
Last updated 3 months ago
---
# Public Decrypt multiple values | Protocol
This example demonstrates the FHE public decryption mechanism with multiple value.
Public decryption is a mechanism that makes encrypted values visible to everyone once decrypted. Unlike user decryption where values remain private to authorized users, public decryption makes the data permanently visible to all participants. The public decryption call occurs onchain through smart contracts, making the decrypted value part of the blockchain's public state.
To run this example correctly, make sure the files are placed in the following directories:
* `.sol` file → `/contracts/`
* `.ts` file → `/test/`
This ensures Hardhat can compile and test your contracts as expected.
PublicDecryptMultipleValues.sol
[](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-multiple-values#tab-publicdecryptmultiplevalues.sol)
PublicDecryptMultipleValues.ts
[](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-multiple-values#tab-publicdecryptmultiplevalues.ts)
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { FHE, ebool, euint32, euint64 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract PublicDecryptMultipleValues is SepoliaConfig {
ebool private _encryptedBool; // = 0 (uninitialized)
euint32 private _encryptedUint32; // = 0 (uninitialized)
euint64 private _encryptedUint64; // = 0 (uninitialized)
bool private _clearBool; // = 0 (uninitialized)
uint32 private _clearUint32; // = 0 (uninitialized)
uint64 private _clearUint64; // = 0 (uninitialized)
// solhint-disable-next-line no-empty-blocks
constructor() {}
function initialize(bool a, uint32 b, uint64 c) external {
// Compute 3 trivial FHE formulas
// _encryptedBool = a ^ false
_encryptedBool = FHE.xor(FHE.asEbool(a), FHE.asEbool(false));
// _encryptedUint32 = b + 1
_encryptedUint32 = FHE.add(FHE.asEuint32(b), FHE.asEuint32(1));
// _encryptedUint64 = c + 1
_encryptedUint64 = FHE.add(FHE.asEuint64(c), FHE.asEuint64(1));
// see `DecryptSingleValueInSolidity.sol` for more detailed explanations
// about FHE permissions and asynchronous public decryption requests.
FHE.allowThis(_encryptedBool);
FHE.allowThis(_encryptedUint32);
FHE.allowThis(_encryptedUint64);
}
function requestDecryptMultipleValues() external {
// To public decrypt multiple values, we must construct an array of the encrypted values
// we want to public decrypt.
//
// ⚠️ Warning: The order of values in the array is critical!
// The FHEVM backend will pass the public decrypted values to the callback function
// in the exact same order they appear in this array.
// Therefore, the order must match the parameter declaration in the callback.
bytes32[] memory cypherTexts = new bytes32[](3);
cypherTexts[0] = FHE.toBytes32(_encryptedBool);
cypherTexts[1] = FHE.toBytes32(_encryptedUint32);
cypherTexts[2] = FHE.toBytes32(_encryptedUint64);
FHE.requestDecryption(
// the list of encrypte values we want to public decrypt
cypherTexts,
// Selector of the Solidity callback function that the FHEVM backend will call with
// the decrypted (clear) values as arguments
this.callbackDecryptMultipleValues.selector
);
}
// ⚠️ WARNING: The `cleartexts` argument is an ABI encoding of the decrypted values associated
// to the handles (using `abi.encode`).
//
// These values' types must match exactly! Mismatched types—such as using `uint32 decryptedUint64`
// instead of the correct `uint64 decryptedUint64` can cause subtle and hard-to-detect bugs,
// especially for developers new to the FHEVM stack.
// Always ensure that the parameter types align with the expected decrypted value types.
//
// !DOUBLE-CHECK!
function callbackDecryptMultipleValues(
uint256 requestID,
bytes memory cleartexts,
bytes memory decryptionProof
) external {
// ⚠️ Don't forget the signature checks! (see `DecryptSingleValueInSolidity.sol` for detailed explanations)
// The signatures are included in the `decryptionProof` parameter.
FHE.checkSignatures(requestID, cleartexts, decryptionProof);
(bool decryptedBool, uint32 decryptedUint32, uint64 decryptedUint64) = abi.decode(cleartexts, (bool, uint32, uint64));
_clearBool = decryptedBool;
_clearUint32 = decryptedUint32;
_clearUint64 = decryptedUint64;
}
function clearBool() public view returns (bool) {
return _clearBool;
}
function clearUint32() public view returns (uint32) {
return _clearUint32;
}
function clearUint64() public view returns (uint64) {
return _clearUint64;
}
}
Copy
import { PublicDecryptMultipleValues, PublicDecryptMultipleValues__factory } from "../../../types";
import type { Signers } from "../../types";
import { HardhatFhevmRuntimeEnvironment } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
import * as hre from "hardhat";
async function deployFixture() {
// Contracts are deployed using the first signer/account by default
const factory = (await ethers.getContractFactory(
"PublicDecryptMultipleValues",
)) as PublicDecryptMultipleValues__factory;
const publicDecryptMultipleValues = (await factory.deploy()) as PublicDecryptMultipleValues;
const publicDecryptMultipleValues_address = await publicDecryptMultipleValues.getAddress();
return { publicDecryptMultipleValues, publicDecryptMultipleValues_address };
}
/**
* This trivial example demonstrates the FHE public decryption mechanism
* and highlights a common pitfall developers may encounter.
*/
describe("PublicDecryptMultipleValues", function () {
let contract: PublicDecryptMultipleValues;
let signers: Signers;
before(async function () {
// Check whether the tests are running against an FHEVM mock environment
if (!hre.fhevm.isMock) {
throw new Error(`This hardhat test suite cannot run on Sepolia Testnet`);
}
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1] };
});
beforeEach(async function () {
// Deploy a new contract each time we run a new test
const deployment = await deployFixture();
contract = deployment.publicDecryptMultipleValues;
});
// ✅ Test should succeed
it("public decryption should succeed", async function () {
// For simplicity, we create 3 trivialy encrypted values onchain.
let tx = await contract.connect(signers.alice).initialize(true, 123456, 78901234567);
await tx.wait();
tx = await contract.requestDecryptMultipleValues();
await tx.wait();
// We use the FHEVM Hardhat plugin to simulate the asynchronous onchain
// public decryption
const fhevm: HardhatFhevmRuntimeEnvironment = hre.fhevm;
// Use the built-in `awaitDecryptionOracle` helper to wait for the FHEVM public decryption oracle
// to complete all pending Solidity public decryption requests.
await fhevm.awaitDecryptionOracle();
// At this point, the Solidity callback should have been invoked by the FHEVM backend.
// We can now retrieve the 3 publicly decrypted (clear) values.
const clearBool = await contract.clearBool();
const clearUint32 = await contract.clearUint32();
const clearUint64 = await contract.clearUint64();
expect(clearBool).to.equal(true);
expect(clearUint32).to.equal(123456 + 1);
expect(clearUint64).to.equal(78901234567 + 1);
});
});
[PreviousPublic Decrypt single value](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-public-decrypt-single-value)
[NextLibrary installation and overview](https://docs.zama.ai/protocol/examples/openzeppelin-confidential-contracts/openzeppelin)
Last updated 21 days ago
---
# Hardhat plugin | Protocol
This section will guide you through writing and testing FHEVM smart contracts in Solidity using [Hardhat](https://hardhat.org/)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat#the-fhevm-hardhat-plugin)
The FHEVM Hardhat Plugin
To write FHEVM smart contracts using Hardhat, you need to install the [FHEVM Hardhat Plugin](https://www.npmjs.com/package/@fhevm/hardhat-plugin)
in your Hardhat project.
This plugin enables you to develop, test, and interact with FHEVM contracts right out of the box.
It extends Hardhat’s functionality with a complete FHEVM API that allows you:
* Encrypt data
* Decrypt data
* Run tests using various FHEVM execution modes
* Write FHEVM-enabled Hardhat Tasks
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat#where-to-go-next)
Where to go next
🟨 Go to [**Setup Hardhat**](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup)
to initialize your FHEVM Hardhat project.
🟨 Go to [**Write FHEVM Tests in Hardhat**](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_test)
for details on writing tests of FHEVM smart contracts using Hardhat.
🟨 Go to [**Run FHEVM Tests in Hardhat**](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/run_test)
to learn how to execute those tests in different FHEVM environments.
🟨 Go to [**Write FHEVM Hardhat Task**](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task)
to learn how to write your own custom FHEVM Hardhat task.
[PreviousDecryption](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle)
[NextWrite FHEVM tests in Hardhat](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_test)
Last updated 1 month ago
---
# Decryption | Protocol
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.
Decryption is essential in two primary cases:
1. **Smart contract logic**: A contract requires plaintext values for computations or decision-making.
2. **User interaction**: Plaintext data needs to be revealed to all users, such as revealing the decision of the vote.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle#overview)
Overview
---------------------------------------------------------------------------------------------------
Decryption in FHEVM is an asynchronous process that involves the Relayer and Key Management System (KMS). Here’s an example of how to safely request decryption in a contract.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle#example-asynchronous-decryption-in-a-contract)
Example: asynchronous decryption in a contract
Copy
pragma solidity ^0.8.24;
import "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract TestAsyncDecrypt is SepoliaConfig {
ebool xBool;
bool public yBool;
bool isDecryptionPending;
uint256 latestRequestId;
constructor() {
xBool = FHE.asEbool(true);
FHE.allowThis(xBool);
}
function requestBool() public {
require(!isDecryptionPending, "Decryption is in progress");
bytes32[] memory cts = new bytes32[](1);
cts[0] = FHE.toBytes32(xBool);
uint256 latestRequestId = FHE.requestDecryption(cts, this.myCustomCallback.selector);
/// @dev This prevents sending multiple requests before the first callback was sent.
isDecryptionPending = true;
}
function myCustomCallback(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) public returns (bool) {
/// @dev This check is used to verify that the request id is the expected one.
require(requestId == latestRequestId, "Invalid requestId");
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
(bool decryptedInput) = abi.decode(cleartexts, (bool));
yBool = decryptedInput;
isDecryptionPending = false;
return yBool;
}
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle#decryption-in-depth)
Decryption in depth
-------------------------------------------------------------------------------------------------------------------------
This document provides a detailed guide on implementing decryption in your smart contracts using the `DecryptionOracle` in fhevm. It covers the setup, usage of the `FHE.requestDecryption` function, and testing with Hardhat.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle#decryptionoracle-setup)
`DecryptionOracle` setup
---------------------------------------------------------------------------------------------------------------------------------
The `DecryptionOracle` is pre-deployed on the FHEVM testnet. It uses a default relayer account specified in the `.env` file.
Anyone can fulfill decryption requests but it is essential to add signature verification (and to include a logic to invalidate the replay of decryption requests). The role of the `DecryptionOracle` contract is to independently verify the KMS signature during execution. This ensures that the relayers cannot manipulate or send fraudulent decryption results, even if compromised.
There are two functions to consider: `requestDecryption` and `checkSignatures`.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle#fhe.requestdecryption-function)
`FHE.requestDecryption` function
You can call the function `FHE.requestDecryption` as such:
Copy
function requestDecryption(
bytes32[] calldata ctsHandles,
bytes4 callbackSelector
) external payable returns (uint256 requestId);
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle#function-arguments)
Function arguments
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`, `euint8`, `euint16`, `euint32`, `euint64` or `eaddress`.
`ctsHandles` is the array of ciphertexts that are requested to be decrypted. 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 once the relayer fulfils the decryption request.
Copy
function [callbackName](uint256 requestID, bytes memory cleartexts, bytes memory decryptionProof) external;
`cleartexts` is the bytes array corresponding to the ABI encoding of all requested decrypted values. Each of these decrypted values' type should be a native Solidity type corresponding to the original ciphertext type, following this table of conventions:
Ciphertext type
Decrypted type
ebool
bool
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 `cleartexts` is an ABI encoded byte array of the results of the decryption of the `ct` array values, i.e their number should be the size of the `ct` array. `decryptionProof` is a byte array containing the KMS signatures and extra data.
`msgValue` is the value in native tokens to be sent to the calling contract during fulfillment, i.e when the callback will be called with the results of decryption.
Notice that the callback should always verify the signatures and implement a replay protection mechanism (see below).
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle#fhe.checksignatures-function)
`FHE.checkSignatures` function
You can call the function `FHE.checkSignatures` as such:
Copy
function checkSignatures(uint256 requestId, bytes memory cleartexts, bytes[] memory signatures);
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle#function-arguments-1)
Function arguments
* `requestID`, is the value that was returned in the `requestDecryption` function.
* `cleartexts`, is an ABI encoding of the decrypted values associated to the handles (using `abi.encode`). This can contain one or multiple values, depending on the number of handles requested in the `requestDecryption` function. Each of these values' type must match the type of the corresponding handle.
* `decryptionProof`, is a byte array containing the KMS signatures and extra data.
This function reverts if the signatures are invalid.
[PreviousError handling](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/error_handling)
[NextHardhat plugin](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat)
Last updated 1 month ago
---
# 2. Write a simple contract | Protocol
In this tutorial, you'll write and test a simple regular Solidity smart contract within the FHEVM Hardhat template to get familiar with Hardhat workflow.
In the [next tutorial](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm)
, you'll learn how to convert this contract into an FHEVM contract.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#prerequisite)
Prerequisite
--------------------------------------------------------------------------------------------------------------------------------------------------
* [Set up your Hardhat envrionment](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup)
.
* Make sure that you Hardhat project is clean and ready to start. See the instructions [here](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/setup#rest-set-the-hardhat-envrionment)
.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#what-youll-learn)
What you'll learn
-----------------------------------------------------------------------------------------------------------------------------------------------------------
By the end of this tutorial, you will learn to:
* Write a minimal Solidity contract using Hardhat.
* Test the contract using TypeScript and Hardhat’s testing framework.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#write-a-simple-contract)
Write a simple contract
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#create-counter.sol)
Create `Counter.sol`
Go to your project's `contracts` directory:
Copy
cd /contracts
From there, create a new file named `Counter.sol` and copy/paste the following Solidity code in it.
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title A simple counter contract
contract Counter {
uint32 private _count;
/// @notice Returns the current count
function getCount() external view returns (uint32) {
return _count;
}
/// @notice Increments the counter by a specific value
function increment(uint32 value) external {
_count += value;
}
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
}
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#compile-counter.sol)
Compile `Counter.sol`
From your project's root directory, run:
Copy
npx hardhat compile
Great! Your Smart Contract is now compiled.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#set-up-the-testing-environment)
Set up the testing environment
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#create-a-test-script-test-counter.ts)
Create a test script `test/Counter.ts`
Go to your project's `test` directory
Copy
cd /test
From there, create a new file named `Counter.ts` and copy/paste the following Typescript skeleton code in it.
Copy
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { ethers } from "hardhat";
describe("Counter", function () {
it("empty test", async function () {
console.log("Cool! The test basic skeleton is running!");
});
});
The file contains the following:
* all the required `import` statements we will need during the various tests
* The `chai` basic statements to run a first empty test named `empty test`
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#run-the-test-test-counter.ts)
Run the test `test/Counter.ts`
From your project's root directory, run:
Copy
npx hardhat test
Output:
Copy
Counter
Cool! The test basic skeleton is running!
✔ empty test
1 passing (1ms)
Great! Your Hardhat test environment is properly setup.
3
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#set-up-the-test-signers)
Set up the test signers
Before interacting with smart contracts in Hardhat tests, we need to initialize signers.
In the context of Ethereum development, a signer represents an entity (usually a wallet) that can send transactions and sign messages. In Hardhat, `ethers.getSigners()` returns a list of pre-funded test accounts.
We’ll define three named signers for convenience:
* `owner` — the deployer of the contract
* `alice` and `bob` — additional simulated users
**Replace the contents of** `**test/Counter.ts**` **with the following:**
Copy
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { ethers } from "hardhat";
type Signers = {
owner: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
describe("Counter", function () {
let signers: Signers;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
it("should work", async function () {
console.log(`address of user owner is ${signers.owner.address}`);
console.log(`address of user alice is ${signers.alice.address}`);
console.log(`address of user bob is ${signers.bob.address}`);
});
});
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
address of user owner is 0x37AC010c1c566696326813b840319B58Bb5840E4
address of user alice is 0xD9F9298BbcD72843586e7E08DAe577E3a0aC8866
address of user bob is 0x3f0CdAe6ebd93F9F776BCBB7da1D42180cC8fcC1
✔ should work
1 passing (2ms)
4
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#set-up-testing-instance)
Set up testing instance
Now that we have our signers set up, we can deploy the smart contract.
To ensure isolated and deterministic tests, we should deploy a fresh instance of `Counter.sol` before each test. This avoids any side effects from previous tests.
The standard approach is to define a `deployFixture()` function that handles contract deployment.
Copy
async function deployFixture() {
const factory = (await ethers.getContractFactory("Counter")) as Counter__factory;
const counterContract = (await factory.deploy()) as Counter;
const counterContractAddress = await counterContract.getAddress();
return { counterContract, counterContractAddress };
}
To run this setup before each test case, call `deployFixture()` inside a `beforeEach` block:
Copy
beforeEach(async () => {
({ counterContract, counterContractAddress } = await deployFixture());
});
This ensures each test runs with a clean, independent contract instance.
Let's put it together. Now your`test/Counter.ts` should look like the following:
Copy
import { Counter, Counter__factory } from "../types";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
type Signers = {
deployer: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
async function deployFixture() {
const factory = (await ethers.getContractFactory("Counter")) as Counter__factory;
const counterContract = (await factory.deploy()) as Counter;
const counterContractAddress = await counterContract.getAddress();
return { counterContract, counterContractAddress };
}
describe("Counter", function () {
let signers: Signers;
let counterContract: Counter;
let counterContractAddress: Counter;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { deployer: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
beforeEach(async () => {
// Deploy a new instance of the contract before each test
({ counterContract, counterContractAddress } = await deployFixture());
});
it("should be deployed", async function () {
console.log(`Counter has been deployed at address ${counterContractAddress}`);
// Test the deployed address is valid
expect(ethers.isAddress(counterContractAddress)).to.eq(true);
});
});
**Run the test:**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output:**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
1 passing (7ms)
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#test-functions)
Test functions
------------------------------------------------------------------------------------------------------------------------------------------------------
Now everything is up and running, you can start testing your contract functions.
1
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#call-the-contract-getcount-view-function)
Call the contract `getCount()` view function
Everything is up and running, we can now call the `Counter.sol` view function `getCount()` !
Just below the test block `it("should be deployed", async function () {...}`,
add the following unit test:
Copy
it("count should be zero after deployment", async function () {
const count = await counterContract.getCount();
console.log(`Counter.getCount() === ${count}`);
// Expect initial count to be 0 after deployment
expect(count).to.eq(0);
});
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
Counter.getCount() === 0
✔ count should be zero after deployment
1 passing (7ms)
2
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#call-the-contract-increment-transaction-function)
Call the contract `increment()` transaction function
Just below the test block `it("count should be zero after deployment", async function () {...}`, add the following test block:
Copy
it("increment the counter by 1", async function () {
const countBeforeInc = await counterContract.getCount();
const tx = await counterContract.connect(signers.alice).increment(1);
await tx.wait();
const countAfterInc = await counterContract.getCount();
expect(countAfterInc).to.eq(countBeforeInc + 1n);
});
**Remarks:**
* `increment()` is a transactional function that modifies the blockchain state.
* It must be signed by a user — here we use `alice`.
* `await wait()` to wait for the transaction to mined.
* The test compares the counter before and after the transaction to ensure it incremented as expected.
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
Counter.getCount() === 0
✔ count should be zero after deployment
✔ increment the counter by 1
2 passing (12ms)
3
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#call-the-contract-decrement-transaction-function)
Call the contract `decrement()` transaction function
Just below the test block `it("increment the counter by 1", async function () {...}`,
add the following test block:
Copy
it("decrement the counter by 1", async function () {
// First increment, count becomes 1
let tx = await counterContract.connect(signers.alice).increment(1);
await tx.wait();
// Then decrement, count goes back to 0
tx = await counterContract.connect(signers.alice).decrement(1);
await tx.wait();
const count = await counterContract.getCount();
expect(count).to.eq(0);
});
* * *
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
Counter.getCount() === 0
✔ count should be zero after deployment
✔ increment the counter by 1
✔ decrement the counter by 1
2 passing (12ms)
Now you have successfully written and tested your counter contract. You should have the following files in your project:
* [`contracts/Counter.sol`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#counter.sol)
— your Solidity smart contract
* [`test/Counter.ts`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#counter.ts)
— your Hardhat test suite written in TypeScript
These files form the foundation of a basic Hardhat-based smart contract project.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/write_a_simple_contract#next-step)
Next step
--------------------------------------------------------------------------------------------------------------------------------------------
Now that you've written and tested a basic Solidity smart contract, you're ready to take the next step.
In the [next tutorial](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm)
, we’ll transform this standard `Counter.sol` contract into `FHECounter.sol`, a trivial FHEVM-compatible version — allowing the counter value to be stored and updated using trivial fully homomorphic encryption.
[PreviousQuick start tutorial](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial)
[Next3\. Turn it into FHEVM](https://docs.zama.ai/protocol/solidity-guides/v0.8/getting-started/quick-start-tutorial/turn_it_into_fhevm)
Last updated 1 month ago
---
# Deploy contracts and run tests | Protocol
In this section, you'll find everything you need to test your FHEVM smart contracts in your [Hardhat](https://hardhat.org/)
project.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/run_test#fhevm-runtime-modes)
FHEVM Runtime Modes
The FHEVM Hardhat plugin provides three **FHEVM runtime modes** tailored for different stages of contract development and testing. Each mode offers a trade-off between speed, encryption, and persistence.
1. The **Hardhat (In-Memory)** default network: 🧪 _Uses mock encryption._ Ideal for regular tests, CI test coverage, and fast feedback during early contract development. No real encryption is used.
2. The **Hardhat Node (Local Server)** network: 🧪 _Uses mock encryption._ Ideal when you need persistent state - for example, when testing frontend interactions, simulating user flows, or validating deployments in a realistic local environment. Still uses mock encryption.
3. The **Sepolia Testnet** network: 🔐 _Uses real encryption._ Use this mode once your contract logic is stable and validated locally. This is the only mode that runs on the full FHEVM stack with **real encrypted values**. It simulates real-world production conditions but is slower and requires Sepolia ETH.
**Zama Testnet** is not a blockchain itself. It is a protocol that enables you to run confidential smart contracts on existing blockchains (such as Ethereum, Base, and others) with the support of encrypted types. See the [FHE on blockchain](https://docs.zama.ai/protocol/protocol/overview)
guide to learn more about the protocol architecture.
Currently, **Zama Protocol** is available on the **Sepolia Testnet**. Support for additional chains will be added in the future. [See the roadmap↗](https://docs.zama.ai/protocol/zama-protocol-litepaper#roadmap)
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/run_test#summary)
Summary
Mode
Encryption
Persistent
Chain
Speed
Usage
Hardhat (default)
🧪 Mock
❌ No
In-Memory
⚡⚡ Very Fast
Fast local testing and coverage
Hardhat Node
🧪 Mock
✅ Yes
Server
⚡ Fast
Frontend integration and local persistent testing
Sepolia Testnet
🔐 Real Encryption
✅ Yes
Server
🐢 Slow
Full-stack validation with real encrypted data
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/run_test#the-fhevm-hardhat-template)
The FHEVM Hardhat Template
To demonstrate the three available testing modes, we'll use the [fhevm-hardhat-template](https://github.com/zama-ai/fhevm-hardhat-template)
, which comes with the FHEVM Hardhat Plugin pre-installed, a basic `FHECounter` smart contract, and ready-to-use tasks for interacting with a deployed instance of this contract.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/run_test#run-on-hardhat-default)
Run on Hardhat (default)
To run your tests in-memory using FHEVM mock values, simply run the following:
Copy
npx hardhat test --network hardhat
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/run_test#run-on-hardhat-node)
Run on Hardhat Node
You can also run your tests against a local Hardhat node, allowing you to deploy contract instances and interact with them in a persistent environment.
1
**Launch the Hardhat Node server:**
* Open a new terminal window.
* From the root project directory, run the following:
Copy
npx hardhat node
2
**Run your test suite (optional):**
From the root project directory:
Copy
npx hardhat test --network localhost
3
**Deploy the** `**FHECounter**` **smart contract on Hardhat Node**
From the root project directory:
Copy
npx hardhat deploy --network localhost
Check the deployed contract FHEVM configuration:
Copy
npx hardhat fhevm check-fhevm-compatibility --network localhost --address
4
**Interact with the deployed** `**FHECounter**` **smart contract**
From the root project directory:
1. Decrypt the current counter value:
Copy
npx hardhat --network localhost task:decrypt-count
1. Increment the counter by 1:
Copy
npx hardhat --network localhost task:increment --value 1
1. Decrypt the new counter value:
Copy
npx hardhat --network localhost task:decrypt-count
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/run_test#run-on-sepolia-ethereum-testnet)
Run on Sepolia Ethereum Testnet
To test your FHEVM smart contract using real encrypted values, you can run your tests on the Sepolia Testnet.
1
**Rebuild the project for Sepolia**
From the root project directory:
Copy
npx hardhat clean
npx hardhat compile --network sepolia
2
**Deploy the** `**FHECounter**` **smart contract on Sepolia**
Copy
npx hardhat deploy --network sepolia
3
**Check the deployed** `**FHECounter**` **contract FHEVM configuration**
From the root project directory:
Copy
npx hardhat fhevm check-fhevm-compatibility --network sepolia --address
If an internal exception is raised, it likely means the contract was not properly compiled for the Sepolia network.
4
**Interact with the deployed** `**FHECounter**` **contract**
From the root project directory:
1. Decrypt the current counter value (⏳ wait...):
Copy
npx hardhat --network sepolia task:decrypt-count
1. Increment the counter by 1 (⏳ wait...):
Copy
npx hardhat --network sepolia task:increment --value 1
1. Decrypt the new counter value (⏳ wait...):
Copy
npx hardhat --network sepolia task:decrypt-count
[PreviousWrite FHEVM tests in Hardhat](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_test)
[NextWrite FHEVM-enabled Hardhat Tasks](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task)
Last updated 1 month ago
---
# Write FHEVM tests in Hardhat | Protocol
In this section, you'll find everything you need to set up a new [Hardhat](https://hardhat.org/)
project and start developing FHEVM smart contracts from scratch using the [FHEVM Hardhat Plugin](https://www.npmjs.com/package/@fhevm/hardhat-plugin)
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_test#enabling-the-fhevm-hardhat-plugin-in-your-hardhat-project)
Enabling the FHEVM Hardhat Plugin in your Hardhat project
Like any Hardhat plugin, the [FHEVM Hardhat Plugin](https://www.npmjs.com/package/@fhevm/hardhat-plugin)
must be enabled by adding the following `import` statement to your `hardhat.config.ts` file:
Copy
import "@fhevm/hardhat-plugin";
Without this import, the Hardhat FHEVM API will **not** be available in your Hardhat runtime environment (HRE).
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_test#accessing-the-hardhat-fhevm-api)
Accessing the Hardhat FHEVM API
The plugin extends the standard [Hardhat Runtime Environment](https://hardhat.org/hardhat-runner/docs/advanced/hardhat-runtime-environment)
(or `hre` in short) with the new `fhevm` Hardhat module.
You can access it in either of the following ways:
Copy
import { fhevm } from "hardhat";
or
Copy
import * as hre from "hardhat";
// Then access: hre.fhevm
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_test#encrypting-values-using-the-hardhat-fhevm-api)
Encrypting Values Using the Hardhat FHEVM API
Suppose the FHEVM smart contract you want to test has a function called `foo` that takes an encrypted `uint32` value as input. The Solidity function `foo` should be declared as follows:
Copy
function foo(externalEunit32 value, bytes calldata memory inputProof);
Where:
* `externalEunit32 value` : is a `bytes32` representing the encrypted `uint32`
* `bytes calldata memory inputProof` : is a `bytes` array representing the zero-knowledge proof of knowledge that validates the encryption
To compute these arguments in TypeScript, you need:
* The **address of the target smart contract**
* The **signer’s address** (i.e., the account sending the transaction)
1
**Create a new encryted input**
Copy
// use the `fhevm` API module from the Hardhat Runtime Environment
const input = fhevm.createEncryptedInput(contractAddress, signers.alice.address);
2
**Add the value you want to encrypt.**
Copy
input.add32(12345);
3
**Perform local encryption.**
Copy
const encryptedInputs = await input.encrypt();
4
**Call the Solidity function**
Copy
const externalUint32Value = encryptedInputs.handles[0];
const inputProof = encryptedInputs.proof;
const tx = await input.foo(externalUint32Value, inputProof);
await tx.wait();
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_test#encryption-examples)
Encryption examples
* [Basic encryption examples](https://docs.zama.ai/protocol/examples/basic/encryption)
* [FHECounter](https://docs.zama.ai/protocol/examples#an-fhe-counter)
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_test#decrypting-values-using-the-hardhat-fhevm-api)
Decrypting values using the Hardhat FHEVM API
Suppose user **Alice** wants to decrypt a `euint32` value that is stored in a smart contract exposing the following Solidity `view` function:
Copy
function getEncryptedUint32Value() public view returns (euint32) { returns _encryptedUint32Value; }
For simplicity, we assume that both Alice’s account and the target smart contract already have the necessary FHE permissions to decrypt this value. For a detailed explanation of how FHE permissions work, see the [`initializeUint32()`](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-decrypt-single-value#tab-decryptsinglevalue.sol)
function in [DecryptSingleValue.sol](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-decrypt-single-value#tab-decryptsinglevalue.sol)
.
1
**Retrieve the encrypted value (a** `**bytes32**` **handle) from the smart contract:**
Copy
const encryptedUint32Value = await contract.getEncryptedUint32Value();
2
**Perform the decryption using the FHEVM API:**
Copy
const clearUint32Value = await fhevm.userDecryptEuint(
FhevmType.euint32, // Encrypted type (must match the Solidity type)
encryptedUint32Value, // bytes32 handle Alice wants to decrypt
contractAddress, // Target contract address
signers.alice, // Alice’s wallet
);
If either the target smart contract or the user does **NOT** have FHE permissions, then the decryption call will fail!
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_test#supported-decryption-types)
Supported Decryption Types
Use the appropriate function for each encrypted data type:
Type
Function
`euintXXX`
`fhevm.userDecryptEuint(...)`
`ebool`
`fhevm.userDecryptEbool(...)`
`eaddress`
`fhevm.userDecryptEaddress(...)`
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_test#decryption-examples)
Decryption examples
* [Basic decryption examples](https://docs.zama.ai/protocol/examples/basic/decryption)
* [FHECounter](https://docs.zama.ai/protocol/examples#an-fhe-counter)
[PreviousHardhat plugin](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat)
[NextDeploy contracts and run tests](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/run_test)
Last updated 1 month ago
---
# Write FHEVM-enabled Hardhat Tasks | Protocol
In this section, you'll learn how to write a custom FHEVM Hardhat task.
Writing tasks is a gas-efficient and flexible way to test your FHEVM smart contracts on the Sepolia network. Creating a custom task is straightforward.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task#prerequisite)
Prerequisite
--------------------------------------------------------------------------------------------------------------------------
* You should be familiar with Hardhat tasks. If you're new to them, refer to the [Hardhat Tasks official documentation](https://hardhat.org/hardhat-runner/docs/guides/tasks#writing-tasks)
.
* You should have already **completed** the [FHEVM Tutorial](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup)
.
* This page provides a step-by-step walkthrough of the `task:decrypt-count` tasks included in the file [tasks/FHECounter.ts](https://github.com/zama-ai/fhevm-hardhat-template/blob/main/tasks/FHECounter.ts)
file, located in the [fhevm-hardhat-template](https://github.com/zama-ai/fhevm-hardhat-template)
repository.
1
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task#a-basic-hardhat-task)
A Basic Hardhat Task.
-------------------------------------------------------------------------------------------------------------------------------------------
Let’s start with a simple example: fetching the current counter value from a basic `Counter.sol` contract.
If you're already familiar with Hardhat and custom tasks, the TypeScript code below should look familiar and be easy to follow:
Copy
task("task:get-count", "Calls the getCount() function of Counter Contract")
.addOptionalParam("address", "Optionally specify the Counter contract address")
.setAction(async function (taskArguments: TaskArguments, hre) {
const { ethers, deployments } = hre;
const CounterDeployement = taskArguments.address
? { address: taskArguments.address }
: await deployments.get("Counter");
console.log(`Counter: ${CounterDeployement.address}`);
const counterContract = await ethers.getContractAt("Counter", CounterDeployement.address);
const clearCount = await counterContract.getCount();
console.log(`Clear count : ${clearCount}`);
});
Now, let’s modify this task to work with FHEVM encrypted values.
2
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task#comment-out-existing-logic-and-rename)
Comment Out Existing Logic and rename
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
First, comment out the existing logic so we can incrementally add the necessary changes for FHEVM integration.
Copy
task("task:get-count", "Calls the getCount() function of Counter Contract")
.addOptionalParam("address", "Optionally specify the Counter contract address")
.setAction(async function (taskArguments: TaskArguments, hre) {
// const { ethers, deployments } = hre;
// const CounterDeployement = taskArguments.address
// ? { address: taskArguments.address }
// : await deployments.get("Counter");
// console.log(`Counter: ${CounterDeployement.address}`);
// const counterContract = await ethers.getContractAt("Counter", CounterDeployement.address);
// const clearCount = await counterContract.getCount();
// console.log(`Clear count : ${clearCount}`);
});
Next, rename the task by replacing:
Copy
task("task:get-count", "Calls the getCount() function of Counter Contract")
With:
Copy
task("task:decrypt-count", "Calls the getCount() function of Counter Contract")
This updates the task name from `task:get-count` to `task:decrypt-count`, reflecting that it now includes decryption logic for FHE-encrypted values.
3
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task#initialize-fhevm-cli-api)
Initialize FHEVM CLI API
--------------------------------------------------------------------------------------------------------------------------------------------------
Replace the line:
Copy
// const { ethers, deployments } = hre;
With:
Copy
const { ethers, deployments, fhevm } = hre;
await fhevm.initializeCLIApi();
Calling `initializeCLIApi()` is essential. Unlike built-in Hardhat tasks like `test` or `compile`, which automatically initialize the FHEVM runtime environment, custom tasks require you to call this function explicitly. **Make sure to call it at the very beginning of your task** to ensure the environment is properly set up.
4
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task#call-the-view-function-getcount-from-the-fhecounter-contract)
Call the view function `getCount` from the FHECounter contract
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Replace the following commented-out lines:
Copy
// const CounterDeployement = taskArguments.address
// ? { address: taskArguments.address }
// : await deployments.get("Counter");
// console.log(`Counter: ${CounterDeployement.address}`);
// const counterContract = await ethers.getContractAt("Counter", CounterDeployement.address);
// const clearCount = await counterContract.getCount();
With the FHEVM equivalent:
Copy
const FHECounterDeployement = taskArguments.address
? { address: taskArguments.address }
: await deployments.get("FHECounter");
console.log(`FHECounter: ${FHECounterDeployement.address}`);
const fheCounterContract = await ethers.getContractAt("FHECounter", FHECounterDeployement.address);
const encryptedCount = await fheCounterContract.getCount();
if (encryptedCount === ethers.ZeroHash) {
console.log(`encrypted count: ${encryptedCount}`);
console.log("clear count : 0");
return;
}
Here, `encryptedCount` is an FHE-encrypted `euint32` primitive. To retrieve the actual value, we need to decrypt it in the next step.
5
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task#decrypt-the-encrypted-count-value)
Decrypt the encrypted count value.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Now replace the following commented-out line:
Copy
// console.log(`Clear count : ${clearCount}`);
With the decryption logic:
Copy
const signers = await ethers.getSigners();
const clearCount = await fhevm.userDecryptEuint(
FhevmType.euint32,
encryptedCount,
FHECounterDeployement.address,
signers[0],
);
console.log(`Encrypted count: ${encryptedCount}`);
console.log(`Clear count : ${clearCount}`);
At this point, your custom Hardhat task is fully configured to work with FHE-encrypted values and ready to run!
6
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task#step-6-run-your-custom-task-using-hardhat-node)
Step 6: Run your custom task using Hardhat Node
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
**Start the Local Hardhat Node:**
* Open a new terminal window.
* From the root project directory, run the following:
Copy
npx hardhat node
**Deploy the FHECounter smart contract on the local Hardhat Node**
Copy
npx hardhat deploy --network localhost
**Run your custom task**
Copy
npx hardhat task:decrypt-count --network localhost
7
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/write_task#step-7-run-your-custom-task-using-sepolia)
Step 7: Run your custom task using Sepolia
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
**Deploy the FHECounter smart contract on Sepolia Testnet (if not already deployed)**
Copy
npx hardhat deploy --network sepolia
**Execute your custom task**
Copy
npx hardhat task:decrypt-count --network sepolia
[PreviousDeploy contracts and run tests](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hardhat/run_test)
[NextFoundry](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/foundry)
Last updated 1 month ago
---
# Encrypted inputs | Protocol
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.
Encrypted inputs are a core feature of FHEVM, enabling users to push encrypted data onto the blockchain while ensuring data confidentiality and integrity.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs#what-are-encrypted-inputs)
What are encrypted inputs?
--------------------------------------------------------------------------------------------------------------------------------------
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs#key-characteristics-of-encrypted-inputs)
Key characteristics of encrypted inputs:
1. **Confidentiality**: Data is encrypted using the public FHE key, ensuring that only authorized parties can decrypt or process the values.
2. **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.
3. **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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs#parameters-in-encrypted-functions)
Parameters in encrypted functions
-----------------------------------------------------------------------------------------------------------------------------------------------------
When a function in a smart contract is called, it may accept two types of parameters for encrypted inputs:
1. `**externalEbool**`**,** `**externalEaddress**`**,**`**externalEuintXX**`: Refers to the index of the encrypted parameter within the proof, representing a specific encrypted input handle.
2. `**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:
Copy
function exampleFunction(
externalEbool param1,
externalEuint64 param2,
externalEuint8 param3,
bytes calldata inputProof
) public {
// Function logic here
}
In this example, `param1`, `param2`, and `param3` are encrypted inputs for `ebool`, `euint64`, and `euint8` while `inputProof` contains the corresponding ZKPoK to validate their authenticity.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs#input-generation-using-hardhat)
Input Generation using Hardhat
In the below example, we use Alice's address to create the encrypted inputs and submits the transaction.
Copy
import { fhevm } from "hardhat";
const input = fhevm.createEncryptedInput(contract.address, signers.alice.address);
input.addBool(canTransfer); // at index 0
input.add64(transferAmount); // at index 1
input.add8(transferType); // at index 2
const encryptedInput = await input.encrypt();
const externalEboolParam1 = encryptedInput.handles[0];
const externalEuint64Param2 = encryptedInput.handles[1];
const externalEuint8Param3 = encryptedInput.handles[2];
const inputProof = encryptedInput.inputProof;
tx = await myContract
.connect(signers.alice)
[\
"exampleFunction(bytes32,bytes32,bytes32,bytes)"\
](signers.bob.address, externalEboolParam1, externalEuint64Param2, externalEuint8Param3, inputProof);
await tx.wait();
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs#input-order)
Input Order
Developers are free to design the function parameters in any order. There is no required correspondence between the order in which encrypted inputs are constructed in TypeScript and the order of arguments in the Solidity function.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs#validating-encrypted-inputs)
Validating encrypted inputs
-----------------------------------------------------------------------------------------------------------------------------------------
Smart contracts process encrypted inputs by verifying them against the associated zero-knowledge proof. This is done using the `FHE.asEuintXX`, `FHE.asEbool`, or `FHE.asEaddress` functions, which validate the input and convert it into the appropriate encrypted type.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs#example-validation)
Example validation
This example demonstrates a function that performs multiple encrypted operations, such as updating a user's encrypted balance and toggling an encrypted boolean flag:
Copy
function myExample(externalEuint64 encryptedAmount, externalEbool encryptedToggle, bytes calldata inputProof) public {
// Validate and convert the encrypted inputs
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
ebool toggleFlag = FHE.fromExternal(encryptedToggle, inputProof);
// Update the user's encrypted balance
balances[msg.sender] = FHE.add(balances[msg.sender], amount);
// Toggle the user's encrypted flag
userFlags[msg.sender] = FHE.not(toggleFlag);
// FHE permissions and function logic here
...
}
// Function to retrieve a user's encrypted balance
function getEncryptedBalance() public view returns (euint64) {
return balances[msg.sender];
}
// Function to retrieve a user's encrypted flag
function getEncryptedFlag() public view returns (ebool) {
return userFlags[msg.sender];
}
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs#example-validation-in-the-confidentialerc20.sol-smart-contract)
Example validation in the `ConfidentialERC20.sol` smart contract
Here’s an example of a smart contract function that verifies an encrypted input before proceeding:
Copy
function transfer(
address to,
externalEuint64 encryptedAmount,
bytes calldata inputProof
) public {
// Verify the provided encrypted amount and convert it into an encrypted uint64
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
// Function logic here, such as transferring funds
...
}
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs#how-validation-works)
How validation works
1. **Input verification**: The `FHE.fromExternal` function ensures that the input is a valid ciphertext with a corresponding ZKPoK.
2. **Type conversion**: The function transforms `externalEbool`, `externalEaddress`, `externalEuintXX` into the appropriate encrypted type (`ebool`, `eaddress`, `euintXX`) for further operations within the contract.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/inputs#best-practices)
Best Practices
---------------------------------------------------------------------------------------------------------------
* **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.
[PreviousGenerate random numbers](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/operations/random)
[NextAccess Control List](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/acl)
Last updated 1 month ago
---
# Error handling | Protocol
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.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/error_handling#challenges-in-error-handling)
**Challenges in error handling**
--------------------------------------------------------------------------------------------------------------------------------------------------------------
In the context of encrypted data:
1. **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.
2. **Limited feedback**: Encrypted computations lack direct mechanisms for exposing failure reasons while maintaining confidentiality.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/error_handling#recommended-approach-error-logging-with-a-handler)
**Recommended approach: Error logging with a handler**
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/error_handling#example-implementation)
**Example implementation**
The following contract snippet demonstrates how to implement and use an error handler:
Copy
struct LastError {
euint8 error; // Encrypted error code
uint timestamp; // Timestamp of the error
}
// Define error codes
euint8 internal NO_ERROR;
euint8 internal NOT_ENOUGH_FUNDS;
constructor() {
NO_ERROR = FHE.asEuint8(0); // Code 0: No error
NOT_ENOUGH_FUNDS = FHE.asEuint8(1); // Code 1: Insufficient funds
}
// Store the last error for each address
mapping(address => LastError) private _lastErrors;
// Event to notify about an error state change
event ErrorChanged(address indexed user);
/**
* @dev Set the last error for a specific address.
* @param error Encrypted error code.
* @param addr Address of the user.
*/
function setLastError(euint8 error, address addr) private {
_lastErrors[addr] = LastError(error, block.timestamp);
emit ErrorChanged(addr);
}
/**
* @dev Internal transfer function with error handling.
* @param from Sender's address.
* @param to Recipient's address.
* @param amount Encrypted transfer amount.
*/
function _transfer(address from, address to, euint32 amount) internal {
// Check if the sender has enough balance to transfer
ebool canTransfer = FHE.le(amount, balances[from]);
// Log the error state: NO_ERROR or NOT_ENOUGH_FUNDS
setLastError(FHE.select(canTransfer, NO_ERROR, NOT_ENOUGH_FUNDS), msg.sender);
// Perform the transfer operation conditionally
balances[to] = FHE.add(balances[to], FHE.select(canTransfer, amount, FHE.asEuint32(0)));
FHE.allowThis(balances[to]);
FHE.allow(balances[to], to);
balances[from] = FHE.sub(balances[from], FHE.select(canTransfer, amount, FHE.asEuint32(0)));
FHE.allowThis(balances[from]);
FHE.allow(balances[from], from);
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/error_handling#how-it-works)
**How It Works**
------------------------------------------------------------------------------------------------------------------------------
1. **Define error codes**:
* `NO_ERROR`: Indicates a successful operation.
* `NOT_ENOUGH_FUNDS`: Indicates insufficient balance for a transfer.
2. **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.
3. **Conditional updates**:
* Use the `FHE.select` function to update balances and log errors based on the transfer condition (`canTransfer`).
4. **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."
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/error_handling#example-error-query)
**Example error query**
--------------------------------------------------------------------------------------------------------------------------------------------
The frontend or another contract can query the `_lastErrors` mapping to retrieve error details:
Copy
/**
* @dev Get the last error for a specific address.
* @param user Address of the user.
* @return error Encrypted error code.
* @return timestamp Timestamp of the error.
*/
function getLastError(address user) public view returns (euint8 error, uint timestamp) {
LastError memory lastError = _lastErrors[user];
return (lastError.error, lastError.timestamp);
}
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/error_handling#benefits-of-this-approach)
**Benefits of this approach**
--------------------------------------------------------------------------------------------------------------------------------------------------------
1. **User feedback**:
* Provides actionable error messages without compromising the confidentiality of encrypted computations.
2. **Scalable error tracking**:
* Logs errors per user, making it easy to identify and debug specific issues.
3. **Event-driven notifications**:
* Enables frontends to react to errors in real time via the `ErrorChanged` event.
By implementing error handlers as demonstrated, developers can ensure a seamless user experience while maintaining the privacy and integrity of encrypted data operations.
[PreviousDealing with branches and conditions](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/logics/loop)
[NextDecryption](https://docs.zama.ai/protocol/solidity-guides/v0.8/smart-contract/oracle)
Last updated 1 month ago
---
# Contract addresses | Protocol
Save this in your `.env` file.
These are Sepolia addresses.
Contract/Service
Address/Value
FHEVM\_EXECUTOR\_CONTRACT
0x848B0066793BcC60346Da1F49049357399B8D595
ACL\_CONTRACT
0x687820221192C5B662b25367F70076A37bc79b6c
HCU\_LIMIT\_CONTRACT
0x594BB474275918AF9609814E68C61B1587c5F838
KMS\_VERIFIER\_CONTRACT
0x1364cBBf2cDF5032C47d8226a6f6FBD2AFCDacAC
INPUT\_VERIFIER\_CONTRACT
0xbc91f3daD1A5F19F8390c400196e58073B6a0BC4
DECRYPTION\_ORACLE\_CONTRACT
0xa02Cda4Ca3a71D7C46997716F4283aa851C28812
DECRYPTION\_ADDRESS
0xb6E160B1ff80D67Bfe90A85eE06Ce0A2613607D1
INPUT\_VERIFICATION\_ADDRESS
0x7048C39f048125eDa9d678AEbaDfB22F7900a29F
RELAYER\_URL
`https://relayer.testnet.zama.cloud`
[PreviousConfiguration](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure)
[NextSupported types](https://docs.zama.ai/protocol/solidity-guides/smart-contract/types)
Last updated 9 days ago
---
# Overview | Protocol
**Welcome to Solidity Guides!**
This section will guide you through writing confidential smart contracts in Solidity using the FHEVM library. With Fully Homomorphic Encryption(FHE), your contracts can operate directly on encrypted data without ever decrypting it onchain.
[](https://docs.zama.ai/protocol/solidity-guides#where-to-go-next)
Where to go next
----------------------------------------------------------------------------------------
If you’re new to the Zama Protocol, start with the [Litepaper](https://docs.zama.ai/protocol/zama-protocol-litepaper)
or the [Protocol Overview](https://docs.zama.ai/protocol)
to understand the foundations.
Otherwise:
🟨 Go to [**What is FHEVM**](https://docs.zama.ai/protocol/solidity-guides/getting-started/overview)
to learn about the core concepts and features.
🟨 Go to [**Quick Start Tutorial**](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial)
to build and test your first confidential smart contract.
🟨 Go to [**Smart Contract Guides**](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure)
for details on encrypted types, supported operations, inputs, ACL, and decryption flows.
🟨 Go to [**Development Guides**](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat)
to set up your local environment with Hardhat or Foundry and deploy FHEVM contracts.
🟨 Go to [**Migration Guide**](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
if you're upgrading from a previous version to v0.7.
[](https://docs.zama.ai/protocol/solidity-guides#help-center)
Help center
------------------------------------------------------------------------------
Ask technical questions and discuss with the community.
* [Community forum](https://community.zama.ai/c/fhevm/15)
* [Discord channel](https://discord.com/invite/zama)
[NextWhat is FHEVM Solidity](https://docs.zama.ai/protocol/solidity-guides/getting-started/overview)
Last updated 9 days ago
---
# Configuration | Protocol
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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure#core-configuration-setup)
Core configuration setup
---------------------------------------------------------------------------------------------------------------------------------
To utilize encrypted computations in Solidity contracts, you must configure the **FHE library** and **Oracle 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.
This library and its associated contracts provide a standardized way to configure and interact with Zama's FHEVM (Fully Homomorphic Encryption Virtual Machine) infrastructure on different Ethereum networks. It supplies the necessary contract addresses for Zama's FHEVM components (`ACL`, `FHEVMExecutor`, `KMSVerifier`, `InputVerifier`) and the decryption oracle, enabling seamless integration for Solidity contracts that require FHEVM support.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure#key-components-configured-automatically)
Key components configured automatically
---------------------------------------------------------------------------------------------------------------------------------------------------------------
1. **FHE library**: Sets up encryption parameters and cryptographic keys.
2. **Oracle**: Manages secure cryptographic operations such as public decryption.
3. **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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure#zamaconfig.sol)
ZamaConfig.sol
-------------------------------------------------------------------------------------------------------------
The `ZamaConfig` library exposes functions to retrieve FHEVM configuration structs and oracle addresses for supported networks (currently only the Sepolia testnet).
Under the hood, this library encapsulates the network-specific addresses of Zama's FHEVM infrastructure into a single struct (`FHEVMConfigStruct`).
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure#sepoliaconfig)
SepoliaConfig
-----------------------------------------------------------------------------------------------------------
The `SepoliaConfig` contract is designed to be inherited by a user contract. The constructor automatically sets up the FHEVM coprocessor and decryption oracle using the configuration provided by the library for the respective network. When a contract inherits from `SepoliaConfig`, the constructor calls `FHE.setCoprocessor` and `FHE.setDecryptionOracle` with the appropriate addresses. This ensures that the inheriting contract is automatically wired to the correct FHEVM contracts and oracle for the target network, abstracting away manual address management and reducing the risk of misconfiguration.
**Example: using Sepolia configuration**
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract MyERC20 is SepoliaConfig {
constructor() {
// Additional initialization logic if needed
}
}
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure#using-isinitialized)
Using `isInitialized`
-------------------------------------------------------------------------------------------------------------------------
The `isInitialized` utility function checks whether an encrypted variable has been properly initialized, preventing unexpected behavior due to uninitialized values.
**Function signature**
Copy
function isInitialized(T v) internal pure returns (bool)
**Purpose**
* Ensures encrypted variables are initialized before use.
* Prevents potential logic errors in contract execution.
**Example: Initialization Check for Encrypted Counter**
Copy
require(FHE.isInitialized(counter), "Counter not initialized!");
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure#summary)
Summary
-----------------------------------------------------------------------------------------------
By leveraging prebuilt a configuration contract like `SepoliaConfig` in `ZamaConfig.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.
[Previous4\. Test the FHEVM contract](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract)
[NextContract addresses](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure/contract_addresses)
Last updated 9 days ago
---
# Hardhat plugin | Protocol
This section will guide you through writing and testing FHEVM smart contracts in Solidity using [Hardhat](https://hardhat.org/)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat#the-fhevm-hardhat-plugin)
The FHEVM Hardhat Plugin
To write FHEVM smart contracts using Hardhat, you need to install the [FHEVM Hardhat Plugin](https://www.npmjs.com/package/@fhevm/hardhat-plugin)
in your Hardhat project.
This plugin enables you to develop, test, and interact with FHEVM contracts right out of the box.
It extends Hardhat’s functionality with a complete FHEVM API that allows you:
* Encrypt data
* Decrypt data
* Run tests using various FHEVM execution modes
* Write FHEVM-enabled Hardhat Tasks
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat#where-to-go-next)
Where to go next
🟨 Go to [**Setup Hardhat**](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup)
to initialize your FHEVM Hardhat project.
🟨 Go to [**Write FHEVM Tests in Hardhat**](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_test)
for details on writing tests of FHEVM smart contracts using Hardhat.
🟨 Go to [**Run FHEVM Tests in Hardhat**](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/run_test)
to learn how to execute those tests in different FHEVM environments.
🟨 Go to [**Write FHEVM Hardhat Task**](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task)
to learn how to write your own custom FHEVM Hardhat task.
[PreviousDecryption](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle)
[NextWrite FHEVM tests in Hardhat](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_test)
Last updated 9 days ago
---
# What is FHEVM Solidity | Protocol
This document provides an overview of key features of the FHEVM smart contract library.
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/overview#configuration-and-initialization)
Configuration and initialization
Smart contracts using FHEVM require proper configuration and initialization:
* **Environment setup**: Import and inherit from environment-specific configuration contracts
* **Relayer configuration**: Configure secure relayer access for cryptographic operations
* **Initialization checks**: Validate encrypted variables are properly initialized before use
For more information see [Configuration](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/overview#encrypted-data-types)
Encrypted data types
FHEVM introduces encrypted data types compatible with Solidity:
* **Booleans**: `ebool`
* **Unsigned Integers**: `euint8`, `euint16`, `euint32`, `euint64`, `euint128`, `euint256`
* **Addresses**: `eaddress`
* **Input**: `externalEbool`, `externalEaddress`, `externalEuintXX` 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](https://docs.zama.ai/protocol/solidity-guides/smart-contract/types)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/overview#casting-types)
Casting types
fhevm provides functions to cast between encrypted types:
* **Casting between encrypted types**: `FHE.asEbool` converts encrypted integers to encrypted booleans
* **Casting to encrypted types**: `FHE.asEuintX` converts plaintext values to encrypted types
* **Casting to encrypted addresses**: `FHE.asEaddress` converts plaintext addresses to encrypted addresses
For more information see [use of encrypted types](https://docs.zama.ai/protocol/solidity-guides/smart-contract/types)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/overview#confidential-computation)
Confidential computation
fhevm enables symbolic execution of encrypted operations, supporting:
* **Arithmetic:** `FHE.add`, `FHE.sub`, `FHE.mul`, `FHE.min`, `FHE.max`, `FHE.neg`, `FHE.div`, `FHE.rem`
* Note: `div` and `rem` operations are supported only with plaintext divisors
* **Bitwise:** `FHE.and`, `FHE.or`, `FHE.xor`, `FHE.not`, `FHE.shl`, `FHE.shr`, `FHE.rotl`, `FHE.rotr`
* **Comparison:** `FHE.eq`, `FHE.ne`, `FHE.lt`, `FHE.le`, `FHE.gt`, `FHE.ge`
* **Advanced:** `FHE.select` for branching on encrypted conditions, `FHE.randEuintX` for on-chain randomness.
For more information on operations, see [Operations on encrypted types](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations)
.
For more information on conditional branching, see [Conditional logic in FHE](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions)
.
For more information on random number generation, see [Generate Random Encrypted Numbers](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/overview#access-control-mechanism)
Access control mechanism
fhevm enforces access control with a blockchain-based Access Control List (ACL):
* **Persistent access**: `FHE.allow`, `FHE.allowThis` grants permanent permissions for ciphertexts.
* **Transient access**: `FHE.allowTransient` provides temporary access for specific transactions.
* **Validation**: `FHE.isSenderAllowed` ensures that only authorized entities can interact with ciphertexts.
For more information see [ACL](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl)
.
[PreviousOverview](https://docs.zama.ai/protocol/solidity-guides)
[NextSet up Hardhat](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup)
Last updated 9 days ago
---
# Quick start tutorial | Protocol
This tutorial guides you to start quickly with Zama’s **Fully Homomorphic Encryption (FHE)** technology for building confidential smart contracts.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial#what-youll-learn)
What You’ll Learn
------------------------------------------------------------------------------------------------------------------------------
In **about 30 minutes**, you'll go from a basic Solidity contract to a fully confidential one using **FHEVM**. Here's what you'll do:
1. Set up your development environment
2. Write a simple Solidity smart contract
3. Convert it into an FHEVM-compatible confidential contract
4. Test your FHEVM-compatible confidential contract
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial#prerequisite)
Prerequisite
---------------------------------------------------------------------------------------------------------------------
* A basic understanding of **Solidity** library and **Ethereum**.
* Some familiarity with **Hardhat.**
**About Hardhat**
[**Hardhat**](https://hardhat.org/)
is a development environment for compiling, deploying, testing, and debugging Ethereum smart contracts. It’s widely used in the Ethereum ecosystem.
In this tutorial, we'll introduce the FHEVM hardhat template that provides an easy way to use FHEVM.
[PreviousSet up Hardhat](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup)
[Next2\. Write a simple contract](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract)
Last updated 9 days ago
---
# Generate random numbers | Protocol
This document explains how to generate cryptographically secure random encrypted numbers fully on-chain using the `FHE` library in fhevm. These numbers are encrypted and remain confidential, enabling privacy-preserving smart contract logic.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random#key-notes-on-random-number-generation)
**Key notes on random number generation**
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
* **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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random#basic-usage)
**Basic usage**
-------------------------------------------------------------------------------------------------------------------
The `FHE` library allows you to generate random encrypted numbers of various bit sizes. Below is a list of supported types and their usage:
Copy
// Generate random encrypted numbers
ebool rb = FHE.randEbool(); // Random encrypted boolean
euint8 r8 = FHE.randEuint8(); // Random 8-bit number
euint16 r16 = FHE.randEuint16(); // Random 16-bit number
euint32 r32 = FHE.randEuint32(); // Random 32-bit number
euint64 r64 = FHE.randEuint64(); // Random 64-bit number
euint128 r128 = FHE.randEuint128(); // Random 128-bit number
euint256 r256 = FHE.randEuint256(); // Random 256-bit number
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random#example-random-boolean)
**Example: Random Boolean**
Copy
function randomBoolean() public returns (ebool) {
return FHE.randEbool();
}
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random#bounded-random-numbers)
**Bounded random numbers**
-----------------------------------------------------------------------------------------------------------------------------------------
To generate random numbers within a specific range, you can specify an **upper bound**. The specified upper bound must be a power of 2. The random number will be in the range `[0, upperBound - 1]`.
Copy
// Generate random numbers with upper bounds
euint8 r8 = FHE.randEuint8(32); // Random number between 0-31
euint16 r16 = FHE.randEuint16(512); // Random number between 0-511
euint32 r32 = FHE.randEuint32(65536); // Random number between 0-65535
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random#example-random-number-with-upper-bound)
**Example: Random number with upper bound**
Copy
function randomBoundedNumber(uint16 upperBound) public returns (euint16) {
return FHE.randEuint16(upperBound);
}
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random#security-considerations)
**Security Considerations**
-------------------------------------------------------------------------------------------------------------------------------------------
* **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.
[PreviousCasting and trivial encryption](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/casting)
[NextEncrypted inputs](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs)
Last updated 9 days ago
---
# Supported types | Protocol
This document introduces the encrypted integer types provided by the `FHE` library in FHEVM and explains their usage, including casting, state variable declarations, and type-specific considerations.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/types#introduction)
Introduction
-----------------------------------------------------------------------------------------------------
The `FHE` 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.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/types#key-features-of-encrypted-types)
Key features of encrypted types
* 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 `FHE` 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 `FHE` 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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/types#list-of-encrypted-types)
List of encrypted types
---------------------------------------------------------------------------------------------------------------------------
The `FHE` library currently supports the following encrypted types:
Type
Bit Length
Supported Operators
Aliases (with supported operators)
Ebool
2
and, or, xor, eq, ne, not, select, rand
Euint8
8
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint16
16
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint32
32
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint64
64
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint128
128
add, sub, mul, div, rem, and, or, xor, shl, shr, rotl, rotr, eq, ne, ge, gt, le, lt, min, max, neg, not, select, rand, randBounded
Euint160
160
Eaddress (eq, ne, select)
Euint256
256
and, or, xor, shl, shr, rotl, rotr, eq, ne, neg, not, select, rand, randBounded
Division (`div`) and remainder (`rem`) operations are only supported when the right-hand side (`rhs`) operand is a plaintext (non-encrypted) value. Attempting to use an encrypted value as `rhs` will result in a panic. This restriction ensures correct and secure computation within the current framework.
Higher-precision integer types are available in the `TFHE-rs` library and can be added to `fhevm` as needed.
[PreviousContract addresses](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure/contract_addresses)
[NextOperations on encrypted types](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations)
Last updated 9 days ago
---
# Reorgs handling | Protocol
This page provides detailed instructions on how to handle reorg risks on Ethereum when using FHEVM.
Since ACL events are propagated from the FHEVM host chain to the [Gateway](https://docs.zama.ai/protocol/protocol/overview/gateway)
immediately after being included in a block, dApp developers must take special care when encrypted information is critically important. For example, if an encrypted handle conceals the private key of a Bitcoin wallet holding significant funds, we need to ensure that this information cannot inadvertently leak to the wrong person due to a reorg on the FHEVM host chain. Therefore, it's the responsibility of dApp developers to prevent such scenarios by implementing a two-step ACL authorization process with a timelock between the request and the ACL call.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/reorgs_handling#simple-example-handling-reorg-risk-on-ethereum)
Simple example: Handling reorg risk on Ethereum
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
On Ethereum, a reorg can be up to 95 slots deep in the worst case, so waiting for more than 95 blocks should ensure that a previously sent transaction has been finalized—unless more than 1/3 of the nodes are malicious and willing to lose their stake, which is highly improbable.
❌ **Instead of writing this contract:**
Copy
contract PrivateKeySale {
euint256 privateKey;
bool isAlreadyBought = false;
constructor(externalEuint256 _privateKey, bytes inputProof) {
privateKey = FHE.fromExternal(_privateKey, inputProof);
FHE.allowThis(privateKey);
}
function buyPrivateKey() external payable {
require(msg.value == 1 ether, "Must pay 1 ETH");
require(!isBought, "Private key already bought");
isBought = true;
FHE.allow(encryptedPrivateKey, msg.sender);
}
}
Since the \`privateKey\`\` encrypted variable contains critical information, we don't want to mistakenly leak it for free if a reorg occurs. This could happen in the previous example because we immediately grant authorization to the buyer in the same transaction that processes the sale.
✅ **We recommend writing something like this instead:**
Copy
contract PrivateKeySale {
euint256 privateKey;
bool isAlreadyBought = false;
uint256 blockWhenBought = 0;
address buyer;
constructor(externalEuint256 _privateKey, bytes inputProof) {
privateKey = FHE.fromExternal(_privateKey, inputProof);
FHE.allowThis(privateKey);
}
function buyPrivateKey() external payable {
require(msg.value == 1 ether, "Must pay 1 ETH");
require(!isBought, "Private key already bought");
isBought = true;
blockWhenBought = block.number;
buyer = msg.sender;
}
function requestACL() external {
require(isBought, "Private key has not been bought yet");
require(block.number > blockWhenBought + 95, "Too early to request ACL, risk of reorg");
FHE.allow(privateKey, buyer);
}
}
This approach ensures that at least 96 blocks have elapsed between the transaction that purchases the private key and the transaction that authorizes the buyer to decrypt it.
This type of contract worsens the user experience by adding a timelock before users can decrypt data, so it should be used sparingly: only when leaked information could be critically important and high-value.
[PreviousACL examples](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples)
[NextLogics](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics)
Last updated 9 days ago
---
# Logics | Protocol
[Branching](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions)
[Dealing with branches and conditions](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/loop)
[Error handling](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/error_handling)
[PreviousReorgs handling](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/reorgs_handling)
[NextBranching](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions)
---
# Foundry | Protocol
This guide explains how to use Foundry with FHEVM for developing smart contracts.
While a Foundry template is currently in development, we strongly recommend using the [Hardhat template](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup)
) for now, as it provides a fully tested and supported development environment for FHEVM smart contracts.
However, you could still use Foundry with the mocked version of the FHEVM, but please be aware that this approach is **NOT** recommended, since the mocked version is not fully equivalent to the real FHEVM node's implementation (see warning in hardhat). In order to do this, you will need to rename your `FHE.sol` imports from `@fhevm/solidity/lib/FHE.sol` to `fhevm/mocks/FHE.sol` in your solidity source files.
[PreviousWrite FHEVM-enabled Hardhat Tasks](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task)
[NextHCU](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu)
Last updated 9 days ago
---
# Decryption | Protocol
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.
Decryption is essential in two primary cases:
1. **Smart contract logic**: A contract requires plaintext values for computations or decision-making.
2. **User interaction**: Plaintext data needs to be revealed to all users, such as revealing the decision of the vote.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle#overview)
Overview
----------------------------------------------------------------------------------------------
Decryption in FHEVM is an asynchronous process that involves the Relayer and Key Management System (KMS). Here’s an example of how to safely request decryption in a contract.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle#example-asynchronous-decryption-in-a-contract)
Example: asynchronous decryption in a contract
Copy
pragma solidity ^0.8.24;
import "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
contract TestAsyncDecrypt is SepoliaConfig {
ebool xBool;
bool public yBool;
bool isDecryptionPending;
uint256 latestRequestId;
constructor() {
xBool = FHE.asEbool(true);
FHE.allowThis(xBool);
}
function requestBool() public {
require(!isDecryptionPending, "Decryption is in progress");
bytes32[] memory cts = new bytes32[](1);
cts[0] = FHE.toBytes32(xBool);
uint256 latestRequestId = FHE.requestDecryption(cts, this.myCustomCallback.selector);
/// @dev This prevents sending multiple requests before the first callback was sent.
isDecryptionPending = true;
}
function myCustomCallback(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) public returns (bool) {
/// @dev This check is used to verify that the request id is the expected one.
require(requestId == latestRequestId, "Invalid requestId");
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
(bool decryptedInput) = abi.decode(cleartexts, (bool));
yBool = decryptedInput;
isDecryptionPending = false;
return yBool;
}
}
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle#decryption-in-depth)
Decryption in depth
--------------------------------------------------------------------------------------------------------------------
This document provides a detailed guide on implementing decryption in your smart contracts using the `DecryptionOracle` in fhevm. It covers the setup, usage of the `FHE.requestDecryption` function, and testing with Hardhat.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle#decryptionoracle-setup)
`DecryptionOracle` setup
----------------------------------------------------------------------------------------------------------------------------
The `DecryptionOracle` is pre-deployed on the FHEVM testnet. It uses a default relayer account specified in the `.env` file.
Anyone can fulfill decryption requests but it is essential to add signature verification (and to include a logic to invalidate the replay of decryption requests). The role of the `DecryptionOracle` contract is to independently verify the KMS signature during execution. This ensures that the relayers cannot manipulate or send fraudulent decryption results, even if compromised.
There are two functions to consider: `requestDecryption` and `checkSignatures`.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle#fhe.requestdecryption-function)
`FHE.requestDecryption` function
You can call the function `FHE.requestDecryption` as such:
Copy
function requestDecryption(
bytes32[] calldata ctsHandles,
bytes4 callbackSelector
) external payable returns (uint256 requestId);
####
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle#function-arguments)
Function arguments
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`, `euint8`, `euint16`, `euint32`, `euint64` or `eaddress`.
`ctsHandles` is the array of ciphertexts that are requested to be decrypted. 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 once the relayer fulfils the decryption request.
Copy
function [callbackName](uint256 requestID, bytes memory cleartexts, bytes memory decryptionProof) external;
`cleartexts` is the bytes array corresponding to the ABI encoding of all requested decrypted values. Each of these decrypted values' type should be a native Solidity type corresponding to the original ciphertext type, following this table of conventions:
Ciphertext type
Decrypted type
ebool
bool
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 `cleartexts` is an ABI encoded byte array of the results of the decryption of the `ct` array values, i.e their number should be the size of the `ct` array. `decryptionProof` is a byte array containing the KMS signatures and extra data.
`msgValue` is the value in native tokens to be sent to the calling contract during fulfillment, i.e when the callback will be called with the results of decryption.
Notice that the callback should always verify the signatures and implement a replay protection mechanism (see below).
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle#fhe.checksignatures-function)
`FHE.checkSignatures` function
You can call the function `FHE.checkSignatures` as such:
Copy
function checkSignatures(uint256 requestId, bytes memory cleartexts, bytes[] memory signatures);
####
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle#function-arguments-1)
Function arguments
* `requestID`, is the value that was returned in the `requestDecryption` function.
* `cleartexts`, is an ABI encoding of the decrypted values associated to the handles (using `abi.encode`). This can contain one or multiple values, depending on the number of handles requested in the `requestDecryption` function. Each of these values' type must match the type of the corresponding handle.
* `decryptionProof`, is a byte array containing the KMS signatures and extra data.
This function reverts if the signatures are invalid.
[PreviousError handling](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/error_handling)
[NextHardhat plugin](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat)
Last updated 9 days ago
---
# Migrate to v0.7 | Protocol
This document provides instructions on migrating from FHEVM v0.6 to v0.7.
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration#from-0.6.x)
From 0.6.x
--------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration#package-and-library)
Package and library
The package is now `@fhevm/solidity` instead of `FHEVM` and the library name has changed from `TFHE` to `FHE`
Copy
import { FHE } from "@fhevm/solidity";
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration#configuration)
Configuration
Configuration has been renamed from `SepoliaZamaConfig` to `SepoliaConfig`.
Copy
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
Also, the function to define manually the Coprocessor has been renamed from `setFHEVM` to `setCoprocessor`, and the function to define the oracle is now integrated into `setCoprocessor`.
Copy
import { ZamaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
constructor () {
FHE.setCoprocessor(ZamaConfig.getSepoliaConfig());
}
You can read more about [Configuration on the dedicated page](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration#decryption-oracle)
Decryption Oracle
Previously, an abstract contract `GatewayCaller` was used to request decryption. It has been replaced by `FHE.requestDecryption`:
Copy
function requestBoolInfinite() public {
bytes32[] memory cts = new bytes32[](1);
cts[0] = FHE.toBytes32(myEncryptedValue);
FHE.requestDecryption(cts, this.myCallback.selector);
}
You can read more about [Decryption Oracle on the dedicated page](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle)
.
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration#deprecation-of-ebytes)
Deprecation of ebytes
`ebytes` has been deprecated and removed from FHEVM.
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration#block-gas-limit)
Block gas limit
Block gas limit has been removed in favor of HCU (Homomorphic Complexity Unit) limit. FHEVM 0.7.0 includes two limits:
* **Sequential homomorphic operations depth limit per transaction**: Controls HCU usage for operations that must be processed in order. This limit is set to **5,000,000** HCU.
* **Global homomorphic operations complexity per transaction**: Controls HCU usage for operations that can be processed in parallel. This limit is set to **20,000,000** HCU.
You can read more about [HCU on the dedicated page](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu)
.
[PreviousHCU](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu)
[NextHow to Transform Your Smart Contract into a FHEVM Smart Contract?](https://docs.zama.ai/protocol/solidity-guides/development-guide/transform_smart_contract_with_fhevm)
Last updated 9 days ago
---
# Casting and trivial encryption | Protocol
This documentation covers the `asEbool`, `asEuintXX`, and `asEaddress` operations provided by the FHE 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 two main use cases:
1. **Trivial encryption**: Converting plaintext values to encrypted types
2. **Type casting**: Converting between different encrypted types
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/casting#id-1.-trivial-encryption)
1\. Trivial encryption
----------------------------------------------------------------------------------------------------------------------------------------
Trivial encryption simply put is a plain text in a format of a ciphertext.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/casting#overview)
Overview
Trivial encryption is the process of converting plaintext values into encrypted types (ciphertexts) compatible with FHE 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`
* `address` → `eaddress`
When doing trivial encryption, the data is made compatible with FHE operations but remains publicly visible on-chain unless explicitly encrypted.
####
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/casting#example)
**Example**
Copy
euint64 value64 = FHE.asEuint64(7262); // Trivial encrypt a uint64
ebool valueBool = FHE.asEbool(true); // Trivial encrypt a boolean
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/casting#id-2.-casting-between-encrypted-types)
2\. Casting between encrypted types
------------------------------------------------------------------------------------------------------------------------------------------------------------------
This type of casting is used to reinterpret or convert one encrypted type into another. For example:
* `euint32` → `euint64`
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 information
>
> * Casting from larger types to smaller types (e.g. `euint64` → `euint32`) will truncate and lose information
>
The table below summarizes the available casting functions:
From type
To type
Function
`euintX`
`euintX`
`FHE.asEuintXX`
`ebool`
`euintX`
`FHE.asEuintXX`
`euintX`
`ebool`
`FHE.asEboolXX`
Casting between encrypted types is efficient and often necessary when handling data with differing precision requirements.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/casting#workflow-for-encrypted-types)
**Workflow for encrypted types**
Copy
// Casting between encrypted types
euint32 value32 = FHE.asEuint32(value64); // Cast to euint32
ebool valueBool = FHE.asEbool(value32); // Cast to ebool
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/casting#overall-operation-summary)
Overall operation summary
--------------------------------------------------------------------------------------------------------------------------------------------
Casting Type
Function
Input Type
Output Type
Trivial encryption
`FHE.asEuintXX(x)`
`uintX`
`euintX`
`FHE.asEbool(x)`
`bool`
`ebool`
`FHE.asEaddress(x)`
`address`
`eaddress`
Conversion between types
`FHE.asEuintXX(x)`
`euintXX`/`ebool`
`euintYY`
`FHE.asEbool(x)`
`euintXX`
`ebool`
[PreviousOperations on encrypted types](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations)
[NextGenerate random numbers](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random)
Last updated 9 days ago
---
# ACL examples | Protocol
This page provides detailed instructions and examples on how to use and implement the ACL (Access Control List) in FHEVM. For an overview of ACL concepts and their importance, refer to the [access control list (ACL) overview](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl)
.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#controlling-access-permanent-and-transient-allowances)
Controlling access: permanent and transient allowances
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The ACL system allows you to define two types of permissions for accessing ciphertexts:
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#permanent-allowance)
Permanent allowance
* **Function**: `FHE.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.
####
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#alternative-solidity-syntax)
Alternative Solidity syntax
You can also use method-chaining syntax for granting allowances since FHE is a Solidity library.
Copy
using FHE for *;
ciphertext.allow(address1).allow(address2);
This is equivalent to calling `FHE.allow(ciphertext, address1)` followed by `FHE.allow(ciphertext, address2)`.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#transient-allowance)
Transient allowance
* **Function**: `FHE.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.
####
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#alternative-solidity-syntax-1)
Alternative Solidity syntax
Method chaining is also available for transient allowances since FHE is a Solidity library.
Copy
using FHE for *;
ciphertext.allowTransient(address1).allowTransient(address2);
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#syntactic-sugar)
Syntactic sugar
* **Function**: `FHE.allowThis(ciphertext)`
* **Equivalent To**: `FHE.allow(ciphertext, address(this))`
* **Purpose**: Simplifies granting permanent access to the current contract for managing ciphertexts.
####
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#alternative-solidity-syntax-2)
Alternative Solidity syntax
You can also use method-chaining syntax for allowThis since FHE is a Solidity library.
Copy
using FHE for *;
ciphertext.allowThis();
####
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#make-publicly-decryptable)
Make publicly decryptable
To make a ciphertext publicly decryptable, you can use the `FHE.makePubliclyDecryptable(ciphertext)` function. This grants decryption rights to anyone, which is useful for scenarios where the encrypted value should be accessible by all.
Copy
// Grant public decryption right to a ciphertext
FHE.makePubliclyDecryptable(ciphertext);
// Or using method syntax:
ciphertext.makePubliclyDecryptable();
* **Function**: `FHE.makePubliclyDecryptable(ciphertext)`
* **Purpose**: Makes the ciphertext decryptable by anyone.
* **Use Case**: When you want to publish encrypted results or data.
> You can combine multiple allowance methods (such as `.allow()`, `.allowThis()`, `.allowTransient()`) directly on ciphertext objects to grant access to several addresses or contracts in a single, fluent statement.
>
> **Example**
>
> Copy
>
> // Grant transient access to one address and permanent access to another address
> ciphertext.allowTransient(address1).allow(address2);
>
> // Grant permanent access to the current contract and another address
> ciphertext.allowThis().allow(address1);
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#best-practices)
Best practices
--------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#verifying-sender-access)
Verifying sender access
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.
####
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#example-scenario-confidential-erc20-attack)
Example scenario: Confidential ERC20 attack
Consider an **Confidential ERC20 token**. An attacker controlling two accounts, **Account A** and **Account B**, with 100 tokens in Account A, could exploit the system as follows:
1. The attacker attempts to send the target user's encrypted balance from **Account A** to **Account B**.
2. 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 `FHE.isSenderAllowed()` function to verify that the sender has legitimate access to the encrypted amount being transferred.
* * *
####
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#example-secure-verification)
Example: secure verification
Copy
function transfer(address to, euint64 encryptedAmount) public {
// Ensure the sender is authorized to access the encrypted amount
require(FHE.isSenderAllowed(encryptedAmount), "Unauthorized access to encrypted amount.");
// Proceed with further logic
...
}
By enforcing this check, you can safeguard against inference attacks and ensure that encrypted values are only manipulated by authorized entities.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#acl-for-user-decryption)
ACL for user decryption
--------------------------------------------------------------------------------------------------------------------------------------
If a ciphertext can be decrypt by a user, explicit access must be granted to them. Additionally, the user decryption mechanism requires the signature of a public key associated with the contract address. Therefore, a value that needs to be decrypted must be explicitly authorized for both the user and the contract.
Due to the user decryption mechanism, a user signs a public key associated with a specific contract; therefore, the ciphertext also needs to be allowed for the contract.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples#example-secure-transfer-in-confidentialerc20)
Example: Secure Transfer in ConfidentialERC20
Copy
function transfer(address to, euint64 encryptedAmount) public {
require(FHE.isSenderAllowed(encryptedAmount), "The caller is not authorized to access this encrypted amount.");
euint64 amount = FHE.asEuint64(encryptedAmount);
ebool canTransfer = FHE.le(amount, balances[msg.sender]);
euint64 newBalanceTo = FHE.add(balances[to], FHE.select(canTransfer, amount, FHE.asEuint64(0)));
balances[to] = newBalanceTo;
// Allow this new balance for both the contract and the owner.
FHE.allowThis(newBalanceTo);
FHE.allow(newBalanceTo, to);
euint64 newBalanceFrom = FHE.sub(balances[from], FHE.select(canTransfer, amount, FHE.asEuint64(0)));
balances[from] = newBalanceFrom;
// Allow this new balance for both the contract and the owner.
FHE.allowThis(newBalanceFrom);
FHE.allow(newBalanceFrom, from);
}
By understanding how to grant and verify permissions, you can effectively manage access to encrypted data in your FHEVM smart contracts. For additional context, see the [ACL overview](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl)
.
[PreviousAccess Control List](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl)
[NextReorgs handling](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/reorgs_handling)
Last updated 9 days ago
---
# How to Transform Your Smart Contract into a FHEVM Smart Contract? | Protocol
This short guide will walk you through converting a standard Solidity contract into one that leverages Fully Homomorphic Encryption (FHE) using FHEVM. This approach lets you develop your contract logic as usual, then adapt it to support encrypted computation for privacy.
For this guide, we will focus on a voting contract example.
* * *
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/transform_smart_contract_with_fhevm#id-1.-start-with-a-standard-solidity-contract)
1\. Start with a Standard Solidity Contract
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Begin by writing your voting contract in Solidity as you normally would. Focus on implementing the core logic and functionality.
Copy
// Standard Solidity voting contract example
pragma solidity ^0.8.0;
contract SimpleVoting {
mapping(address => bool) public hasVoted;
uint64 public yesVotes;
uint64 public noVotes;
uint256 public voteDeadline;
function vote(bool support) public {
require(block.timestamp <= voteDeadline, "Too late to vote");
require(!hasVoted[msg.sender], "Already voted");
hasVoted[msg.sender] = true;
if (support) {
yesVotes += 1;
} else {
noVotes += 1;
}
}
function getResults() public view returns (uint64, uint64) {
return (yesVotes, noVotes);
}
}
* * *
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/transform_smart_contract_with_fhevm#id-2.-identify-sensitive-data-and-operations)
2\. Identify Sensitive Data and Operations
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Review your contract and determine which variables, functions, or computations require privacy. In this example, the vote counts (`yesVotes`, `noVotes`) and individual votes should be encrypted.
* * *
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/transform_smart_contract_with_fhevm#id-3.-integrate-fhevm-and-update-your-business-logic-accordingly)
3\. Integrate FHEVM and update your business logic accordingly.
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Replace standard data types and operations with their FHEVM equivalents for the identified sensitive parts. Use encrypted types and FHEVM library functions to perform computations on encrypted data.
Copy
pragma solidity ^0.8.0;
import "@fhevm/solidity/lib/FHE.sol";
import {SepoliaConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
contract EncryptedSimpleVoting is SepoliaConfig {
enum VotingStatus {
Open,
DecryptionInProgress,
ResultsDecrypted
}
mapping(address => bool) public hasVoted;
VotingStatus public status;
uint64 public decryptedYesVotes;
uint64 public decryptedNoVotes;
uint256 public voteDeadline;
euint64 private encryptedYesVotes;
euint64 private encryptedNoVotes;
constructor() {
encryptedYesVotes = FHE.asEuint64(0);
encryptedNoVotes = FHE.asEuint64(0);
FHE.allowThis(encryptedYesVotes);
FHE.allowThis(encryptedNoVotes);
}
function vote(externalEbool support, bytes memory inputProof) public {
require(block.timestamp <= voteDeadline, "Too late to vote");
require(!hasVoted[msg.sender], "Already voted");
hasVoted[msg.sender] = true;
ebool isSupport = FHE.fromExternal(support, inputProof);
encryptedYesVotes = FHE.select(isSupport, FHE.add(encryptedYesVotes, 1), encryptedYesVotes);
encryptedNoVotes = FHE.select(isSupport, encryptedNoVotes, FHE.add(encryptedNoVotes, 1));
FHE.allowThis(encryptedYesVotes);
FHE.allowThis(encryptedNoVotes);
}
function requestVoteDecryption() public {
require(block.timestamp > voteDeadline, "Voting is not finished");
bytes32[] memory cts = new bytes32[](2);
cts[0] = FHE.toBytes32(encryptedYesVotes);
cts[1] = FHE.toBytes32(encryptedNoVotes);
uint256 requestId = FHE.requestDecryption(cts, this.callbackDecryptVotes.selector);
status = VotingStatus.DecryptionInProgress;
}
function callbackDecryptVotes(uint256 requestId, bytes memory cleartexts, bytes memory decryptionProof) public {
FHE.checkSignatures(requestId, cleartexts, decryptionProof);
(uint64 yesVotes, uint64 noVotes) = abi.decode(cleartexts, (uint64, uint64));
decryptedYesVotes = yesVotes;
decryptedNoVotes = noVotes;
status = VotingStatus.ResultsDecrypted;
}
function getResults() public view returns (uint64, uint64) {
require(status == VotingStatus.ResultsDecrypted, "Results were not decrypted");
return (
decryptedYesVotes,
decryptedNoVotes
);
}
}
Adjust your contract’s code to accept and return encrypted data where necessary. This may involve changing function parameters and return types to work with ciphertexts instead of plaintext values, as shown above.
* The `vote` function now has two parameters: `support` and `inputProof`.
* The `getResults` can only be called after the decryption occurred. Otherwise, the decrypted results are not visible to anyone.
However, it is far from being the main change. As this example illustrates, working with FHEVM often requires re-architecting the original logic to support privacy.
In the updated code, the logic becomes async; results are hidden until a request (to the oracle) explicitely has to be made to decrypt publically the vote results.
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/transform_smart_contract_with_fhevm#conclusion)
Conclusion
----------------------------------------------------------------------------------------------------------------------------------
As this short guide showed, integrating with FHEVM not only requires integration with the FHEVM stack, it also requires refactoring your business logic to support mechanism to swift between encrypted and non-encrypted components of the logic.
[PreviousMigrate to v0.7](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
Last updated 9 days ago
---
# Encrypted inputs | Protocol
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.
Encrypted inputs are a core feature of FHEVM, enabling users to push encrypted data onto the blockchain while ensuring data confidentiality and integrity.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs#what-are-encrypted-inputs)
What are encrypted inputs?
---------------------------------------------------------------------------------------------------------------------------------
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs#key-characteristics-of-encrypted-inputs)
Key characteristics of encrypted inputs:
1. **Confidentiality**: Data is encrypted using the public FHE key, ensuring that only authorized parties can decrypt or process the values.
2. **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.
3. **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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs#parameters-in-encrypted-functions)
Parameters in encrypted functions
------------------------------------------------------------------------------------------------------------------------------------------------
When a function in a smart contract is called, it may accept two types of parameters for encrypted inputs:
1. `**externalEbool**`**,** `**externalEaddress**`**,**`**externalEuintXX**`: Refers to the index of the encrypted parameter within the proof, representing a specific encrypted input handle.
2. `**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:
Copy
function exampleFunction(
externalEbool param1,
externalEuint64 param2,
externalEuint8 param3,
bytes calldata inputProof
) public {
// Function logic here
}
In this example, `param1`, `param2`, and `param3` are encrypted inputs for `ebool`, `euint64`, and `euint8` while `inputProof` contains the corresponding ZKPoK to validate their authenticity.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs#input-generation-using-hardhat)
Input Generation using Hardhat
In the below example, we use Alice's address to create the encrypted inputs and submits the transaction.
Copy
import { fhevm } from "hardhat";
const input = fhevm.createEncryptedInput(contract.address, signers.alice.address);
input.addBool(canTransfer); // at index 0
input.add64(transferAmount); // at index 1
input.add8(transferType); // at index 2
const encryptedInput = await input.encrypt();
const externalEboolParam1 = encryptedInput.handles[0];
const externalEuint64Param2 = encryptedInput.handles[1];
const externalEuint8Param3 = encryptedInput.handles[2];
const inputProof = encryptedInput.inputProof;
tx = await myContract
.connect(signers.alice)
[\
"exampleFunction(bytes32,bytes32,bytes32,bytes)"\
](signers.bob.address, externalEboolParam1, externalEuint64Param2, externalEuint8Param3, inputProof);
await tx.wait();
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs#input-order)
Input Order
Developers are free to design the function parameters in any order. There is no required correspondence between the order in which encrypted inputs are constructed in TypeScript and the order of arguments in the Solidity function.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs#validating-encrypted-inputs)
Validating encrypted inputs
------------------------------------------------------------------------------------------------------------------------------------
Smart contracts process encrypted inputs by verifying them against the associated zero-knowledge proof. This is done using the `FHE.asEuintXX`, `FHE.asEbool`, or `FHE.asEaddress` functions, which validate the input and convert it into the appropriate encrypted type.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs#example-validation)
Example validation
This example demonstrates a function that performs multiple encrypted operations, such as updating a user's encrypted balance and toggling an encrypted boolean flag:
Copy
function myExample(externalEuint64 encryptedAmount, externalEbool encryptedToggle, bytes calldata inputProof) public {
// Validate and convert the encrypted inputs
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
ebool toggleFlag = FHE.fromExternal(encryptedToggle, inputProof);
// Update the user's encrypted balance
balances[msg.sender] = FHE.add(balances[msg.sender], amount);
// Toggle the user's encrypted flag
userFlags[msg.sender] = FHE.not(toggleFlag);
// FHE permissions and function logic here
...
}
// Function to retrieve a user's encrypted balance
function getEncryptedBalance() public view returns (euint64) {
return balances[msg.sender];
}
// Function to retrieve a user's encrypted flag
function getEncryptedFlag() public view returns (ebool) {
return userFlags[msg.sender];
}
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs#example-validation-in-the-confidentialerc20.sol-smart-contract)
Example validation in the `ConfidentialERC20.sol` smart contract
Here’s an example of a smart contract function that verifies an encrypted input before proceeding:
Copy
function transfer(
address to,
externalEuint64 encryptedAmount,
bytes calldata inputProof
) public {
// Verify the provided encrypted amount and convert it into an encrypted uint64
euint64 amount = FHE.fromExternal(encryptedAmount, inputProof);
// Function logic here, such as transferring funds
...
}
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs#how-validation-works)
How validation works
1. **Input verification**: The `FHE.fromExternal` function ensures that the input is a valid ciphertext with a corresponding ZKPoK.
2. **Type conversion**: The function transforms `externalEbool`, `externalEaddress`, `externalEuintXX` into the appropriate encrypted type (`ebool`, `eaddress`, `euintXX`) for further operations within the contract.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs#best-practices)
Best Practices
----------------------------------------------------------------------------------------------------------
* **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.
[PreviousGenerate random numbers](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random)
[NextAccess Control List](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl)
Last updated 9 days ago
---
# Dealing with branches and conditions | Protocol
This document explains how to handle branches, loops or conditions when working with Fully Homomorphic Encryption (FHE), specifically when the condition / index is encrypted.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/loop#breaking-a-loop)
Breaking a loop
-----------------------------------------------------------------------------------------------------------------
❌ In FHE, it is not possible to break a loop based on an encrypted condition. For example, this would not work:
Copy
euint8 maxValue = FHE.asEuint(6); // Could be a value between 0 and 10
euint8 x = FHE.asEuint(0);
// some code
while(FHE.lt(x, maxValue)){
x = FHE.add(x, 2);
}
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 `FHE.select` inside the loop.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/loop#suggested-approach)
Suggested approach
-----------------------------------------------------------------------------------------------------------------------
✅ For example, the previous code could maybe be replaced by the following snippet:
Copy
euint8 maxValue = FHE.asEuint(6); // Could be a value between 0 and 10
euint8 x;
// some code
for (uint32 i = 0; i < 10; i++) {
euint8 toAdd = FHE.select(FHE.lt(x, maxValue), 2, 0);
x = FHE.add(x, toAdd);
}
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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/loop#best-practices)
Best practices
---------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/loop#obfuscate-branching)
Obfuscate branching
The previous paragraph emphasized that branch logic should rely as much as possible on `FHE.select` instead of decryptions. It hides effectively which branch has been executed.
However, this is sometimes not enough. Enhancing the privacy of smart contracts often requires revisiting your application's logic.
For example, if implementing a simple AMM for two encrypted ERC20 tokens based on a linear constant function, it is recommended to not only hide the amounts being swapped, but also the token which is swapped in a pair.
✅ Here is a very simplified example implementation, we suppose here that the rate between tokenA and tokenB is constant and equals to 1:
Copy
// typically either encryptedAmountAIn or encryptedAmountBIn is an encrypted null value
// ideally, the user already owns some amounts of both tokens and has pre-approved the AMM on both tokens
function swapTokensForTokens(
externalEuint32 encryptedAmountAIn,
externalEuint32 encryptedAmountBIn,
bytes calldata inputProof
) external {
euint32 encryptedAmountA = FHE.asEuint32(encryptedAmountAIn, inputProof); // even if amount is null, do a transfer to obfuscate trade direction
euint32 encryptedAmountB = FHE.asEuint32(encryptedAmountBIn, inputProof); // even if amount is null, do a transfer to obfuscate trade direction
// send tokens from user to AMM contract
FHE.allowTransient(encryptedAmountA, tokenA);
IConfidentialERC20(tokenA).transferFrom(msg.sender, address(this), encryptedAmountA);
FHE.allowTransient(encryptedAmountB, tokenB);
IConfidentialERC20(tokenB).transferFrom(msg.sender, address(this), encryptedAmountB);
// send tokens from AMM contract to user
// Price of tokenA in tokenB is constant and equal to 1, so we just swap the encrypted amounts here
FHE.allowTransient(encryptedAmountB, tokenA);
IConfidentialERC20(tokenA).transfer(msg.sender, encryptedAmountB);
FHE.allowTransient(encryptedAmountA, tokenB);
IConfidentialERC20(tokenB).transferFrom(msg.sender, address(this), encryptedAmountA);
}
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/loop#avoid-using-encrypted-indexes)
Avoid using encrypted indexes
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.
Copy
euint32 x;
euint32[] encArray;
function setXwithEncryptedIndex(externalEuint32 encryptedIndex, bytes calldata inputProof) public {
euint32 index = FHE.asEuint32(encryptedIndex, inputProof);
for (uint32 i = 0; i < encArray.length; i++) {
ebool isEqual = FHE.eq(index, i);
x = FHE.select(isEqual, encArray[i], x);
}
FHE.allowThis(x);
}
[PreviousBranching](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions)
[NextError handling](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/error_handling)
Last updated 9 days ago
---
# Branching | Protocol
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.
This document covers encrypted branching and how to move from an encrypted condition to a non-encrypted business logic in your smart contract.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions#what-is-confidential-branching)
What is confidential branching?
------------------------------------------------------------------------------------------------------------------------------------------------------
In FHEVM, when you perform [comparison operations](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations#comparison-operations)
, 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 `FHE.select` function, which acts as a ternary operator for encrypted values.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions#using-fhe.select-for-conditional-logic)
**Using** `**FHE.select**` **for conditional logic**
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The `FHE.select` function enables branching logic by selecting one of two encrypted values based on an encrypted condition (`ebool`). It works as follows:
Copy
FHE.select(condition, valueIfTrue, valueIfFalse);
* `**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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions#example-auction-bidding-logic)
**Example: Auction Bidding Logic**
--------------------------------------------------------------------------------------------------------------------------------------------------------
Here's an example of using conditional logic to update the highest winning number in a guessing game:
Copy
function bid(externalEuint64 encryptedValue, bytes calldata inputProof) external onlyBeforeEnd {
// Convert the encrypted input to an encrypted 64-bit integer
euint64 bid = FHE.asEuint64(encryptedValue, inputProof);
// Compare the current highest bid with the new bid
ebool isAbove = FHE.lt(highestBid, bid);
// Update the highest bid if the new bid is greater
highestBid = FHE.select(isAbove, bid, highestBid);
// Allow the contract to use the updated highest bid ciphertext
FHE.allowThis(highestBid);
}
This is a simplified example to demonstrate the functionality.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions#how-does-it-work)
How Does It Work?
* **Comparison**:
* The `FHE.lt` function compares `highestBid` and `bid`, returning an `ebool` (`isAbove`) that indicates whether the new bid is higher.
* **Selection**:
* The `FHE.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 `FHE.allowThis`.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions#key-considerations)
Key Considerations
-----------------------------------------------------------------------------------------------------------------------------
* **Value change behavior:** Each time `FHE.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 `FHE.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., `FHE.allowThis`, `FHE.allow`) to ensure the updated ciphertexts are authorized for use in future computations or transactions.
* * *
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions#how-to-branch-to-a-non-confidential-path)
How to branch to a non-confidential path?
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
So far, this section only covered how to do branching using encrypted variables. However, there may be many cases where the "public" contract logic will depend on the outcome from a encrypted path.
To do so, there are only one way to branch from an encrypted path to a non-encrypted path: it requires a public decryption using the oracle. Hence, any contract logic that requires moving from an encrypted input to a non-encrypted path always requires an async contract logic.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions#example-auction-bidding-logic-item-release)
**Example: Auction Bidding Logic: Item Release**
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Going back to our previous example with the auction bidding logic. Let's assume that the winner of the auction can receive some prize, which is not confidential.
Copy
bool public isPrizeDistributed;
eaddress internal highestBidder;
euint64 internal highestBid;
function bid(externalEuint64 encryptedValue, bytes calldata inputProof) external onlyBeforeEnd {
// Convert the encrypted input to an encrypted 64-bit integer
euint64 bid = FHE.asEuint64(encryptedValue, inputProof);
// Compare the current highest bid with the new bid
ebool isAbove = FHE.lt(highestBid, bid);
// Update the highest bid if the new bid is greater
highestBid = FHE.select(isAbove, bid, highestBid);
// Update the highest bidder address if the new bid is greater
highestBidder = FHE.select(isAbove, FHE.asEaddress(msg.sender), currentBidder));
// Allow the contract to use the highest bidder address
FHE.allowThis(highestBidder);
// Allow the contract to use the updated highest bid ciphertext
FHE.allowThis(highestBid);
}
function revealWinner() external onlyAfterEnd {
bytes32[] memory cts = new bytes32[](2);
cts[0] = FHE.toBytes32(highestBidder);
uint256 requestId = FHE.requestDecryption(cts, this.transferPrize.selector);
}
function transferPrize(uint256 requestId, address auctionWinner, bytes memory signatures) external {
require(!isPrizeDistributed, "Prize has already been distributed");
FHE.verifySignatures(requestId, signatures)
isPrizeDistributed = true;
// Business logic to transfer the prize to the auction winner
}
This is a simplified example to demonstrate the functionality.
As you can see the in the above example, the path to move from an encrypted condition to a decrypted business logic must be async and requires calling the decryption oracle contract to reveal the result of the logic using encrypted variables.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/conditions#summary)
Summary
-------------------------------------------------------------------------------------------------------
* `**FHE.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.
[PreviousLogics](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics)
[NextDealing with branches and conditions](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/loop)
Last updated 9 days ago
---
# Access Control List | Protocol
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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl#what-is-the-acl)
What is the ACL?
----------------------------------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl#why-is-the-acl-important)
Why is the ACL important?
----------------------------------------------------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl#how-does-the-acl-work)
How does the ACL work?
----------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl#types-of-access)
Types of access
* **Permanent allowance**:
* Configured using `FHE.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 `FHE.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.
* **Permanent public allowance**:
* Configured using `FHE.makePubliclyDecryptable(ciphertext)`.
* Grants long-term access to the ciphertext for any user.
* Stored in a dedicated contract for persistent storage.
**Syntactic sugar**:
* `FHE.allowThis(ciphertext)` is shorthand for `FHE.allow(ciphertext, address(this))`. It authorizes the current contract to reuse a ciphertext handle in future transactions.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl#transient-vs.-permanent-allowance)
Transient vs. permanent allowance
Allowance type
Purpose
Storage type
Use case
**Transient**
Temporary access during a transaction.
[Transient storage](https://eips.ethereum.org/EIPS/eip-1153)
(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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl#granting-and-verifying-access)
Granting and verifying access
-------------------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl#granting-access)
Granting access
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl#verifying-access)
Verifying access
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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl#practical-uses-of-the-acl)
Practical uses of the ACL
-----------------------------------------------------------------------------------------------------------------------------
* **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.
* * *
For a detailed explanation of the ACL's functionality, including code examples and advanced configurations, see [ACL examples](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples)
.
[PreviousEncrypted inputs](https://docs.zama.ai/protocol/solidity-guides/smart-contract/inputs)
[NextACL examples](https://docs.zama.ai/protocol/solidity-guides/smart-contract/acl/acl_examples)
Last updated 9 days ago
---
# Operations on encrypted types | Protocol
This document outlines the operations supported on encrypted types in the `FHE` library, enabling arithmetic, bitwise, comparison, and more on Fully Homomorphic Encryption (FHE) ciphertexts.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations#arithmetic-operations)
Arithmetic operations
----------------------------------------------------------------------------------------------------------------------------
The following arithmetic operations are supported for encrypted integers (`euintX`):
Name
Function name
Symbol
Type
Add
`FHE.add`
`+`
Binary
Subtract
`FHE.sub`
`-`
Binary
Multiply
`FHE.mul`
`*`
Binary
Divide (plaintext divisor)
`FHE.div`
Binary
Reminder (plaintext divisor)
`FHE.rem`
Binary
Negation
`FHE.neg`
`-`
Unary
Min
`FHE.min`
Binary
Max
`FHE.max`
Binary
Division (FHE.div) and remainder (FHE.rem) operations are currently supported only with plaintext divisors.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations#bitwise-operations)
Bitwise operations
----------------------------------------------------------------------------------------------------------------------
The FHE library also supports bitwise operations, including shifts and rotations:
Name
Function name
Symbol
Type
Bitwise AND
`FHE.and`
`&`
Binary
Bitwise OR
`FHE.or`
`|`
Binary
Bitwise XOR
`FHE.xor`
`^`
Binary
Bitwise NOT
`FHE.not`
`~`
Unary
Shift Right
`FHE.shr`
Binary
Shift Left
`FHE.shl`
Binary
Rotate Right
`FHE.rotr`
Binary
Rotate Left
`FHE.rotl`
Binary
The shift operators `FHE.shr` and `FHE.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, `FHE.shr(euint64 x, 70)` is equivalent to `FHE.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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations#comparison-operations)
Comparison operations
----------------------------------------------------------------------------------------------------------------------------
Encrypted integers can be compared using the following functions:
Name
Function name
Symbol
Type
Equal
`FHE.eq`
Binary
Not equal
`FHE.ne`
Binary
Greater than or equal
`FHE.ge`
Binary
Greater than
`FHE.gt`
Binary
Less than or equal
`FHE.le`
Binary
Less than
`FHE.lt`
Binary
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations#ternary-operation)
Ternary operation
--------------------------------------------------------------------------------------------------------------------
The `FHE.select` function is a ternary operation that selects one of two encrypted values based on an encrypted condition:
Name
Function name
Symbol
Type
Select
`FHE.select`
Ternary
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations#random-operations)
Random operations
--------------------------------------------------------------------------------------------------------------------
You can generate cryptographically secure random numbers fully on-chain:
**Name**
**Function Name**
**Symbol**
**Type**
Random Unsigned Integer
`FHE.randEuintX()`
Random
For more details, refer to the [Random Encrypted Numbers](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/random)
document.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations#best-practices)
Best Practices
--------------------------------------------------------------------------------------------------------------
Here are some best practices to follow when using encrypted operations in your smart contracts:
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations#use-the-appropriate-encrypted-type-size)
Use the appropriate encrypted type size
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:
Copy
// Bad: Using euint256 for small numbers wastes gas
euint64 age = FHE.asEuint128(25); // age will never exceed 255
euint64 percentage = FHE.asEuint128(75); // percentage is 0-100
✅ Instead, use the smallest appropriate type:
Copy
// Good: Using appropriate sized types
euint8 age = FHE.asEuint8(25); // age fits in 8 bits
euint8 percentage = FHE.asEuint8(75); // percentage fits in 8 bits
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations#use-scalar-operands-when-possible-to-save-gas)
Use scalar operands when possible to save gas
Some FHE 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:
Copy
euint32 x;
...
x = FHE.add(x,FHE.asEuint(42));
✅ Than this one:
Copy
euint32 x;
// ...
x = FHE.add(x,42);
Despite both leading to the same encrypted result!
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations#beware-of-overflows-of-fhe-arithmetic-operators)
Beware of overflows of FHE arithmetic operators
FHE arithmetic operators can overflow. Do not forget to take into account such a possibility when implementing FHEVM smart contracts.
❌ For example, if you wanted to create a mint function for an encrypted ERC20 token with an encrypted `totalSupply` state variable, this code is vulnerable to overflows:
Copy
function mint(externalEuint32 encryptedAmount, bytes calldata inputProof) public {
euint32 mintedAmount = FHE.asEuint32(encryptedAmount, inputProof);
totalSupply = FHE.add(totalSupply, mintedAmount);
balances[msg.sender] = FHE.add(balances[msg.sender], mintedAmount);
FHE.allowThis(balances[msg.sender]);
FHE.allow(balances[msg.sender], msg.sender);
}
✅ But you can fix this issue by using `FHE.select` to cancel the mint in case of an overflow:
Copy
function mint(externalEuint32 encryptedAmount, bytes calldata inputProof) public {
euint32 mintedAmount = FHE.asEuint32(encryptedAmount, inputProof);
euint32 tempTotalSupply = FHE.add(totalSupply, mintedAmount);
ebool isOverflow = FHE.lt(tempTotalSupply, totalSupply);
totalSupply = FHE.select(isOverflow, totalSupply, tempTotalSupply);
euint32 tempBalanceOf = FHE.add(balances[msg.sender], mintedAmount);
balances[msg.sender] = FHE.select(isOverflow, balances[msg.sender], tempBalanceOf);
FHE.allowThis(balances[msg.sender]);
FHE.allow(balances[msg.sender], msg.sender);
}
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.
[PreviousSupported types](https://docs.zama.ai/protocol/solidity-guides/smart-contract/types)
[NextCasting and trivial encryption](https://docs.zama.ai/protocol/solidity-guides/smart-contract/operations/casting)
Last updated 9 days ago
---
# Deploy contracts and run tests | Protocol
In this section, you'll find everything you need to test your FHEVM smart contracts in your [Hardhat](https://hardhat.org/)
project.
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/run_test#fhevm-runtime-modes)
FHEVM Runtime Modes
The FHEVM Hardhat plugin provides three **FHEVM runtime modes** tailored for different stages of contract development and testing. Each mode offers a trade-off between speed, encryption, and persistence.
1. The **Hardhat (In-Memory)** default network: 🧪 _Uses mock encryption._ Ideal for regular tests, CI test coverage, and fast feedback during early contract development. No real encryption is used.
2. The **Hardhat Node (Local Server)** network: 🧪 _Uses mock encryption._ Ideal when you need persistent state - for example, when testing frontend interactions, simulating user flows, or validating deployments in a realistic local environment. Still uses mock encryption.
3. The **Sepolia Testnet** network: 🔐 _Uses real encryption._ Use this mode once your contract logic is stable and validated locally. This is the only mode that runs on the full FHEVM stack with **real encrypted values**. It simulates real-world production conditions but is slower and requires Sepolia ETH.
**Zama Testnet** is not a blockchain itself. It is a protocol that enables you to run confidential smart contracts on existing blockchains (such as Ethereum, Base, and others) with the support of encrypted types. See the [FHE on blockchain](https://docs.zama.ai/protocol/protocol/overview)
guide to learn more about the protocol architecture.
Currently, **Zama Protocol** is available on the **Sepolia Testnet**. Support for additional chains will be added in the future. [See the roadmap↗](https://docs.zama.ai/protocol/zama-protocol-litepaper#roadmap)
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/run_test#summary)
Summary
Mode
Encryption
Persistent
Chain
Speed
Usage
Hardhat (default)
🧪 Mock
❌ No
In-Memory
⚡⚡ Very Fast
Fast local testing and coverage
Hardhat Node
🧪 Mock
✅ Yes
Server
⚡ Fast
Frontend integration and local persistent testing
Sepolia Testnet
🔐 Real Encryption
✅ Yes
Server
🐢 Slow
Full-stack validation with real encrypted data
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/run_test#the-fhevm-hardhat-template)
The FHEVM Hardhat Template
To demonstrate the three available testing modes, we'll use the [fhevm-hardhat-template](https://github.com/zama-ai/fhevm-hardhat-template)
, which comes with the FHEVM Hardhat Plugin pre-installed, a basic `FHECounter` smart contract, and ready-to-use tasks for interacting with a deployed instance of this contract.
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/run_test#run-on-hardhat-default)
Run on Hardhat (default)
To run your tests in-memory using FHEVM mock values, simply run the following:
Copy
npx hardhat test --network hardhat
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/run_test#run-on-hardhat-node)
Run on Hardhat Node
You can also run your tests against a local Hardhat node, allowing you to deploy contract instances and interact with them in a persistent environment.
1
**Launch the Hardhat Node server:**
* Open a new terminal window.
* From the root project directory, run the following:
Copy
npx hardhat node
2
**Run your test suite (optional):**
From the root project directory:
Copy
npx hardhat test --network localhost
3
**Deploy the** `**FHECounter**` **smart contract on Hardhat Node**
From the root project directory:
Copy
npx hardhat deploy --network localhost
Check the deployed contract FHEVM configuration:
Copy
npx hardhat fhevm check-fhevm-compatibility --network localhost --address
4
**Interact with the deployed** `**FHECounter**` **smart contract**
From the root project directory:
1. Decrypt the current counter value:
Copy
npx hardhat --network localhost task:decrypt-count
1. Increment the counter by 1:
Copy
npx hardhat --network localhost task:increment --value 1
1. Decrypt the new counter value:
Copy
npx hardhat --network localhost task:decrypt-count
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/run_test#run-on-sepolia-ethereum-testnet)
Run on Sepolia Ethereum Testnet
To test your FHEVM smart contract using real encrypted values, you can run your tests on the Sepolia Testnet.
1
**Rebuild the project for Sepolia**
From the root project directory:
Copy
npx hardhat clean
npx hardhat compile --network sepolia
2
**Deploy the** `**FHECounter**` **smart contract on Sepolia**
Copy
npx hardhat deploy --network sepolia
3
**Check the deployed** `**FHECounter**` **contract FHEVM configuration**
From the root project directory:
Copy
npx hardhat fhevm check-fhevm-compatibility --network sepolia --address
If an internal exception is raised, it likely means the contract was not properly compiled for the Sepolia network.
4
**Interact with the deployed** `**FHECounter**` **contract**
From the root project directory:
1. Decrypt the current counter value (⏳ wait...):
Copy
npx hardhat --network sepolia task:decrypt-count
1. Increment the counter by 1 (⏳ wait...):
Copy
npx hardhat --network sepolia task:increment --value 1
1. Decrypt the new counter value (⏳ wait...):
Copy
npx hardhat --network sepolia task:decrypt-count
[PreviousWrite FHEVM tests in Hardhat](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_test)
[NextWrite FHEVM-enabled Hardhat Tasks](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task)
Last updated 9 days ago
---
# Write FHEVM-enabled Hardhat Tasks | Protocol
In this section, you'll learn how to write a custom FHEVM Hardhat task.
Writing tasks is a gas-efficient and flexible way to test your FHEVM smart contracts on the Sepolia network. Creating a custom task is straightforward.
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task#prerequisite)
Prerequisite
---------------------------------------------------------------------------------------------------------------------
* You should be familiar with Hardhat tasks. If you're new to them, refer to the [Hardhat Tasks official documentation](https://hardhat.org/hardhat-runner/docs/guides/tasks#writing-tasks)
.
* You should have already **completed** the [FHEVM Tutorial](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup)
.
* This page provides a step-by-step walkthrough of the `task:decrypt-count` tasks included in the file [tasks/FHECounter.ts](https://github.com/zama-ai/fhevm-hardhat-template/blob/main/tasks/FHECounter.ts)
file, located in the [fhevm-hardhat-template](https://github.com/zama-ai/fhevm-hardhat-template)
repository.
1
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task#a-basic-hardhat-task)
A Basic Hardhat Task.
--------------------------------------------------------------------------------------------------------------------------------------
Let’s start with a simple example: fetching the current counter value from a basic `Counter.sol` contract.
If you're already familiar with Hardhat and custom tasks, the TypeScript code below should look familiar and be easy to follow:
Copy
task("task:get-count", "Calls the getCount() function of Counter Contract")
.addOptionalParam("address", "Optionally specify the Counter contract address")
.setAction(async function (taskArguments: TaskArguments, hre) {
const { ethers, deployments } = hre;
const CounterDeployement = taskArguments.address
? { address: taskArguments.address }
: await deployments.get("Counter");
console.log(`Counter: ${CounterDeployement.address}`);
const counterContract = await ethers.getContractAt("Counter", CounterDeployement.address);
const clearCount = await counterContract.getCount();
console.log(`Clear count : ${clearCount}`);
});
Now, let’s modify this task to work with FHEVM encrypted values.
2
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task#comment-out-existing-logic-and-rename)
Comment Out Existing Logic and rename
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
First, comment out the existing logic so we can incrementally add the necessary changes for FHEVM integration.
Copy
task("task:get-count", "Calls the getCount() function of Counter Contract")
.addOptionalParam("address", "Optionally specify the Counter contract address")
.setAction(async function (taskArguments: TaskArguments, hre) {
// const { ethers, deployments } = hre;
// const CounterDeployement = taskArguments.address
// ? { address: taskArguments.address }
// : await deployments.get("Counter");
// console.log(`Counter: ${CounterDeployement.address}`);
// const counterContract = await ethers.getContractAt("Counter", CounterDeployement.address);
// const clearCount = await counterContract.getCount();
// console.log(`Clear count : ${clearCount}`);
});
Next, rename the task by replacing:
Copy
task("task:get-count", "Calls the getCount() function of Counter Contract")
With:
Copy
task("task:decrypt-count", "Calls the getCount() function of Counter Contract")
This updates the task name from `task:get-count` to `task:decrypt-count`, reflecting that it now includes decryption logic for FHE-encrypted values.
3
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task#initialize-fhevm-cli-api)
Initialize FHEVM CLI API
---------------------------------------------------------------------------------------------------------------------------------------------
Replace the line:
Copy
// const { ethers, deployments } = hre;
With:
Copy
const { ethers, deployments, fhevm } = hre;
await fhevm.initializeCLIApi();
Calling `initializeCLIApi()` is essential. Unlike built-in Hardhat tasks like `test` or `compile`, which automatically initialize the FHEVM runtime environment, custom tasks require you to call this function explicitly. **Make sure to call it at the very beginning of your task** to ensure the environment is properly set up.
4
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task#call-the-view-function-getcount-from-the-fhecounter-contract)
Call the view function `getCount` from the FHECounter contract
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Replace the following commented-out lines:
Copy
// const CounterDeployement = taskArguments.address
// ? { address: taskArguments.address }
// : await deployments.get("Counter");
// console.log(`Counter: ${CounterDeployement.address}`);
// const counterContract = await ethers.getContractAt("Counter", CounterDeployement.address);
// const clearCount = await counterContract.getCount();
With the FHEVM equivalent:
Copy
const FHECounterDeployement = taskArguments.address
? { address: taskArguments.address }
: await deployments.get("FHECounter");
console.log(`FHECounter: ${FHECounterDeployement.address}`);
const fheCounterContract = await ethers.getContractAt("FHECounter", FHECounterDeployement.address);
const encryptedCount = await fheCounterContract.getCount();
if (encryptedCount === ethers.ZeroHash) {
console.log(`encrypted count: ${encryptedCount}`);
console.log("clear count : 0");
return;
}
Here, `encryptedCount` is an FHE-encrypted `euint32` primitive. To retrieve the actual value, we need to decrypt it in the next step.
5
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task#decrypt-the-encrypted-count-value)
Decrypt the encrypted count value.
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Now replace the following commented-out line:
Copy
// console.log(`Clear count : ${clearCount}`);
With the decryption logic:
Copy
const signers = await ethers.getSigners();
const clearCount = await fhevm.userDecryptEuint(
FhevmType.euint32,
encryptedCount,
FHECounterDeployement.address,
signers[0],
);
console.log(`Encrypted count: ${encryptedCount}`);
console.log(`Clear count : ${clearCount}`);
At this point, your custom Hardhat task is fully configured to work with FHE-encrypted values and ready to run!
6
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task#step-6-run-your-custom-task-using-hardhat-node)
Step 6: Run your custom task using Hardhat Node
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
**Start the Local Hardhat Node:**
* Open a new terminal window.
* From the root project directory, run the following:
Copy
npx hardhat node
**Deploy the FHECounter smart contract on the local Hardhat Node**
Copy
npx hardhat deploy --network localhost
**Run your custom task**
Copy
npx hardhat task:decrypt-count --network localhost
7
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_task#step-7-run-your-custom-task-using-sepolia)
Step 7: Run your custom task using Sepolia
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
**Deploy the FHECounter smart contract on Sepolia Testnet (if not already deployed)**
Copy
npx hardhat deploy --network sepolia
**Execute your custom task**
Copy
npx hardhat task:decrypt-count --network sepolia
[PreviousDeploy contracts and run tests](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/run_test)
[NextFoundry](https://docs.zama.ai/protocol/solidity-guides/development-guide/foundry)
Last updated 9 days ago
---
# Error handling | Protocol
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.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/error_handling#challenges-in-error-handling)
**Challenges in error handling**
---------------------------------------------------------------------------------------------------------------------------------------------------------
In the context of encrypted data:
1. **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.
2. **Limited feedback**: Encrypted computations lack direct mechanisms for exposing failure reasons while maintaining confidentiality.
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/error_handling#recommended-approach-error-logging-with-a-handler)
**Recommended approach: Error logging with a handler**
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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.
###
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/error_handling#example-implementation)
**Example implementation**
The following contract snippet demonstrates how to implement and use an error handler:
Copy
struct LastError {
euint8 error; // Encrypted error code
uint timestamp; // Timestamp of the error
}
// Define error codes
euint8 internal NO_ERROR;
euint8 internal NOT_ENOUGH_FUNDS;
constructor() {
NO_ERROR = FHE.asEuint8(0); // Code 0: No error
NOT_ENOUGH_FUNDS = FHE.asEuint8(1); // Code 1: Insufficient funds
}
// Store the last error for each address
mapping(address => LastError) private _lastErrors;
// Event to notify about an error state change
event ErrorChanged(address indexed user);
/**
* @dev Set the last error for a specific address.
* @param error Encrypted error code.
* @param addr Address of the user.
*/
function setLastError(euint8 error, address addr) private {
_lastErrors[addr] = LastError(error, block.timestamp);
emit ErrorChanged(addr);
}
/**
* @dev Internal transfer function with error handling.
* @param from Sender's address.
* @param to Recipient's address.
* @param amount Encrypted transfer amount.
*/
function _transfer(address from, address to, euint32 amount) internal {
// Check if the sender has enough balance to transfer
ebool canTransfer = FHE.le(amount, balances[from]);
// Log the error state: NO_ERROR or NOT_ENOUGH_FUNDS
setLastError(FHE.select(canTransfer, NO_ERROR, NOT_ENOUGH_FUNDS), msg.sender);
// Perform the transfer operation conditionally
balances[to] = FHE.add(balances[to], FHE.select(canTransfer, amount, FHE.asEuint32(0)));
FHE.allowThis(balances[to]);
FHE.allow(balances[to], to);
balances[from] = FHE.sub(balances[from], FHE.select(canTransfer, amount, FHE.asEuint32(0)));
FHE.allowThis(balances[from]);
FHE.allow(balances[from], from);
}
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/error_handling#how-it-works)
**How It Works**
-------------------------------------------------------------------------------------------------------------------------
1. **Define error codes**:
* `NO_ERROR`: Indicates a successful operation.
* `NOT_ENOUGH_FUNDS`: Indicates insufficient balance for a transfer.
2. **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.
3. **Conditional updates**:
* Use the `FHE.select` function to update balances and log errors based on the transfer condition (`canTransfer`).
4. **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."
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/error_handling#example-error-query)
**Example error query**
---------------------------------------------------------------------------------------------------------------------------------------
The frontend or another contract can query the `_lastErrors` mapping to retrieve error details:
Copy
/**
* @dev Get the last error for a specific address.
* @param user Address of the user.
* @return error Encrypted error code.
* @return timestamp Timestamp of the error.
*/
function getLastError(address user) public view returns (euint8 error, uint timestamp) {
LastError memory lastError = _lastErrors[user];
return (lastError.error, lastError.timestamp);
}
[](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/error_handling#benefits-of-this-approach)
**Benefits of this approach**
---------------------------------------------------------------------------------------------------------------------------------------------------
1. **User feedback**:
* Provides actionable error messages without compromising the confidentiality of encrypted computations.
2. **Scalable error tracking**:
* Logs errors per user, making it easy to identify and debug specific issues.
3. **Event-driven notifications**:
* Enables frontends to react to errors in real time via the `ErrorChanged` event.
By implementing error handlers as demonstrated, developers can ensure a seamless user experience while maintaining the privacy and integrity of encrypted data operations.
[PreviousDealing with branches and conditions](https://docs.zama.ai/protocol/solidity-guides/smart-contract/logics/loop)
[NextDecryption](https://docs.zama.ai/protocol/solidity-guides/smart-contract/oracle)
Last updated 9 days ago
---
# HCU | Protocol
This guide explains how to use Fully Homomorphic Encryption (FHE) operations in your smart contracts on FHEVM. Understanding HCU is critical for designing efficient confidential smart contracts.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#overview)
Overview
---------------------------------------------------------------------------------------------------
FHE operations in FHEVM are computationally intensive compared to standard Ethereum operations, as they require complex mathematical computations to maintain privacy and security. To manage computational load and prevent potential denial-of-service attacks, FHEVM implements a metering system called **Homomorphic Complexity Units ("HCU")**.
To represent this complexity, we introduced the **Homomorphic Complexity Unit ("HCU")**. In Solidity, each FHE operation consumes a set amount of HCU based on the operational computational complexity for hardware computation. Since FHE transactions are symbolic, this helps preventing resource exhaustion outside of the blockchain.
To do so, there is a contract named `HCULimit`, which monitors HCU consumption for each transaction and enforces two key limits:
* **Sequential homomorphic operations depth limit per transaction**: Controls HCU usage for operations that must be processed in order.
* **Global homomorphic operations complexity per transaction**: Controls HCU usage for operations that can be processed in parallel.
If either limit is exceeded, the transaction will revert.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#hcu-limit)
HCU limit
-----------------------------------------------------------------------------------------------------
The current devnet has an HCU limit of **20,000,000** per transaction and an HCU depth limit of **5,000,000** per transaction. If either HCU limit is exceeded, the transaction will revert.
To resolve this, you must do one of the following:
* Refactor your code to reduce the number of FHE operations in your transaction.
* Split your FHE operations across multiple independent transactions.
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#hcu-costs-for-common-operations)
HCU costs for common operations
-------------------------------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#boolean-operations-ebool)
Boolean operations (`ebool`)
Function Name
HCU
`and`/`or`/`xor`
26,000
`not`
30,000
* * *
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#unsigned-integer-operations)
Unsigned integer operations
HCU increase with the bit-width of the encrypted integer type. Below are the detailed costs for various operations on encrypted types.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#id-8-bit-encrypted-integers-euint8)
**8-bit Encrypted integers (**`**euint8**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
84,000
87,000
`sub`
83,000
84,000
`mul`
117,000
146,000
`div`
203,000
\-
`rem`
387,000
\-
`and`
28,000
29,000
`or`
28,000
28,000
`xor`
29,000
29,000
`shr`
28,000
88,000
`shl`
29,000
86,000
`rotr`
29,000
86,000
`rotl`
29,000
87,000
`eq`
52,000
49,000
`ne`
49,000
52,000
`ge`
60,000
55,000
`gt`
53,000
56,000
`le`
53,000
54,000
`lt`
51,000
56,000
`min`
86,000
111,000
`max`
81,000
111,000
`neg`
\-
72,000
`not`
\-
8,000
`select`
\-
43,000
`randEuint8()`
\-
100,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#id-16-bit-encrypted-integers-euint16)
**16-bit Encrypted integers (**`**euint16**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
87,000
87,000
`sub`
86,000
88,000
`mul`
176,000
207,000
`div`
283,000
\-
`rem`
513,000
\-
`and`
29,000
29,000
`or`
29,000
29,000
`xor`
29,000
29,000
`shr`
29,000
118,000
`shl`
29,000
118,000
`rotr`
30,000
117,000
`rotl`
29,000
117,000
`eq`
52,000
78,000
`ne`
51,000
82,000
`ge`
60,000
80,000
`gt`
53,000
83,000
`le`
54,000
80,000
`lt`
53,000
80,000
`min`
86,000
141,000
`max`
83,000
140,000
`neg`
\-
89,000
`not`
\-
15,000
`select`
\-
44,000
`randEuint16()`
\-
100,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#id-32-bit-encrypted-integers-euint32)
**32-bit Encrypted Integers (**`**euint32**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
87,000
121,000
`sub`
87,000
120,000
`mul`
244,000
313,000
`div`
397,000
\-
`rem`
714,000
\-
`and`
29,000
30,000
`or`
30,000
31,000
`xor`
30,000
30,000
`shr`
30,000
150,000
`shl`
30,000
150,000
`rotr`
30,000
149,000
`rotl`
30,000
150,000
`eq`
81,000
82,000
`ne`
80,000
84,000
`ge`
81,000
111,000
`gt`
82,000
111,000
`le`
80,000
113,000
`lt`
80,000
111,000
`min`
113,000
177,000
`max`
112,000
174,000
`neg`
\-
116,000
`not`
\-
28,000
`select`
\-
45,000
`randEuint32()`
\-
100,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#id-64-bit-encrypted-integers-euint64)
**64-bit Encrypted integers (**`**euint64**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
128,000
156,000
`sub`
129,000
159,000
`mul`
346,000
571,000
`div`
651,000
\-
`rem`
1,111,000
\-
`and`
33,000
33,000
`or`
32,000
33,000
`xor`
33,000
32,000
`shr`
34,000
203,000
`shl`
33,000
203,000
`rotr`
34,000
206,000
`rotl`
34,000
203,000
`eq`
83,000
116,000
`ne`
84,000
111,000
`ge`
112,000
146,000
`gt`
113,000
141,000
`le`
113,000
146,000
`lt`
113,000
142,000
`min`
149,000
210,000
`max`
147,000
211,000
`neg`
\-
150,000
`not`
\-
84,000
`select`
\-
52,000
`randEuint64()`
\-
100,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#id-128-bit-encrypted-integers-euint128)
**128-bit Encrypted integers (**`**euint128**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
159,000
249,000
`sub`
159,000
244,000
`mul`
646,000
1,671,000
`div`
1,290,000
\-
`rem`
1,900,000
\-
`and`
33,000
34,000
`or`
34,000
35,000
`xor`
35,000
35,000
`shr`
33,000
254,000
`shl`
33,000
251,000
`rotr`
34,000
261,000
`rotl`
33,000
264,000
`eq`
115,000
117,000
`ne`
115,000
116,000
`ge`
144,000
206,000
`gt`
144,000
206,000
`le`
143,000
204,000
`lt`
143,000
204,000
`min`
180,000
280,000
`max`
181,000
274,000
`neg`
\-
241,000
`not`
\-
109,000
`select`
\-
51,000
`randEuint128()`
\-
100,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#id-256-bit-encrypted-integers-euint256)
**256-bit Encrypted integers (**`**euint256**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`and`
37,000
38,000
`or`
37,000
37,000
`xor`
37,000
37,000
`shr`
37,000
359,000
`shl`
37,000
359,000
`rotr`
37,000
367,000
`rotl`
37,000
367,000
`eq`
117,000
151,000
`ne`
117,000
149,000
`neg`
\-
269,000
`not`
\-
216,000
`select`
\-
71,000
`randEuint256()`
\-
100,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#encrypted-addresses-euint160)
Encrypted addresses (`euint160`)\*\*
When using `eaddress` (internally represented as `euint160`), the HCU costs for equality and inequality checks are as follows:
Function name
HCU (scalar)
HCU (non-scalar)
`eq`
115,000
125,000
`ne`
115,000
124,000
[](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/hcu#additional-operations)
Additional Operations
-----------------------------------------------------------------------------------------------------------------------------
Function name
HCU
`cast`
200
`trivialEncrypt`
100-800
`randBounded`
100,000
`rand`
100,000
`select`
43,000-71,000
[PreviousFoundry](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/foundry)
[NextMigrate to v0.7](https://docs.zama.ai/protocol/solidity-guides/v0.7/development-guide/migration)
Last updated 3 months ago
---
# 3. Turn it into FHEVM | Protocol
In this tutorial, you'll learn how to take a basic Solidity smart contract and progressively upgrade it to support Fully Homomorphic Encryption using the FHEVM library by Zama.
Starting with the plain `Counter.sol` contract that you built from the ["Write a simple contract" tutorial](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract)
, and step-by-step, you’ll learn how to:
* Replace standard types with encrypted equivalents
* Integrate zero-knowledge proof validation
* Enable encrypted on-chain computation
* Grant permissions for secure off-chain decryption
By the end, you'll have a fully functional smart contract that supports FHE computation.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#initiate-the-contract)
Initiate the contract
----------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#create-the-fhecounter.sol-file)
Create the `FHECounter.sol` file
Navigate to your project’s `contracts` directory:
Copy
cd /contracts
From there, create a new file named `FHECounter.sol`, and copy the following Solidity code into it:
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title A simple counter contract
contract Counter {
uint32 private _count;
/// @notice Returns the current count
function getCount() external view returns (uint32) {
return _count;
}
/// @notice Increments the counter by a specific value
function increment(uint32 value) external {
_count += value;
}
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
}
This is a plain `Counter` contract that we’ll use as the starting point for adding FHEVM functionality. We will modify this contract step-by-step to progressively integrate FHEVM capabilities.
2
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#turn-counter-into-fhecounter)
Turn `Counter` into `FHECounter`
To begin integrating FHEVM features into your contract, we first need to import the required FHEVM libraries.
**Replace the current header**
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
**With this updated header:**
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { FHE, euint32, externalEuint32 } from "@fhevm/solidity/lib/FHE.sol";
import { SepoliaConfig } from "@fhevm/solidity/config/ZamaConfig.sol";
These imports:
* **FHE** — the core library to work with FHEVM encrypted types
* **euint32** and **externalEuint32** — encrypted uint32 types used in FHEVM
* **SepoliaConfig** — provides the FHEVM configuration for the Sepolia network. Inheriting from it enables your contract to use the FHE library
**Replace the current contract declaration:**
Copy
/// @title A simple counter contract
contract Counter {
**With the updated declaration :**
Copy
/// @title A simple FHE counter contract
contract FHECounter is SepoliaConfig {
This change:
* Renames the contract to `FHECounter`
* Inherits from `SepoliaConfig` to enable FHEVM support
This contract must inherit from the `SepoliaConfig` abstract contract; otherwise, it will not be able to execute any FHEVM-related functionality on Sepolia or Hardhat.
From your project's root directory, run:
Copy
npx hardhat compile
Great! Your smart contract is now compiled and ready to use **FHEVM features.**
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#apply-fhe-functions-and-types)
Apply FHE functions and types
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#comment-out-the-increment-and-decrement-functions)
Comment out the `increment()` and `decrement()` Functions
Before we move forward, let’s comment out the `increment()` and `decrement()` functions in `FHECounter`. We'll replace them later with updated versions that support FHE-encrypted operations.
Copy
/// @notice Increments the counter by a specific value
// function increment(uint32 value) external {
// _count += value;
// }
/// @notice Decrements the counter by a specific value
// function decrement(uint32 value) external {
// require(_count >= value, "Counter: cannot decrement below zero");
// _count -= value;
// }
2
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#replace-uint32-with-the-fhevm-euint32-type)
Replace `uint32` with the FHEVM `euint32` Type
We’ll now switch from the standard Solidity `uint32` type to the encrypted FHEVM type `euint32`.
This enables private, homomorphic computation on encrypted integers.
**Replace**
Copy
uint32 _count;
and
Copy
function getCount() external view returns (uint32) {
**With :**
Copy
euint32 _count;
and
Copy
function getCount() external view returns (euint32) {
3
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#replace-increment-uint32-value-with-the-fhevm-version-increment-externaleuint32-value)
Replace `increment(uint32 value)` with the FHEVM version `increment(externalEuint32 value)`
To support encrypted input, we will update the increment function to accept a value encrypted off-chain.
Instead of using a `uint32`, the new version will accept an `externalEuint32`, which is an encrypted integer produced off-chain and sent to the smart contract.
To ensure the validity of this encrypted value, we also include a second argument:`inputProof`, a bytes array containing a Zero-Knowledge Proof of Knowledge (ZKPoK) that proves two things:
1. The `externalEuint32` was encrypted off-chain by the function caller (`msg.sender`)
2. The `externalEuint32` is bound to the contract (`address(this)`) and can only be processed by it.
**Replace**
Copy
/// @notice Increments the counter by a specific value
// function increment(uint32 value) external {
// _count += value;
// }
**With :**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
// _count += value;
}
4
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#convert-externaleuint32-to-euint32)
Convert `externalEuint32` to `euint32`
You cannot directly use `externalEuint32` in FHE operations. To manipulate it with the FHEVM library, you first need to convert it into the native FHE type `euint32`.
This conversion is done using:
Copy
FHE.fromExternal(inputEuint32, inputProof);
This method verifies the zero-knowledge proof and returns a usable encrypted value within the contract.
**Replace**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
// _count += value;
}
**With :**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
// _count += value;
}
5
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#convert-_count--value-into-its-fhevm-equivalent)
Convert `_count += value` into its FHEVM equivalent
To perform the update `_count += value` in a Fully Homomorphic way, we use the `FHE.add()` operator. This function allows us to compute the FHE sum of 2 encrypted integers.
**Replace**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
// _count += value;
}
**With :**
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, evalue);
}
This FHE operation allows the smart contract to process encrypted values without ever decrypting them — a core feature of FHEVM that enables on-chain privacy.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#grant-fhe-permissions)
Grant FHE Permissions
----------------------------------------------------------------------------------------------------------------------------------------------------------
This step is critical! You must grant FHE permissions to both the contract and the caller to ensure the encrypted `_count` value can be decrypted off-chain by the caller. Without these 2 permissions, the caller will not be able to compute the clear result.
To grant FHE permission we will call the `FHE.allow()` function.
####
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#replace)
Replace
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, evalue);
}
####
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#with)
With :
Copy
/// @notice Increments the counter by a specific value
function increment(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 evalue = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.add(_count, evalue);
FHE.allowThis(_count);
FHE.allow(_count, msg.sender);
}
We grant **two** FHE permissions here — not just one. In the next part of the tutorial, you'll learn why **both** are necessary.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#convert-decrement-to-its-fhevm-equivalent)
Convert `decrement()` to its FHEVM equivalent
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Just like with the `increment()` migration, we’ll now convert the `decrement()` function to its FHEVM-compatible version.
Replace :
Copy
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
with the following :
Copy
/// @notice Decrements the counter by a specific value
/// @dev This example omits overflow/underflow checks for simplicity and readability.
/// In a production contract, proper range checks should be implemented.
function decrement(externalEuint32 inputEuint32, bytes calldata inputProof) external {
euint32 encryptedEuint32 = FHE.fromExternal(inputEuint32, inputProof);
_count = FHE.sub(_count, encryptedEuint32);
FHE.allowThis(_count);
FHE.allow(_count, msg.sender);
}
The `increment()` and `decrement()` functions do not perform any overflow or underflow checks.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm#compile-fhecounter.sol)
Compile `FHECounter.sol`
--------------------------------------------------------------------------------------------------------------------------------------------------------------
From your project's root directory, run:
Copy
npx hardhat compile
Congratulations! Your smart contract is now fully **FHEVM-compatible**.
Now you should have the following files in your project:
* [`contracts/FHECounter.sol`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#fhecounter.sol)
— your Solidity smart FHEVM contract
* [`test/FHECounter.ts`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#fhecounter.ts)
— your FHEVM Hardhat test suite written in TypeScript
In the [next tutorial](https://github.com/zama-ai/fhevm/blob/release/0.9.x/docs/solidity-guides/getting-started/quick-start-tutorial/test_fhevm_contract.md)
, we’ll move on to the **TypeScript integration**, where you’ll learn how to interact with your newly upgraded FHEVM contract in a test suite.
[Previous2\. Write a simple contract](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract)
[Next4\. Test the FHEVM contract](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract)
Last updated 9 days ago
---
# Set up Hardhat | Protocol
In this section, you’ll learn how to set up a FHEVM Hardhat development environment using the **FHEVM Hardhat template** as a starting point for building and testing fully homomorphic encrypted smart contracts.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#create-a-local-hardhat-project)
Create a local Hardhat Project
------------------------------------------------------------------------------------------------------------------------------------------
1
####
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#install-a-node.js-tls-version)
Install a Node.js TLS version
Ensure that Node.js is installed on your machine.
* Download and install the recommended LTS (Long-Term Support) version from the [official website](https://nodejs.org/en)
.
* Use an **even-numbered** version (e.g., `v18.x`, `v20.x`)
**Hardhat** does not support odd-numbered Node.js versions. If you’re using one (e.g., v21.x, v23.x), Hardhat will display a persistent warning message and may behave unexpectedly.
To verify your installation:
Copy
node -v
npm -v
2
####
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#create-a-new-github-repository-from-the-fhevm-hardhat-template)
Create a new GitHub repository from the FHEVM Hardhat template.
1. On GitHub, navigate to the main page of the [FHEVM Hardhat template](https://github.com/zama-ai/fhevm-hardhat-template)
repository.
2. Above the file list, click the green **Use this template** button.
3. Follow the instructions to create a new repository from the FHEVM Hardhat template.
See Github doc: [Creating a repository from a template](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template#creating-a-repository-from-a-template)
3
####
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#clone-your-newly-created-github-repository-locally)
Clone your newly created GitHub repository locally
Now that your GitHub repository has been created, you can clone it to your local machine:
Copy
cd
git clone
# Navigate to the root of your new FHEVM Hardhat project
cd
Next, let’s install your local Hardhat development environment.
4
####
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#install-your-fhevm-hardhat-project-dependencies)
Install your FHEVM Hardhat project dependencies
From the project root directory, run:
Copy
npm install
This will install all required dependencies defined in your `package.json`, setting up your local FHEVM Hardhat development environment.
5
####
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#set-up-the-hardhat-configuration-variables-optional)
Set up the Hardhat configuration variables (optional)
If you do plan to deploy to the Sepolia Ethereum Testnet, you'll need to set up the following [Hardhat Configuration variables](https://hardhat.org/hardhat-runner/docs/guides/configuration-variables)
.
`MNEMONIC`
A mnemonic is a 12-word seed phrase used to generate your Ethereum wallet keys.
1. Get one by creating a wallet with [MetaMask](https://metamask.io/)
, or using any trusted mnemonic generator.
2. Set it up in your Hardhat project:
Copy
npx hardhat vars set MNEMONIC
`INFURA_API_KEY`
The INFURA project key allows you to connect to Ethereum testnets like Sepolia.
1. Obtain one by following the [Infura + MetaMask](https://docs.metamask.io/services/get-started/infura/)
setup guide.
2. Configure it in your project:
Copy
npx hardhat vars set INFURA_API_KEY
**Default Values**
If you skip this step, Hardhat will fall back to these defaults:
* `MNEMONIC` = "test test test test test test test test test test test junk"
* `INFURA_API_KEY` = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"
These defaults are not suitable for real deployments.
####
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#missing-variable-error)
Missing variable error:
If any of the requested Hardhat Configuration Variables is missing, you'll get an error message like this one:`Error HH1201: Cannot find a value for the configuration variable 'MNEMONIC'. Use 'npx hardhat vars set MNEMONIC' to set it or 'npx hardhat var setup' to list all the configuration variables used by this project.`
Congratulations! You're all set to start building your confidential dApp.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#optional-settings)
Optional settings
----------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#install-vscode-extensions)
Install VSCode extensions
If you're using Visual Studio Code, there are some extensions available to improve you your development experience:
* [Prettier - Code formatter by prettier.io](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
— ID:`esbenp.prettier-vscode`,
* [ESLint by Microsoft](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
— ID:`dbaeumer.vscode-eslint`
Solidity support (pick one only):
* [Solidity by Juan Blanco](https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity)
— ID:`juanblanco.solidity`
* [Solidity by Nomic Foundation](https://marketplace.visualstudio.com/items?itemName=NomicFoundation.hardhat-solidity)
— ID:`nomicfoundation.hardhat-solidity`
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#reset-the-hardhat-project)
Reset the Hardhat project
If you'd like to start from a clean slate, you can reset your FHEVM Hardhat project by removing all example code and generated files.
Copy
# Navigate to the root of your new FHEVM Hardhat project
cd
Then run:
Copy
rm -rf test/* src/* tasks/* deploy ./fhevmTemp ./artifacts ./cache ./coverage ./types ./coverage.json ./dist
[PreviousWhat is FHEVM Solidity](https://docs.zama.ai/protocol/solidity-guides/getting-started/overview)
[NextQuick start tutorial](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial)
Last updated 9 days ago
---
# Write FHEVM tests in Hardhat | Protocol
In this section, you'll find everything you need to set up a new [Hardhat](https://hardhat.org/)
project and start developing FHEVM smart contracts from scratch using the [FHEVM Hardhat Plugin](https://www.npmjs.com/package/@fhevm/hardhat-plugin)
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_test#enabling-the-fhevm-hardhat-plugin-in-your-hardhat-project)
Enabling the FHEVM Hardhat Plugin in your Hardhat project
Like any Hardhat plugin, the [FHEVM Hardhat Plugin](https://www.npmjs.com/package/@fhevm/hardhat-plugin)
must be enabled by adding the following `import` statement to your `hardhat.config.ts` file:
Copy
import "@fhevm/hardhat-plugin";
Without this import, the Hardhat FHEVM API will **not** be available in your Hardhat runtime environment (HRE).
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_test#accessing-the-hardhat-fhevm-api)
Accessing the Hardhat FHEVM API
The plugin extends the standard [Hardhat Runtime Environment](https://hardhat.org/hardhat-runner/docs/advanced/hardhat-runtime-environment)
(or `hre` in short) with the new `fhevm` Hardhat module.
You can access it in either of the following ways:
Copy
import { fhevm } from "hardhat";
or
Copy
import * as hre from "hardhat";
// Then access: hre.fhevm
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_test#encrypting-values-using-the-hardhat-fhevm-api)
Encrypting Values Using the Hardhat FHEVM API
Suppose the FHEVM smart contract you want to test has a function called `foo` that takes an encrypted `uint32` value as input. The Solidity function `foo` should be declared as follows:
Copy
function foo(externalEunit32 value, bytes calldata memory inputProof);
Where:
* `externalEunit32 value` : is a `bytes32` representing the encrypted `uint32`
* `bytes calldata memory inputProof` : is a `bytes` array representing the zero-knowledge proof of knowledge that validates the encryption
To compute these arguments in TypeScript, you need:
* The **address of the target smart contract**
* The **signer’s address** (i.e., the account sending the transaction)
1
**Create a new encryted input**
Copy
// use the `fhevm` API module from the Hardhat Runtime Environment
const input = fhevm.createEncryptedInput(contractAddress, signers.alice.address);
2
**Add the value you want to encrypt.**
Copy
input.add32(12345);
3
**Perform local encryption.**
Copy
const encryptedInputs = await input.encrypt();
4
**Call the Solidity function**
Copy
const externalUint32Value = encryptedInputs.handles[0];
const inputProof = encryptedInputs.proof;
const tx = await input.foo(externalUint32Value, inputProof);
await tx.wait();
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_test#encryption-examples)
Encryption examples
* [Basic encryption examples](https://docs.zama.ai/protocol/examples/basic/encryption)
* [FHECounter](https://docs.zama.ai/protocol/examples#an-fhe-counter)
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_test#decrypting-values-using-the-hardhat-fhevm-api)
Decrypting values using the Hardhat FHEVM API
Suppose user **Alice** wants to decrypt a `euint32` value that is stored in a smart contract exposing the following Solidity `view` function:
Copy
function getEncryptedUint32Value() public view returns (euint32) { returns _encryptedUint32Value; }
For simplicity, we assume that both Alice’s account and the target smart contract already have the necessary FHE permissions to decrypt this value. For a detailed explanation of how FHE permissions work, see the [`initializeUint32()`](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-decrypt-single-value#tab-decryptsinglevalue.sol)
function in [DecryptSingleValue.sol](https://docs.zama.ai/protocol/examples/basic/decryption/fhe-decrypt-single-value#tab-decryptsinglevalue.sol)
.
1
**Retrieve the encrypted value (a** `**bytes32**` **handle) from the smart contract:**
Copy
const encryptedUint32Value = await contract.getEncryptedUint32Value();
2
**Perform the decryption using the FHEVM API:**
Copy
const clearUint32Value = await fhevm.userDecryptEuint(
FhevmType.euint32, // Encrypted type (must match the Solidity type)
encryptedUint32Value, // bytes32 handle Alice wants to decrypt
contractAddress, // Target contract address
signers.alice, // Alice’s wallet
);
If either the target smart contract or the user does **NOT** have FHE permissions, then the decryption call will fail!
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_test#supported-decryption-types)
Supported Decryption Types
Use the appropriate function for each encrypted data type:
Type
Function
`euintXXX`
`fhevm.userDecryptEuint(...)`
`ebool`
`fhevm.userDecryptEbool(...)`
`eaddress`
`fhevm.userDecryptEaddress(...)`
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/write_test#decryption-examples)
Decryption examples
* [Basic decryption examples](https://docs.zama.ai/protocol/examples/basic/decryption)
* [FHECounter](https://docs.zama.ai/protocol/examples#an-fhe-counter)
[PreviousHardhat plugin](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat)
[NextDeploy contracts and run tests](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/run_test)
Last updated 9 days ago
---
# 2. Write a simple contract | Protocol
In this tutorial, you'll write and test a simple regular Solidity smart contract within the FHEVM Hardhat template to get familiar with Hardhat workflow.
In the [next tutorial](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm)
, you'll learn how to convert this contract into an FHEVM contract.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#prerequisite)
Prerequisite
---------------------------------------------------------------------------------------------------------------------------------------------
* [Set up your Hardhat envrionment](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup)
.
* Make sure that you Hardhat project is clean and ready to start. See the instructions [here](https://docs.zama.ai/protocol/solidity-guides/getting-started/setup#rest-set-the-hardhat-envrionment)
.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#what-youll-learn)
What you'll learn
------------------------------------------------------------------------------------------------------------------------------------------------------
By the end of this tutorial, you will learn to:
* Write a minimal Solidity contract using Hardhat.
* Test the contract using TypeScript and Hardhat’s testing framework.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#write-a-simple-contract)
Write a simple contract
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#create-counter.sol)
Create `Counter.sol`
Go to your project's `contracts` directory:
Copy
cd /contracts
From there, create a new file named `Counter.sol` and copy/paste the following Solidity code in it.
Copy
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/// @title A simple counter contract
contract Counter {
uint32 private _count;
/// @notice Returns the current count
function getCount() external view returns (uint32) {
return _count;
}
/// @notice Increments the counter by a specific value
function increment(uint32 value) external {
_count += value;
}
/// @notice Decrements the counter by a specific value
function decrement(uint32 value) external {
require(_count >= value, "Counter: cannot decrement below zero");
_count -= value;
}
}
2
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#compile-counter.sol)
Compile `Counter.sol`
From your project's root directory, run:
Copy
npx hardhat compile
Great! Your Smart Contract is now compiled.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#set-up-the-testing-environment)
Set up the testing environment
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#create-a-test-script-test-counter.ts)
Create a test script `test/Counter.ts`
Go to your project's `test` directory
Copy
cd /test
From there, create a new file named `Counter.ts` and copy/paste the following Typescript skeleton code in it.
Copy
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { ethers } from "hardhat";
describe("Counter", function () {
it("empty test", async function () {
console.log("Cool! The test basic skeleton is running!");
});
});
The file contains the following:
* all the required `import` statements we will need during the various tests
* The `chai` basic statements to run a first empty test named `empty test`
2
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#run-the-test-test-counter.ts)
Run the test `test/Counter.ts`
From your project's root directory, run:
Copy
npx hardhat test
Output:
Copy
Counter
Cool! The test basic skeleton is running!
✔ empty test
1 passing (1ms)
Great! Your Hardhat test environment is properly setup.
3
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#set-up-the-test-signers)
Set up the test signers
Before interacting with smart contracts in Hardhat tests, we need to initialize signers.
In the context of Ethereum development, a signer represents an entity (usually a wallet) that can send transactions and sign messages. In Hardhat, `ethers.getSigners()` returns a list of pre-funded test accounts.
We’ll define three named signers for convenience:
* `owner` — the deployer of the contract
* `alice` and `bob` — additional simulated users
**Replace the contents of** `**test/Counter.ts**` **with the following:**
Copy
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { ethers } from "hardhat";
type Signers = {
owner: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
describe("Counter", function () {
let signers: Signers;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { owner: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
it("should work", async function () {
console.log(`address of user owner is ${signers.owner.address}`);
console.log(`address of user alice is ${signers.alice.address}`);
console.log(`address of user bob is ${signers.bob.address}`);
});
});
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
address of user owner is 0x37AC010c1c566696326813b840319B58Bb5840E4
address of user alice is 0xD9F9298BbcD72843586e7E08DAe577E3a0aC8866
address of user bob is 0x3f0CdAe6ebd93F9F776BCBB7da1D42180cC8fcC1
✔ should work
1 passing (2ms)
4
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#set-up-testing-instance)
Set up testing instance
Now that we have our signers set up, we can deploy the smart contract.
To ensure isolated and deterministic tests, we should deploy a fresh instance of `Counter.sol` before each test. This avoids any side effects from previous tests.
The standard approach is to define a `deployFixture()` function that handles contract deployment.
Copy
async function deployFixture() {
const factory = (await ethers.getContractFactory("Counter")) as Counter__factory;
const counterContract = (await factory.deploy()) as Counter;
const counterContractAddress = await counterContract.getAddress();
return { counterContract, counterContractAddress };
}
To run this setup before each test case, call `deployFixture()` inside a `beforeEach` block:
Copy
beforeEach(async () => {
({ counterContract, counterContractAddress } = await deployFixture());
});
This ensures each test runs with a clean, independent contract instance.
Let's put it together. Now your`test/Counter.ts` should look like the following:
Copy
import { Counter, Counter__factory } from "../types";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";
type Signers = {
deployer: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
async function deployFixture() {
const factory = (await ethers.getContractFactory("Counter")) as Counter__factory;
const counterContract = (await factory.deploy()) as Counter;
const counterContractAddress = await counterContract.getAddress();
return { counterContract, counterContractAddress };
}
describe("Counter", function () {
let signers: Signers;
let counterContract: Counter;
let counterContractAddress: Counter;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { deployer: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
beforeEach(async () => {
// Deploy a new instance of the contract before each test
({ counterContract, counterContractAddress } = await deployFixture());
});
it("should be deployed", async function () {
console.log(`Counter has been deployed at address ${counterContractAddress}`);
// Test the deployed address is valid
expect(ethers.isAddress(counterContractAddress)).to.eq(true);
});
});
**Run the test:**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output:**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
1 passing (7ms)
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#test-functions)
Test functions
-------------------------------------------------------------------------------------------------------------------------------------------------
Now everything is up and running, you can start testing your contract functions.
1
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#call-the-contract-getcount-view-function)
Call the contract `getCount()` view function
Everything is up and running, we can now call the `Counter.sol` view function `getCount()` !
Just below the test block `it("should be deployed", async function () {...}`,
add the following unit test:
Copy
it("count should be zero after deployment", async function () {
const count = await counterContract.getCount();
console.log(`Counter.getCount() === ${count}`);
// Expect initial count to be 0 after deployment
expect(count).to.eq(0);
});
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
Counter.getCount() === 0
✔ count should be zero after deployment
1 passing (7ms)
2
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#call-the-contract-increment-transaction-function)
Call the contract `increment()` transaction function
Just below the test block `it("count should be zero after deployment", async function () {...}`, add the following test block:
Copy
it("increment the counter by 1", async function () {
const countBeforeInc = await counterContract.getCount();
const tx = await counterContract.connect(signers.alice).increment(1);
await tx.wait();
const countAfterInc = await counterContract.getCount();
expect(countAfterInc).to.eq(countBeforeInc + 1n);
});
**Remarks:**
* `increment()` is a transactional function that modifies the blockchain state.
* It must be signed by a user — here we use `alice`.
* `await wait()` to wait for the transaction to mined.
* The test compares the counter before and after the transaction to ensure it incremented as expected.
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
Counter.getCount() === 0
✔ count should be zero after deployment
✔ increment the counter by 1
2 passing (12ms)
3
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#call-the-contract-decrement-transaction-function)
Call the contract `decrement()` transaction function
Just below the test block `it("increment the counter by 1", async function () {...}`,
add the following test block:
Copy
it("decrement the counter by 1", async function () {
// First increment, count becomes 1
let tx = await counterContract.connect(signers.alice).increment(1);
await tx.wait();
// Then decrement, count goes back to 0
tx = await counterContract.connect(signers.alice).decrement(1);
await tx.wait();
const count = await counterContract.getCount();
expect(count).to.eq(0);
});
* * *
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
Counter.getCount() === 0
✔ count should be zero after deployment
✔ increment the counter by 1
✔ decrement the counter by 1
2 passing (12ms)
Now you have successfully written and tested your counter contract. You should have the following files in your project:
* [`contracts/Counter.sol`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#counter.sol)
— your Solidity smart contract
* [`test/Counter.ts`](https://docs.zama.ai/protocol/examples/basic/fhe-counter#counter.ts)
— your Hardhat test suite written in TypeScript
These files form the foundation of a basic Hardhat-based smart contract project.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/write_a_simple_contract#next-step)
Next step
---------------------------------------------------------------------------------------------------------------------------------------
Now that you've written and tested a basic Solidity smart contract, you're ready to take the next step.
In the [next tutorial](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm)
, we’ll transform this standard `Counter.sol` contract into `FHECounter.sol`, a trivial FHEVM-compatible version — allowing the counter value to be stored and updated using trivial fully homomorphic encryption.
[PreviousQuick start tutorial](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial)
[Next3\. Turn it into FHEVM](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm)
Last updated 9 days ago
---
# HCU | Protocol
This guide explains how to use Fully Homomorphic Encryption (FHE) operations in your smart contracts on FHEVM. Understanding HCU is critical for designing efficient confidential smart contracts.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#overview)
Overview
---------------------------------------------------------------------------------------------------
FHE operations in FHEVM are computationally intensive compared to standard Ethereum operations, as they require complex mathematical computations to maintain privacy and security. To manage computational load and prevent potential denial-of-service attacks, FHEVM implements a metering system called **Homomorphic Complexity Units ("HCU")**.
To represent this complexity, we introduced the **Homomorphic Complexity Unit ("HCU")**. In Solidity, each FHE operation consumes a set amount of HCU based on the operational computational complexity for hardware computation. Since FHE transactions are symbolic, this helps preventing resource exhaustion outside of the blockchain.
To do so, there is a contract named `HCULimit`, which monitors HCU consumption for each transaction and enforces two key limits:
* **Sequential homomorphic operations depth limit per transaction**: Controls HCU usage for operations that must be processed in order.
* **Global homomorphic operations complexity per transaction**: Controls HCU usage for operations that can be processed in parallel.
If either limit is exceeded, the transaction will revert.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#hcu-limit)
HCU limit
-----------------------------------------------------------------------------------------------------
The current devnet has an HCU limit of **20,000,000** per transaction and an HCU depth limit of **5,000,000** per transaction. If either HCU limit is exceeded, the transaction will revert.
To resolve this, you must do one of the following:
* Refactor your code to reduce the number of FHE operations in your transaction.
* Split your FHE operations across multiple independent transactions.
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#hcu-costs-for-common-operations)
HCU costs for common operations
-------------------------------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#boolean-operations-ebool)
Boolean operations (`ebool`)
Function name
HCU (scalar)
HCU (non-scalar)
`and`
22,000
25,000
`or`
22,000
24,000
`xor`
2,000
22,000
`not`
\-
2
`select`
\-
55,000
`randEbool`
\-
19,000
* * *
###
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#unsigned-integer-operations)
Unsigned integer operations
HCU increase with the bit-width of the encrypted integer type. Below are the detailed costs for various operations on encrypted types.
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#id-8-bit-encrypted-integers-euint8)
**8-bit Encrypted integers (**`**euint8**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
84,000
88,000
`sub`
84,000
91,000
`mul`
122,000
150,000
`div`
210,000
\-
`rem`
440,000
\-
`and`
31,000
31,000
`or`
30,000
30,000
`xor`
31,000
31,000
`shr`
32,000
91,000
`shl`
32,000
92,000
`rotr`
31,000
93,000
`rotl`
31,000
91,000
`eq`
55,000
55,000
`ne`
55,000
55,000
`ge`
52,000
63,000
`gt`
52,000
59,000
`le`
58,000
58,000
`lt`
52,000
59,000
`min`
84,000
119,000
`max`
89,000
121,000
`neg`
\-
79,000
`not`
\-
9
`select`
\-
55,000
`randEuint8`
\-
23,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#id-16-bit-encrypted-integers-euint16)
**16-bit Encrypted integers (**`**euint16**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
93,000
93,000
`sub`
93,000
93,000
`mul`
193,000
222,000
`div`
302,000
\-
`rem`
580,000
\-
`and`
31,000
31,000
`or`
30,000
31,000
`xor`
31,000
31,000
`shr`
32,000
123,000
`shl`
32,000
125,000
`rotr`
31,000
125,000
`rotl`
31,000
125,000
`eq`
55,000
83,000
`ne`
55,000
83,000
`ge`
55,000
84,000
`gt`
55,000
84,000
`le`
58,000
83,000
`lt`
58,000
84,000
`min`
88,000
146,000
`max`
89,000
145,000
`neg`
\-
93,000
`not`
\-
16
`select`
\-
55,000
`randEuint16`
\-
23,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#id-32-bit-encrypted-integers-euint32)
**32-bit Encrypted Integers (**`**euint32**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
95,000
125,000
`sub`
95,000
125,000
`mul`
265,000
328,000
`div`
438,000
\-
`rem`
792,000
\-
`and`
32,000
32,000
`or`
32,000
32,000
`xor`
32,000
32,000
`shr`
32,000
163,000
`shl`
32,000
162,000
`rotr`
32,000
160,000
`rotl`
32,000
163,000
`eq`
82,000
86,000
`ne`
83,000
85,000
`ge`
84,000
118,000
`gt`
84,000
118,000
`le`
84,000
117,000
`lt`
83,000
117,000
`min`
117,000
182,000
`max`
117,000
180,000
`neg`
\-
131,000
`not`
\-
32
`select`
\-
55,000
`randEuint32`
\-
24,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#id-64-bit-encrypted-integers-euint64)
**64-bit Encrypted integers (**`**euint64**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
133,000
162,000
`sub`
133,000
162,000
`mul`
365,000
596,000
`div`
715,000
\-
`rem`
1,153,000
\-
`and`
34,000
34,000
`or`
34,000
34,000
`xor`
34,000
34,000
`shr`
34,000
209,000
`shl`
34,000
208,000
`rotr`
34,000
209,000
`rotl`
34,000
209,000
`eq`
83,000
120,000
`ne`
84,000
118,000
`ge`
116,000
152,000
`gt`
117,000
152,000
`le`
119,000
149,000
`lt`
118,000
146,000
`min`
150,000
219,000
`max`
149,000
218,000
`neg`
\-
131,000
`not`
\-
63
`select`
\-
55,000
`randEuint64`
\-
24,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#id-128-bit-encrypted-integers-euint128)
**128-bit Encrypted integers (**`**euint128**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
172,000
259,000
`sub`
172,000
260,000
`mul`
696,000
1,686,000
`div`
1,225,000
\-
`rem`
1,943,000
\-
`and`
37,000
37,000
`or`
37,000
37,000
`xor`
37,000
37,000
`shr`
37,000
272,000
`shl`
37,000
272,000
`rotr`
37,000
283,000
`rotl`
37,000
278,000
`eq`
117,000
122,000
`ne`
117,000
122,000
`ge`
149,000
210,000
`gt`
150,000
218,000
`le`
150,000
218,000
`lt`
149,000
215,000
`min`
186,000
289,000
`max`
180,000
290,000
`neg`
\-
168,000
`not`
\-
130
`select`
\-
57,000
`randEuint128`
\-
25,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#id-256-bit-encrypted-integers-euint256)
**256-bit Encrypted integers (**`**euint256**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`and`
38,000
38,000
`or`
38,000
38,000
`xor`
39,000
39,000
`shr`
38,000
369,000
`shl`
39,000
378,000
`rotr`
40,000
375,000
`rotl`
38,000
378,000
`eq`
118,000
152,000
`ne`
117,000
150,000
`neg`
\-
269,000
`not`
\-
130
`select`
\-
108,000
`randEuint256`
\-
30,000
####
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#encrypted-addresses-euint160)
**Encrypted addresses (**`**euint160**`**)**
When using `eaddress` (internally represented as `euint160`), the HCU costs for equality and inequality checks and select are as follows:
Function name
HCU (scalar)
HCU (non-scalar)
`eq`
115,000
125,000
`ne`
115,000
124,000
`select`
\-
83,000
[](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/hcu#additional-operations)
Additional Operations
-----------------------------------------------------------------------------------------------------------------------------
Function name
HCU
`cast`
32
`trivialEncrypt`
32
`randBounded`
23,000-30,000
[PreviousFoundry](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/foundry)
[NextMigration guide](https://docs.zama.ai/protocol/solidity-guides/v0.8/development-guide/migration)
Last updated 1 month ago
---
# HCU | Protocol
This guide explains how to use Fully Homomorphic Encryption (FHE) operations in your smart contracts on FHEVM. Understanding HCU is critical for designing efficient confidential smart contracts.
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#overview)
Overview
----------------------------------------------------------------------------------------------
FHE operations in FHEVM are computationally intensive compared to standard Ethereum operations, as they require complex mathematical computations to maintain privacy and security. To manage computational load and prevent potential denial-of-service attacks, FHEVM implements a metering system called **Homomorphic Complexity Units ("HCU")**.
To represent this complexity, we introduced the **Homomorphic Complexity Unit ("HCU")**. In Solidity, each FHE operation consumes a set amount of HCU based on the operational computational complexity for hardware computation. Since FHE transactions are symbolic, this helps preventing resource exhaustion outside of the blockchain.
To do so, there is a contract named `HCULimit`, which monitors HCU consumption for each transaction and enforces two key limits:
* **Sequential homomorphic operations depth limit per transaction**: Controls HCU usage for operations that must be processed in order.
* **Global homomorphic operations complexity per transaction**: Controls HCU usage for operations that can be processed in parallel.
If either limit is exceeded, the transaction will revert.
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#hcu-limit)
HCU limit
------------------------------------------------------------------------------------------------
The current devnet has an HCU limit of **20,000,000** per transaction and an HCU depth limit of **5,000,000** per transaction. If either HCU limit is exceeded, the transaction will revert.
To resolve this, you must do one of the following:
* Refactor your code to reduce the number of FHE operations in your transaction.
* Split your FHE operations across multiple independent transactions.
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#hcu-costs-for-common-operations)
HCU costs for common operations
--------------------------------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#boolean-operations-ebool)
Boolean operations (`ebool`)
Function name
HCU (scalar)
HCU (non-scalar)
`and`
22,000
25,000
`or`
22,000
24,000
`xor`
2,000
22,000
`not`
\-
2
`select`
\-
55,000
`randEbool`
\-
19,000
* * *
###
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#unsigned-integer-operations)
Unsigned integer operations
HCU increase with the bit-width of the encrypted integer type. Below are the detailed costs for various operations on encrypted types.
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#id-8-bit-encrypted-integers-euint8)
**8-bit Encrypted integers (**`**euint8**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
84,000
88,000
`sub`
84,000
91,000
`mul`
122,000
150,000
`div`
210,000
\-
`rem`
440,000
\-
`and`
31,000
31,000
`or`
30,000
30,000
`xor`
31,000
31,000
`shr`
32,000
91,000
`shl`
32,000
92,000
`rotr`
31,000
93,000
`rotl`
31,000
91,000
`eq`
55,000
55,000
`ne`
55,000
55,000
`ge`
52,000
63,000
`gt`
52,000
59,000
`le`
58,000
58,000
`lt`
52,000
59,000
`min`
84,000
119,000
`max`
89,000
121,000
`neg`
\-
79,000
`not`
\-
9
`select`
\-
55,000
`randEuint8`
\-
23,000
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#id-16-bit-encrypted-integers-euint16)
**16-bit Encrypted integers (**`**euint16**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
93,000
93,000
`sub`
93,000
93,000
`mul`
193,000
222,000
`div`
302,000
\-
`rem`
580,000
\-
`and`
31,000
31,000
`or`
30,000
31,000
`xor`
31,000
31,000
`shr`
32,000
123,000
`shl`
32,000
125,000
`rotr`
31,000
125,000
`rotl`
31,000
125,000
`eq`
55,000
83,000
`ne`
55,000
83,000
`ge`
55,000
84,000
`gt`
55,000
84,000
`le`
58,000
83,000
`lt`
58,000
84,000
`min`
88,000
146,000
`max`
89,000
145,000
`neg`
\-
93,000
`not`
\-
16
`select`
\-
55,000
`randEuint16`
\-
23,000
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#id-32-bit-encrypted-integers-euint32)
**32-bit Encrypted Integers (**`**euint32**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
95,000
125,000
`sub`
95,000
125,000
`mul`
265,000
328,000
`div`
438,000
\-
`rem`
792,000
\-
`and`
32,000
32,000
`or`
32,000
32,000
`xor`
32,000
32,000
`shr`
32,000
163,000
`shl`
32,000
162,000
`rotr`
32,000
160,000
`rotl`
32,000
163,000
`eq`
82,000
86,000
`ne`
83,000
85,000
`ge`
84,000
118,000
`gt`
84,000
118,000
`le`
84,000
117,000
`lt`
83,000
117,000
`min`
117,000
182,000
`max`
117,000
180,000
`neg`
\-
131,000
`not`
\-
32
`select`
\-
55,000
`randEuint32`
\-
24,000
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#id-64-bit-encrypted-integers-euint64)
**64-bit Encrypted integers (**`**euint64**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
133,000
162,000
`sub`
133,000
162,000
`mul`
365,000
596,000
`div`
715,000
\-
`rem`
1,153,000
\-
`and`
34,000
34,000
`or`
34,000
34,000
`xor`
34,000
34,000
`shr`
34,000
209,000
`shl`
34,000
208,000
`rotr`
34,000
209,000
`rotl`
34,000
209,000
`eq`
83,000
120,000
`ne`
84,000
118,000
`ge`
116,000
152,000
`gt`
117,000
152,000
`le`
119,000
149,000
`lt`
118,000
146,000
`min`
150,000
219,000
`max`
149,000
218,000
`neg`
\-
131,000
`not`
\-
63
`select`
\-
55,000
`randEuint64`
\-
24,000
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#id-128-bit-encrypted-integers-euint128)
**128-bit Encrypted integers (**`**euint128**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`add`
172,000
259,000
`sub`
172,000
260,000
`mul`
696,000
1,686,000
`div`
1,225,000
\-
`rem`
1,943,000
\-
`and`
37,000
37,000
`or`
37,000
37,000
`xor`
37,000
37,000
`shr`
37,000
272,000
`shl`
37,000
272,000
`rotr`
37,000
283,000
`rotl`
37,000
278,000
`eq`
117,000
122,000
`ne`
117,000
122,000
`ge`
149,000
210,000
`gt`
150,000
218,000
`le`
150,000
218,000
`lt`
149,000
215,000
`min`
186,000
289,000
`max`
180,000
290,000
`neg`
\-
168,000
`not`
\-
130
`select`
\-
57,000
`randEuint128`
\-
25,000
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#id-256-bit-encrypted-integers-euint256)
**256-bit Encrypted integers (**`**euint256**`**)**
Function name
HCU (scalar)
HCU (non-scalar)
`and`
38,000
38,000
`or`
38,000
38,000
`xor`
39,000
39,000
`shr`
38,000
369,000
`shl`
39,000
378,000
`rotr`
40,000
375,000
`rotl`
38,000
378,000
`eq`
118,000
152,000
`ne`
117,000
150,000
`neg`
\-
269,000
`not`
\-
130
`select`
\-
108,000
`randEuint256`
\-
30,000
####
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#encrypted-addresses-euint160)
**Encrypted addresses (**`**euint160**`**)**
When using `eaddress` (internally represented as `euint160`), the HCU costs for equality and inequality checks and select are as follows:
Function name
HCU (scalar)
HCU (non-scalar)
`eq`
115,000
125,000
`ne`
115,000
124,000
`select`
\-
83,000
[](https://docs.zama.ai/protocol/solidity-guides/development-guide/hcu#additional-operations)
Additional Operations
------------------------------------------------------------------------------------------------------------------------
Function name
HCU
`cast`
32
`trivialEncrypt`
32
`randBounded`
23,000-30,000
[PreviousFoundry](https://docs.zama.ai/protocol/solidity-guides/development-guide/foundry)
[NextMigrate to v0.7](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
Last updated 9 days ago
---
# 4. Test the FHEVM contract | Protocol
In this tutorial, you’ll learn how to migrate a standard Hardhat test suite - from `Counter.ts` to its FHEVM-compatible version `FHECounter.ts` — and progressively enhance it to support Fully Homomorphic Encryption using Zama’s FHEVM library.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#set-up-the-fhevm-testing-environment)
Set up the FHEVM testing environment
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#create-a-test-script-test-fhecounter.ts)
Create a test script `test/FHECounter.ts`
Go to your project's `test` directory
Copy
cd /test
From there, create a new file named `FHECounter.ts` and copy/paste the following Typescript skeleton code in it.
Copy
import { FHECounter, FHECounter__factory } from "../types";
import { FhevmType } from "@fhevm/hardhat-plugin";
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers, fhevm } from "hardhat";
type Signers = {
deployer: HardhatEthersSigner;
alice: HardhatEthersSigner;
bob: HardhatEthersSigner;
};
async function deployFixture() {
const factory = (await ethers.getContractFactory("FHECounter")) as FHECounter__factory;
const fheCounterContract = (await factory.deploy()) as FHECounter;
const fheCounterContractAddress = await fheCounterContract.getAddress();
return { fheCounterContract, fheCounterContractAddress };
}
describe("FHECounter", function () {
let signers: Signers;
let fheCounterContract: FHECounter;
let fheCounterContractAddress: string;
before(async function () {
const ethSigners: HardhatEthersSigner[] = await ethers.getSigners();
signers = { deployer: ethSigners[0], alice: ethSigners[1], bob: ethSigners[2] };
});
beforeEach(async () => {
({ fheCounterContract, fheCounterContractAddress } = await deployFixture());
});
it("should be deployed", async function () {
console.log(`FHECounter has been deployed at address ${fheCounterContractAddress}`);
// Test the deployed address is valid
expect(ethers.isAddress(fheCounterContractAddress)).to.eq(true);
});
// it("count should be zero after deployment", async function () {
// const count = await counterContract.getCount();
// console.log(`Counter.getCount() === ${count}`);
// // Expect initial count to be 0 after deployment
// expect(count).to.eq(0);
// });
// it("increment the counter by 1", async function () {
// const countBeforeInc = await counterContract.getCount();
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
// });
// it("decrement the counter by 1", async function () {
// // First increment, count becomes 1
// let tx = await counterContract.connect(signers.alice).increment();
// await tx.wait();
// // Then decrement, count goes back to 0
// tx = await counterContract.connect(signers.alice).decrement(1);
// await tx.wait();
// const count = await counterContract.getCount();
// expect(count).to.eq(0);
// });
});
####
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#whats-different-from-counter.ts)
What’s Different from `Counter.ts`?
* This test file is structurally similar to the original `Counter.ts`, but it uses the FHEVM-compatible smart contract `FHECounter` instead of the regular `Counter`.
– For clarity, the `Counter` unit tests are included as comments, allowing you to better understand how each part is adapted during the migration to FHEVM.
* While the test logic remains the same, this version is now set up to support encrypted computations via the FHEVM library — enabling tests that manipulate confidential values directly on-chain.
2
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#run-the-test-test-fhecounter.ts)
Run the test `test/FHECounter.ts`
From your project's root directory, run:
Copy
npx hardhat test
Output:
Copy
FHECounter
FHECounter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
1 passing (1ms)
Great! Your Hardhat FHEVM test environment is properly setup.
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#test-functions)
Test functions
-------------------------------------------------------------------------------------------------------------------------------------------------
Now everything is up and running, you can start testing your contract functions.
1
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#call-the-contract-getcount-view-function)
Call the contract `getCount()` view function
Replace the commented‐out test for the legacy `Counter` contract:
Copy
// it("count should be zero after deployment", async function () {
// const count = await counterContract.getCount();
// console.log(`Counter.getCount() === ${count}`);
// // Expect initial count to be 0 after deployment
// expect(count).to.eq(0);
// });
with its FHEVM equivalent:
Copy
it("encrypted count should be uninitialized after deployment", async function () {
const encryptedCount = await fheCounterContract.getCount();
// Expect initial count to be bytes32(0) after deployment,
// (meaning the encrypted count value is uninitialized)
expect(encryptedCount).to.eq(ethers.ZeroHash);
});
**What’s different?**
– `encryptedCount` is no longer a plain TypeScript number. It is now a hexadecimal string representing a Solidity `bytes32` value, known as an **FHEVM handle**. This handle points to an encrypted FHEVM primitive of type `euint32`, which internally represents an encrypted Solidity `uint32` primitive type.
* `encryptedCount` is equal to `0x0000000000000000000000000000000000000000000000000000000000000000` which means that `encryptedCount` is uninitialized, and does not reference to any encrypted value at this point.
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
Counter
Counter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
✔ encrypted count should be uninitialized after deployment
2 passing (7ms)
2
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#setup-the-increment-function-unit-test)
Setup the `increment()` function unit test
We’ll migrate the `increment()` unit test to FHEVM step by step. To start, let’s handle the value of the counter before the first increment. As explained above, the counter is initially a `bytes32` value equal to zero, meaning the FHEVM `euint32` variable is uninitialized.
We’ll interpret this as if the underlying clear value is 0.
Replace the commented‐out test for the legacy `Counter` contract:
Copy
// it("increment the counter by 1", async function () {
// const countBeforeInc = await counterContract.getCount();
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
// });
with the following:
Copy
it("increment the counter by 1", async function () {
const encryptedCountBeforeInc = await fheCounterContract.getCount();
expect(encryptedCountBeforeInc).to.eq(ethers.ZeroHash);
const clearCountBeforeInc = 0;
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
});
3
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#encrypt-the-increment-function-argument)
Encrypt the `increment()` function argument
The `increment()` function takes a single argument: the value by which the counter should be incremented. In the initial version of `Counter.sol`, this value is a clear `uint32`.
We’ll switch to passing an encrypted value instead, using FHEVM `externalEuint32` primitive type. This allows us to securely increment the counter without revealing the input value on-chain.
We are using an `externalEuint32` instead of a regular `euint32`. This tells the FHEVM that the encrypted `uint32` was provided externally (e.g., by a user) and must be verified for integrity and authenticity before it can be used within the contract.
Replace :
Copy
it("increment the counter by 1", async function () {
const encryptedCountBeforeInc = await fheCounterContract.getCount();
expect(encryptedCountBeforeInc).to.eq(ethers.ZeroHash);
const clearCountBeforeInc = 0;
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
});
with the following:
Copy
it("increment the counter by 1", async function () {
const encryptedCountBeforeInc = await fheCounterContract.getCount();
expect(encryptedCountBeforeInc).to.eq(ethers.ZeroHash);
const clearCountBeforeInc = 0;
// Encrypt constant 1 as a euint32
const clearOne = 1;
const encryptedOne = await fhevm
.createEncryptedInput(fheCounterContractAddress, signers.alice.address)
.add32(clearOne)
.encrypt();
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
});
`fhevm.createEncryptedInput(fheCounterContractAddress, signers.alice.address)` creates an encrypted value that is bound to both the contract (`fheCounterContractAddress`) and the user (`signers.alice.address`). This means only Alice can use this encrypted value, and only within the `FHECounter.sol` contract at that specific address. **It cannot be reused by another user or in a different contract, ensuring data confidentiality and binding context-specific encryption.**
4
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#call-the-increment-function-with-the-encrypted-argument)
Call the `increment()` function with the encrypted argument
Now that we have an encrypted argument, we can call the `increment()` function with it.
Below, you’ll notice that the updated `increment()` function now takes **two arguments instead of one.**
This is because the FHEVM requires both:
1. The `externalEuint32` — the encrypted value itself
2. An accompanying **Zero-Knowledge Proof of Knowledge** (`inputProof`) — which verifies that the encrypted input is securely bound to:
* the caller (Alice, the transaction signer), and
* the target smart contract (where `increment()` is being executed)
This ensures that the encrypted value cannot be reused in a different context or by a different user, preserving **confidentiality and integrity.**
Replace :
Copy
// const tx = await counterContract.connect(signers.alice).increment(1);
// await tx.wait();
with the following:
Copy
const tx = await fheCounterContract.connect(signers.alice).increment(encryptedOne.handles[0], encryptedOne.inputProof);
await tx.wait();
At this point the counter has been successfully incremented by 1 using a **Fully Homomorphic Encryption (FHE)**. In the next step, we will retrieve the updated encrypted counter value and decrypt it locally. But before we move on, let’s quickly run the tests to make sure everything is working correctly.
* * *
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
FHECounter
FHECounter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
✔ encrypted count should be uninitialized after deployment
✔ increment the counter by 1
3 passing (7ms)
5
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#call-the-getcount-function-and-decrypt-the-value)
Call the `getCount()` function and Decrypt the value
Now that the counter has been incremented using an encrypted input, it's time to **read the updated encrypted value** from the smart contract and **decrypt it** using the `userDecryptEuint` function provided by the FHEVM Hardhat Plugin.
The `userDecryptEuint` function takes four parameters:
1. **FhevmType**: The integer type of the FHE-encrypted value. In this case, we're using `FhevmType.euint32` because the counter is a `uint32`.
2. **Encrypted handle**: A 32-byte FHEVM handle representing the encrypted value you want to decrypt.
3. **Smart contract address**: The address of the contract that has permission to access the encrypted handle.
4. **User signer**: The signer (e.g., signers.alice) who has permission to access the handle.
Note: Permissions to access the FHEVM handle are set on-chain using the `FHE.allow()` Solidity function (see FHECounter.sol).
Replace :
Copy
// const countAfterInc = await counterContract.getCount();
// expect(countAfterInc).to.eq(countBeforeInc + 1n);
with the following:
Copy
const encryptedCountAfterInc = await fheCounterContract.getCount();
const clearCountAfterInc = await fhevm.userDecryptEuint(
FhevmType.euint32,
encryptedCountAfterInc,
fheCounterContractAddress,
signers.alice,
);
expect(clearCountAfterInc).to.eq(clearCountBeforeInc + clearOne);
* * *
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
FHECounter
FHECounter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
✔ encrypted count should be uninitialized after deployment
✔ increment the counter by 1
3 passing (7ms)
6
###
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#call-the-contract-decrement-function)
Call the contract `decrement()` function
Similarly to the previous test, we’ll now call the `decrement()` function using an encrypted input.
Replace :
Copy
// it("decrement the counter by 1", async function () {
// // First increment, count becomes 1
// let tx = await counterContract.connect(signers.alice).increment();
// await tx.wait();
// // Then decrement, count goes back to 0
// tx = await counterContract.connect(signers.alice).decrement(1);
// await tx.wait();
// const count = await counterContract.getCount();
// expect(count).to.eq(0);
// });
with the following:
Copy
it("decrement the counter by 1", async function () {
// Encrypt constant 1 as a euint32
const clearOne = 1;
const encryptedOne = await fhevm
.createEncryptedInput(fheCounterContractAddress, signers.alice.address)
.add32(clearOne)
.encrypt();
// First increment by 1, count becomes 1
let tx = await fheCounterContract.connect(signers.alice).increment(encryptedOne.handles[0], encryptedOne.inputProof);
await tx.wait();
// Then decrement by 1, count goes back to 0
tx = await fheCounterContract.connect(signers.alice).decrement(encryptedOne.handles[0], encryptedOne.inputProof);
await tx.wait();
const encryptedCountAfterDec = await fheCounterContract.getCount();
const clearCountAfterDec = await fhevm.userDecryptEuint(
FhevmType.euint32,
encryptedCountAfterDec,
fheCounterContractAddress,
signers.alice,
);
expect(clearCountAfterDec).to.eq(0);
});
* * *
**Run the test**
From your project's root directory, run:
Copy
npx hardhat test
**Expected Output**
Copy
FHECounter
FHECounter has been deployed at address 0x7553CB9124f974Ee475E5cE45482F90d5B6076BC
✔ should be deployed
✔ encrypted count should be uninitialized after deployment
✔ increment the counter by 1
✔ decrement the counter by 1
4 passing (7ms)
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#congratulations-youve-completed-the-full-tutorial)
Congratulations! You've completed the full tutorial.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
You have successfully written and tested your FHEVM-based counter smart contract. By now, your project should include the following files:
* [`contracts/FHECounter.sol`](https://docs.zama.ai/protocol/examples#tab-fhecounter.sol)
— your Solidity smart contract
* [`test/FHECounter.ts`](https://docs.zama.ai/protocol/examples#tab-fhecounter.ts)
— your Hardhat test suite written in TypeScript
[](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/test_the_fhevm_contract#next-step)
Next step
---------------------------------------------------------------------------------------------------------------------------------------
If you would like to deploy your project on the testnet, or learn more about using FHEVM Hardhat Plugin, head to [Deploy contracts and run tests](https://docs.zama.ai/protocol/solidity-guides/development-guide/hardhat/run_test)
.
[Previous3\. Turn it into FHEVM](https://docs.zama.ai/protocol/solidity-guides/getting-started/quick-start-tutorial/turn_it_into_fhevm)
[NextConfiguration](https://docs.zama.ai/protocol/solidity-guides/smart-contract/configure)
Last updated 9 days ago
---
# Roadmap | Change Log
[](https://docs.zama.ai/change-log#current-testnet-status)
**Current Testnet status**
------------------------------------------------------------------------------------------
Deployed and planned FHEVM versions on the Testnet
Status
Versions
Details
Current deployed
FHEVM v0.8
[Change log ↗](https://docs.zama.ai/change-log/release/fhevm-v0.8-september-2025)
Released
FHEVM v0.9
[Change log ↗](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025)
Planned
FHEVM v0.10
[Preview ↗](https://docs.zama.ai/change-log/coming-soon/fhevm-v0.10-october-2025)
See the full version status from the [Zama Protocol Version Dashboard](https://zamablockchain.grafana.net/public-dashboards/4027c482ad1e44ddb1336ec04cc5a1db)
.
[](https://docs.zama.ai/change-log#breaking-changes-testnet-state-reset)
Breaking changes: Testnet state reset
-------------------------------------------------------------------------------------------------------------------
**FHEVM v0.9** release to Testnet introduces **a state reset.** Make sure to perform the action required below to ensure your dApps continue to run smoothly.
[](https://docs.zama.ai/change-log#upcoming-milestones)
Upcoming milestones
--------------------------------------------------------------------------------
A rolling list of what’s next. Sorted by date.
Date (Planned)
Milestone
Details
**October 20-24**
New testnet deployment with FHEVM v0.9
Distributed keygen, contracts, coprocessors, relayers
**October 27-31**
Migration period
Users migrate dApps to the new Testnet; data will not be preserved
**Nov 03**
Old Testnet deprecation
Tear down infrastructure
###
[](https://docs.zama.ai/change-log#whats-changing)
What’s changing
* All existing state will be reset. Data will not carry over to the new Testnet
* Smart contracts must be redeployed on the new Testnet
* dApps may need to be reconfigured (e.g. pointing to new contract addresses, endpoints)
###
[](https://docs.zama.ai/change-log#actions-required)
Actions required
* **October 10** \- After the new Testnet goes live
* Re-deploy all smart contracts
* Re-configure your dApps to interact with the newly deployed contracts
* **October 13-17 -** Migration window
* Complete migration to the new Testnet
[NextFHEVM v0.10 - October 2025](https://docs.zama.ai/change-log/coming-soon/fhevm-v0.10-october-2025)
Last updated 5 days ago
---
# Welcome to the Zama Programs Documentations | Community Docs
###
[](https://docs.zama.ai/programs/#zama-creator-program)
Zama Creator Program
* Visit the [leaderboard](https://www.zama.ai/programs/creator-program)
* Read [FAQ](https://docs.zama.ai/programs/developer-program/frequently-asked-questions)
* Join on [guild.xyz](https://guild.xyz/zama/creator-program)
###
[](https://docs.zama.ai/programs/#zama-developer-program)
Zama Developer Program
* See the [winning projects](https://www.zama.ai/programs/developer-program)
* Read [FAQ](https://docs.zama.ai/programs/creator-program/frequently-asked-questions)
* Join on [guild.xyz](https://guild.xyz/zama/developer-program)
###
[](https://docs.zama.ai/programs/#zama-tester-program)
Zama Tester Program
* Visit the leaderboard - Coming soon
* Read [FAQ](https://docs.zama.ai/programs/tester-program/frequently-asked-questions)
* Join on [guild.xyz](https://guild.xyz/zama/tester-program/)
[NextFrequently Asked Questions](https://docs.zama.ai/programs/creator-program/frequently-asked-questions)
Last updated 1 month ago
---
# Welcome to fhEVM | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
[](https://docs.zama.ai/fhevm/0.6#get-started)
Get started
---------------------------------------------------------------
Learn the basics of fhEVM, set it up, and make it run with ease.
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview)

**Overview**
Explore the suite of fhEVM protocol.
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1)

**Quick start with Remix**
Learn and prototype in the in-browser IDE.
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1/hardhat)

**Get started with Hardhat**
Develop in production-ready envrionment.
[](https://docs.zama.ai/fhevm/0.6#develop-a-fhevm-smart-contract)
Develop a fhEVM smart contract
-----------------------------------------------------------------------------------------------------
Start developing fhEVM smart contracts in Solidity by exploring its core features, discovering essential guides, and learning more with user-friendly tutorials.

**Smart contract**
Learn core Solidity library.
* [Key features](https://docs.zama.ai/fhevm/0.6/smart-contract/key_concepts)
* [Use encrypted types](https://docs.zama.ai/fhevm/0.6/smart-contract/types)

**Frontend**
Write a dAPP frontend.
* [Set up](https://docs.zama.ai/fhevm/0.6/frontend/setup)
* [Build a web application](https://docs.zama.ai/fhevm/0.6/frontend/webapp)

**Tutorials**
Build quickly with tutorials.
* [See all tutorials](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials)
[](https://docs.zama.ai/fhevm/0.6#explore-more)
Explore more
-----------------------------------------------------------------
Access to additional resources and join the Zama community.
###
[](https://docs.zama.ai/fhevm/0.6#explanations)
Explanations
Explore the technical architecture of the fhEVM protocol and the underlying cryptographic principles that power it.
* [Architecture overview](https://docs.zama.ai/fhevm/0.6/explanations/architecture_overview)
* [FHE on blockchain](https://docs.zama.ai/fhevm/0.6/explanations/fhe-on-blockchain)
* [fhEVM components](https://docs.zama.ai/fhevm/0.6/explanations/fhevm-components)
* [Encryption, decryption re-encryption and computation](https://docs.zama.ai/fhevm/0.6/explanations/d_re_ecrypt_compute)
###
[](https://docs.zama.ai/fhevm/0.6#references)
References
Refer to the API and access additional resources for in-depth explanations while working with fhEVM.
* [API function specifications](https://docs.zama.ai/fhevm/0.6/references/functions)
* [Repositories](https://docs.zama.ai/fhevm/0.6/references/repositories)
###
[](https://docs.zama.ai/fhevm/0.6#supports)
Supports
Ask technical questions and discuss with the community. Our team of experts usually answers within 24 hours in working days.
* [Community forum](https://community.zama.ai/c/fhevm/15)
* [Discord channel](https://discord.com/invite/fhe-org)
* [Telegram](https://t.me/+Ojt5y-I7oR42MTkx)
###
[](https://docs.zama.ai/fhevm/0.6#developers)
Developers
Collaborate with us to advance the FHE spaces and drive innovation together.
* [Contribute to fhEVM](https://docs.zama.ai/fhevm/0.6/developer/contribute)
* [Follow the development roadmap](https://docs.zama.ai/fhevm/0.6/developer/roadmap)
* [See the latest test release note](https://github.com/zama-ai/fhevm-solidity/releases)
* [Request a feature](https://github.com/zama-ai/fhevm-solidity/issues/new?assignees=&labels=enhancement&projects=&template=feature-request.md&title=)
* [Report a bug](https://github.com/zama-ai/fhevm-solidity/issues/new?assignees=&labels=bug&projects=&template=bug_report_fhevm.md&title=)
* * *
**Zama 5-Question Developer Survey**
We want to hear from you! Take 1 minute to share your thoughts and help us enhance our documentation and libraries. **👉** [**Click here**](https://www.zama.ai/developer-survey)
to participate.
Last updated 5 months ago
Was this helpful?
---
# Welcome | Concrete ML
[](https://docs.zama.ai/concrete-ml#get-started)
Get started
-----------------------------------------------------------------
Learn the basics of Concrete ML, set it up, and make it run with ease.
[](https://docs.zama.ai/concrete-ml/get-started/getting-started)

**What is Concrete ML**
Understand the Concrete ML library with a full example.
[](https://docs.zama.ai/concrete-ml/get-started/pip_installing)

**Installation**
Follow the step-by-step guide to install Concrete ML in your project.
[](https://docs.zama.ai/concrete-ml/get-started/concepts)

**Key concepts**
Understand important cryptographic concepts to implement Concrete ML.
[](https://docs.zama.ai/concrete-ml#build-with-concrete-ml)
Build with Concrete ML
---------------------------------------------------------------------------------------
Start building with Concrete ML by exploring its core features, discovering essential guides, and learning more with user-friendly tutorials.

**Fundamentals**
Explore core features.
* [Built-in models](https://docs.zama.ai/concrete-ml/tutorials/ml_examples)
* [Deep learning](https://docs.zama.ai/concrete-ml/tutorials/dl_examples)

**Guides**
Deploy your projects.
* [Prediction with FHE](https://docs.zama.ai/concrete-ml/guides/prediction_with_fhe)
* [Production deployment](https://docs.zama.ai/concrete-ml/guides/client_server)

**Tutorials**
Learn more with tutorials.
* [Start here](https://docs.zama.ai/concrete-ml/tutorials/showcase#start-here)
* [Go further](https://docs.zama.ai/concrete-ml/tutorials/showcase#go-further)
[](https://docs.zama.ai/concrete-ml#explore-more)
Explore more
-------------------------------------------------------------------
Access to additional resources and join the Zama community.
###
[](https://docs.zama.ai/concrete-ml#references-and-explanations)
References & Explanations
Refer to the API, review product architecture, and access additional resources for in-depth explanations while working with Concrete ML.
* [Security and correctness](https://docs.zama.ai/concrete-ml/explanations/security_and_correctness)
* [API](https://docs.zama.ai/concrete-ml/references/api)
* [Quantization](https://docs.zama.ai/concrete-ml/explanations/quantization)
* [Pruning](https://docs.zama.ai/concrete-ml/explanations/pruning)
* [Compilation](https://docs.zama.ai/concrete-ml/explanations/compilation)
* [Advanced features](https://docs.zama.ai/concrete-ml/explanations/advanced_features)
* [Project architecture](https://docs.zama.ai/concrete-ml/explanations/inner-workings)
###
[](https://docs.zama.ai/concrete-ml#support-channels)
Support channels
Ask technical questions and discuss with the community. Our team of experts usually answers within 24 hours in working days.
* [Community channels](https://zama.ai/community-channels)
###
[](https://docs.zama.ai/concrete-ml#developers)
Developers
Collaborate with us to advance the FHE spaces and drive innovation together.
* [Contribute to Concrete ML](https://docs.zama.ai/concrete-ml/developers/contributing)
* [Check the latest release note](https://github.com/zama-ai/concrete-ml/releases)
* [Request a feature](https://github.com/zama-ai/concrete-ml/issues/new?assignees=&labels=feature&projects=&template=feature_request.md)
* [Report a bug](https://github.com/zama-ai/concrete-ml/issues/new?assignees=&labels=bug&projects=&template=bug_report.md)
* * *
**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**](https://www.zama.ai/developer-survey)
to participate.
Last updated 6 months ago
Was this helpful?
---
# Welcome to TFHE-rs | TFHE-rs
TFHE-rs is a pure Rust implementation of TFHE for Boolean and integer arithmetics over encrypted data. It includes a Rust and C API, as well as a client-side WASM API.
TFHE-rs also includes a [GPU accelerated backend](https://docs.zama.ai/tfhe-rs/hardware-acceleration/run-on-gpu)
as well as an [HPU accelerated backend](https://docs.zama.ai/tfhe-rs/hardware-acceleration/run-on-hpu)
.
[](https://docs.zama.ai/tfhe-rs#get-started)
Get started
-------------------------------------------------------------
Learn the basics of TFHE-rs, set it up, and make it run with ease.
[](https://docs.zama.ai/tfhe-rs/get-started/getting-started)

**What is TFHE-rs?**
Understand TFHE-rs library and basic cryptographic concepts
[](https://docs.zama.ai/tfhe-rs/get-started/installation)

**Installation**
Follow the step by step guide to import TFHE-rs in your project
[](https://docs.zama.ai/tfhe-rs/get-started/quick-start)

**Quick start**
See a full example of using TFHE-rs to compute on encrypted data
[](https://docs.zama.ai/tfhe-rs#build-with-tfhe-rs)
Build with TFHE-rs
---------------------------------------------------------------------------
Start building with TFHE-rs by exploring its core features, discovering essential guides, and learning more with user-friendly tutorials.

**FHE Computations**
Run FHE computation on encrypted data.
* [Types](https://docs.zama.ai/tfhe-rs/fhe-computation/types)
* [Operations](https://docs.zama.ai/tfhe-rs/fhe-computation/operations)

**Configuration**
Advanced configuration for better performance.
* [Advanced Rust](https://docs.zama.ai/tfhe-rs/configuration/rust-configuration)
* [GPU acceleration](https://docs.zama.ai/tfhe-rs/hardware-acceleration/run-on-gpu)
* [HPU acceleration](https://docs.zama.ai/tfhe-rs/hardware-acceleration/run-on-hpu)

**Integration**
Use TFHE-rs in different contexts or platforms..
* [C API](https://docs.zama.ai/tfhe-rs/integration/c-api)
* [JS on WASM API](https://docs.zama.ai/tfhe-rs/integration/js-on-wasm-api)
[](https://docs.zama.ai/tfhe-rs#explore-more)
Explore more
---------------------------------------------------------------
Access to additional resources and join the Zama community.
###
[](https://docs.zama.ai/tfhe-rs#tutorials)
Tutorials
Explore step-by-step guides that walk you through real-world uses of TFHE-rs.
* [Homomorphic parity bit](https://docs.zama.ai/tfhe-rs/tutorials/parity-bit)
: Learn how to implement a parity bit calculation over encrypted data
* [Homomorphic case changing on ASCII string](https://docs.zama.ai/tfhe-rs/tutorials/ascii-fhe-string)
: See how to process string data securely by changing cases while keeping the data encrypted.
* [SHA256 with Boolean API](https://docs.zama.ai/tfhe-rs/tutorials/sha256-bool)
: Delve into a more complex example: implementing the SHA256 hash function entirely on encrypted boolean values.
* [All tutorials](https://docs.zama.ai/tfhe-rs/tutorials/see-all-tutorials)
: A complete list of all available tutorials in one place.tutorials: A complete list of all available tutorials in one place.
###
[](https://docs.zama.ai/tfhe-rs#references-and-explanations)
References & Explanations
Take a deep dive into TFHE-rs, exploring APIs from the highest to the lowest level of abstraction and accessing additional resources for in-depth explanations.
* [Rust API reference](https://docs.rs/tfhe/latest/tfhe/)
: High-level API that abstracts cryptographic complexities and simplifies the development and more
* [Fine-grained APIs](https://docs.zama.ai/tfhe-rs/references/fine-grained-apis)
: Mid-level APIs that enable evaluation of Boolean, short integer, and integer circuits
* [Core crypto API](https://docs.zama.ai/tfhe-rs/references/core-crypto-api)
: Low-level API with the primitive functions and types of the TFHE scheme
* [TFHE deep dive](https://docs.zama.ai/tfhe-rs/explanations/tfhe-deep-dive)
: Resources that explain the Fully Homomorphic Encryption scheme - TFHE
* [TFHE-rs handbook](https://github.com/zama-ai/tfhe-rs-handbook)
: Document describing algorithms implemented in TFHE-rs
###
[](https://docs.zama.ai/tfhe-rs#support-channels)
Support channels
Ask technical questions and discuss with the community. Our team of experts usually answers within 24 hours during working days.
* [Community forum](https://community.zama.ai/)
* [Discord channel](https://discord.com/invite/fhe-org)
###
[](https://docs.zama.ai/tfhe-rs#developers)
Developers
Collaborate with us to advance the FHE spaces and drive innovation together.
* [Contribute to TFHE-rs](https://docs.zama.ai/tfhe-rs/developers/contributing)
* [Check the latest release note](https://github.com/zama-ai/tfhe-rs/releases)
* [Request a feature](https://github.com/zama-ai/tfhe-rs/issues/new?assignees=&labels=feature_request&projects=&template=feature_request.md&title=)
* [Report a bug](https://github.com/zama-ai/tfhe-rs/issues/new?assignees=&labels=triage_required&projects=&template=bug_report.md&title=)
* * *
Last updated 16 hours ago
Was this helpful?
---
# Welcome | Concrete
[](https://docs.zama.ai/concrete#get-started)
Get started
--------------------------------------------------------------
Learn the basics of Concrete, set it up, and make it run with ease.
[](https://docs.zama.ai/concrete/get-started/get-started)

**What is Concrete**
Understand the basic concepts of the Concrete library.
[](https://docs.zama.ai/concrete/get-started/installing)

**Installation**
Follow the step by step guide to install Concrete in your project
[](https://docs.zama.ai/concrete/get-started/quick_start)

**Quick start**
See a full example of using Concrete to compute on encrypted data
[](https://docs.zama.ai/concrete#build-with-concrete)
Build with Concrete
------------------------------------------------------------------------------
Start building with Concrete by exploring its core features, discovering essential guides, and learning more with step-by-step tutorials.

**Fundamentals**
Explore the core features.
* [Core features](https://docs.zama.ai/concrete/explanations/fhe_basics)
* [Compilation](https://docs.zama.ai/concrete/compilation/combining/composition)
* [Execution/Analysis](https://docs.zama.ai/concrete/execution-analysis/simulation)

**Guides**
Deploy your project.
* [Configure](https://docs.zama.ai/concrete/guides/configure)
* [Deploy](https://docs.zama.ai/concrete/guides/deploy)

**Tutorials**
Learn more with tutorials.
* [Start here](https://docs.zama.ai/concrete/tutorials/see-all-tutorials#start-here)
* [Go further](https://docs.zama.ai/concrete/tutorials/see-all-tutorials#go-further)
[](https://docs.zama.ai/concrete#explore-more)
Explore more
----------------------------------------------------------------
Access to additional resources and join the Zama community.
###
[](https://docs.zama.ai/concrete#explanations)
Explanations
Refer to the API, review product architecture, and access additional resources for in-depth explanations while working with Concrete.
* [API](https://docs.zama.ai/concrete/references/api)
* [Frontend fusing](https://docs.zama.ai/concrete/explanations/fusing)
* [Compiler backend](https://docs.zama.ai/concrete/developers/contributing/backends)
* [Optimizer](https://docs.zama.ai/concrete/developers/contributing/optimizer)
###
[](https://docs.zama.ai/concrete#support-channels)
Support channels
Ask technical questions and discuss with the community. Our team of experts usually answers within 24 hours in working days.
* [Community forum](https://community.zama.ai/c/concrete/)
* [Discord channel](https://discord.com/invite/zama)
###
[](https://docs.zama.ai/concrete#developers)
Developers
Collaborate with us to advance the FHE spaces and drive innovation together.
* [Contribute to Concrete](https://docs.zama.ai/concrete/developers/contributing)
* [Check the latest release note](https://github.com/zama-ai/concrete/releases)
* [Request a feature](https://github.com/zama-ai/concrete/issues/new?assignees=&labels=feature&projects=&template=features.md)
* [Report a bug](https://github.com/zama-ai/concrete/issues/new?assignees=&labels=bug%2C+triage&projects=&template=bug_report.md)
Last updated 3 months ago
Was this helpful?
---
# FHEVM v0.10 - October 2025 | Change Log
[](https://docs.zama.ai/change-log/coming-soon/fhevm-v0.10-october-2025#preview)
Preview
---------------------------------------------------------------------------------------------
* * *
The upcoming v0.10 releases will introduce a dedicated payment contract within the Gateway and enables flexible delegation of decryption rights through smart contracts.
These updates will improve fee management, access control, and overall usability of encrypted operations.
[](https://docs.zama.ai/change-log/coming-soon/fhevm-v0.10-october-2025#new-features)
New Features
-------------------------------------------------------------------------------------------------------
* * *
* **Gateway payment contract**: Add a dedicated payment contract within the Gateway to handle fee management for Coprocessor and KMS operations (input, decryption).
* **Delegation via Smart Contracts**: Users will be able to delegate decryption rights to other addresses, with fine-grained control over scope and duration:
* Explicit authorization for another address to generate EIP-712 signatures and perform `userDecrypt` operations.
* Delegation validity defined by timestamp, supporting temporary or session-based access.
* Delegation scoped to specific contract addresses for strict, context-aware access control.
[](https://docs.zama.ai/change-log/coming-soon/fhevm-v0.10-october-2025#improvements)
Improvements
-------------------------------------------------------------------------------------------------------
* * *
* Enhanced flexibility in encrypted data access through delegation.
* More transparent and structured fee management with a dedicated Gateway payment flow.
[](https://docs.zama.ai/change-log/coming-soon/fhevm-v0.10-october-2025#upcoming-resources)
Upcoming resources
-------------------------------------------------------------------------------------------------------------------
* * *
* GitHub release
* Documentation
[PreviousRoadmap](https://docs.zama.ai/change-log)
[NextFHEVM v0.9 - October 2025](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025)
Last updated 20 days ago
---
# TFHE-rs v1.4 - October 2025 | Change Log
[](https://docs.zama.ai/change-log/tfhe-rs#summary)
Summary
----------------------------------------------------------------
* * *
TFHE-rs v1.4.1 improves performance, adds new cryptographic capabilities, and enhances hardware support across CPU, GPU, and HPU backends.
See full details below:
* [CPU](https://docs.zama.ai/change-log/tfhe-rs#cpu)
* [GPU](https://docs.zama.ai/change-log/tfhe-rs#gpu)
* [HPU](https://docs.zama.ai/change-log/tfhe-rs#hpu)
[](https://docs.zama.ai/change-log/tfhe-rs#cpu)
CPU
--------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs#highlights)
Highlights
The CPU backend introduces new APIs for additional security guarantees, extended atomic pattern support, and new encrypted data handling capabilities:
* **Security** — Introduces the \`ReRand\` feature to ensure security under the sIND-CPAᴰ model.
* **Extended KS32 AP support** : The keyswitch 32 atomic pattern (KS32 AP) now supports compact public key encryption, keyswitching, compression, and noise squashing.
* **Performance**: KS32 AP provides a 10–19% speedup on 64-bit integer operations.
* **Encrypted data handling**: Adds KVStore to manipulate hashmaps in a blind way to update encrypted values.
* **Parameter clarity**: Parameter sets are now standardized and exposed as \`MetaParameters\`.
###
[](https://docs.zama.ai/change-log/tfhe-rs#new-features)
New Features
* Add MetaParameters
* Add multi bit PBS support to noise squashing
* Add noise squashing support for the KS32 AP
* Add ciphertext compression support for the KS32 AP
* Add compact public key encryption support for the KS32 AP
* Add quasi-uniform OPRF over any range for `tfhe::integer`
* Add KVStore for blind encrypted key-value updates
* Add flip operation
* Add ReRand primitives for sIND-CPAᴰ security
* Add XOF keyset
* Make `FheUint`/`FheInt`/`FheBool` compatible with AP params for conformance
* Add missing `safe_deser` for ServerKey in the C API
###
[](https://docs.zama.ai/change-log/tfhe-rs#improvements)
Improvements
* Improve FFT and NTT plan cache locking
###
[](https://docs.zama.ai/change-log/tfhe-rs#fixes)
Fixes
* Set correct degree for noise squashed decompressed ciphertext
* Avoid potential overflow for GLWE encryption on 32 bits platforms
* Fix NTT plan yielding incorrect results for a class of primes
* Fix scalar size check before ZK public key encryption
[](https://docs.zama.ai/change-log/tfhe-rs#gpu)
GPU
--------------------------------------------------------
* * *
The GPU backend receives major performance upgrades, improved PBS techniques, and new compression and benchmarking capabilities:
* **Performance**: All operations see 2× speedup on H100 GPUs, with certain primitives (multiplication, division, OPRF, ilog2, scalar division and multiplication) reaching 3–10× acceleration.
* **PBS enhancements**: A new technique called "mean reduction" replaces the previous technique "drift" for classical PBS, to keep the same cryptographic parameters without the need for an additional key.
* **Noise squashing:** Multi-bit noise squashing is introduced, providing up to 4× faster execution compared to classical PBS.
* Compression: Adds support for 128-bit compression.
* New benchmark: A new benchmark on GPU is introduced to perform AES encryption using FHE (in counter mode).
* Parameter clarity: Parameter sets are now standardized and exposed as \`MetaParameters\`.
###
[](https://docs.zama.ai/change-log/tfhe-rs#new-features-1)
New Features
* Add 128-bit multi-bit PBS for noise squashing
* Add 128-bit compression
* Add the centered modulus switch technique to reduce noise in the classical PBS
* FHE encryption of AES 128 in counter mode on GPU (available in the integer API)
###
[](https://docs.zama.ai/change-log/tfhe-rs#improvements-1)
Improvements
* Create specialized version of multi-bit pbs using thread block clusters: this results in a significant performance improvement on all operations on H100 (x2)
* Improve the multi-GPU communication scheme
* Use CUDA mempools to optimize memory reuse
* Improve division performance on nodes with 4 GPUs or more: overall division is 4x faster than in the previous release
* Improve encrypted random generation (OPRF) performance by implementing it in CUDA/C++ instead of Rust (results in 10x faster OPRF)
* Improve ilog2 performance by implementing it in CUDA/C++ instead of Rust
* Enable lut generation with preallocated CPU buffers to avoid some synchronizations with the CPU in comparisons
* Add an assert to be sure the carry part has correct size in expand
* Create message extract lut only when needed for carry propagation
* Internal refactors to enhance the C++/Rust interface (pass streams and gpu indexes in a struct, pass compression data via a struct)
###
[](https://docs.zama.ai/change-log/tfhe-rs#fixes-1)
Fixes
* Fix memory leak in multi-gpu calculations
* Fix pbs128 multi-gpu bug
* Fix some wrong indexes used in `cuda_set_device()`.
* Fix inconsistent types to avoid overflows
* Add missing syncs when releasing scalar ops and returning trivial radix
* Fix the decompression function signature in the CUDA backend
[](https://docs.zama.ai/change-log/tfhe-rs#hpu)
HPU
--------------------------------------------------------
* * *
The HPU backend improves overall latency and execution throughput:
* **Latency reduction**: Overall execution latency is reduced across all HPU operations.
* **Throughput increase**: New SIMD operations have been added, which are further enhancing the throughput of HPU on a single V80 FPGA.
###
[](https://docs.zama.ai/change-log/tfhe-rs#new-features-2)
New Features
* Add 400Mhz HPU v2.1 bitstream
* Add ERC20\_SIMD & ADD\_SIMD operations
* Add support of servers with multiple V80 boards (only one is used)
###
[](https://docs.zama.ai/change-log/tfhe-rs#improvements-2)
Improvements
* Improve latency & throughput benches (HLAPI & integer) to execute some new operations and be more stable
* Improve scheduling of MUL operation
* Reduce a bit SW latency to push IOp and receive IOp acknowledge
* In HPU v2.1 bitstream:
* Compiled with Vivado 2025.1
* Improved place & route (especially on reset) to reach 400Mhz
* Increase bandwidth to load BSK & KSK
* Improved accumulator (MMACC) structure to match PBS batch size (12)
###
[](https://docs.zama.ai/change-log/tfhe-rs#fixes-2)
Fixes
* Stabilize HPU IOp queue
* Fix a few operations (ilog2, trail0/1, ovf\_mul...)
[](https://docs.zama.ai/change-log/tfhe-rs#resources)
Resources
--------------------------------------------------------------------
* [GitHub release](https://github.com/zama-ai/tfhe-rs/releases/tag/tfhe-rs-1.4.0)
* [Documentation](https://docs.zama.ai/tfhe-rs/1.4)
[NextTFHE-rs v1.3 - July 2025](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3)
Last updated 15 hours ago
---
# FHEVM v0.7 - July 2025 | Change Log
[](https://docs.zama.ai/change-log/release/fhevm-v0.7-july-2025#highlights)
Highlights
-------------------------------------------------------------------------------------------
* * *
This version introduces the first iteration of the Zama Protocol, featuring several foundational components and improvements:
* **Gateway**: Introduced as a core component to orchestrate protocol operations.
* **Coprocessor Input Verification**: Input verification is now enforced on the coprocessor side for enhanced security.
* **Decryption Pipeline**: Coprocessors now prepare ciphertexts for decryption.
* **Solidity Library**: The Solidity library has been restructured and modernized to align with the protocol's new architecture.
###
[](https://docs.zama.ai/change-log/release/fhevm-v0.7-july-2025#breaking-changes)
Breaking changes
* Renamed the library from `TFHE` to `FHE`
* Introduced FHE.requestDecryption with support for msg.value, deprecating GatewayCaller
* Removed `ebytesXXX` types
* Replaced einput with `externalEuintXXX`, `externalEbool`, and `externalEaddress`
* Introduced per-transaction operation limits, replacing the previous per-block limit
[](https://docs.zama.ai/change-log/release/fhevm-v0.7-july-2025#resources)
Resources
-----------------------------------------------------------------------------------------
* * *
* [GitHub Release](https://github.com/zama-ai/fhevm/releases/tag/v0.7.0)
* [Documentation](https://docs.zama.ai/protocol/solidity-guides/v0.7)
[PreviousFHEVM v0.8 - September 2025](https://docs.zama.ai/change-log/release/fhevm-v0.8-september-2025)
Last updated 1 month ago
---
# Zama Protocol Docs | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
Zama released the Confidential Blockchain Protocol Testnet. Refer to the latest docs [here.](https://docs.zama.ai/protocol)
Last updated 1 month ago
Was this helpful?
---
# FHEVM v0.9 - October 2025 | Change Log
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#highlights)
Highlights
----------------------------------------------------------------------------------------------
* * *
The upcoming v.0.9 version will introduce new keygen capabilities, dynamic coprocessor management, and a redesigned decryption events workflow. These changes improve flexibility, scalability, and consensus handling while deprecating older event formats.
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#new-features)
New features
--------------------------------------------------------------------------------------------------
* * *
* **Support generation of FHE key and CRS on-chain**:
* Request the generation of an FHE key and a CRS directly through the Gateway.
* New environment variables:
* For the gateway contracts:
* `KMS_GENERATION_THRESHOLD` : The threshold used to validate the consensus on an FHE key or CRS generation.
* `KMS_NODE_STORAGE_URL_[0-N]`: The storage base URL where public materials are stored for each KMS node.
####
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#obsolete-environment-variable)
Obsolete environment variable
The following environment variables are not used anymore:
* `FHE_PARAMS_NAME`
* `FHE_PARAMS_DIGEST`
* For the coprocessor (`gw-listener`):
* `KMS_GENERATION_ADDRESS` : The address of the `KMSGeneration` gateway contract.
* For the connector:
* `KMS_GENERATION_ADDRESS` : The address of the `KMSGeneration` gateway contract.
* **New** `**PauserSet**` **immutable contract**
* Host and Gateway contracts can now be paused by any addresses added in the `PauserSet` contract.
* New environment variables:
* For the gateway contracts:
* `NUM_PAUSERS` : The number of pauser addresses to add. Should be set to `n_kms + n_copro`, with `n_kms` the number of registered KMS nodes and `n_copro` the number of registered coprocessors.
* `PAUSER_ADDRESS_[0-N]` : The pauser addresses.
####
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#obsolete-environment-variable-1)
Obsolete environment variable
The following environment variable is not used anymore:
* `PAUSER_ADDRESS`
* For the host contracts:
* `NUM_PAUSERS` : The number of pauser addresses to add. Should be set to `n_kms + n_copro`, with `n_kms` the number of registered KMS nodes and `n_copro` the number of registered coprocessors.
* `PAUSER_ADDRESS_[0-N]` : The pauser addresses.
####
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#obsolete-environment-variable-2)
Obsolete environment variable
The following environment variable is not used anymore:
* `PAUSER_ADDRESS`
* **Re-randomisation of transaction inputs**
* All inputs (including from state) of transactions are re-encrypted before evaluation of FHE operations to provide [sIND-CPAD security](https://www.zama.ai/post/drifting-towards-better-error-probabilities-in-fully-homomorphic-encryption)
.
* This new feature is transparent to users.
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#improvements)
Improvements
--------------------------------------------------------------------------------------------------
* * *
* **User decryption response**:
* Encrypted shares and signatures are no longer aggregated on-chain in the Gateway. Each response sent by a KMS now directly emits an event containing them separately.
* New events introduced in the `Decryption` contract:
* `UserDecryptionResponse(uint256 indexed decryptionId, uint256 indexShare, bytes userDecryptedShare, bytes signature, bytes extraData);`
* `UserDecryptionResponseThresholdReached(uint256 indexed decryptionId);`
####
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#breaking-changes)
Breaking changes
The following event is deprecated from the Gateway's `Decryption` contract:
* `PublicDecryptionResponse(uint256 indexed decryptionId, bytes decryptedResult, bytes[] signatures, bytes extraData)`
* **User decryption request**
* The user EIP712 signature verification is simplified in the Gateway's `Decryption` contract.
####
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#breaking-changes-1)
Breaking changes
The `uint256 contractsChainId` field is no longer part of the `UserDecryptRequestVerification` struct used for EIP712 signature verification.
* **Gateway contract renaming**
* Two contracts have been renamed.
####
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#breaking-changes-2)
Breaking changes
The following Gateway contracts have been renamed:
* `MultichainAcl` -> `MultichainACL`
* `KmsManagement` -> `KMSGeneration`
As a consequence, the following environment variable have been renamed:
* `KMS_MANAGEMENT_ADDRESS` -> `KMS_GENERATION_ADDRESS`
* `KMS_CONNECTOR_KMS_MANAGEMENT_CONTRACT__ADDRESS` -> `KMS_CONNECTOR_KMS_GENERATION_CONTRACT__ADDRESS`
Also, in the `values.yaml` files of the KMS Connector's Helm chart, the following field has been renamed:
* `kmsManagement` -> `kmsGeneration`
* **Gateway check functions replaced**
* All external `check...` view functions have been removed from the Gateway contracts.
* Associated errors have been moved to different contracts or deleted.
* They have been replaced by equivalent `is...` view functions that no longer revert and instead return a boolean.
####
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#breaking-changes-3)
Breaking changes
All `check...` view functions are not longer available in the Gateway contracts and associated events have been moved deleted. For example:
* `checkPublicDecryptAllowed` have been replaced by `isPublicDecryptAllowed`
* `PublicDecryptNotAllowed` error have been moved to `Decryption` contract
[](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025#resources)
Resources
--------------------------------------------------------------------------------------------
* * *
* [GitHub release](https://github.com/zama-ai/fhevm/releases/tag/v0.9.0)
* [Documentation](https://docs.zama.ai/protocol/solidity-guides/v0.8)
[PreviousFHEVM v0.10 - October 2025](https://docs.zama.ai/change-log/coming-soon/fhevm-v0.10-october-2025)
[NextFHEVM v0.8 - September 2025](https://docs.zama.ai/change-log/release/fhevm-v0.8-september-2025)
Last updated 16 hours ago
---
# FHEVM v0.8 - September 2025 | Change Log
[](https://docs.zama.ai/change-log/release/fhevm-v0.8-september-2025#highlights)
Highlights
------------------------------------------------------------------------------------------------
* * *
This version brings several new features that makes the FHEVM more scalable, secure, and developer-friendly:
* **New KMS connecto**r for modular integration
* **Compressed ciphertexts** for lighter payloads
* **Flexible** `**extraData**` **field** for richer apps
* **Post-quantum ML-KEM512** for faster, smaller decrypts
* **Stronger chain resilience & ERC-7995** compliance
[](https://docs.zama.ai/change-log/release/fhevm-v0.8-september-2025#new-features)
New features
----------------------------------------------------------------------------------------------------
* * *
* **New KMS connector:** Introduced a new Key Management System connector to improve modularity and integration.
* **Compressed ciphertext support:** Added support for compressed ciphertexts in both the SnS worker and KMS, reducing payload sizes.
* **Generic** `**extraData**` **field:** Gateway functions, events, and signed structs now include a generic `extraData` field to improve extensibility and custom data support.
* `**SepoliaConfig**` **update:** Introduced the `protocolId()` function to support protocol identification.
[](https://docs.zama.ai/change-log/release/fhevm-v0.8-september-2025#improvements)
Improvements
----------------------------------------------------------------------------------------------------
* * *
* **ERC-7995 compatibility:** Updated the Oracle’s expected callback function interface for compliance, following [ERC-7995](https://github.com/ethereum/ERCs/pull/1143)
.
####
[](https://docs.zama.ai/change-log/release/fhevm-v0.8-september-2025#breaking-changes)
Breaking Changes
**Oracle callback function signature** now requires the following format:
Copy
function callbackExample(
uint256 requestID,
bytes memory cleartexts,
bytes memory decryptionProof
) external;
* **Reduced user decrypt payload size:** Migrated to **ML-KEM512** (128-bit equivalent post-quantum security), significantly reducing decrypt response sizes and allowing more responses per block.
* **Host listener:** Implemented reorganization handling in the host listener for increased chain resilience.
* **Library storage layout:** Adjusted storage layout to align with the standard’s guidelines.
[](https://docs.zama.ai/change-log/release/fhevm-v0.8-september-2025#resources)
Resources
----------------------------------------------------------------------------------------------
* * *
* [GitHub Release](https://github.com/zama-ai/fhevm/releases/tag/v0.8.0)
* [Documentation](https://docs.zama.ai/protocol/solidity-guides/v0.8)
[PreviousFHEVM v0.9 - October 2025](https://docs.zama.ai/change-log/release/fhevm-v0.9-october-2025)
[NextFHEVM v0.7 - July 2025](https://docs.zama.ai/change-log/release/fhevm-v0.7-july-2025)
Last updated 23 days ago
---
# Overview | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)

fhEVM is a suite of solutions that enables confidential smart contracts on the EVM using **Fully Homomorphic Encryption (FHE)**. This document provides a high-level overview of the fhEVM suite along with onboarding guidance tailored to specific audiences.
###
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview#for-dapp-developers)
For dApp developers
The fhEVM Protocol provides a `**TFHE**` **Solidity library** for building confidential smart contracts, a `**fhevm.js**` **Javascript library** to enable front‐end FHE interactions, and a range of developer tools, examples, and templates to streamline the usage for developers.
####
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview#smart-contract-development)
Smart contract development
Repository
Description
[fhevm](https://github.com/zama-ai/fhevm-solidity/)
Solidity library for FHE operations (e.g., encryption/decryption, arithmetic) within smart contracts.
[fhevm-hardhat-template](https://github.com/zama-ai/fhevm-hardhat-template)
Hardhat template with scripts for compiling, deploying, and testing FHE‐enabled contracts.
fhevm-foundry-template - _coming soon_
Foundry template for building FHE smart contracts.
[fhevm-contracts](https://github.com/zama-ai/fhevm-contracts)
Ready‐to‐use FHE smart contract example covering finance, governance, and ERC‐20 tokens use cases.
####
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview#frontend-development)
Frontend development
Repository
Description
[fhevmjs](https://github.com/zama-ai/fhevm-js/)
JavaScript library for client‐side FHE, enabling encryption, decryption, and data handling.
[fhevm-react-template](https://github.com/zama-ai/fhevm-react-template)
React.js template to quickly spin up FHE‐enabled dApps.
[fhevm-next-template](https://github.com/zama-ai/fhevm-next-template)
Next.js template for integrating FHE in server‐side rendered or hybrid web apps.
[fhevm-vue-template](https://github.com/zama-ai/fhevm-vue-template)
Vue.js template for creating privacy‐preserving dApps with encrypted data
####
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview#examples-and-resources)
Examples & Resources
Repository
Description
[dapps](https://github.com/zama-ai/dapps)
Sample decentralized applications demonstrating FHE with real‐world code.
[Zama Bounty Program](https://github.com/zama-ai/bounty-program)
Explore open challenges and submit contributions to earn rewards.
[Awesome Zama](https://github.com/zama-ai/awesome-zama)
A curated list by the team at Zama of blog posts, libraries, research papers, and tutorials on Fully Homomorphic Encryption (FHE).
###
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview#for-network-builders)
For network builders
To **integrate FHE at the protocol level** or operate an **FHE‐enabled network**, fhEVM offers the fhevm backend modules. These repositories include the foundational implementations that enables FHE in blockchain systems, ensuring that privacy remains at the core of your network architecture.
Repository
Description
[fhevm-backend](https://github.com/zama-ai/fhevm-backend)
Rust backend & Go‐Ethereum modules, enabling native or coprocessor‐based FHE.
[fhevm-go](https://github.com/zama-ai/fhevm-go/)
Go implementation of the FHE Virtual Machine
[zbc-go-ethereum](https://github.com/zama-ai/zbc-go-ethereum/)
Modified go-ethereum with enhanced FHE support
[PreviousWelcome to fhEVM](https://docs.zama.ai/fhevm/0.6)
[NextQuick Start](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1)
Last updated 5 months ago
Was this helpful?
---
# Remix | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
This tutorial covers the essentials steps to quickly write and deploy confidential ERC20 smart contract using the Zama Plug in.
Remix is a powerful in-browser IDE for Ethereum smart contract development. It offers a fast, intuitive way to write and deploy confidential ERC20 contracts using Zama’s fhEVM plugin—no local setup required.
**In this tutorial, you will learn to:**
1. Set up Remix for fhEVM development.
2. Connect your wallet (e.g., MetaMask) to Remix.
3. Deploy a **ConfidentialERC20** contract.
4. Interact with your deployed contract directly in the Remix interface.
By the end, you’ll have a working confidential ERC20 token on Sepolia network and know how to perform encrypted transactions.
[PreviousQuick Start](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1)
[Next1\. Setting up Remix](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1/remix/remix)
Last updated 7 months ago
Was this helpful?
---
# Quick Start | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
This tutorial guides you to start quickly with Zama’s **Fully Homomorphic Encryption (FHE)** technology for building confidential smart contracts.
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1#what-youll-learn)
What You’ll Learn
-----------------------------------------------------------------------------------------------------
In about 20 minutes, you will:
* Build your first **confidential ERC20** contract that leverages FHE.
* Deploy the contract on the **Sepolia** Network.
* **Mint tokens** and **perform transactions** in FHE.
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1#prerequisite)
Prerequisite
--------------------------------------------------------------------------------------------
* A basic understanding of **Solidity** library and **Ethereum**.
* A certain amount of **Sepolia ETH** available.
* If you don’t have enough ETH, use a Sepolia faucet to request free SepoliaETH for testing such as [Alchemy Faucet](https://www.alchemy.com/faucets/ethereum-sepolia)
or [QuickNode Faucet](https://faucet.quicknode.com/ethereum/sepolia)
.
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1#what-is-confidenetial-erc20)
What is Confidenetial ERC20
--------------------------------------------------------------------------------------------------------------------------
The contract that you will build with this tutorial is called `ConfidentialERC20Mintable` — a privacy-preserving ERC20 implementation that leverages **FHE** to keep balances and transactions confidential. To understand this contract, let’s first introduce the foundational concepts.
**RC20**
ERC20 is a widely used token standard on Ethereum that defines a set of rules for creating and managing fungible tokens. These tokens are efficient but lack privacy — balances and transactions are visible to anyone on the blockchain.
**Confidential ERC20**
Zama’s `ConfidentialERC20` introduces privacy to ERC20 tokens by storing balances and transactions in an encrypted format using FHE.
The `ConfidentialERC20` contract still supports standard ERC20 functions such as `transfer`, `approve`, `transferFrom`, `balanceOf`, and `totalSupply` but ensures these operations are processed securely with encrypted data.
To explore the implementation details of ConfidentialERC20, check out the [Zama blog post](https://www.zama.ai/post/confidential-erc-20-tokens-using-homomorphic-encryption)
.
**Confidential ERC-20 Mintable**
The contract that we will build in this tutorial is `ConfidentialERC20Mintable` . It's built on top of `ConfidentialERC20` by adding secure minting capabilities. This allows authorized accounts to create new tokens, while maintaining the privacy guarantees of encrypted balances and transactions.
The `ConfidentialERC20Mintable` contract ensures:
* **Enhanced privacy**: Balances are stored as encrypted values (`euint64`), preventing public inspection of account balances.
* **Secure transactions**: Token transfers are processed securely, maintaining confidentiality of amounts.
* **Owner visibility**: Only account owners can decrypt and view their balances.
[](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1#next-steps)
Next steps
----------------------------------------------------------------------------------------
Choose your path and get started:
* [**Remix Guide**](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1/remix)
– Rapid in‐browser setup, great for **learning** and fast **prototyping**.
* [**Hardhat Guide**](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1/hardhat)
– Full-fledged development environment, suitable for **production**.
[PreviousOverview](https://docs.zama.ai/fhevm/0.6/getting-started/overview)
[NextRemix](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1/remix)
Last updated 6 months ago
Was this helpful?
---
# Hardhat | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
This tutorial covers the essential steps to quickly write and deploy a confidential ERC20 smart contract using the [fhEVM Hardhat template](https://github.com/zama-ai/fhevm-hardhat-template)
.
Hardhat is a flexible, extensible development environment for Ethereum, providing a robust toolkit for compiling, testing, and deploying smart contracts. With Zama’s fhEVM integration, you can develop confidential ERC20 contracts locally and then seamlessly deploy them to a real fhEVM node.
**In this tutorial, you will learn to:**
1. Set up a Hardhat project with the [**fhEVM Hardhat Template**](https://github.com/zama-ai/fhevm-hardhat-template)
.
2. Write confidential contracts utilizing encrypted data types.
3. Test your contracts in **mocked mode** (for rapid feedback and coverage).
4. Deploy your confidential ERC20 contract to the Sepolia test network.
5. Interact with your deployed contract, including performing encrypted transfers.
By following these steps, you’ll get familiar with the complete development workflow using Hardhat, and be able to build, test and deploy confidential ERC20 smart contracts on Sepolia testnet.
[Previous4\. Interacting with the contract](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1/remix/interact)
[NextPrerequisites](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1/hardhat/prerequisites)
Last updated 7 months ago
Was this helpful?
---
# Frequently Asked Questions | Community Docs
[PreviousFrequently Asked Questions](https://docs.zama.ai/programs/developer-program/frequently-asked-questions)
Last updated 1 month ago
---
# See all tutorials | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
[](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials#solidity-smart-contracts-templates-fhevm-contracts)
Solidity smart contracts templates - `fhevm-contracts`
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The [fhevm-contracts repository](https://github.com/zama-ai/fhevm-contracts)
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 [contracts standard library guide](https://docs.zama.ai/fhevm/0.6/smart-contract/contracts)
.
####
[](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials#token)
Token
* [ConfidentialERC20](https://github.com/zama-ai/fhevm-contracts/blob/main/contracts/token/ERC20/ConfidentialERC20.sol)
: Standard ERC20 with encryption.
* [ConfidentialERC20Mintable](https://github.com/zama-ai/fhevm-contracts/blob/main/contracts/token/ERC20/extensions/ConfidentialERC20Mintable.sol)
: ERC20 with minting capabilities.
* [ConfidentialERC20WithErrors](https://github.com/zama-ai/fhevm-contracts/blob/main/contracts/token/ERC20/extensions/ConfidentialERC20WithErrors.sol)
: ERC20 with integrated error handling.
* [ConfidentialERC20WithErrorsMintable](https://github.com/zama-ai/fhevm-contracts/blob/main/contracts/token/ERC20/extensions/ConfidentialERC20WithErrorsMintable.sol)
: ERC20 with both minting and error handling.
####
[](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials#governance)
Governance
* [ConfidentialERC20Votes](https://github.com/zama-ai/fhevm-contracts/blob/main/contracts/governance/ConfidentialERC20Votes.sol)
: Confidential ERC20 governance token implementation. [It is based on Comp.sol](https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/Comp.sol)
.
* [ConfidentialGovernorAlpha](https://github.com/zama-ai/fhevm-contracts/blob/main/contracts/governance/ConfidentialGovernorAlpha.sol)
: A governance contract for managing proposals and votes. [It is based on GovernorAlpha.sol](https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/GovernorAlpha.sol)
.
####
[](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials#utils)
Utils
* [EncryptedErrors](https://github.com/zama-ai/fhevm-contracts/blob/main/contracts/utils/EncryptedErrors.sol)
: Provides error management utilities for encrypted contracts.
[](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials#code-examples-on-github)
Code examples on GitHub
-------------------------------------------------------------------------------------------------------------------
* [Blind Auction](https://github.com/zama-ai/dapps/tree/main/hardhat/contracts/auctions)
: A smart contract for conducting blind auctions where bids are encrypted and the winning bid remains private.
* [Decentralized ID](https://github.com/zama-ai/dapps/tree/main/hardhat/contracts/decIdentity)
: A blockchain-based identity management system using smart contracts to store and manage encrypted personal data.
* [FheWordle](https://github.com/zama-ai/dapps/tree/main/hardhat/contracts/fheWordle)
: A privacy-preserving implementation of the popular word game Wordle where players guess a secret encrypted word through encrypted letter comparisons.
* [Cipherbomb](https://github.com/immortal-tofu/cipherbomb)
: A multiplayer game where players must defuse an encrypted bomb by guessing the correct sequence of numbers.
* [Voting example](https://github.com/allemanfredi/suffragium)
: 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.
[](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials#frontend-examples)
Frontend examples
-------------------------------------------------------------------------------------------------------
* [Cipherbomb UI](https://github.com/immortal-tofu/cipherbomb-ui)
: A multiplayer game where players must defuse an encrypted bomb by guessing the correct sequence of numbers.
[](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials#blog-tutorials)
Blog tutorials
-------------------------------------------------------------------------------------------------
* [Suffragium: An Encrypted Onchain Voting System Leveraging ZK and FHE Using Zama's fhEVM](https://www.zama.ai/post/encrypted-onchain-voting-using-zk-and-fhe-with-zama-fhevm)
- Nov 2024
[](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials#video-tutorials)
Video tutorials
---------------------------------------------------------------------------------------------------
* [How to do Confidential Transactions Directly on Ethereum?](https://www.youtube.com/watch?v=aDv2WYOpVqA)
- Nov 2024
* [Zama - FHE on Ethereum (Presentation at The Zama CoFHE Shop during EthCC 7)](https://www.youtube.com/watch?v=WngC5cvV_fc&ab_channel=Zama)
- Jul 2024
**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**](https://www.zama.ai/developer-survey)
to participate.
###
[](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials#legacy-not-compatible-with-latest-fhevm)
Legacy - Not compatible with latest fhEVM
* [Build an Encrypted Wordle Game Onchain using FHE and Zama's fhEVM](https://www.zama.ai/post/build-an-encrypted-wordle-game-onchain-using-fhe-and-zama-fhevm)
- February 2024
* [Programmable Privacy and Onchain Compliance using Homomorphic Encryption](https://www.zama.ai/post/programmable-privacy-and-onchain-compliance-using-homomorphic-encryption)
- November 2023
* [Confidential DAO Voting Using Homomorphic Encryption](https://www.zama.ai/post/confidential-dao-voting-using-homomorphic-encryption)
- October 2023
* [On-chain Blind Auctions Using Homomorphic Encryption and the fhEVM](https://www.zama.ai/post/on-chain-blind-auctions-using-homomorphic-encryption)
- July 2023
* [Confidential ERC-20 Tokens Using Homomorphic Encryption and the fhEVM](https://www.zama.ai/post/confidential-erc-20-tokens-using-homomorphic-encryption)
- June 2023
* [Using asynchronous decryption in Solidity contracts with fhEVM](https://www.zama.ai/post/video-tutorial-using-asynchronous-decryption-in-solidity-contracts-with-fhevm)
- April 2024
* [Accelerate your code testing and get code coverage using fhEVM mocks](https://www.zama.ai/post/video-tutorial-accelerate-your-code-testing-and-get-code-coverage-using-fhevm-mocks)
- January 2024
* [Use the CMUX operator on Zama’s fhEVM](https://www.youtube.com/watch?v=7icM0EOSvU0)
- October 2023
* [\[Video tutorial\] How to Write Confidential Smart Contracts Using Zama's fhEVM](https://www.zama.ai/post/video-tutorial-how-to-write-confidential-smart-contracts-using-zamas-fhevm)
- October 2023
* [Workshop during ETHcc: Homomorphic Encryption in the EVM](https://www.youtube.com/watch?v=eivfVykPP8U)
- July 2023
[Previous5\. Interacting with the contract](https://docs.zama.ai/fhevm/0.6/getting-started/overview-1/hardhat/5.-interacting-with-the-contract)
[NextKey features](https://docs.zama.ai/fhevm/0.6/smart-contract/key_concepts)
Last updated 7 months ago
Was this helpful?
---
# Frequently Asked Questions | Community Docs
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#general-information)
General Information
---------------------------------------------------------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#what-is-the-zama-developer-program)
What is the Zama Developer Program?
A monthly program that rewards developers to build confidential dApps using the Zama Protocol.
Zama Developer Program includes two main tracks:
* Builder Track – Monthly program for developers building dApps with FHEVM.
* Bounty Track – Monthly program for developers tackling specific technical challenges.
Please avoid including **“Zama”** in your project name. Be creative! This helps prevent confusion and keeps the community safe from potential scam attempts.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#who-is-it-for)
Who is it for?
Developers and engineers with the knowledge to build dApps on the blockchain.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#is-the-program-currently-live)
Is the program currently live?
Yes – in BETA. We're testing and iterating based on feedback to improve the experience and reward structure.
####
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#what-are-the-rewards)
What are the rewards?
* A share of the monthly reward pool
* Exclusive collaboration opportunities
* Startup support and mentorship
* Access to VC and funding networks
* Early access to Zama technologies and updates
* Social media features and promotional support
* Exclusive Discord roles and channels
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#where-can-i-join-the-program)
Where can I join the program?
Join here: [](https://guild.xyz/zama/creator-program)
[https://guild.xyz/zama/developer-program](https://guild.xyz/zama/developer-program)
Explore previous winners' projects: [https://www.zama.ai/programs/developer-program](https://zama-project.webflow.io/programs/developer-program)
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#getting-started)
Getting Started
-------------------------------------------------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#how-does-it-work)
How does it work?
1. Join via [Guild.xyz](http://guild.xyz/)
and connect your wallet & GitHub
2. Verify your Github account on Guild to access to tech support channels on Discord
3. Submit a confidential dApp or bounty project to qualify as a Contributor
4. Zama team reviews monthly submissions
5. Earn rewards if selected among the monthly Top 5 builders or Top 3 bounty submissions.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#whats-the-monthly-timeline-and-process)
What's the monthly timeline and process?
* Program Opens – 1st day of the month: New season begins
* Creation Period – 1st day to the last day of the month: Build your dApp demo
* Submission Deadline – Last day of the month @ 23:59 AOE: Snapshot taken for the review and ranking
* Review period: Zama reviews submissions, validates levels, and selects winners
* Winner announcement : Winners announced in Discord #developer-program channel during the following month
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#requirements)
Requirements
-------------------------------------------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#what-are-the-tasks-and-roles)
What are the tasks and roles
* Developer Program - Access : Verify your GitHub account on Guild
* Developer Program - Contributor: Submit valid solutions to the Developer Program
* Developer Program - Winner: Be selected as top monthly winners
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#what-counts-as-a-valid-submission)
What counts as a valid submission?
For the Builder Track:
* A functioning dApp demo using the Zama Protocol
* Includes both smart contract + frontend (demo video optional but recommended)
* Clear documentation explaining the project
For the Bounty Track:
* Follow the specific requirements listed in the bounty announcements on [zama.ai/programs/developer-program](https://zama.ai/programs/developer-program)
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#what-are-the-judging-criteria)
What are the judging criteria?
Every submission will be evaluated based on the following aspects:
Baseline Requirements (50%)
* Original tech architecture with solidity contracts – 35%: Unique logic, meaningful use of FHE, not just boilerplate.
* Working demo deployment – 15%: Live, functional deployment that demonstrates the project in action.
_(If either is missing, the project is not considered valid.)_
Quality & Completeness (30%)
* Testing – 10%: Unit/integration tests showing reliability and correctness.
* UI/UX design – 10%: Intuitive, polished, and user-friendly interface.
* Presentation video – 10%: Clear walkthrough of the idea, flow, and usage.
Differentiators (20%)
* Development effort – 10%: Depth of technical work, completeness of the solution.
* Business potential – 10%: Possibility to evolve into a sustainable product, attract users/partners.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#can-i-submit-multiple-projects)
Can I submit multiple projects?
There's no restriction on the number of submissions, but only your best submission will be considered for the reward each month. We strongly encourage you to focus on one project per cohort, as rewards are based on the overall quality of a project.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#can-i-participate-more-than-once)
Can I participate more than once?
Yes. You can submit a new project every month and compete again, even if you’ve won before.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#can-i-submit-the-same-project-again-if-not-won)
Can I submit the same project again if not won?
Yes. We encourage you to refine your work based on the judging criteria and submit again in the following month.
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#rewards-and-rankings)
Rewards & Rankings
---------------------------------------------------------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#who-gets-the-rewards)
Who gets the rewards?
Each month, up to 8 top projects are selected as Developer Program Winners:
* Top 5 projects of the Builder track
* Top 3 projects of the Bounty track
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#how-are-winners-selected)
How are winners selected?
Valid submissions are evaluated based on these criteria:
* Technical execution: Correct, secure use of FHEVM and Solidity best practices.
* Completeness: End-to-end workflow from smart contract to front end.
* Innovation: Originality and potential impact of the project.
* Usability & Clarity: Clear documentation, clean user interface, and easy-to-follow testing instructions.
* Presentation (Optional): A clear demo video or walkthrough that explains the project.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#how-much-can-i-earn)
How much can I earn?
During the BETA phase, the monthly reward pool may change according to the quality of the contributions. The reward amount of each month will be announced alongside the final leaderboard [here](https://www.zama.ai/developer-program)
.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#how-do-i-claim-my-reward)
How do I claim my reward?
All rewards accumulate until TGE and you can claim accumulated rewards afterward.
Note that your Guild connection is required to track and attribute rewards. Please make sure you’ve connected the following in Guild:
* Wallet
* Email address
* GitHub account
Once everything is connected, follow Zama on social media and Discord to stay updated on reward announcements.
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#support-and-community)
Support & Community
-----------------------------------------------------------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#why-is-the-guild-not-verifying-my-commits)
Why is the Guild not verifying my commits?
If you’re a legitimate developer and have trouble meeting all the Guild requirements, please send an email to marketing\[at\]zama.ai with the following information:
* GitHub username
* Email connected to Guild
* Discord user name
* your personal information, such as CV, past projects, demo, etc. that could help us to verify your profile.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#what-is-the-startup-program)
What is the Startup Program?
The Startup Program is an exclusive tier designed for professional teams who have already built or are currently building functional applications using the Zama Protocol. To join or learn more, please contact marketing\[at\]zama.ai.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#where-can-i-get-help)
Where can I get help?
* Discord: Go to #creator-program channel to ask questions or give feedbacks
* Guild.xyz: Check your role status and requirements
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#can-i-collaborate-with-other-creators)
Can I collaborate with other creators?
Yes! Community collaboration is encouraged. Just ensure each creator's individual contributions meet the program requirements.
###
[](https://docs.zama.ai/programs/developer-program/frequently-asked-questions#terms-and-conditions)
Terms and conditions
* Zama reserves all rights to determine submission eligibility
* Zama has final say on winner selection and reward distribution
* Program rules, requirements, and rewards may change during BETA
* Violations (spam, plagiarism, fake engagement) result in disqualification
Note: This program is in BETA - terms are subject to change based on participant feedback.
[PreviousFrequently Asked Questions](https://docs.zama.ai/programs/creator-program/frequently-asked-questions)
[NextFrequently Asked Questions](https://docs.zama.ai/programs/tester-program/frequently-asked-questions)
Last updated 9 hours ago
---
# Frequently Asked Questions | Community Docs
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#general-information)
General Information
-------------------------------------------------------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#what-is-the-zama-creator-program)
What is the Zama Creator Program?
A monthly program rewarding creators for high-quality content about Zama & FHE on X(Twitter) and Farcaster.
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#who-is-it-for)
Who is it for?
Content creator - or anyone passionate about sharing their insights on FHE and Zama.
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#is-the-program-currently-live)
Is the program currently live?
Yes – in BETA. We're testing and iterating based on community feedback to improve the experience and rewards structure.
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#what-are-the-rewards)
What are the rewards?
* Monthly prize pool
* ZAMA OG NFT granting:
* Alpha access to Zama's next updates
* Exclusive Discord roles & channels
* Zama exclusive merch
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#where-can-i-join-the-program)
Where can I join the program?
Join the program : [https://guild.xyz/zama/creator-program](https://guild.xyz/zama/creator-program)
Leaderboard: [https://www.zama.ai/programs/creator-program](https://www.zama.ai/programs/developer-program)
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#getting-started)
Getting Started
-----------------------------------------------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#how-does-it-work)
How does it work?
1. Join Guild.xyz at [https://guild.xyz/zama/creator-program](https://guild.xyz/zama/creator-program)
2. Connect wallet, email and social platform accounts
3. Follow @zama\_fhe on your chosen platform(s)
4. Create original posts abut Zama and/or FHE
5. **Include #ZamaCreatorProgram hashtag and/or tag @zama\_fhe in your posts**
6. Share the link post in Discord (optional but recommended)
7. Wait for final results announced in the following month
8. Claim your total reward after the $ZAMA TGE
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#whats-the-monthly-timeline-and-process)
What's the monthly timeline and process?
* Program Opens – 1st day of the month: New season begins
* Creation Period – 1st to last day of the month: Create content
* Submission Deadline – Last day @ 23:59 AOE: Snapshot taken for algorithm ranking
* Review Period: Zama team reviews all contributions to validate the algorithm ranking and select the Creator’s Choices.
* Winners Announced: Final leaderboard is announced during the next month.
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#content-requirements)
Content Requirements
---------------------------------------------------------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#what-counts-as-eligible-content)
What counts as eligible content?
For both X(Twitter) and Farcaster, core requirements are:
* Original posts about Zama and/or FHE
* **Must tag @zama\_fhe and/or use #ZamaCreatorProgram (essential for tracking)**
* (Optional) Share your post in #creator-program-links on Discord
See more details on the page: [https://www.zama.ai/programs/creator-program](https://www.zama.ai/programs/developer-program)
**⚠️ Important notes:**
* The hashtag and/or mention is essential — it's how we track your post for rewards and rankings!
* Spam, plagiarism, or fake engagement (bought likes/followers) will be removed from the competition and leaderboard once found.
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#how-many-posts-should-i-create)
How many posts should I create?
You need to create at least 2 posts to be tracked by the algorithm. After that, you can post as many times as you like. Rankings are based on the combined reach and impact of all your eligible posts.
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#rewards-and-rankings)
Rewards & Rankings
-------------------------------------------------------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#who-gets-the-rewards)
Who gets the rewards?
We're actively scaling the leaderboard to reward more content creators.
On X (Twitter):
* Top 100 – Share the monthly reward pool
* Zama OG NFTs
* Season 1 (Concluded) → Top 250 get "Zama OG 001" NFT
* Season 2 (Concluded) → Top 500 get "Zama OG 002" NFT
* Season 3 (Live now) → Top 1000 get "Zama OG 003" NFT
* 3 Creator’s Choice – Exceptional reward pool
On Farcaster:
* 3 Creator’s Choice – Exceptional reward pool
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#how-are-winners-selected)
How are winners selected?
* The leaderboard on X are selected by an algorithm using Cookie3 metrics such as followers, impressions, and engagement. See algorithm details [here](https://www.zama.ai/programs/creator-program)
.
* The Creator’s Choice winners on both X and Farcaster are hand-picked by Zama based on originality, impact, and quality.
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#how-much-can-i-earn)
How much can I earn?
During the BETA phase, the monthly reward pool may change according to the quality of the contributions. The reward amount of each month will be announced alongside the final leaderboard [here](https://www.zama.ai/creator-program)
.
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#how-do-i-claim-my-rewards)
How do I claim my rewards?
All monetary rewards accumulate and are claimable at TGE.
Note that your Guild connection is required to track and attribute rewards. Please make sure you’ve connected the following in Guild:
* Wallet
* Email address
* X account (for X content creation)
* Farcaster profile (for Farcaster content creation)
Once everything is connected, follow Zama on social media and Discord to stay updated on reward announcements.
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#support-and-community)
Support & Community
---------------------------------------------------------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#where-can-i-get-help)
Where can I get help?
* Discord: Go to #creator-program channel to ask questions or give feedbacks
* Guild.xyz: Check your role status and requirements
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#can-i-collaborate-with-other-creators)
Can I collaborate with other creators?
Yes! Community collaboration is encouraged. Just ensure each creator's individual contributions meet the program requirements.
###
[](https://docs.zama.ai/programs/creator-program/frequently-asked-questions#terms-and-conditions)
Terms and conditions
* Zama reserves all rights to determine submission eligibility
* Zama has final say on winner selection and reward distribution
* Program rules, requirements, and rewards may change during BETA
* Violations (spam, plagiarism, fake engagement) result in disqualification
Note: This program is in BETA - terms are subject to change based on participant feedback.
[PreviousWelcome to the Zama Programs Documentations](https://docs.zama.ai/programs)
[NextFrequently Asked Questions](https://docs.zama.ai/programs/developer-program/frequently-asked-questions)
Last updated 13 days ago
---
# Configuration | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/configure#core-configuration-setup)
Core configuration setup
------------------------------------------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/configure#key-components-configured-automatically)
Key components configured automatically
------------------------------------------------------------------------------------------------------------------------------------------------
1. **TFHE library**: Sets up encryption parameters and cryptographic keys.
2. **Gateway**: Manages secure cryptographic operations, including reencryption and decryption.
3. **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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/configure#zamafhevmconfig.sol)
ZamaFHEVMConfig.sol
--------------------------------------------------------------------------------------------------------
This configuration contract initializes the **fhEVM environment** with required encryption parameters.
**Import based on your environment:**
Copy
// For Ethereum Sepolia
import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";
**Purpose:**
* Sets encryption parameters such as cryptographic keys and supported ciphertext types.
* Ensures proper initialization of the FHEVM environment.
**Example: using Sepolia configuration**
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";
contract MyERC20 is SepoliaZamaFHEVMConfig {
constructor() {
// Additional initialization logic if needed
}
}
[](https://docs.zama.ai/fhevm/0.6/smart-contract/configure#zamagatewayconfig.sol)
ZamaGatewayConfig.sol
------------------------------------------------------------------------------------------------------------
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**
Copy
// For Ethereum Sepolia
import { SepoliaZamaGatewayConfig } from "fhevm/config/ZamaGatewayConfig.sol";
**Purpose**
* Configures the Gateway for secure cryptographic operations.
* Facilitates reencryption and decryption requests.
**Example: Configuring the gateway with Sepolia settings**
Copy
import "fhevm/lib/TFHE.sol";
import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";
import { SepoliaZamaGatewayConfig } from "fhevm/config/ZamaGatewayConfig.sol";
import "fhevm/gateway/GatewayCaller.sol";
contract Test is SepoliaZamaFHEVMConfig, SepoliaZamaGatewayConfig, GatewayCaller {
constructor() {
// Gateway and FHEVM environment initialized automatically
}
}
[](https://docs.zama.ai/fhevm/0.6/smart-contract/configure#using-isinitialized)
Using `isInitialized`
----------------------------------------------------------------------------------------------------------
The `isInitialized` utility function checks whether an encrypted variable has been properly initialized, preventing unexpected behavior due to uninitialized values.
**Function signature**
Copy
function isInitialized(T v) internal pure returns (bool)
**Purpose**
* Ensures encrypted variables are initialized before use.
* Prevents potential logic errors in contract execution.
**Example: Initialization Check for Encrypted Counter**
Copy
require(TFHE.isInitialized(counter), "Counter not initialized!");
[](https://docs.zama.ai/fhevm/0.6/smart-contract/configure#summary)
Summary
--------------------------------------------------------------------------------
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.
[PreviousKey features](https://docs.zama.ai/fhevm/0.6/smart-contract/key_concepts)
[NextFhEVM contracts](https://docs.zama.ai/fhevm/0.6/smart-contract/contracts)
Last updated 10 months ago
Was this helpful?
---
# Decryption | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
[Decryption](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt)
[Decryption in depth](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt_details)
[Re-encryption](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/reencryption)
[PreviousEncrypted Inputs](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs)
[NextDecryption](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt)
Last updated 10 months ago
Was this helpful?
---
# ACL examples | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
This page provides detailed instructions and examples on how to use and implement the ACL (Access Control List) in fhEVM. For an overview of ACL concepts and their importance, refer to the [access control list (ACL) overview](https://docs.zama.ai/fhevm/0.6/smart-contract/acl)
.
* * *
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#controlling-access-permanent-and-transient-allowances)
Controlling access: permanent and transient allowances
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
The ACL system allows you to define two types of permissions for accessing ciphertexts:
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#permanent-allowance)
Permanent allowance
* **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.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#transient-allowance)
Transient allowance
* **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.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#syntactic-sugar)
Syntactic sugar
* **Function**: `TFHE.allowThis(ciphertext)`
* **Equivalent To**: `TFHE.allow(ciphertext, address(this))`
* **Purpose**: Simplifies granting permanent access to the current contract for managing ciphertexts.
* * *
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#example-granting-permissions-in-a-multi-contract-setup)
Example: granting permissions in a multi-contract setup
Copy
import "fhevm/lib/TFHE.sol";
import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";
contract SecretGiver is SepoliaZamaFHEVMConfig {
SecretStore public secretStore;
constructor() {
secretStore = new SecretStore();
}
function giveMySecret() public {
// Create my secret - asEuint16 gives automatically transient allowance for the resulting handle (note: an onchain trivial encryption is not secret)
euint16 mySecret = TFHE.asEuint16(42);
// Allow temporarily the SecretStore contract to manipulate `mySecret`
TFHE.allowTransient(mySecret, address(secretStore));
// Call `secretStore` with `mySecret`
secretStore.storeSecret(mySecret);
}
}
Copy
contract SecretStore is SepoliaZamaFHEVMConfig {
euint16 public secretResult;
function storeSecret(euint16 callerSecret) public {
// Verify that the caller has also access to this ciphertext
require(TFHE.isSenderAllowed(callerSecret), "The caller is not authorized to access this secret.");
// do some FHE computation (result is automatically put in the ACL transient storage)
euint16 computationResult = TFHE.add(callerSecret, 3);
// then store the resulting ciphertext handle in the contract storage
secretResult = computationResult;
// Make the temporary allowance for this ciphertext permanent to let the contract able to reuse it at a later stage or request a decryption of it
TFHE.allowThis(secretResult); // this is strictly equivalent to `TFHE.allow(secretResult, address(this));``
}
}
* * *
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#automatic-transient-allowance)
Automatic transient allowance
-----------------------------------------------------------------------------------------------------------------------------------
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()`
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#example-random-value-generation)
Example: random value generation
Copy
function randomize() public {
// Generate a random encrypted value with transient allowance
euint64 random = TFHE.randEuint64();
// Convert the transient allowance into a permanent one
TFHE.allowThis(random);
}
* * *
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#best-practices)
🔧 Best practices
--------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#verifying-sender-access)
Verifying sender access
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.
####
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#example-scenario-encrypted-erc20-attack)
Example scenario: Encrypted ERC20 attack
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:
1. The attacker attempts to send the target user's encrypted balance from **Account A** to **Account B**.
2. 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.
* * *
####
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#example-secure-verification)
Example: secure verification
Copy
function transfer(address to, euint64 encryptedAmount, bytes calldata inputProof) public {
// Ensure the sender is authorized to access the encrypted amount
require(TFHE.isSenderAllowed(encryptedAmount), "Unauthorized access to encrypted amount.");
// Proceed with further logic
euint64 amount = TFHE.asEuint64(encryptedAmount);
...
}
By enforcing this check, you can safeguard against inference attacks and ensure that encrypted values are only manipulated by authorized entities.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#acl-for-reencryption)
ACL for reencryption
-----------------------------------------------------------------------------------------------------------------
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.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples#example-secure-transfer-in-encrypted-erc-20)
Example: Secure Transfer in Encrypted ERC-20
Copy
function transfer(address to, euint64 encryptedAmount) public {
require(TFHE.isSenderAllowed(encryptedAmount), "The caller is not authorized to access this encrypted amount.");
euint64 amount = TFHE.asEuint64(encryptedAmount);
ebool canTransfer = TFHE.le(amount, balances[msg.sender]);
euint64 newBalanceTo = TFHE.add(balances[to], TFHE.select(canTransfer, amount, TFHE.asEuint64(0)));
balances[to] = newBalanceTo;
// Allow this new balance for both the contract and the owner.
TFHE.allowThis(newBalanceTo);
TFHE.allow(newBalanceTo, to);
euint64 newBalanceFrom = TFHE.sub(balances[from], TFHE.select(canTransfer, amount, TFHE.asEuint64(0)));
balances[from] = newBalanceFrom;
// Allow this new balance for both the contract and the owner.
TFHE.allowThis(newBalanceFrom);
TFHE.allow(newBalanceFrom, from);
}
* * *
By understanding how to grant and verify permissions, you can effectively manage access to encrypted data in your fhEVM smart contracts. For additional context, see the [ACL overview](https://docs.zama.ai/fhevm/0.6/smart-contract/acl)
.
[PreviousAccess Control List](https://docs.zama.ai/fhevm/0.6/smart-contract/acl)
[NextEncrypted Inputs](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs)
Last updated 10 months ago
Was this helpful?
---
# FhEVM contracts | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
This guide explains how to use the [fhEVM Contracts standard library](https://github.com/zama-ai/fhevm-contracts/tree/main)
. This library provides secure, extensible, and pre-tested Solidity templates designed for developing smart contracts on fhEVM using the TFHE library.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/contracts#overview)
Overview
----------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/contracts#installation)
Installation
------------------------------------------------------------------------------------------
Install the library using your preferred package manager:
Copy
# Using npm
npm install fhevm-contracts
# Using Yarn
yarn add fhevm-contracts
# Using pnpm
pnpm add fhevm-contracts
[](https://docs.zama.ai/fhevm/0.6/smart-contract/contracts#example)
Example
--------------------------------------------------------------------------------
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/contracts#local-testing-with-the-mock-network)
Local testing with the mock network
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:
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";
import { ConfidentialERC20 } from "fhevm-contracts/contracts/token/ERC20/ConfidentialERC20.sol";
contract MyERC20 is SepoliaZamaFHEVMConfig, ConfidentialERC20 {
constructor() ConfidentialERC20("MyToken", "MYTOKEN") {
_unsafeMint(1000000, msg.sender);
}
}
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/contracts#deploying-to-ethereum-sepolia)
Deploying to Ethereum Sepolia
When deploying to Sepolia, you can use the `SepoliaZamaFHEVMConfig` which provides the correct configuration for the Sepolia testnet:
Copy
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.24;
import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";
import { ConfidentialERC20 } from "fhevm-contracts/contracts/token/ERC20/ConfidentialERC20.sol";
contract MyERC20 is SepoliaZamaFHEVMConfig, ConfidentialERC20 {
constructor() ConfidentialERC20("MyToken", "MYTOKEN") {
_unsafeMint(1000000, msg.sender);
}
}
[](https://docs.zama.ai/fhevm/0.6/smart-contract/contracts#best-practices-for-contract-inheritance)
Best practices for contract inheritance
------------------------------------------------------------------------------------------------------------------------------------------------
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**:
Copy
contract MyERC20 is SepoliaZamaFHEVMConfig, ConfidentialERC20 { ... }
❌ **Wrong order**:
Copy
contract MyERC20 is ConfidentialERC20, SepoliaZamaFHEVMConfig { ... }
[](https://docs.zama.ai/fhevm/0.6/smart-contract/contracts#available-contracts)
Available contracts
--------------------------------------------------------------------------------------------------------
For a list of all available contracts see the page [See all tutorials](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials)
[PreviousConfiguration](https://docs.zama.ai/fhevm/0.6/smart-contract/configure)
[NextSupported types](https://docs.zama.ai/fhevm/0.6/smart-contract/types)
Last updated 8 months ago
Was this helpful?
---
# Decryption in depth | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt_details#gatewaycontract-set-up)
`GatewayContract` set up
---------------------------------------------------------------------------------------------------------------------------------
The `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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt_details#gateway.requestdecryption-function)
`Gateway.requestDecryption` function
---------------------------------------------------------------------------------------------------------------------------------------------------------
The interface of the `Gateway.requestDecryption` function from previous snippet is the following:
Copy
function requestDecryption(
uint256[] calldata ctsHandles,
bytes4 callbackSelector,
uint256 msgValue,
uint256 maxTimestamp,
bool passSignaturesToCaller
) external virtual returns (uint256 initialCounter) {
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt_details#parameters)
Parameters
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`:
Copy
function [callbackName](uint256 requestID, XXX x_0, XXX x_1, ..., XXX x_N-1) external onlyGateway
Or, alternatively, if `passSignaturesToCaller` is set to `true`:
Copy
function [callbackName](uint256 requestID, XXX x_0, XXX x_1, ..., XXX x_N-1, bytes[] memory signatures) external onlyGateway
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:
Ciphertext type
Decrypted type
ebool
bool
euint4
uint8
euint8
uint8
euint16
uint16
euint32
uint32
euint64
uint64
euint128
uint128
euint256
uint256
eaddress
address
Here `callbackName` is a custom name given by the developer to the callback function, `requestID` will be the request id of the decryption (could be commented if not needed in the logic, but must be present) and `x_0`, `x_1`, ... `x_N-1` are the results of the decryption of the `ct` array values, i.e their number should be the size of the `ct` array.
`msgValue` is the value in native tokens to be sent to the calling contract during fulfillment, i.e when the callback will be called with the results of decryption.
`maxTimestamp` is the maximum timestamp after which the callback will not be able to receive the results of decryption, i.e the fulfillment transaction will fail in this case. This can be used for time-sensitive applications, where we prefer to reject decryption results on too old, out-of-date, values.
`passSignaturesToCaller` determines whether the callback needs to transmit signatures from the KMS or not. This is useful if the dApp developer wants to remove trust from the Gateway service and prefers to check the KMS signatures directly from within his dApp smart contract. A concrete example of how to verify the KMS signatures inside a dApp is available [here](https://github.com/zama-ai/fhevm-solidity/blob/2ff952e8e038e56246c840af31ca3fadd7fccedd/examples/TestAsyncDecrypt.sol#L82-L94)
in the `requestBoolTrustless` function.
> _**WARNING:**_ Notice that the callback should be protected by the `onlyGateway` modifier to ensure security, as only the `GatewayContract` 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:
Copy
function addParamsEBool(uint256 requestID, ebool _ebool) internal;
function addParamsEUint4(uint256 requestID, euint4 _euint4) internal;
function addParamsEUint8(uint256 requestID, euint8 _euint8) internal;
function addParamsEUint16(uint256 requestID, euint16 _euint16) internal;
function addParamsEUint32(uint256 requestID, euint32 _euint32) internal;
function addParamsEUint64(uint256 requestID, euint64 _euint64) internal;
function addParamsEAddress(uint256 requestID, eaddress _eaddress) internal;
function addParamsAddress(uint256 requestID, address _address) internal;
function addParamsUint256(uint256 requestID, uint256 _uint) internal;
With their corresponding getter functions to be used inside the callback:
Copy
function getParamsEBool(uint256 requestID) internal;
function getParamsEUint4(uint256 requestID) internal;
function getParamsEUint8(uint256 requestID) internal;
function getParamsEUint16(uint256 requestID) internal;
function getParamsEUint32(uint256 requestID) internal;
function getParamsEUint64(uint256 requestID) internal;
function getParamsEAddress(uint256 requestID) internal;
function getParamsAddress(uint256 requestID) internal;
function getParamsUint256(uint256 requestID) internal;
For example, see this snippet where we add two `uint256`s during the request call, to make them available later during the callback:
Copy
pragma solidity ^0.8.24;
import "fhevm/lib/TFHE.sol";
import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";
import { SepoliaZamaGatewayConfig } from "fhevm/config/ZamaGatewayConfig.sol";
import "fhevm/gateway/GatewayCaller.sol";
contract TestAsyncDecrypt is SepoliaZamaFHEVMConfig, SepoliaZamaGatewayConfig, GatewayCaller {
euint32 xUint32;
uint32 public yUint32;
constructor() {
xUint32 = TFHE.asEuint32(32);
TFHE.allowThis(xUint32);
}
function requestUint32(uint32 input1, uint32 input2) public {
uint256[] memory cts = new uint256[](1);
cts[0] = Gateway.toUint256(xUint32);
uint256 requestID = Gateway.requestDecryption(cts, this.callbackUint32.selector, 0, block.timestamp + 100, false);
addParamsUint256(requestID, input1);
addParamsUint256(requestID, input2);
}
function callbackUint32(uint256 requestID, uint32 decryptedInput) public onlyGateway returns (uint32) {
uint256[] memory params = getParamsUint256(requestID);
unchecked {
uint32 result = uint32(params[0]) + uint32(params[1]) + decryptedInput;
yUint32 = result;
return result;
}
}
When the decryption request is fufilled by the relayer, the `GatewayContract` contract, when calling the callback function, will also emit the following event:
Copy
event ResultCallback(uint256 indexed requestID, bool success, bytes result);
The first argument is the `requestID` of the corresponding decryption request, `success` is a boolean assessing if the call to the callback succeeded, and `result` is the bytes array corresponding to the return data from the callback.
In your hardhat tests, if you sent some transactions which are requesting one or several decryptions and you wish to await the fulfillment of those decryptions, you should import the two helper methods `initGateway` and `awaitAllDecryptionResults` from the `asyncDecrypt.ts` utility file. This would work both when testing on a fhEVM node or in mocked mode. Here is a simple hardhat test for the previous `TestAsyncDecrypt` contract (more examples can be seen [here](https://github.com/zama-ai/fhevm-solidity/blob/main/test/gatewayDecrypt/testAsyncDecrypt.ts)
):
Copy
import { initGateway, awaitAllDecryptionResults } from "../asyncDecrypt";
import { getSigners, initSigners } from "../signers";
import { expect } from "chai";
import { ethers } from "hardhat";
describe("TestAsyncDecrypt", function () {
before(async function () {
await initGateway();
await initSigners(3);
this.signers = await getSigners();
});
beforeEach(async function () {
const contractFactory = await ethers.getContractFactory("TestAsyncDecrypt");
this.contract = await contractFactory.connect(this.signers.alice).deploy();
});
it("test async decrypt uint32", async function () {
const tx2 = await this.contract.connect(this.signers.carol).requestUint32(5, 15, { gasLimit: 500_000 }); // custom gasLimit to avoid gas estimation error in fhEVM mode
await tx2.wait();
await awaitAllDecryptionResults();
const y = await this.contract.yUint32();
expect(y).to.equal(52); // 5+15+32
});
});
You should initialize the gateway by calling `initGateway` at the top of the `before` block - more specifically, before doing any transaction which could involve a decryption request. Notice that when testing on the fhEVM, a decryption is fulfilled usually 2 blocks after the request, while in mocked mode the fulfillment will always happen as soon as you call the `awaitAllDecryptionResults` helper function. A good way to standardize hardhat tests is hence to always call the `awaitAllDecryptionResults` function which will ensure that all pending decryptions are fulfilled in both modes.
[PreviousDecryption](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt)
[NextRe-encryption](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/reencryption)
Last updated 5 months ago
Was this helpful?
---
# Supported types | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/types#introduction)
Introduction
--------------------------------------------------------------------------------------
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.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/types#key-features-of-encrypted-types)
Key features of encrypted types
* 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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/types#list-of-encrypted-types)
List of encrypted types
------------------------------------------------------------------------------------------------------------
The `TFHE` library currently supports the following encrypted types:
Type
Supported
`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
Higher-precision integer types are available in the `TFHE-rs` library and can be added to `fhEVM` as needed.
[PreviousFhEVM contracts](https://docs.zama.ai/fhevm/0.6/smart-contract/contracts)
[NextOperations on encrypted types](https://docs.zama.ai/fhevm/0.6/smart-contract/operations)
Last updated 10 months ago
Was this helpful?
---
# Decryption | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
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](https://docs.zama.ai/fhevm/0.6/explanations/d_re_ecrypt_compute)
.
Decryption is essential in two primary cases:
1. **Smart contract logic**: A contract requires plaintext values for computations or decision-making.
2. **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](https://docs.zama.ai/fhevm/0.6/explanations/d_re_ecrypt_compute)
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt#overview)
Overview
-------------------------------------------------------------------------------------------
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:
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt#example-asynchronous-decryption-in-a-contract)
Example: asynchronous decryption in a contract
Copy
pragma solidity ^0.8.24;
import "fhevm/lib/TFHE.sol";
import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";
import { SepoliaZamaGatewayConfig } from "fhevm/config/ZamaGatewayConfig.sol";
import "fhevm/gateway/GatewayCaller.sol";
contract TestAsyncDecrypt is SepoliaZamaFHEVMConfig, SepoliaZamaGatewayConfig, GatewayCaller {
ebool xBool;
bool public yBool;
constructor() {
xBool = TFHE.asEbool(true);
TFHE.allowThis(xBool);
}
function requestBool() public {
uint256[] memory cts = new uint256[](1);
cts[0] = Gateway.toUint256(xBool);
Gateway.requestDecryption(cts, this.myCustomCallback.selector, 0, block.timestamp + 100, false);
}
function myCustomCallback(uint256 /*requestID*/, bool decryptedInput) public onlyGateway returns (bool) {
yBool = decryptedInput;
return yBool;
}
####
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt#key-additions-to-the-code)
Key additions to the code
1. **Configuration imports**: The configuration contracts are imported to set up the FHEVM environment and Gateway.
Copy
import { SepoliaZamaFHEVMConfig } from "fhevm/config/ZamaFHEVMConfig.sol";
import { SepoliaZamaGatewayConfig } from "fhevm/config/ZamaGatewayConfig.sol";
2. `**GatewayCaller**` **import**: The `GatewayCaller` contract is imported to enable decryption requests.
Copy
import "fhevm/gateway/GatewayCaller.sol";
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt#next-steps)
Next steps
Explore advanced decryption techniques and learn more about re-encryption:
* [Decryption in depth](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt_details)
* [Re-encryption](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/reencryption)
[PreviousDecryption](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption)
[NextDecryption in depth](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt_details)
Last updated 8 months ago
Was this helpful?
---
# Encrypted Inputs | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
This document introduces the concept of encrypted inputs in the fhEVM, explaining their role, structure, validation process, and how developers can integrate them into smart contracts and applications.
Understanding how encryption, decryption and reencryption works is a prerequisite before implementation, see [Encryption, Decryption, Re-encryption, and Computation](https://docs.zama.ai/fhevm/0.6/explanations/d_re_ecrypt_compute)
Encrypted inputs are a core feature of fhEVM, enabling users to push encrypted data onto the blockchain while ensuring data confidentiality and integrity.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs#what-are-encrypted-inputs)
What are encrypted inputs?
------------------------------------------------------------------------------------------------------------------
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.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs#key-characteristics-of-encrypted-inputs)
Key characteristics of encrypted inputs:
1. **Confidentiality**: Data is encrypted using the public FHE key, ensuring that only authorized parties can decrypt or process the values.
2. **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.
3. **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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs#parameters-in-encrypted-functions)
Parameters in encrypted functions
---------------------------------------------------------------------------------------------------------------------------------
When a function in a smart contract is called, it may accept two types of parameters for encrypted inputs:
1. `**einput**`: Refers to the index of the encrypted parameter, representing a specific encrypted input handle.
2. `**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:
Copy
function myExample(
address account,
uint id,
bool isAllowed,
einput param1,
einput param2,
einput param3,
bytes calldata inputProof
) public {
// Function logic here
}
In this example, `param1`, `param2`, and `param3` are encrypted inputs, while `inputProof` contains the corresponding ZKPoK to validate their authenticity.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs#client-side-implementation)
Client-Side implementation
-------------------------------------------------------------------------------------------------------------------
To interact with such a function, developers can use the [fhevmjs](https://github.com/zama-ai/fhevmjs)
library to create and manage encrypted inputs. Below is an example implementation:
Copy
import { createInstances } from "../instance";
import { getSigners, initSigners } from "../signers";
await initSigners(); // Initialize signers
const signers = await getSigners();
const instance = await createInstances(this.signers);
// Create encrypted inputs
const input = instance.createEncryptedInput(contractAddress, userAddress);
const inputs = input.add64(64).addBool(true).add8(4).encrypt(); // Encrypt the parameters
// Call the smart contract function with encrypted inputs
contract.myExample(
"0xa5e1defb98EFe38EBb2D958CEe052410247F4c80", // Account address
32, // Plaintext parameter
true, // Plaintext boolean parameter
inputs.handles[0], // Handle for the first parameter
inputs.handles[1], // Handle for the second parameter
inputs.handles[2], // Handle for the third parameter
inputs.inputProof, // Proof to validate all encrypted inputs
);
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs#validating-encrypted-inputs)
Validating encrypted inputs
---------------------------------------------------------------------------------------------------------------------
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.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs#example-validation-that-goes-along-the-client-side-implementation)
Example validation that goes along the client-Side implementation
This example demonstrates a function that performs multiple encrypted operations, such as updating a user's encrypted balance and toggling an encrypted boolean flag:
Copy
function myExample(
einput encryptedAmount,
einput encryptedToggle,
bytes calldata inputProof
) public {
// Validate and convert the encrypted inputs
euint64 amount = TFHE.asEuint64(encryptedAmount, inputProof);
ebool toggleFlag = TFHE.asEbool(encryptedToggle, inputProof);
// Update the user's encrypted balance
balances[msg.sender] = TFHE.add(balances[msg.sender], amount);
// Toggle the user's encrypted flag
userFlags[msg.sender] = TFHE.not(toggleFlag);
}
// Function to retrieve a user's encrypted balance
function getEncryptedBalance() public view returns (euint64) {
return balances[msg.sender];
}
// Function to retrieve a user's encrypted flag
function getEncryptedFlag() public view returns (ebool) {
return userFlags[msg.sender];
}
}
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs#example-validation-in-the-encryptederc20.sol-smart-contract)
Example validation in the `encryptedERC20.sol` smart contract
Here’s an example of a smart contract function that verifies an encrypted input before proceeding:
Copy
function transfer(
address to,
einput encryptedAmount,
bytes calldata inputProof
) public {
// Verify the provided encrypted amount and convert it into an encrypted uint64
euint64 amount = TFHE.asEuint64(encryptedAmount, inputProof);
// Function logic here, such as transferring funds
...
}
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs#how-validation-works)
How validation works
1. **Input verification**: The `TFHE.asEuintXX` function ensures that the input is a valid ciphertext with a corresponding ZKPoK.
2. **Type conversion**: The function transforms the `einput` into the appropriate encrypted type (`euintXX`, `ebool`, etc.) for further operations within the contract.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/inputs#best-practices)
Best Practices
-------------------------------------------------------------------------------------------
* **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.
[PreviousACL examples](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples)
[NextDecryption](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption)
Last updated 7 months ago
Was this helpful?
---
# Re-encryption | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
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](https://docs.zama.ai/fhevm/0.6/explanations/d_re_ecrypt_compute)
.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/reencryption#when-to-use-re-encryption)
When to use re-encryption
----------------------------------------------------------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/reencryption#overview)
Overview
------------------------------------------------------------------------------------------------
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:
1. Retrieving the ciphertext from the blockchain using a contract’s view function.
2. Re-encrypting the ciphertext client-side with the user’s public key, ensuring only the user can decrypt it.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/reencryption#step-1-retrieve-the-ciphertext)
Step 1: retrieve the ciphertext
---------------------------------------------------------------------------------------------------------------------------------------------
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:
Copy
import "fhevm/lib/TFHE.sol";
contract ConfidentialERC20 {
...
function balanceOf(account address) public view returns (bytes euint64) {
return balances[msg.sender];
}
...
}
Here, `balanceOf` allows retrieval of the user’s encrypted balance stored on the blockchain.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/reencryption#step-2-re-encrypt-the-ciphertext)
Step 2: re-encrypt the ciphertext
-------------------------------------------------------------------------------------------------------------------------------------------------
Re-encryption is performed client-side using the `fhevmjs` library. [Refer to the guide](https://docs.zama.ai/fhevm/0.6/frontend/webapp)
to learn how to include `fhevmjs` in your project. Below is an example of how to implement reencryption in a dApp:
Copy
import { createInstances } from "../instance";
import { getSigners, initSigners } from "../signers";
import abi from "./abi.json";
import { Contract, BrowserProvider } from "ethers";
import { createInstance } from "fhevmjs/bundle";
const CONTRACT_ADDRESS = "";
const provider = new BrowserProvider(window.ethereum);
const accounts = await provider.send("eth_requestAccounts", []);
const USER_ADDRESS = accounts[0];
await initSigners(); // Initialize signers
const signers = await getSigners();
const instance = await createInstances(this.signers);
// Generate the private and public key, used for the reencryption
const { publicKey, privateKey } = instance.generateKeypair();
// Create an EIP712 object for the user to sign.
const eip712 = instance.createEIP712(publicKey, CONTRACT_ADDRESS);
// Request the user's signature on the public key
const params = [USER_ADDRESS, JSON.stringify(eip712)];
const signature = await window.ethereum.request({ method: "eth_signTypedData_v4", params });
// Get the ciphertext to reencrypt
const ConfidentialERC20 = new Contract(CONTRACT_ADDRESS, abi, signer).connect(provider);
const encryptedBalance = ConfidentialERC20.balanceOf(userAddress);
// This function will call the gateway and decrypt the received value with the provided private key
const userBalance = instance.reencrypt(
encryptedBalance, // the encrypted balance
privateKey, // the private key generated by the dApp
publicKey, // the public key generated by the dApp
signature, // the user's signature of the public key
CONTRACT_ADDRESS, // The contract address where the ciphertext is
USER_ADDRESS, // The user address where the ciphertext is
);
console.log(userBalance);
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.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/reencryption#key-additions-to-the-code)
Key additions to the code
* `**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.
[PreviousDecryption in depth](https://docs.zama.ai/fhevm/0.6/smart-contract/decryption/decrypt_details)
[NextIf sentences](https://docs.zama.ai/fhevm/0.6/smart-contract/loop)
Last updated 8 months ago
Was this helpful?
---
# Key features | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
This document provides an overview of key features of the fhEVM smart contract library.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/key_concepts#configuration-and-initialization)
Configuration and initialization
Smart contracts using fhEVM require proper configuration and initialization:
* **Environment setup**: Import and inherit from environment-specific configuration contracts
* **Gateway configuration**: Configure secure gateway access for cryptographic operations
* **Initialization checks**: Validate encrypted variables are properly initialized before use
For more information see [Configuration](https://docs.zama.ai/fhevm/0.6/smart-contract/configure)
.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/key_concepts#encrypted-data-types)
Encrypted data types
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](https://docs.zama.ai/fhevm/0.6/smart-contract/types)
.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/key_concepts#casting-types)
Casting 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](https://docs.zama.ai/fhevm/0.6/smart-contract/types)
.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/key_concepts#confidential-computation)
Confidential computation
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](https://docs.zama.ai/fhevm/0.6/smart-contract/operations)
.
For more information on conditional branching, see [Conditional logic in FHE](https://docs.zama.ai/fhevm/0.6/smart-contract/conditions)
.
For more information on random number generation, see [Generate Random Encrypted Numbers](https://docs.zama.ai/fhevm/0.6/smart-contract/random)
.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/key_concepts#access-control-mechanism)
Access control mechanism
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](https://docs.zama.ai/fhevm/0.6/smart-contract/acl)
.
[PreviousSee all tutorials](https://docs.zama.ai/fhevm/0.6/tutorials/see-all-tutorials)
[NextConfiguration](https://docs.zama.ai/fhevm/0.6/smart-contract/configure)
Last updated 8 months ago
Was this helpful?
---
# Access Control List | FHEVM
[Zama released the Confidential Blockchain Protocol Testnet (FHEVM v0.7). The v0.6 reached its end of life.\
\
Check the migration guide](https://docs.zama.ai/protocol/solidity-guides/development-guide/migration)
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl#what-is-the-acl)
What is the ACL?
-------------------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl#why-is-the-acl-important)
Why is the ACL important?
-------------------------------------------------------------------------------------------------------------
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl#how-does-the-acl-work)
How does the ACL work?
-------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl#types-of-access)
Types of access
* **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.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl#transient-vs.-permanent-allowance)
Transient vs. permanent allowance
Allowance type
Purpose
Storage type
Use case
**Transient**
Temporary access during a transaction.
[Transient storage](https://eips.ethereum.org/EIPS/eip-1153)
(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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl#granting-and-verifying-access)
Granting and verifying access
----------------------------------------------------------------------------------------------------------------------
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl#granting-access)
Granting access
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.
###
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl#verifying-access)
Verifying access
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.
[](https://docs.zama.ai/fhevm/0.6/smart-contract/acl#practical-uses-of-the-acl)
Practical uses of the ACL
--------------------------------------------------------------------------------------------------------------
* **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.
* * *
For a detailed explanation of the ACL's functionality, including code examples and advanced configurations, see [ACL examples](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples)
.
[PreviousOperations on encrypted types](https://docs.zama.ai/fhevm/0.6/smart-contract/operations)
[NextACL examples](https://docs.zama.ai/fhevm/0.6/smart-contract/acl/acl_examples)
Last updated 10 months ago
Was this helpful?
---
# TFHE-rs v1.1 - April 2025 | Change Log
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#summary)
Summary
-------------------------------------------------------------------------------------
* * *
TFHE-rs v1.1.0 brings several new features and improvements on both the CPU & GPU backends:
* **CPU**: This release introduces new scalar operations including CMUX/Select, subtraction with the scalar on the left, and dot product between a vector of Booleans and scalars. It also adds user-friendly APIs to manage noise squashing.
* **GPU**: This release adds 128-bit Programmable Bootstrapping (PBS) and upgrades cryptographic parameters to match the CPU standard, now offering a failure probability of 2⁻¹²⁸ for FHE operations.
See full details below:
* [CPU](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#cpu)
* [GPU](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#gpu)
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#breaking-changes)
Breaking changes
* Integer block rotations and block shift primitives' directions have been inverted to fix their meaning.
* The NTT for the prime 264−232+12^{64} - 2^{32} + 1264−232+1 now uses new twiddle factors, allowing bit shifts instead of multiplications. Older NTT keys are now incompatible.
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#cpu)
CPU
-----------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#new-features)
New features
* Add scalar subtraction with the scalar as the left operand in the integer and High-Level API
* Add scalar `Select` in the integer and High-Level API, allowing use of scalar values
* Add dot product between vectors of `FheBool`
* Add trivial encrypt/decrypt support for string types
* Add chunked `LweBootstrapKey` and `SeededLweBootstrapKey` generation for memory-constrained systems
* Add a noise squashing API in the integer and High-Level API to support use cases requiring noise flooding
* Add the `extended-types` feature, enabling more static typing in the High-Level API
* Add GLWE keyswitch primitives
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#improvements)
Improvements
* The NTT for the Solinas prime 264−232+12^{64} - 2^{32} + 1264−232+1 now uses twiddles enabling bit shifts instead of costly multiplications
* Removed usage of `unwrap` in various conformance checks
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#fixes)
Fixes
* Fix a corner case in encryption where negative values were sometimes not sign-extended
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#gpu)
GPU
-----------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#new-features-1)
New features
* Implement `fft128` in the CUDA backend
* Implement 128-bit classic PBS
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#improvements-1)
Improvements
* Add modulus-switch noise reduction on GPU for the classical PBS
* Update GPU cryptographic parameters to reach a 2⁻¹²⁸ probability of failure, as on CPU
* Use hexes to initialize twiddles for 64-bit FFT for better precision
* Refactor `double2` operators to use CUDA intrinsics and match CPU floating-point arithmetic
* Track degree and noise level in all integer operations in the CUDA backend
* Fix block comparison logic with zero to match the CPU implementation
* Retain LUT indexes on the CPU for each LUT application to avoid copying them back from GPU
* Add alias for GPU compression parameters
* Detect first/last iteration of split-kernel multi-bit & classical PBS via template argument
* Detect first/last iteration of 128-bit PBS via template argument
* Modify integer & ERC20 throughput benchmarks for better multi-GPU performance
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#fixes-1)
Fixes
* Fix max shared memory bug for cooperative-groups PBS
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1#resources)
Resources
-----------------------------------------------------------------------------------------
* [GitHub release](https://github.com/zama-ai/tfhe-rs/releases/tag/tfhe-rs-1.1.0)
* [Documentation](https://docs.zama.ai/tfhe-rs/1.1)
[PreviousTFHE-rs v1.2 - May 2025](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2)
[NextTFHE-rs v1.0 January 2025](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0)
Last updated 16 hours ago
---
# TFHE-rs v1.0 January 2025 | Change Log
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#summary)
Summary
-------------------------------------------------------------------------------------
* * *
**TFHE-rs v1.0.0 marks the first official stable release of the TFHE-rs library.**
TFHE-rs v1.0.0 stabilizes the high-level API for the x86 CPU backend and introduces new parameters for the classic PBS with an error probability lower than $2^{-128}$.
This milestone release empowers developers with robust, performant, and user-friendly cryptographic primitives—ensuring greater reliability and efficiency for secure computations.
See full details below:
* [CPU](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#cpu)
* [GPU](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#gpu)
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#breaking-changes)
Breaking Changes
* The trait HlCompactable is now required for types used in a CompactCiphertextList
* GpuIndex has been refactored and its internal field is no longer public. Use `new` and `try_new` to create a GpuIndex
* Conformance parameters names have been updated and now follow the "StructConformanceParam" naming schemer for a given Struct
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#cpu)
CPU
-----------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#new-features)
New features
* Add a modulus switch noise reduction technique, greatly improving performance for low error probabilities.
* Add Abs to the high-level C API binding
* Add a named implementation for integer compression/decompression, allowing safe serialization
* Make strings compatible with the compact and compressed lists
* Add parameters for the classic PBS in shortint with a probability of failure less than 2^-128
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#improvements)
Improvements
* Use destructuring in more places to ensure exhaustive field checks in some parts of the API
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#fixes)
Fixes
* Fix deserialization of old structures, which were renamed but still supported
* Fix compression, which was crashing if output compute parameters were Multi Bit
* Fix decompression of ciphertext lists after a safe deserialization for various device selections
* Fix that trivial ciphertexts were crashing compression due to an invalid noise check
* Fix rotations/shifts on less than 2 blocks
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#gpu)
GPU
-----------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#new-features-1)
New features
* Add encrypted Pseudo Random Generation
* Add GPU selection in high-level API
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#improvements-1)
Improvements
* Optimized packing keyswitch
* GpuIndex now enforces a validity invariant at creation time
* Enable more samples in the keyswitch
* Enable more samples in PBS (TBC variant)
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#fixes-1)
Fixes
* Fix corner cases in match value function
* Fix scalar mul with 1 block
* Fix internal indices for multi-GPU contexts
* Fix some noise/degree bugs
* Fix degree after shift/rotate
* Fix wrong degree in ciphertexts after decompression, which led to degraded performance
* Fix compressed ciphertext lists conversions between CPU and GPU
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.0#resources)
Resources
-----------------------------------------------------------------------------------------
* [GitHub release](https://github.com/zama-ai/tfhe-rs/releases/tag/tfhe-rs-1.0.0)
* [Documentation](https://docs.zama.ai/tfhe-rs/1.0)
[PreviousTFHE-rs v1.1 - April 2025](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1)
Last updated 16 hours ago
---
# TFHE-rs v1.2 - May 2025 | Change Log
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#summary)
Summary
-------------------------------------------------------------------------------------
* * *
TFHE-rs v1.2.0 introduces the new **HPU** backend. The HPU (Homorphic Processing Unit) is a hardware accelerator for FHE operations.
See full details below:
* [CPU](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#cpu)
* [GPU](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#gpu)
* [HPU](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#hpu)
####
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#breaking-changes)
Breaking changes
* The shortint `ServerKey` does not directly hold the bootstrapping and keyswitch keys anymore. Instead, they are stored inside a generic `AtomicPatternServerKey` object which allows to customize the content of the key materials.
* The conformance parameters for the integer `ServerKey` are now wrapped inside `AtomicPatternParameters`.
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#cpu)
CPU
-----------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#new-features)
New features
* Add back&forth NTT implementation
* Add support for dynamic atomic pattern at the shortint level. They allow to customize how lookup tables are evaluated.
* Add the KeySwitch32 atomic pattern
* Enable custom modulus generation for TUniform
* Add AsRef implementation on ServerKey to access NoiseSquashingKey
* Run ZK verification inside dedicated thread pools to redcuce the latency
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#fixes)
Fixes
* Fix success probability for Ternary Uniform generation
* Remove additional body coeff in multi bit ms compression
* Check that crs group element at index n is 0
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#gpu)
GPU
-----------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#new-features-1)
New features
* Implement ZK's expand
* Implement 128 bit classic CG PBS
* Add memory tracking functions for add, subtract, scalar add and scalar subtract
* Add necessary entry points for 128 bit compression
* Add circulant matrix for one vs many poly product
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#fixes-1)
Fixes
* Update panic condition on upper bound for the number of cuda blocks to apply only to Thread Block Clusters
* Fix multi device execution with drift
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#hpu)
HPU
-----------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#new-features-2)
New features
* Add Hpu backend implementation
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2#resources)
Resources
-----------------------------------------------------------------------------------------
* * *
* [GitHub release](https://github.com/zama-ai/tfhe-rs/releases/tag/tfhe-rs-1.2.0)
* [Documentation](https://docs.zama.ai/tfhe-rs/1.2)
[PreviousTFHE-rs v1.3 - July 2025](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3)
[NextTFHE-rs v1.1 - April 2025](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.1)
Last updated 16 hours ago
---
# TFHE-rs v1.3 - July 2025 | Change Log
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#summary)
Summary
-------------------------------------------------------------------------------------
* * *
TFHE-rs v1.3.0 introduces several new features both focused on performance and on the usability of the library. The HPU now supports more operations and now has a parameter set matching the CPU and GPU in terms of computation probability of error.
See full details below:
* [CPU](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#cpu)
* [GPU](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#gpu)
* [HPU](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#hpu)
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#cpu)
CPU
-----------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#new-features)
New features
* Add chunked generation for the `LweKeyswitchKey`
* Add multi bit PBS for 128 bits moduli
* Add Atomic Pattern support at the `ClientKey` level
* Add `OverflowingNeg` in the High Level API
* Add compression support after noise squashing
* Add modulus switch noise compensation technique and centering
* Add a different hashing mode for ZK v2 allowing for faster verification
* Add a more granular conformance check for ZK proofs
* Add a "key chain" mechanism to update old ciphertexts parameters to newer ones
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#improvements)
Improvements
New algorithm for division, 36% improvement for 64 bits division with default parameters, now run in 5.5s vs 8.6s
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#gpu)
GPU
-----------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#new-features-1)
New features
* All operations now come with a utility function to query how much memory that function will require on GPU:
* All integer operations (bitwise operations, comparisons, shift/rotate, cmux, addition, subtraction, multiplication, division, etc.)
* Operations on booleans
* Compression/decompression
* Encrypted random generation
* Add support for GPU-accelerated expand on the HL Api
* Allow a user to perform computation on multi-gpu using a custom selection of GPUs
* Add squash noise in the high level API
* Add support to GPU-accelerated expand to CompactCiphextList
* Add cuda debug target for integer tests via a Cargo feature
* Add move\_to\_current\_device for booleans
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#improvements-1)
Improvements
* Fix degrees after abs
* Allow to build with both GPU & HPU features enabled
* Add indexes to modulus switch noise reduction
* Add missing error checks after some kernels
* Fix a linking problem on Hopper GPUs
* Fix hardcoded use of message modulus in some operations
* Fix degrees after bitxor
* Prevent nvToolsExt inclusion when not profiling
* Fix degrees after scalar bitxor
* Fix race condition on expand when on multi-gpu
* Fix the packing keyswitch buffer not being allocated on large parameter sets
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#fixes)
Fixes
* Use cooperative groups based PBS on H100s when possible on large batches
* Optimize sum\_ciphertexts in cuda backend (ilog2 and scalar div got significant performance improvements thanks to this)
* Increase keyswitch occupancy to 100%
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#hpu)
HPU
-----------------------------------------------------------------------------
* * *
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#new-features-2)
New features
* Add modulus-switch noise reduction (centered binary)
* Update HPU parameter set to reach a 2^-128 probability of failure, as on CPU & GPU
* Add support of most of the missing operations: div, max/min, shift, rot, leading/trailing zeros/ones
* Simplify & accelerate FPGA loading by using PCIe instead of loading flash at each bitstream update
###
[](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.3#resources)
Resources
* * *
* [GitHub release](https://github.com/zama-ai/tfhe-rs/releases/tag/tfhe-rs-1.3.0)
* [Documentation](https://docs.zama.ai/tfhe-rs/1.3)
[PreviousTFHE-rs v1.4 - October 2025](https://docs.zama.ai/change-log/tfhe-rs)
[NextTFHE-rs v1.2 - May 2025](https://docs.zama.ai/change-log/tfhe-rs/release/tfhe-rs-v1.2)
Last updated 17 hours ago
---
# Email Protection | Cloudflare
Please enable cookies.
Email Protection
================
You are unable to access this email address docs.zama.ai
--------------------------------------------------------
The website from which you got to this page is protected by Cloudflare. Email addresses on that page have been hidden in order to keep them from being accessed by malicious bots. **You must enable Javascript in your browser in order to decode the e-mail address**.
If you have a website and are interested in protecting it in a similar way, you can [sign up for Cloudflare](https://www.cloudflare.com/sign-up?utm_source=email_protection)
.
* [How does Cloudflare protect email addresses on website from spammers?](https://developers.cloudflare.com/waf/tools/scrape-shield/email-address-obfuscation/)
* [Can I sign up for Cloudflare?](https://developers.cloudflare.com/fundamentals/setup/account/create-account/)
Cloudflare Ray ID: **98f623a3ee481715** • Your IP: Click to reveal 54.237.218.47 • Performance & security by [Cloudflare](https://www.cloudflare.com/5xx-error-landing)
---
# Welcome to TFHE-rs | TFHE-rs
[](https://docs.zama.ai/tfhe-rs/1.0#get-started)
Get started
-----------------------------------------------------------------
Learn the basics of TFHE-rs, set it up, and make it run with ease.
[](https://docs.zama.ai/tfhe-rs/1.0/get-started/getting_started)

**What is TFHE-rs?**
Understand TFHE-rs library and basic cryptographic concepts
[](https://docs.zama.ai/tfhe-rs/1.0/get-started/installation)

**Installation**
Follow the step by step guide to import TFHE-rs in your project
[](https://docs.zama.ai/tfhe-rs/1.0/get-started/quick_start)

**Quick start**
See a full example of using TFHE-rs to compute on encrypted data
[](https://docs.zama.ai/tfhe-rs/1.0#build-with-tfhe-rs)
Build with TFHE-rs
-------------------------------------------------------------------------------
Start building with TFHE-rs by exploring its core features, discovering essential guides, and learning more with user-friendly tutorials.

**FHE Computations**
Run FHE computation on encrypted data.
* [Types](https://docs.zama.ai/tfhe-rs/1.0/fhe-computation/types)
* [Operations](https://docs.zama.ai/tfhe-rs/1.0/fhe-computation/operations)

**Configuration**
Advanced configuration for better performance.
* [Advanced Rust](https://docs.zama.ai/tfhe-rs/1.0/configuration/rust_configuration)
* [GPU acceleration](https://docs.zama.ai/tfhe-rs/1.0/configuration/run_on_gpu)

**Integration**
Use TFHE-rs in different contexts or platforms..
* [C API](https://docs.zama.ai/tfhe-rs/1.0/integration/c_api)
* [JS on WASM API](https://docs.zama.ai/tfhe-rs/1.0/integration/js_on_wasm_api)
[](https://docs.zama.ai/tfhe-rs/1.0#explore-more)
Explore more
-------------------------------------------------------------------
Access to additional resources and join the Zama community.
###
[](https://docs.zama.ai/tfhe-rs/1.0#tutorials)
Tutorials
Explore step-by-step guides that walk you through real-world uses of TFHE-rs.
* [Homomorphic parity bit](https://docs.zama.ai/tfhe-rs/1.0/tutorials/parity_bit)
: Learn how to implement a parity bit calculation over encrypted data
* [Homomorphic case changing on ASCII string](https://docs.zama.ai/tfhe-rs/1.0/tutorials/ascii_fhe_string)
: See how to process string data securely by changing cases while keeping the data encrypted.
* [SHA256 with Boolean API](https://docs.zama.ai/tfhe-rs/1.0/tutorials/sha256_bool)
: Delve into a more complex example: implementing the SHA256 hash function entirely on encrypted boolean values.
* [All tutorials](https://docs.zama.ai/tfhe-rs/1.0/tutorials/see-all-tutorials)
: A complete list of all available tutorials in one place.tutorials: A complete list of all available tutorials in one place.
###
[](https://docs.zama.ai/tfhe-rs/1.0#references-and-explanations)
References & Explanations
Take a deep dive into TFHE-rs, exploring APIs from the highest to the lowest level of abstraction and accessing additional resources for in-depth explanations.
* [Rust API reference](https://docs.rs/tfhe/latest/tfhe/)
: High-level API that abstracts cryptographic complexities and simplifies the development and more
* [Fine-grained APIs](https://docs.zama.ai/tfhe-rs/1.0/references/fine-grained-apis)
: Mid-level APIs that enable evaluation of Boolean, short integer, and integer circuits
* [Core crypto API](https://docs.zama.ai/tfhe-rs/1.0/references/core-crypto-api)
: Low-level API with the primitive functions and types of the TFHE scheme
* [TFHE deep dive](https://docs.zama.ai/tfhe-rs/1.0/explanations/tfhe-deep-dive)
: Resources that explain the Fully Homomorphic Encryption scheme - TFHE
###
[](https://docs.zama.ai/tfhe-rs/1.0#support-channels)
Support channels
Ask technical questions and discuss with the community. Our team of experts usually answers within 24 hours during working days.
* [Community forum](https://community.zama.ai/)
* [Discord channel](https://discord.com/invite/zama)
###
[](https://docs.zama.ai/tfhe-rs/1.0#developers)
Developers
Collaborate with us to advance the FHE spaces and drive innovation together.
* [Contribute to TFHE-rs](https://docs.zama.ai/tfhe-rs/1.0/developers/contributing)
* [Check the latest release note](https://github.com/zama-ai/tfhe-rs/releases)
* [Request a feature](https://github.com/zama-ai/tfhe-rs/issues/new?assignees=&labels=feature_request&projects=&template=feature_request.md&title=)
* [Report a bug](https://github.com/zama-ai/tfhe-rs/issues/new?assignees=&labels=triage_required&projects=&template=bug_report.md&title=)
* * *
**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**](https://www.zama.ai/developer-survey)
to participate.
Last updated 7 months ago
Was this helpful?
---
# Welcome to TFHE-rs | TFHE-rs
[](https://docs.zama.ai/tfhe-rs/1.1#get-started)
Get started
-----------------------------------------------------------------
Learn the basics of TFHE-rs, set it up, and make it run with ease.
[](https://docs.zama.ai/tfhe-rs/1.1/get-started/getting_started)

**What is TFHE-rs?**
Understand TFHE-rs library and basic cryptographic concepts
[](https://docs.zama.ai/tfhe-rs/1.1/get-started/installation)

**Installation**
Follow the step by step guide to import TFHE-rs in your project
[](https://docs.zama.ai/tfhe-rs/1.1/get-started/quick_start)

**Quick start**
See a full example of using TFHE-rs to compute on encrypted data
[](https://docs.zama.ai/tfhe-rs/1.1#build-with-tfhe-rs)
Build with TFHE-rs
-------------------------------------------------------------------------------
Start building with TFHE-rs by exploring its core features, discovering essential guides, and learning more with user-friendly tutorials.

**FHE Computations**
Run FHE computation on encrypted data.
* [Types](https://docs.zama.ai/tfhe-rs/1.1/fhe-computation/types)
* [Operations](https://docs.zama.ai/tfhe-rs/1.1/fhe-computation/operations)

**Configuration**
Advanced configuration for better performance.
* [Advanced Rust](https://docs.zama.ai/tfhe-rs/1.1/configuration/rust_configuration)
* [GPU acceleration](https://docs.zama.ai/tfhe-rs/1.1/configuration/run_on_gpu)

**Integration**
Use TFHE-rs in different contexts or platforms..
* [C API](https://docs.zama.ai/tfhe-rs/1.1/integration/c_api)
* [JS on WASM API](https://docs.zama.ai/tfhe-rs/1.1/integration/js_on_wasm_api)
[](https://docs.zama.ai/tfhe-rs/1.1#explore-more)
Explore more
-------------------------------------------------------------------
Access to additional resources and join the Zama community.
###
[](https://docs.zama.ai/tfhe-rs/1.1#tutorials)
Tutorials
Explore step-by-step guides that walk you through real-world uses of TFHE-rs.
* [Homomorphic parity bit](https://docs.zama.ai/tfhe-rs/1.1/tutorials/parity_bit)
: Learn how to implement a parity bit calculation over encrypted data
* [Homomorphic case changing on ASCII string](https://docs.zama.ai/tfhe-rs/1.1/tutorials/ascii_fhe_string)
: See how to process string data securely by changing cases while keeping the data encrypted.
* [SHA256 with Boolean API](https://docs.zama.ai/tfhe-rs/1.1/tutorials/sha256_bool)
: Delve into a more complex example: implementing the SHA256 hash function entirely on encrypted boolean values.
* [All tutorials](https://docs.zama.ai/tfhe-rs/1.1/tutorials/see-all-tutorials)
: A complete list of all available tutorials in one place.tutorials: A complete list of all available tutorials in one place.
###
[](https://docs.zama.ai/tfhe-rs/1.1#references-and-explanations)
References & Explanations
Take a deep dive into TFHE-rs, exploring APIs from the highest to the lowest level of abstraction and accessing additional resources for in-depth explanations.
* [Rust API reference](https://docs.rs/tfhe/latest/tfhe/)
: High-level API that abstracts cryptographic complexities and simplifies the development and more
* [Fine-grained APIs](https://docs.zama.ai/tfhe-rs/1.1/references/fine-grained-apis)
: Mid-level APIs that enable evaluation of Boolean, short integer, and integer circuits
* [Core crypto API](https://docs.zama.ai/tfhe-rs/1.1/references/core-crypto-api)
: Low-level API with the primitive functions and types of the TFHE scheme
* [TFHE deep dive](https://docs.zama.ai/tfhe-rs/1.1/explanations/tfhe-deep-dive)
: Resources that explain the Fully Homomorphic Encryption scheme - TFHE
* [TFHE-rs handbook](https://github.com/zama-ai/tfhe-rs-handbook)
: Document describing algorithms implemented in TFHE-rs
###
[](https://docs.zama.ai/tfhe-rs/1.1#support-channels)
Support channels
Ask technical questions and discuss with the community. Our team of experts usually answers within 24 hours during working days.
* [Community forum](https://community.zama.ai/)
* [Discord channel](https://discord.com/invite/zama)
###
[](https://docs.zama.ai/tfhe-rs/1.1#developers)
Developers
Collaborate with us to advance the FHE spaces and drive innovation together.
* [Contribute to TFHE-rs](https://docs.zama.ai/tfhe-rs/1.1/developers/contributing)
* [Check the latest release note](https://github.com/zama-ai/tfhe-rs/releases)
* [Request a feature](https://github.com/zama-ai/tfhe-rs/issues/new?assignees=&labels=feature_request&projects=&template=feature_request.md&title=)
* [Report a bug](https://github.com/zama-ai/tfhe-rs/issues/new?assignees=&labels=triage_required&projects=&template=bug_report.md&title=)
* * *
**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**](https://www.zama.ai/developer-survey)
to participate.
Last updated 6 months ago
Was this helpful?
---