Level: Beginner | Duration: 3 hoursPrerequisites: Module 02: Development Setup
Overview
FHEVM provides a rich set of encrypted data types that mirror familiar Solidity types. Understanding these types — their sizes, capabilities, gas costs, and storage behavior — is fundamental to writing effective confidential smart contracts.Learning Objectives
By completing this module you will be able to:- List and describe all 8 core encrypted types:
ebool,euint8througheuint256,eaddress - Understand how encrypted values are represented as handles on-chain
- Choose the correct encrypted type for a given use case
- Properly store and manage encrypted state variables
- Convert between plaintext and encrypted types using
FHE.asEuintXX()
1. Overview of Encrypted Types
FHEVM offers the following encrypted types:| Encrypted Type | Plaintext Equivalent | Bit Width | Typical Use Case |
|---|---|---|---|
ebool | bool | 2 bits | Flags, conditions |
euint8 | uint8 | 8 bits | Small counters, scores |
euint16 | uint16 | 16 bits | Moderate ranges |
euint32 | uint32 | 32 bits | General purpose integers |
euint64 | uint64 | 64 bits | Balances, timestamps |
euint128 | uint128 | 128 bits | Large numbers |
euint256 | uint256 | 256 bits | Hashes, full-range values |
eaddress | address | 160 bits | Encrypted addresses |
2. Handles: How Encrypted Data Lives On-Chain
What is a Handle?
When you create an encrypted value, the actual ciphertext is stored in the FHE co-processor. On the EVM side, your contract only holds a handle — auint256 reference that points to the ciphertext.
Key Implications
Storage cost
Storage cost
Storing an encrypted value costs the same as storing a
uint256 (one storage slot).No on-chain inspection
No on-chain inspection
You cannot read or log the plaintext value from within the contract.
Handle reuse
Handle reuse
The same plaintext encrypted twice will produce different handles (different ciphertexts).
Null handle
Null handle
An uninitialized encrypted variable has handle
0, which is invalid for operations.3. Boolean Type: ebool
Declaration and Initialization
Use Cases
- Encrypted flags (is a user verified? is an account frozen?)
- Results of encrypted comparisons
- Conditional logic with
FHE.select()
Creating from Comparisons
4. Unsigned Integer Types: euint8 to euint256
euint8 — 8-bit Encrypted Integer
euint16 — 16-bit Encrypted Integer
euint32 — 32-bit Encrypted Integer
The most commonly used type. Good balance between range and gas cost.euint64 — 64-bit Encrypted Integer
euint128 and euint256
For very large numbers. Note that larger types consume more gas for operations.Important Limitation:
euint256 does NOT support arithmetic operations (add, sub, mul, min, max) or ordering comparisons (le, lt, ge, gt). It supports:- Bitwise:
FHE.and(),FHE.or(),FHE.xor() - Equality:
FHE.eq(),FHE.ne() - Shift/Rotate:
FHE.shl(),FHE.shr(),FHE.rotl(),FHE.rotr() - Conditional:
FHE.select()
euint128 or smaller for arithmetic operations.5. Encrypted Address: eaddress
Declaration and Usage
Use Cases
- Hidden recipients in transfer protocols
- Anonymous voting (hiding voter addresses)
- Sealed-bid auctions (hiding bidder identity)
Comparing Encrypted Addresses
6. Type Conversion Functions
All conversions go throughFHE.asXXX():
| Function | Input | Output |
|---|---|---|
FHE.asEbool(bool) | bool | ebool |
FHE.asEuint8(uint8) | uint8 | euint8 |
FHE.asEuint16(uint16) | uint16 | euint16 |
FHE.asEuint32(uint32) | uint32 | euint32 |
FHE.asEuint64(uint64) | uint64 | euint64 |
FHE.asEuint128(uint128) | uint128 | euint128 |
FHE.asEuint256(uint256) | uint256 | euint256 |
FHE.asEaddress(address) | address | eaddress |
These functions encrypt plaintext values on-chain. The plaintext is visible in the transaction calldata. For truly private inputs from users, use
externalEuintXX and FHE.fromExternal() (covered in Module 06).Encrypted-to-Encrypted Casting
Warning: Downcasting silently truncates. There is no overflow check on encrypted values!
7. Storage Patterns
Pattern 1: Initialize in Constructor
Pattern 2: Lazy Initialization
Pattern 3: Mapping with Encrypted Values
Pattern 4: Array of Encrypted Values
8. ACL Basics: Who Can Use Encrypted Values?
When you create or update an encrypted value, you must explicitly grant access to it:FHE.allowThis(handle)— Allows this contract to use the value in future transactionsFHE.allow(handle, address)— Allows a specific address to request decryption/reencryption of the value
Why two separate permissions?
Why two separate permissions?
allowThisis needed because even the contract that created the ciphertext cannot use it in a subsequent transaction without permissionallowis needed so users can request reencryption (off-chain viewing) of their data
9. Gas Considerations
Larger types cost more gas for operations:| Type | Relative Gas Cost |
|---|---|
ebool | Lowest |
euint8 | Low |
euint16 | Low-Medium |
euint32 | Medium |
euint64 | Medium-High |
euint128 | High |
euint256 | Highest |
Rule of thumb: Always use the smallest type that fits your data range.
10. Common Mistakes
Mistake 1: Using Uninitialized Encrypted Variables
Mistake 2: Forgetting FHE.allowThis()
Mistake 3: Exposing Plaintext via FHE.asEuintXX()
11. Practical Example: Encrypted Types Demo
Summary
- FHEVM provides 8 core encrypted types covering booleans, integers (8-256 bit), and addresses
- Encrypted values are stored as handles (uint256 references) pointing to ciphertexts in the co-processor
- Use
FHE.asXXX()to convert plaintext to encrypted (but note the plaintext is visible on-chain) - Always initialize encrypted variables and call
FHE.allowThis()after updates - Use
FHE.allowThis()for contract access andFHE.allow()for user access — both are required after every update - Choose the smallest type that fits your data to minimize gas costs
Next Module: Module 04: Operations on Encrypted Data