Concept · Encrypted handles

Encrypted inputs + handles

Three types wrap FHE state at the SDK boundary. Knowing which is which removes a whole class of misuse.

EncryptedInput — what you submit#

An EncryptedInput is { handle: Hex; inputProof: Hex } — the ciphertext handle plus the KMS input proof returned by encryptUint64 / encryptUint64Batch (which yields EncryptedInputs: { handles: Hex[]; inputProof: Hex }). The discriminated union { type: "euint64", value: bigint } is FheValueInput — the internal per-value input the SDK feeds to the injected encryptor, not the encrypted output you submit.

You usually never see EncryptedInput directly — write hooks like useCreateVesting take a { amount: bigint, ... } arg and wrap it for you. The type is exposed for two cases: batch encryption (where you build an N-input proof in one round-trip) and bring-your-own relayer paths.

EncryptedHandle — what lives on chain#

An EncryptedHandle is a bytes32 string. It's what the contract stores in place of a cleartext uint64 / uint128. The SDK exposes EncryptedHandle as a reading-clarity alias of Hex (it adds no extra narrowing) so the intent is legible at call sites without blocking the build.

Handles flow OUT of read hooks (encrypted views) and INTO write hooks (transfers, disperse, splits). The handle itself reveals nothing — it's just a pointer into the coprocessor's ciphertext storage.

EncryptedViewResult — what an FHE read returns#

Some "reads" are actually mutations because the contract has to call FHE.allow on each access to grant ACL to the caller. useGetVestedAmount, useGetClaimableAmount, and their admin variants all return { handle, hash }:

  • handleis the encrypted result you'll decrypt
  • hash is the tx hash that granted you ACL

Pair the handle with useDecryptedHandle to get the plaintext via the connected wallet's signature.

Confidential-token gotchas#

Encrypted state changes how failure looks. On a transparent ERC-20 an impossible transfer reverts; on a confidential token the chain often can't revert without disclosing the very value it's hiding, so the operation completes and the outcome is encrypted instead.

A successful tx hash therefore only proves the call executed. To learn whether tokens moved, you need a decrypted read: fetch the relevant encrypted view (the recipient's balance, or the transferred-amount handle) and pair it with useDecryptedHandle— an encrypted zero after the transfer means the sender's balance didn't cover the amount.

Note that preflight does not close this gap: it deliberately skips encrypted token balances (it can't decrypt them on your behalf), so a green preflight plus a mined receipt still doesn't prove value moved. The same confidentiality principle shows up in splits, where FHE.min silently caps an over-allocated child instead of reverting — see scale-ratio math.

See also