Overview
FHE operations are 50,000-100,000x more expensive than plaintext operations. Understanding gas costs is critical for building practical FHE smart contracts. This guide provides comprehensive cost data, comparisons, and optimization strategies.
Why FHE Gas Costs Are High
Every FHE operation is processed by a dedicated coprocessor performing homomorphic computation on ciphertexts:
- Ciphertext Size: Encrypted values are kilobytes in size vs. 32 bytes for plaintext
- Computation Complexity: Homomorphic operations require extensive lattice-based mathematics
- Bit Width Scaling: Larger types (euint256) cost 5-10x more than smaller types (euint8)
- Noise Management: Some operations require bootstrapping - the most expensive FHE operation
Operation Cost Table
All costs are approximate and measured on local fhEVM mock environment. Actual costs vary by network.
Arithmetic Operations
| Operation | euint8 | euint16 | euint32 | euint64 | euint128 | euint256 |
|---|
FHE.add | ~80k | ~120k | ~180k | ~250k | ~400k | ~600k |
FHE.sub | ~80k | ~120k | ~180k | ~250k | ~400k | ~600k |
FHE.mul | ~150k | ~250k | ~400k | ~700k | ~1.2M | ~2M |
FHE.div (scalar) | ~250k | ~400k | ~700k | ~1.2M | ~2M | ~3.5M |
FHE.rem (scalar) | ~250k | ~400k | ~700k | ~1.2M | ~2M | ~3.5M |
FHE.neg | ~60k | ~90k | ~140k | ~200k | ~350k | ~500k |
FHE.min | ~180k | ~280k | ~450k | ~750k | ~1.3M | ~2.2M |
FHE.max | ~180k | ~280k | ~450k | ~750k | ~1.3M | ~2.2M |
Comparison Operations
| Operation | euint8 | euint16 | euint32 | euint64 | euint128 | euint256 |
|---|
FHE.eq | ~120k | ~180k | ~280k | ~450k | ~800k | ~1.4M |
FHE.ne | ~120k | ~180k | ~280k | ~450k | ~800k | ~1.4M |
FHE.gt | ~120k | ~180k | ~280k | ~450k | ~800k | ~1.4M |
FHE.ge | ~120k | ~180k | ~280k | ~450k | ~800k | ~1.4M |
FHE.lt | ~120k | ~180k | ~280k | ~450k | ~800k | ~1.4M |
FHE.le | ~120k | ~180k | ~280k | ~450k | ~800k | ~1.4M |
Bitwise Operations
| Operation | euint8 | euint16 | euint32 | euint64 | euint128 | euint256 |
|---|
FHE.and | ~60k | ~90k | ~140k | ~200k | ~350k | ~500k |
FHE.or | ~60k | ~90k | ~140k | ~200k | ~350k | ~500k |
FHE.xor | ~60k | ~90k | ~140k | ~200k | ~350k | ~500k |
FHE.not | ~50k | ~75k | ~120k | ~170k | ~300k | ~450k |
FHE.shl | ~90k | ~140k | ~220k | ~350k | ~600k | ~1M |
FHE.shr | ~90k | ~140k | ~220k | ~350k | ~600k | ~1M |
FHE.rotl | ~90k | ~140k | ~220k | ~350k | ~600k | ~1M |
FHE.rotr | ~90k | ~140k | ~220k | ~350k | ~600k | ~1M |
Conditional Selection
| Operation | euint8 | euint16 | euint32 | euint64 | euint128 | euint256 |
|---|
FHE.select | ~70k | ~100k | ~160k | ~230k | ~380k | ~560k |
Other Operations
| Operation | Gas Cost (Approx) |
|---|
FHE.fromExternal() | ~50-150k (varies by type) |
FHE.asEuint32() (plaintext) | ~50k |
FHE.randEuint32() | ~100-200k |
FHE.allow() | ~40-50k |
FHE.allowThis() | ~40-50k |
FHE.allowTransient() | ~25-35k (30% cheaper) |
Cost Comparison: FHE vs Plaintext
| Operation | Plaintext | FHE (euint32) | Ratio |
|---|
| Addition | ~3 gas | ~180k gas | 60,000x |
| Multiplication | ~5 gas | ~400k gas | 80,000x |
| Comparison | ~3 gas | ~280k gas | 93,000x |
| Storage write | ~5-20k gas | Same (handle) | 1x |
Key Insight: Gas cost is dominated by computation, not storage.
Type Selection Impact
Gas Savings by Type
| Type | Add Cost | Relative | Use Case |
|---|
euint8 | ~80k | 1.0x (baseline) | Flags, counters (0-255) |
euint16 | ~120k | 1.5x | Small amounts, indices |
euint32 | ~180k | 2.25x | Timestamps, balances |
euint64 | ~250k | 3.1x | Token balances (6-8 decimals) |
euint128 | ~400k | 5x | Large tokens (18 decimals) |
euint256 | ~600k | 7.5x | Maximum compatibility |
Example Savings:
// ❌ Expensive: euint64 for vote counter (0-1000)
euint64 voteCount; // FHE.add costs ~250k gas
// ✅ Efficient: euint16 sufficient
euint16 voteCount; // FHE.add costs ~120k gas (52% savings)
Optimization Strategies
Strategy 1: Use Smallest Type That Fits
// ❌ Wasteful
euint256 voteCount; // 0-100 votes, FHE.add ~600k gas
// ✅ Efficient
euint8 voteCount; // 0-255 range, FHE.add ~80k gas (87% savings)
Decision Tree:
Value range 0-255? -> euint8
Value range 0-65,535? -> euint16
Value range 0-4.3B? -> euint32
Value range 0-18 quintillion? -> euint64
Larger? -> euint128 or euint256
Strategy 2: Use Plaintext Second Operands
Operations with plaintext second operands are 30-40% cheaper:
// ❌ Expensive: Both encrypted
euint32 result = FHE.add(value, FHE.asEuint32(10)); // ~180k gas
// ✅ Cheaper: Plaintext second operand
euint32 result = FHE.add(value, 10); // ~120k gas (33% savings)
Applicable Operations:
- Arithmetic:
add, sub, mul, div, rem
- Comparison:
eq, ne, gt, ge, lt, le
- Bitwise:
and, or, xor, shl, shr
Strategy 3: Minimize FHE Operation Count
// ❌ Multiple operations (4 FHE ops = ~720k gas)
euint32 a = FHE.add(x, y);
euint32 b = FHE.add(a, z);
euint32 c = FHE.mul(b, 2);
// ✅ Combine operations (2 FHE ops = ~360k gas)
euint32 sum = FHE.add(FHE.add(x, y), z); // Chain additions
euint32 result = FHE.mul(sum, 2);
Strategy 4: Cache Comparison Results
// ❌ Recomputes comparison twice (~560k gas)
euint64 r1 = FHE.select(FHE.gt(a, b), x, y);
euint64 r2 = FHE.select(FHE.gt(a, b), m, n);
// ✅ Compute once, reuse (~440k gas)
ebool isGreater = FHE.gt(a, b); // ~280k gas (once)
euint64 r1 = FHE.select(isGreater, x, y); // ~160k gas
euint64 r2 = FHE.select(isGreater, m, n); // ~160k gas
Strategy 5: Use Shift Instead of Mul/Div for Powers of 2
// ❌ Expensive: Multiply by 8
euint32 result = FHE.mul(value, 8); // ~400k gas
// ✅ Cheaper: Shift left by 3 (2^3 = 8)
euint32 result = FHE.shl(value, 3); // ~220k gas (45% savings)
// ❌ Expensive: Divide by 4
euint32 result = FHE.div(value, 4); // ~700k gas
// ✅ Cheaper: Shift right by 2 (2^2 = 4)
euint32 result = FHE.shr(value, 2); // ~220k gas (69% savings)
Strategy 6: Use FHE.min/max Instead of Compare + Select
// ❌ Verbose: Compare + Select (~440k gas)
ebool isLess = FHE.lt(a, b); // ~280k gas
euint32 minimum = FHE.select(isLess, a, b); // ~160k gas
// ✅ Built-in: FHE.min (~450k gas, similar but cleaner)
euint32 minimum = FHE.min(a, b);
Strategy 7: Pre-compute Off-Chain
If computation involves only plaintext, do it client-side:
// ❌ On-chain computation (3 FHE ops ~760k gas)
function submitOrder(
externalEuint64 encPrice,
externalEuint64 encQty,
externalEuint64 encFee,
bytes calldata proof
) external {
euint64 price = FHE.fromExternal(encPrice, proof);
euint64 qty = FHE.fromExternal(encQty, proof);
euint64 fee = FHE.fromExternal(encFee, proof);
euint64 total = FHE.add(FHE.mul(price, qty), fee); // 2 FHE ops
}
// ✅ Client computes: total = price * qty + fee, sends result
function submitOrder(
externalEuint64 encTotal,
bytes calldata proof
) external {
euint64 total = FHE.fromExternal(encTotal, proof); // 0 FHE ops
FHE.allowThis(total);
}
Strategy 8: Use Transient Permissions
// ❌ Persistent permission (~50k gas)
FHE.allow(tempValue, callback);
ICallback(callback).process(tempValue);
// ✅ Transient permission (~30k gas, 40% savings)
FHE.allowTransient(tempValue, callback);
ICallback(callback).process(tempValue);
Strategy 9: Batch ACL Calls
While ACL operations must be called individually, organize them logically:
// ✅ Organized: All ACL at end
balances[from] = newFromBal;
balances[to] = newToBal;
// Batch ACL calls
FHE.allowThis(balances[from]);
FHE.allow(balances[from], from);
FHE.allowThis(balances[to]);
FHE.allow(balances[to], to);
Strategy 10: Avoid Unnecessary Encryption
Not everything needs to be encrypted:
// ❌ Everything encrypted
euint32 public totalVotes; // Total count - does this need to be secret?
euint32 public votingDeadline; // Deadline - public info
// ✅ Hybrid approach
uint256 public totalVoters; // Public count
uint256 public votingDeadline; // Public deadline
euint32 private yesVotes; // Secret tally
euint32 private noVotes; // Secret tally
Real Gas Benchmark Results
From test suite (328 tests, local fhEVM mock):
Function Call Costs (Selected)
| Contract | Function | Gas Cost | Notes |
|---|
| ConfidentialERC20 | transfer | ~292k | Balance check + 2 updates + ACL |
| ConfidentialERC20 | transferFrom | ~446k | Most expensive: allowance + balance checks |
| ConfidentialVoting | vote | ~292k | Vote comparison + counter update |
| SealedBidAuction | bid | ~396k | Encrypted comparison + 2 selects |
| EncryptedLottery | drawWinner | ~171k | Random generation + storage |
| SimpleCounter | increment | ~208k | Add + ACL |
| RandomDemo | generateRandom32 | ~126k | Random generation |
Deployment Costs
| Contract | Deployment Gas | Complexity |
|---|
| SimpleStorage | 162k | Minimal |
| ConfidentialERC20 | 1.24M | Full ERC-20 |
| SealedBidAuction | 1.51M | Complex logic |
Gas Budgeting
Estimating Transaction Cost
Total Gas = Base (21k)
+ Contract logic
+ Σ(FHE operations)
+ Σ(ACL operations)
+ Σ(Input conversions)
Example: ERC-20 Transfer
Operation | Gas
------------------------------|----------
Base transaction | 21,000
FHE.fromExternal (euint64) | 100,000
FHE.ge (balance check) | 450,000
FHE.sub (sender balance) | 250,000
FHE.add (receiver balance) | 250,000
FHE.select (sender) | 230,000
FHE.select (receiver) | 230,000
FHE.allowThis x2 | 100,000
FHE.allow x2 | 100,000
Contract overhead | 50,000
------------------------------|----------
TOTAL ESTIMATED | ~1,781,000
Gas Limit Configuration
// hardhat.config.js
module.exports = {
networks: {
hardhat: {
gas: 30_000_000,
blockGasLimit: 30_000_000,
},
},
};
Profiling in Tests
const tx = await contract.transfer(recipient, encryptedAmount, proof);
const receipt = await tx.wait();
console.log(`Transfer gas used: ${receipt.gasUsed.toString()}`);
Hardhat Gas Reporter
npm install hardhat-gas-reporter --save-dev
// hardhat.config.js
require("hardhat-gas-reporter");
module.exports = {
gasReporter: {
enabled: true,
currency: "USD",
},
};
Cost Reduction Checklist
Future Cost Reductions
FHE gas costs are expected to decrease due to:
- Hardware acceleration: GPU and ASIC-based FHE accelerators
- Algorithmic improvements: New FHE schemes and optimizations
- Coprocessor optimization: Continuous TFHE-rs library improvements
- Batching: Better operation batching at coprocessor level
- Custom gas pricing: Network-specific pricing models
Current costs reflect early-stage technology. Costs will follow Moore’s Law-like improvements over time.
Important Notes
FHE operations cost 50,000-100,000x more than plaintext. Design contracts to minimize encrypted computations.
Storage cost is the same as plaintext (encrypted values are stored as 32-byte handles). Computation dominates gas usage.
Using euint8 instead of euint256 reduces gas costs by 7.5x for most operations.