Overview
In FHEVM, all data is encrypted on-chain. The Access Control List (ACL) system determines which addresses can operate on encrypted values. Every ciphertext has an associated ACL that tracks authorized addresses.Key insight: Without proper ACL management, even the contract that created a ciphertext cannot use it in future transactions.
Why ACL is Necessary
The Problem
Consider a token contract storing encrypted balances:- The contract itself cannot read stored ciphertexts in future transactions
- Users cannot decrypt their own balances
- Other contracts cannot interact with the ciphertexts
The Solution
The ACL system provides granular, per-ciphertext permissions. Each time you create or update a ciphertext, you must explicitly grant access.Core ACL Functions
FHE.allowThis(handle) — Self-Permission
Grants the current contract permission to use a ciphertext.
ACLDemo.sol
ACLDemo.sol
FHE.allow(handle, address) — Grant to Specific Address
Grants persistent access to a specific address (EOA or contract).
MultiUserVault.sol
FHE.allowTransient(handle, address) — Temporary Permission
Grants access that lasts only for the current transaction. Useful for inter-contract calls.
ACLDemo.sol
| Feature | FHE.allow() | FHE.allowTransient() |
|---|---|---|
| Duration | Permanent | Transaction only |
| Storage cost | Higher | Lower |
| Use case | User access | Inter-contract calls |
FHE.makePubliclyDecryptable(handle) — Public Reveal
Makes an encrypted value decryptable by anyone. Use for vote results, auction winners, etc.
ACLDemo.sol
FHE.isSenderAllowed(handle) — Permission Check
Returns true if msg.sender has access to a ciphertext.
ACLDemo.sol
Common Patterns
Pattern 1: Multi-User Vault with ACL Isolation
MultiUserVault.sol
Pattern 2: Secret Sharing
UserDecrypt.sol
Critical ACL Rules
New Handle = New ACL
Every FHE operation creates a new ciphertext with an empty ACL.
No Automatic Access
The creator gets no automatic access — always call
FHE.allowThis().Per-Handle, Not Per-Variable
ACL is tied to the handle, not the storage slot.
No Direct Revocation
Create a new ciphertext to “revoke” — old handle retains access.
Common Mistakes
Mistake 1: Forgetting FHE.allowThis() After Update
Mistake 2: Not Granting User Access
Summary
| Function | Purpose | Duration |
|---|---|---|
FHE.allowThis(handle) | Grant current contract access | Persistent |
FHE.allow(handle, addr) | Grant specific address access | Persistent |
FHE.allowTransient(handle, addr) | Grant temporary access | Transaction only |
FHE.makePubliclyDecryptable(handle) | Reveal to everyone | Persistent (irreversible) |
FHE.isSenderAllowed(handle) | Check if msg.sender has access | N/A (view) |
Next Module
Learn how users submit truly private data using client-side encryption