For the complete documentation index, see llms.txt. This page is also available as Markdown.

Write FHEVM tests in Foundry

This page shows how to write FHEVM tests in Foundry using forge-fhevm.

Inherit from FhevmTest

Every FHEVM test contract inherits from FhevmTest. Calling super.setUp() deploys the FHEVM host contracts at their canonical deterministic addresses.

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

import {FhevmTest} from "forge-fhevm/FhevmTest.sol";
import {FHE} from "@fhevm/solidity/lib/FHE.sol";
import "encrypted-types/EncryptedTypes.sol";

contract MyTest is FhevmTest {
    MyContract myContract;

    function setUp() public override {
        super.setUp(); // deploy FHEVM host contracts
        myContract = new MyContract();
    }
}

Encrypt inputs

Use the encrypt* helpers to build a (handle, proof) pair for any contract that calls FHE.fromExternal.

1

Encrypt a value

The two-argument overload uses address(this) as the implicit user:

2

Encrypt for a specific user

The three-argument overload binds the proof to a different user:

3

Call the contract

Supported encrypt helpers

Function
Value type
Returned handle

encryptBool

bool

externalEbool

encryptUint8

uint8

externalEuint8

encryptUint16

uint16

externalEuint16

encryptUint32

uint32

externalEuint32

encryptUint64

uint64

externalEuint64

encryptUint128

uint128

externalEuint128

encryptUint256

uint256

externalEuint256

encryptAddress

address

externalEaddress

Each call to encrypt* increments an internal nonce, so encrypting the same value twice produces different handles.

Decrypt results

forge-fhevm exposes three decryption modes that mirror production decryption flows. Pick the one that matches your contract's pattern.

decrypt(handle) — low-level lookup

Direct cleartext for the handle. No ACL or proof checks. Best for unit assertions:

decrypt() has typed overloads for every encrypted type:

publicDecrypt(handles) — KMS-signed public decryption

Use when your contract verifies decryption proofs on-chain via FHE.checkSignatures(). Returns cleartexts and a KMS-signed proof:

userDecrypt(handle, user, contract, signature) — user-facing flow

The full user decryption flow with persistent ACL checks and EIP-712 signature verification:

Error
Cause

UserAddressEqualsContractAddress

userAddress == contractAddress

UserNotAuthorizedForDecrypt

User lacks persistent ACL permission

ContractNotAuthorizedForDecrypt

Contract lacks persistent ACL permission

InvalidUserDecryptSignature

Signature does not recover to userAddress

ACL permissions are granted by the contract under test as part of its business logic — for example, when a token's mint calls FHE.allow(balance, owner). You don't need to grant permissions manually in tests.

Full counter test example

A complete counter test is shipped in fhevm-foundry-template/test/FHECounter.t.sol:

Run the tests

Where to go next

🟨 Go to Deploy FHEVM contracts with Foundry to deploy your contract to a local Anvil node or to Sepolia.

🟨 Go to forge-fhevm API reference for the full FhevmTest API.

Last updated