Skip to main content
New API: Library is FHE, imported from @fhevm/solidity/lib/FHE.sol

Setup

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { FHE, euint32, euint64, ebool, eaddress } from "@fhevm/solidity/lib/FHE.sol";
import { ZamaEthereumConfig } from "@fhevm/solidity/config/ZamaConfig.sol";

contract MyContract is ZamaEthereumConfig {
    // Your encrypted contract logic here
}

Encrypted Types

Boolean

TypeDescriptionPlaintext Equivalent
eboolEncrypted booleanbool

Unsigned Integers

TypeBitsRangePlaintext Equivalent
euint880 — 255uint8
euint16160 — 65,535uint16
euint32320 — 4,294,967,295uint32
euint64640 — 18.4 * 10^18uint64
euint1281280 — 3.4 * 10^38uint128
euint2562560 — 1.15 * 10^77uint256

Address

TypeDescriptionPlaintext Equivalent
eaddressEncrypted Ethereum addressaddress

External Input Types

External TypeConverts To
externalEboolebool
externalEuint8euint8
externalEuint16euint16
externalEuint32euint32
externalEuint64euint64
externalEuint128euint128
externalEuint256euint256
externalEaddresseaddress

Input Conversion

Convert external encrypted inputs to internal encrypted types:
function receiveInput(externalEuint32 encInput, bytes calldata inputProof) external {
    euint32 value = FHE.fromExternal(encInput, inputProof);
    // Now 'value' can be used in FHE operations
    FHE.allowThis(value);
}
FunctionInputOutput
FHE.fromExternal(externalEbool, inputProof)externalEbool, bytes calldata inputProofebool
FHE.fromExternal(externalEuint8, inputProof)externalEuint8, bytes calldata inputProofeuint8
FHE.fromExternal(externalEuint32, inputProof)externalEuint32, bytes calldata inputProofeuint32
FHE.fromExternal(externalEuint64, inputProof)externalEuint64, bytes calldata inputProofeuint64

Plaintext to Encrypted Conversion

Encrypt a plaintext constant within the contract:
euint32 encryptedFive = FHE.asEuint32(5);
ebool encryptedTrue = FHE.asEbool(true);
eaddress encAddr = FHE.asEaddress(msg.sender);
FunctionInputOutput
FHE.asEbool(bool)boolebool
FHE.asEuint8(uint8)Plaintext valueeuint8
FHE.asEuint32(uint32)Plaintext valueeuint32
FHE.asEuint64(uint64)Plaintext valueeuint64
FHE.asEaddress(address)addresseaddress

Type Casting

euint8 small = FHE.asEuint8(42);
euint32 bigger = FHE.asEuint32(small);   // Upcast: euint8 -> euint32
euint8 back = FHE.asEuint8(bigger);      // Downcast: euint32 -> euint8 (may truncate)

Arithmetic Operations

OperationFunctionExample
AdditionFHE.add(a, b)euint32 sum = FHE.add(x, y);
SubtractionFHE.sub(a, b)euint32 diff = FHE.sub(x, y);
MultiplicationFHE.mul(a, b)euint32 prod = FHE.mul(x, y);
DivisionFHE.div(a, plaintext)euint32 quot = FHE.div(x, 2);
RemainderFHE.rem(a, plaintext)euint32 mod = FHE.rem(x, 3);
NegationFHE.neg(a)euint32 neg = FHE.neg(x);
MinimumFHE.min(a, b)euint32 m = FHE.min(x, y);
MaximumFHE.max(a, b)euint32 m = FHE.max(x, y);
Note: div and rem only accept a plaintext (scalar) second operand. You cannot divide or take the remainder by an encrypted value.Overflow behavior: Operations wrap silently. FHE.add on euint8 with values 200 + 100 = 44 (wraps at 256). No revert.

Comparison Operations

All comparisons return ebool (encrypted boolean).
OperationFunctionExample
EqualFHE.eq(a, b)ebool isEq = FHE.eq(x, y);
Not equalFHE.ne(a, b)ebool isNe = FHE.ne(x, y);
Greater thanFHE.gt(a, b)ebool isGt = FHE.gt(x, y);
Greater or equalFHE.ge(a, b)ebool isGe = FHE.ge(x, y);
Less thanFHE.lt(a, b)ebool isLt = FHE.lt(x, y);
Less or equalFHE.le(a, b)ebool isLe = FHE.le(x, y);

Bitwise Operations

OperationFunctionExample
ANDFHE.and(a, b)euint8 r = FHE.and(x, y);
ORFHE.or(a, b)euint8 r = FHE.or(x, y);
XORFHE.xor(a, b)euint8 r = FHE.xor(x, y);
NOTFHE.not(a)euint8 r = FHE.not(x);
Also works on ebool:
ebool result = FHE.and(cond1, cond2);   // logical AND
ebool result = FHE.or(cond1, cond2);    // logical OR
ebool result = FHE.not(cond1);          // logical NOT

Shift and Rotate Operations

