Skip to main content
Every encrypted value in a contract is a handle: a bytes32 identifier for a ciphertext that CoFHE holds offchain. euint32, ebool, and the rest are Solidity user-defined value types over that bytes32.
FHE.unwrap and FHE.wrapEuintXX move between the two spellings. They are the only FHE functions that do no work at all.

What the two functions do

Both are internal pure. They perform no cryptography, make no call to the TaskManager, touch no storage, and cost nothing beyond the surrounding code. They change how Solidity types the same 32 bytes, and nothing else. There is one unwrap for every encrypted type, and one wrap per type because the return type has to differ: wrapEbool, wrapEuint8, wrapEuint16, wrapEuint32, wrapEuint64, wrapEuint128, and wrapEaddress.
The bindings give you the same thing in dot form, so myValue.unwrap() reads better inside an expression. See bindings.

Wrapping does not grant permission

Wrapping a handle grants you nothing. The type records what kind of value this is. The ACL decides whether you may use it. Those are separate systems, and wrap only touches the first one. You can wrap any 32 bytes you like. Nothing reverts, because nothing is checked:
That line succeeds. The first FHE operation on notYours is where it fails, because the TaskManager checks whether this contract is allowed on that handle:
So wrap is a cast, not an acquisition. Holding a typed value is not the same as being permitted to compute on it.

Handles are public

A handle is not a secret. It sits in contract storage, travels in calldata, and shows up in event logs. Anyone can read one. That is fine, because a handle reveals nothing about the plaintext. It is an identifier, not the ciphertext and not the value. Knowing that Alice’s balance is handle 0x9f3c… tells you nothing about the balance. What a handle does give its holder is the ability to name that ciphertext in a call. That is why the ACL exists, and why the next section matters.

When to unwrap

Reach for unwrap when you need the value to be plain bytes32 because something else demands it:
  • Storing a handle in a generic bytes32 slot or struct field.
  • Emitting a handle in an event so a client can read it back.
  • Using a handle as a mapping key.
  • Comparing two handles for identity, which asks “is this the same ciphertext”, not “are these values equal”. For an encrypted comparison use FHE.eq.

When to wrap

Reach for wrapEuintXX when you are recovering a handle your own contract already owns and stored as bytes32:
This is safe because the contract is already allowed on the handle. Wrapping only restores the type the storage slot lost.

When not to wrap

Do not wrap a handle that arrived from outside the contract.A function that accepts a bytes32 (or a bare euintXX) from a caller and computes on it can be turned into a decryption oracle.FHE operations check the permission of the contract performing them, not of whoever called it. A contract is always allowed on its own stored state. So an attacker passes a handle read from your storage or an event. The operation passes the ACL check because you hold the value, and the function hands back something derived from a ciphertext they were never meant to read.
Use a type that carries provenance instead. There is one for every way a value can reach you, so a wrapped bytes32 parameter is never the right answer: Each conversion authenticates the value as part of converting it, which is exactly what wrapping a raw handle skips. The two receive forms are not interchangeable: pick by how the value arrived, because naming the wrong party silently weakens the check rather than failing. All of these are covered in inputs.

Checking a handle is real

wrap accepts anything, so a wrapped value can be meaningless. FHE.isInitialized tells you whether a handle is non-zero, which catches an unset storage slot:
It does not tell you the handle refers to a ciphertext that exists, or that you are allowed on it. Only the operation itself can tell you that.
  • Inputs: how values arrive from users and from other contracts.
  • Access control: what the ACL permits, and how to grant it.
  • Utility functions: the reference entries for unwrap, wrap, and isInitialized.