Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Encrypting Inputs

encryptInputs encrypts plaintext values into FHE ciphertexts that can be passed as inputs to a confidential smart contract transaction. Values must be encrypted before being passed on-chain to preserve confidentiality.

It returns an EncryptInputsBuilder which lets you configure the encryption and then call .execute() to run it.

The flow is as follows:

  1. Decide which plaintext value(s) you want to encrypt.
  2. Wrap each value with a typing helper (e.g. Encryptable.uint32(…)) to construct typed encryptable inputs (see Encryptable — typing inputs).
  3. Call client.encryptInputs([...]).execute() to produce EncryptedItemInput objects (see Builder API).
  4. Submit a transaction to your contract and pass the encrypted inputs as InE* parameters (see Writing encrypted data to a contract).

Prerequisites

  1. Create and connect a client (see the client page).

encryptInputs(...) requires a connected client so the SDK can resolve chainId, account, and the underlying RPC clients.

  1. Know which encrypted type you want to encode each value as.

The type is chosen by which Encryptable.* factory you use (and must match the Solidity parameter type your contract expects, e.g. InEuint32 vs InEuint64).

Basic usage

await cofheClient.connect(publicClient, walletClient);
 
const encrypted = await cofheClient
  .encryptInputs([
    Encryptable.uint32(42n),
    Encryptable.bool(true),
    Encryptable.address('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'),
  ])
  .execute();
 
const [eUint32, eBool, eAddress] = encrypted;

The return type is a typed tuple that mirrors the array you pass in — each element is the corresponding Encrypted*Input type. For the exact shape of each item, see EncryptedItemInput — the result type.

What encryptInputs returns

Running .execute() returns a typed tuple of EncryptedItemInput items that mirrors the array you passed to encryptInputs([...]).

Each item contains the handle (ctHash in the SDK type), the encrypted type (utype), the security zone, and a verifier signature authorizing that input.

For the exact shape, see EncryptedItemInput — the result type.

Builder API

.execute() — required, call last

Runs the encryption pipeline and returns the EncryptedItemInput[] tuple.

await cofheClient.connect(publicClient, walletClient);
 
const [encryptedAge, encryptedFlag] = await cofheClient
  .encryptInputs([Encryptable.uint8(25n), Encryptable.bool(true)])
  .execute(); 

.setAccount(address) — optional

Override the address that "owns" the encrypted input. Only that address will be allowed to use the encrypted inputs on-chain. Defaults to the account from the connected WalletClient.

await cofheClient.connect(publicClient, walletClient);
 
const encrypted = await cofheClient
  .encryptInputs([Encryptable.uint64(10n)])
  .setAccount('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045') 
  .execute();

.setChainId(chainId) — optional

Override the chain the encrypted input will be used on. Defaults to the chain ID of the connected PublicClient.

await cofheClient.connect(publicClient, walletClient);
 
const encrypted = await cofheClient
  .encryptInputs([Encryptable.uint64(10n)])
  .setChainId(11155111) 
  .execute();

.asHashPlusProof() — optional

Switches the result format to hash plus proof. Instead of returning an array of EncryptedItemInput structs (one per encrypted value), .execute() returns an array of per-item bytes32 hashes followed by a single combined bytes proof string.

Use this when your contract uses the externalEuint* + bytes proof calling convention rather than the legacy InE* struct pattern.

await cofheClient.connect(publicClient, walletClient);
 
// Returns [hash, proof] for a single input
const [encHash, encProof] = await cofheClient
  .encryptInputs([Encryptable.uint32(42n)])
  .asHashPlusProof() 
  .execute();
 
// encHash  → `0x${string}` (32-byte hex, e.g. externalEuint32)
// encProof → `0x${string}` (combined proof bytes for all inputs)

See Writing Encrypted Data — Hash plus proof for a full contract call example.

.setUseWorker(boolean) — optional

Overrides the useWorkers flag from CofheConfig for this specific call. When true (the default), ZK proof generation runs in a Web Worker to avoid blocking the main thread. No-op in a node environment.

await cofheClient.connect(publicClient, walletClient);
 
