Overview
The old decryption pattern usedFHE.decrypt(ctHash) to trigger an asynchronous decryption, followed by FHE.getDecryptResultSafe(ctHash) to read the result once available. The new pattern replaces FHE.decrypt with an off-chain decryption step using the Client SDK, and uses FHE.publishDecryptResult to submit the result on-chain with a cryptographic proof.
This guide walks through concrete before/after Solidity examples.
What changed
| Old pattern | New pattern | |
|---|---|---|
| Trigger decrypt | FHE.decrypt(ctHash) (on-chain) | FHE.allowPublic(ctHash) (on-chain) + client.decryptForTx(ctHash) (off-chain) |
| Submit result | Automatic (async, no proof) | FHE.publishDecryptResult(ctHash, plaintext, signature) |
| Verify only | N/A | FHE.verifyDecryptResult(ctHash, plaintext, signature) |
| Read result | FHE.getDecryptResultSafe(ctHash) | FHE.getDecryptResultSafe(ctHash) (same) |
The key difference:
FHE.decrypt triggered decryption without any proof. The new flow requires a Threshold Network signature, ensuring the plaintext is cryptographically verified before being used on-chain.The New Decryption Flow
Grant decryption permission (on-chain)
Instead of calling
FHE.decrypt(), mark the value as decryptable:Decrypt off-chain (client-side)
The client requests decryption from the Threshold Network, which returns the plaintext and a signature:
Example 1: Simple counter
A minimal example showing how the reveal pattern changes.Example 2: Token unshield (FHERC20Wrapper)
The unshield flow is whereFHE.decrypt was most commonly used. It already followed a two-step pattern (unshield + claim), which maps naturally to the new flow.
FHE.decrypt(burned)→FHE.allowPublic(burned)— no on-chain decryption is triggered, just a permission grantclaimUnshielded(bytes32 ctHash)→claimUnshielded(bytes32 ctHash, uint64 decryptedAmount, bytes signature)— the caller now provides the decrypted value + proofFHE.getDecryptResultSafe→FHE.publishDecryptResult— the contract verifies the Threshold Network signature instead of polling for a result
Example 3: Revealing a vote result
A simple pattern: revealing a single encrypted vote count after a deadline.publishDecryptResult vs verifyDecryptResult
| Method | Stores result on-chain | Others can read it | Use case |
|---|---|---|---|
publishDecryptResult | Yes | Yes, via getDecryptResultSafe | Revealing results publicly (auctions, votes, counters) |
verifyDecryptResult | No | No | One-time verification (transfers, burns) |
verifyDecryptResult when you only need to confirm the plaintext is authentic and don’t need other contracts or future calls to read it:
Migration checklist
Replace FHE.decrypt with FHE.allowPublic
In the function that previously called
FHE.decrypt(ctHash), replace it with FHE.allowPublic(ctHash).Add a finalize function
Create a new function that accepts
(plaintext, signature) parameters and calls FHE.publishDecryptResult or FHE.verifyDecryptResult.Next Steps
- Read about Decryption Operations for the full reference
- See Adding FHE to an Existing Contract for a complete contract migration
- Learn about Access Control for managing decrypt permissions
- Check the Client SDK decrypt guide for the full client-side API