OperationFunctionExample
Shift leftFHE.shl(a, b)euint32 r = FHE.shl(x, 2);
Shift rightFHE.shr(a, b)euint32 r = FHE.shr(x, 2);
Rotate leftFHE.rotl(a, b)euint32 r = FHE.rotl(x, 4);
Rotate rightFHE.rotr(a, b)euint32 r = FHE.rotr(x, 4);

Conditional Selection (Encrypted Ternary)

The fundamental branching primitive in FHE. Replaces if/else for encrypted conditions.
// FHE.select(condition, valueIfTrue, valueIfFalse)
euint32 result = FHE.select(isGreater, largeValue, smallValue);
FunctionSignatureDescription
FHE.selectFHE.select(ebool, euintXX, euintXX) -> euintXXReturns first value if true, second if false
Nested selects (multi-way):
// Encrypted equivalent of: if (a) x; else if (b) y; else z;
euint32 result = FHE.select(condA, x, FHE.select(condB, y, z));

Random Number Generation

Generate encrypted random values on-chain:
FunctionOutput Type
FHE.randEbool()ebool
FHE.randEuint8()euint8
FHE.randEuint16()euint16
FHE.randEuint32()euint32
FHE.randEuint64()euint64
Bounded random (upper bound, exclusive):
// Random number from 0 to 99
euint32 dice = FHE.randEuint32(100);
FHE.allowThis(dice);

Access Control (ACL)

Persistent Permissions

FunctionDescription
FHE.allow(ciphertext, address)Allow address to use ciphertext
FHE.allowThis(ciphertext)Allow this contract to use ciphertext

Transient Permissions

FunctionDescription
FHE.allowTransient(ciphertext, address)Temporarily allow address for current transaction

Permission Checks

FunctionDescription
FHE.isSenderAllowed(ciphertext)Is msg.sender allowed to use this ciphertext?
FHE.isAllowed(ciphertext, address)Is address allowed to use this ciphertext?

Common ACL Pattern

// After ANY operation that produces a new ciphertext:
euint32 newBalance = FHE.add(balance, amount);
FHE.allowThis(newBalance);           // Contract can use it in future txs
FHE.allow(newBalance, owner);        // Owner can view/decrypt it

Decryption

Re-encryption (User-Specific, Off-Chain)

Re-encryption is handled client-side using the Relayer SDK (@zama-fhe/relayer-sdk).
// Return the encrypted handle -- client decrypts via instance.userDecrypt()
function viewMyBalance() external view returns (euint64) {
    return balances[msg.sender]; // ACL must have been set via FHE.allow(balance, msg.sender)
}

Public Decryption (On-Chain Reveal)

// Step 1: Make a value publicly decryptable
FHE.makePubliclyDecryptable(encryptedValue);

// Step 2: Check if a value has been marked for public decryption
bool canDecrypt = FHE.isPubliclyDecryptable(encryptedValue);
FunctionDescription
FHE.makePubliclyDecryptable(ciphertext)Mark an encrypted value for public decryption
FHE.isPubliclyDecryptable(ciphertext)Check if a value has been marked publicly decryptable

Complete Example: Encrypted Transfer

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { FHE, euint64, ebool } from "@fhevm/solidity/lib/FHE.sol";
import { ZamaEthereumConfig } from "@fhevm/solidity/config/ZamaConfig.sol";

contract ConfidentialToken is ZamaEthereumConfig {
    mapping(address => euint64) private balances;

    function transfer(address to, externalEuint64 encAmount, bytes calldata inputProof) external {
        euint64 amount = FHE.fromExternal(encAmount, inputProof);

        // Encrypted balance check
        ebool hasEnough = FHE.ge(balances[msg.sender], amount);

        // Conditional update (silent fail if insufficient)
        euint64 newSenderBal = FHE.select(
            hasEnough,
            FHE.sub(balances[msg.sender], amount),
            balances[msg.sender]
        );
        euint64 newReceiverBal = FHE.select(
            hasEnough,
            FHE.add(balances[to], amount),
            balances[to]
        );

        // Update storage
        balances[msg.sender] = newSenderBal;
        balances[to] = newReceiverBal;

        // ACL: allow contract + owners
        FHE.allowThis(newSenderBal);
        FHE.allow(newSenderBal, msg.sender);
        FHE.allowThis(newReceiverBal);
        FHE.allow(newReceiverBal, to);
    }
}

Quick Rules

Always call FHE.allowThis()

After storing an encrypted value. The contract needs permission to read its own storage.

Never use if on encrypted values

Use FHE.select() instead.

Use externalEuintXX for inputs

Convert with FHE.fromExternal(value, inputProof).

Both operands must match types

Or second operand is plaintext.

Overflow wraps silently

There is no revert on overflow.

Use the smallest type

Gas cost scales with bit width.

Decryption patterns

Use FHE.makePubliclyDecryptable() for on-chain reveal or client-side re-encryption via instance.userDecrypt().

Inherit ZamaEthereumConfig

In your contract for proper fhEVM configuration.

Build docs developers (and LLMs) love