Learning Objectives
By the end of this module, you will:- Understand the gas cost model for FHE operations
- Choose the right encrypted type size for each use case
- Apply optimization patterns to reduce gas consumption by 30-70%
- Profile and benchmark FHE contract gas usage
- Identify and eliminate unnecessary encrypted operations
Why Gas Optimization is Critical for FHE
In traditional Solidity, a plaintext addition costs roughly 3-5 gas. An encrypted addition (FHE.add(euint32, euint32)) costs approximately 90,000 gas — a factor of 20,000x more expensive.
The good news: there are concrete, repeatable patterns that can reduce your FHE gas costs by 30-70% without changing your contract’s logic.
FHE Gas Cost Reference
Complete Gas Cost Table
| Operation | euint8 | euint16 | euint32 | euint64 | euint128 | euint256 |
|---|---|---|---|---|---|---|
FHE.add (enc+enc) | ~50k | ~65k | ~90k | ~130k | ~180k | ~250k |
FHE.add (enc+plain) | ~35k | ~45k | ~65k | ~95k | ~135k | ~190k |
FHE.sub (enc-enc) | ~50k | ~65k | ~90k | ~130k | ~180k | ~250k |
FHE.mul (enc*enc) | ~120k | ~150k | ~200k | ~300k | ~450k | ~600k |
FHE.mul (enc*plain) | ~80k | ~100k | ~140k | ~210k | ~320k | ~430k |
FHE.div (enc/plain) | ~50k | ~65k | ~90k | ~130k | ~180k | ~250k |
FHE.select | ~45k | ~50k | ~60k | ~80k | ~110k | ~150k |
FHE.eq / FHE.ne | ~45k | ~50k | ~60k | ~80k | ~110k | ~150k |
FHE.gt / FHE.lt | ~45k | ~50k | ~60k | ~80k | ~110k | ~150k |
FHE.min / FHE.max | ~90k | ~100k | ~120k | ~160k | ~220k | ~300k |
Optimization Strategy 1: Choose the Right Type Size
This is the simplest and most impactful optimization. If a value is guaranteed to fit in 8 bits, useeuint8 instead of euint32 or euint64.
Right-Sized Types
| Data | Range | Best Type |
|---|---|---|
| Age | 0 – 255 | euint8 |
| Percentage | 0 – 100 | euint8 |
| Year | 2000 – 2100 | euint16 |
| Token balance | 0 – 2^64 | euint64 |
| Vote count (small DAO) | 0 – 65535 | euint16 |
| Boolean flag | 0 or 1 | ebool |
Code Example
Optimization Strategy 2: Use Plaintext Operands
When one operand is a known constant or a value that doesn’t need to be secret, pass it as plaintext. The FHE coprocessor can optimize the circuit when one operand is in the clear.The Rule
If the second operand is not secret, always pass it as plaintext:Which Operations Support Plaintext Operands?
All of the following accept a plaintext as the second (right-hand) operand:- Arithmetic:
add,sub,mul,rem - Comparison:
eq,ne,lt,le,gt,ge - Min/Max:
min,max - Bitwise:
and,or,xor - Shift/Rotate:
shl,shr,rotl,rotr
Division (
div) already requires a plaintext divisor — you cannot divide by an encrypted value.Optimization Strategy 3: Minimize FHE Operations
Every FHE operation has a significant fixed cost. Reducing the total number of operations is often more impactful than optimizing individual ones.Combine Conditions
Pre-compute in Plaintext
Anything that can be computed in plaintext should be computed in plaintext:Optimization Strategy 4: Batch Processing
In traditional Solidity, individual transactions are cheap. With FHE, each transaction has base overhead (21,000 gas + contract call overhead). Batching multiple updates into a single transaction amortizes this overhead.Example: Updating Multiple Balances
Optimization Strategy 5: Caching and Storage Trade-offs
If an encrypted value is computed from other encrypted values, and those source values don’t change often, cache the result instead of recomputing it every time.Example: Tax Rate Calculation
The Storage Trade-off
Storing an encrypted value in a state variable (SSTORE) costs gas too — roughly 20,000 gas for a new slot, 5,000 for an update. The trade-off:
| Scenario | Better Approach |
|---|---|
| Value recomputed 1-2 times | Recompute (storage cost not worth it) |
| Value recomputed 3+ times | Cache it (amortized savings exceed storage cost) |
| Value changes every block | Recompute (cache invalidated immediately) |
| Value changes rarely | Cache it (big win) |
Optimization Strategy 6: Lazy Evaluation
Lazy evaluation means deferring an expensive computation until its result is actually needed. If the result may never be needed, you save the cost entirely.Example: Deferred Squaring
updateValue is called 10 times before computeResult is called once, you save 9 multiplications worth of gas (9 x ~200k = ~1.8M gas saved).
Gas Profiling Technique
You cannot optimize what you cannot measure. Here’s how to profile FHE gas usage in your Hardhat tests.Using receipt.gasUsed
Comparing Two Implementations
Setting Gas Budgets
For production contracts, establish maximum gas budgets per function:Real-World Example: Optimizing a Confidential ERC-20 Transfer
Let’s walk through a realistic optimization of a confidential token transfer function.Before: Naive Implementation
Before: Naive Implementation
After: Optimized Implementation
After: Optimized Implementation
FHE.asEuint32(amount) cast and used the plaintext amount directly as the second operand. This eliminated the cast entirely and made the arithmetic operations cheaper.
Optimization Checklist
Use this checklist when reviewing your FHE contracts:Plaintext operands?
For every FHE operation, is the second operand truly secret? If not, pass it as plaintext.
Minimum operations?
Can any FHE operations be eliminated by refactoring or pre-computing in plaintext?
Unnecessary casts?
Are you calling
FHE.asEuintXX() on values that could be passed as plaintext operands directly?Common Anti-Patterns
Anti-Pattern 1: “Encrypt Everything”
Anti-Pattern 2: “One Operation Per Transaction”
Anti-Pattern 3: “Ignoring Type Sizes”
Anti-Pattern 4: “Recomputing Constants”
Summary of Gas Savings by Pattern
| Pattern | Typical Savings | Difficulty |
|---|---|---|
| Right-sized types | 30-60% | Easy |
| Plaintext operands | 15-35% | Easy |
| Minimize operations | 20-80% | Medium |
| Batch processing | 10-20% (tx overhead) | Easy |
| Caching | 25-50% (on repeated calls) | Medium |
| Lazy evaluation | 0-90% (depends on access pattern) | Hard |
| Redundant select elimination | 10-30% | Medium |
| Avoid unnecessary casts | 5-15% | Easy |
Key Takeaways
FHE operations cost 10-100x more
Optimization is not optional — it’s essential for practical FHE contracts.
Use plaintext operands
The single biggest win — requires zero architectural changes.
Right-size types
Second easiest win — use euint8 when 8 bits suffice.
Measure gas
Always measure with
receipt.gasUsed before and after optimizing.Before encrypting a value, ask yourself: “Does this actually need to be secret?”
Reference Contracts
- GasOptimized.sol (
contracts/15-gas-optimization/GasOptimized.sol:1) — Before/after optimization patterns - GasBenchmark.sol (
contracts/15-gas-optimization/GasBenchmark.sol:1) — Isolated benchmarks for individual FHE operations