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

Writing Encrypted Data to Contract

This page covers the “encrypt → write tx” flow: encrypt plaintext values into ciphertext hashes and pass them, plus one shared signature, directly into a contract call.

encryptInputs returns a [...hashes, signature] tuple — one external* handle per input, followed by a single signature that authenticates the whole batch together. The on-chain CoFHE library validates that signature before the contract can use any of the ciphertexts.

The flow is as follows:

  1. Ensure your contract function accepts external* handles, each followed by a bytes parameter carrying the shared signature.
  2. Encrypt the plaintext values with encryptInputs.
  3. Send a transaction, passing the hashes and the signature as arguments.

Prerequisites

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

  2. Your contract function must accept external* handles (externalEbool, externalEuint32, etc.) plus a bytes parameter carrying the shared batch signature. Convention is to place the bytes immediately after the handle it authenticates — FHE.asEuintXX(hash, proof) is a pair. Other arguments may follow.

The encrypted type you choose in JavaScript/TypeScript must match the Solidity parameter type:

  • Encryptable.uint32(...)externalEuint32
  • Encryptable.bool(...)externalEbool
  • Encryptable.address(...)externalEaddress

Example: encrypt and call a contract

The reference contract used throughout these docs is EncryptedCounter.sol. It exposes setCount(externalEuint32[], bytes) which is the canonical “encrypt input → send in the same transaction” flow.

Solidity
// From EncryptedCounter.sol (see /reference/encrypted-counter-sol)
function setCount(externalEuint32[] memory _inCounts, bytes memory _signature) external onlyOwner {
  count = FHE.asEuint32s(_inCounts, _signature)[0];
  FHE.allowThis(count);
  FHE.allowSender(count);
  decrypted = false;
  decryptedCount = 0;
}

Automatic argument wiring with @cofhe/abi

For contracts with a mix of plain and encrypted arguments, @cofhe/abi's extractEncryptableValues/insertEncryptedValues will detect the external* parameters and inject the signature for you, so you can call encryptInputs and build the final call args from the ABI alone — see the @cofhe/react useCofheEncryptAndWriteContract hook for a ready-made version of this pattern.

Common pitfalls

  • Wrong Encryptable type: the Encryptable.* factory must match the Solidity parameter type (externalEuint32 vs externalEuint64, etc).
  • Missing or misplaced signature parameter: a contract function with external* inputs needs a plain bytes parameter immediately after them to receive the shared batch signature. Other arguments may follow it, but the encrypted parameters themselves must be adjacent.
  • Wrong account / chain: encrypted inputs are authorized for a specific account + chainId. If you encrypt under the wrong wallet/network, the contract call may revert.
  • Splitting a batch: don't reorder the hashes relative to how they were encrypted, and don't pass a subset of a batch's hashes with its signature — the signature covers the exact ordered set produced by that execute() call.