const encrypted = await cofheClient
  .encryptInputs([Encryptable.uint32(7n)])
  .setUseWorker(false) 
  .execute();

.onStep(callback) — optional

Registers a callback that fires at the start and end of each encryption step. Useful for building progress indicators.

The callback receives the current EncryptStep enum value and a context object with isStart, isEnd, and duration (milliseconds, only meaningful on isEnd).

await cofheClient.connect(publicClient, walletClient);
 
const encrypted = await cofheClient
  .encryptInputs([Encryptable.uint64(10n)])
  .onStep((step, ctx) => {

    if (ctx?.isStart) console.log(`Starting: ${step}`); 
    if (ctx?.isEnd) console.log(`Done: ${step} (${ctx.duration}ms)`); 
  }) 
  .execute();

The EncryptStep enum values fired in order:

EncryptStep.InitTfhe; // 'initTfhe'
EncryptStep.FetchKeys; // 'fetchKeys'
EncryptStep.Pack; // 'pack'
EncryptStep.Prove; // 'prove'
EncryptStep.Verify; // 'verify'

The encryption flow

Calling .execute() runs five sequential steps. You can observe them via the .onStep() callback.

StepDescription
InitTfheLazy-initializes the TFHE WASM module (browser/Node). A no-op after the first call.
FetchKeysFetches (or loads from cache) the FHE public key and CRS for the target chain.
PackPacks the plaintext values into a ZK list ready for proving.
ProveGenerates the ZK proof of knowledge (ZKPoK). Uses a Web Worker when available.
VerifySends the proof to the CoFHE verifier. Returns signed EncryptedItemInput objects.

Encryptable — typing inputs

Use the Encryptable factory to create the items you want to encrypt. Each factory function accepts the plaintext value and an optional securityZone.

FactoryData typeSolidity input param
Encryptable.bool(value)booleanInEbool
Encryptable.uint8(value)bigint | stringInEuint8
Encryptable.uint16(value)bigint | stringInEuint16
Encryptable.uint32(value)bigint | stringInEuint32
Encryptable.uint64(value)bigint | stringInEuint64
Encryptable.uint128(value)bigint | stringInEuint128
Encryptable.address(value)bigint | stringInEaddress

You can also use the generic Encryptable.create(type, value) form:

Encryptable.create('uint32', 42n);
Encryptable.create('bool', false);
Encryptable.create('address', '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045');

Bit limit: A single encryptInputs call may encrypt at most 2048 bits of plaintext in total. Exceeding this limit throws a ZkPackFailed error.

Result types

EncryptedItemInput — default format

Each element of the returned array is an EncryptedItemInput:

type EncryptedItemInput = {
  ctHash: bigint; // The handle registered with CoFHE
  securityZone: number; // The security zone the input was encrypted under
  utype: FheTypes; // The FHE type (Bool, Uint8, …, Uint160)
  signature: string; // CoFHE verifier signature authorizing this input
};

Pass these directly into a contract function that accepts InEuint* structs. The contract's CoFHE library validates the signature on-chain before operating on the ciphertext.

HashPlusProofResult — hash plus proof format

When .asHashPlusProof() is used, .execute() returns a typed tuple of the form [...hashes, proof]:

  • One 0x-prefixed 32-byte hash string per input (typed as externalEuint* / externalEbool / externalEaddress on the Solidity side).
  • One combined bytes proof string at the end (concatenation of all per-item signatures).
// For one input:
// [externalHash, combinedProof]
 
// For two inputs:
// [externalHash1, externalHash2, combinedProof]

Use this format with Solidity functions that accept externalEuint* + bytes proof instead of the InE* struct.

Common pitfalls

  • Wrong Encryptable type: Encryptable.uint32(...) must match what your Solidity function expects (e.g. InEuint32).
  • Wrong account / chain: encrypted inputs are authorized for a specific account + chainId. If you override these (or connect to the wrong network/wallet), your inputs may not be usable for the intended transaction.
  • Bit limit exceeded: a single call can encrypt at most 2048 bits of plaintext. Exceeding this throws ZkPackFailed.