ERC-7984 Confidential Token Invariants
Tokens are never created or destroyed by a transfer.
Sufficient transfers move exactly the requested amount.
Insufficient transfers change nothing and never revert.
In the formal view, each statement ranges over all transfer inputs and states satisfying the assumptions listed below.
ERC-7984 is a confidential fungible token standard co-developed by Zama and OpenZeppelin for the fhEVM. All balances and transfer amounts are encrypted as euint64 ciphertext handles using Fully Homomorphic Encryption. Users can hold, send, and receive tokens without revealing their balances to anyone, including validators.
ERC-20 transfer
Alice has 100, sends 50 to Bob
✓ Transfer succeeds
Alice has 30, sends 50 to Bob
✕ Transaction reverts. Everyone sees it failed.
ERC-7984 transfer (encrypted)
Alice has 100, sends 50 to Bob
✓ 50 moved, but amounts stay hidden
Alice has 30, sends 50 to Bob
✓ Tx succeeds, but 0 moved. No one can tell it failed.
ERC-7984 never reverts on insufficient balance. Reverting would reveal that the sender doesn't have enough funds.
The main difference from ERC-20: because the contract cannot branch on encrypted values, transfers with insufficient balance silently transfer zero instead of reverting. FHE.select picks the result without revealing which path was taken. Arithmetic wraps at 264 (not 2256), and FHESafeMath.tryIncrease / tryDecrease detect overflow and underflow without revealing whether they occurred.
We also proved that a transfer never reverts based on balance sufficiency. The only reverts come from plaintext checks (zero address, uninitialized balance). An on-chain observer cannot learn whether the sender had enough tokens by watching whether the transaction succeeded.
Why this matters
In a standard ERC-20, a broken transfer reverts and the user sees the error. In ERC-7984, a broken transfer looks identical to a successful one from the outside. If the accounting logic had a bug, tokens could silently appear or vanish with no on-chain signal that anything went wrong.
The confidentiality that protects users also hides accounting errors. If a bug caused tokens to silently vanish, there would be no on-chain signal. Formal verification closes that gap: if the invariants hold for all inputs, the accounting is correct regardless of what the ciphertexts contain.
What these invariants cover
Eleven theorems across five functions: transfer (conservation, sufficient, insufficient, no-revert, preserves supply), transferFrom (conservation), mint (increases supply, overflow protection), burn (sufficient, insufficient), and setOperator (state update correctness).
Out of scope: the FHE encryption layer (handled by Zama's TFHE library), the ACL system (FHE.allow, which controls ciphertext access, not balances), cryptographic proof verification (fromExternal + inputProof), off-chain gateway decryption, and receiver callbacks (AndCall variants). These are separate systems that do not affect balance state.
How this was proven
The contract model covers all five functions: the three _update paths (transfer, mint, burn), plus transferFrom and setOperator. Arithmetic uses tryIncrease64 / tryDecrease64, matching the Solidity exactly. Because euint64 values are modeled as Uint256 rather than a bounded type, some theorem statements carry explicit < 2^64 preconditions on inputs and balances; these reflect what the FHE type enforces at the contract level, not additional contract assumptions. _operators is modeled as a native nested mapping via storageMap2. blockTimestamp is passed as an explicit parameter to transferFrom for the operator expiry check.
Token conservation is straightforward to state: show that balances[from] + balances[to] is the same before and after, for all inputs. If the sender has enough, exactly amount moves. If not, select picks zero, both balances write back unchanged, and the sum is trivially preserved.
Most theorems handle the two branches of FHE.select separately: the sufficient-balance path and the insufficient-balance path. Each branch reduces to a concrete arithmetic statement that the proof checker verifies automatically. The non-revert theorem works differently: instead of branching on balance, it goes through every require in the function and shows each one is already satisfied by the preconditions. Since no require checks whether the balance is sufficient, balance never enters the picture.
Verify it yourself
git clone https://github.com/lfglabs-dev/verity-benchmark cd verity-benchmark lake build Benchmark.Cases.Zama.ERC7984ConfidentialToken.Proofs
If the build succeeds, the proofs are correct. Source repository
Proof status
All 11 theorems are proven across five functions. Every task file is sorry-free.
| Function | Conservation | Sufficient | Insufficient | No revert | Supply | Overflow |
|---|---|---|---|---|---|---|
| transfer | proven | proven | proven | proven | proven | — |
| mint | — | proven | — | — | proven | proven |
| burn | — | proven | proven | — | proven | — |
| transferFrom | proven | — | — | — | — | — |
| setOperator | updates proven | |||||
Conservation: sender + receiver sum preserved. Sufficient: correct amount moves when balance covers it. Insufficient: no change when balance is too low. No revert: transfer does not revert on insufficient balance. Supply: totalSupply changes correctly (or not at all for transfers). Overflow: mint detects uint64 overflow and mints nothing. setOperator has a separate updates theorem: it writes exactly the caller/operator expiry pair without affecting any other storage.
Assumptions
The proofs use zero axioms. Two hypotheses encode plaintext preconditions that the contract checks explicitly.
hInitbalanceInitialized[from] != 0FHE.isInitialized gate
The sender's balance has been initialized. The contract checks
FHE.isInitialized(fromBalance)and reverts withERC7984ZeroBalanceif not, meaning the transfer is from an account that has received tokens before.hDistinctsender != recipientStorage model
Sender and receiver are different addresses. Required because the model reads and writes balance slots independently. A self-transfer is a separate (trivial) case.
Learn more
ERC-7984: The Confidential Token Standard Explained, by Zama.
OpenZeppelin Confidential Contracts, the upstream implementation.
What is a formal proof? A short explanation for non-specialists.
More research
Alchemix V3 Earmark Conservation
Formally verified earmark conservation invariant for Alchemix V3's lazy-accrual debt accounting system.

Reserve DTF Auction Price Band Invariant
Formally verified per-pair price band invariant for the auction pricing path in Reserve DTF Protocol.
Cork Protocol Pool Solvency
Formally verified solvency invariant for the unwindExerciseOther function in Cork Phoenix.