Skip to main content
@cofhe/sdk is the successor to cofhejs, redesigned around an explicit, builder-pattern API that gives you full control over encryption, decryption, and permit management.

Why migrate?

  • Explicit API — no more implicit initialization or auto-generated permits. Every action is opt-in.
  • Builder patternencryptInputs, decryptForView, and decryptForTx use a chainable builder so you can set overrides (account, chain, callbacks) before calling .execute().
  • decryptForTx featurecofhejs does not provide an API for generating decryption signatures for on-chain usage.
  • Deferred key loading — FHE keys and TFHE WASM are fetched lazily on the first encryptInputs call, not during initialization.
  • Better multichain support — configure multiple chains up front and override per-call.
  • Structured errors — typed CofheError objects with error codes replace the Result wrapper.

Requirements

  • Node.js 18+
  • TypeScript 5+
  • Viem 2+

Installation

Remove cofhejs and install @cofhe/sdk:
npm uninstall cofhejs && npm install @cofhe/sdk

1. Initialization

The single cofhejs.initializeWithEthers(...) / cofhejs.initializeWithViem(...) call is replaced by a three-step flow: create a config, create a client, then connect. FHE keys and WASM are no longer fetched eagerly during init — they are deferred until the first encryptInputs call.
cofhejs@cofhe/sdk
Entrycofhejs.initializeWithEthers(...) / cofhejs.initializeWithViem(...)createCofheConfig(...)createCofheClient(config)client.connect(...)
Key fetchingImmediate (during init)Deferred (first encryptInputs call)
WASM initImmediate (during init)Deferred (first encryptInputs call)
Environment"LOCAL" / "TESTNET" / "MAINNET" stringChain objects via supportedChains: [chains.sepolia]
Provider formatEthers provider/signer or viem clientsAlways viem clients (use adapters for ethers)

Before (cofhejs)

import { cofhejs } from 'cofhejs/node';

await cofhejs.initializeWithEthers({
  ethersProvider: provider,
  ethersSigner: signer,
  environment: 'TESTNET',
});

After (@cofhe/sdk)

import { createCofheConfig, createCofheClient } from '@cofhe/sdk/web';
import { chains } from '@cofhe/sdk/chains';

const config = createCofheConfig({
  supportedChains: [chains.sepolia],
});
const client = createCofheClient(config);

await client.connect(publicClient, walletClient);

2. Encrypting inputs

cofhejs.encrypt(...) is replaced by a builder: client.encryptInputs([...]).execute().
cofhejs@cofhe/sdk
Functioncofhejs.encrypt([...], callback)client.encryptInputs([...]).execute()
Return valueResult<T> with .success / .data / .errorDirect value (throws CofheError on failure)
Progress callbackSecond argument to encrypt.onStep(callback) on the builder
OverridesNot available.setAccount(...), .setChainId(...), .setUseWorker(...)

Before (cofhejs)

import { cofhejs, Encryptable } from 'cofhejs/node';

const result = await cofhejs.encrypt(
  [Encryptable.uint64(42n), Encryptable.bool(true)],
  (state) => console.log(state)
);

if (!result.success) {
  console.error(result.error);
  return;
}

const [eAmount, eFlag] = result.data;

After (@cofhe/sdk)

import { Encryptable, EncryptStep } from '@cofhe/sdk';

const [eAmount, eFlag] = await client
  .encryptInputs([Encryptable.uint64(42n), Encryptable.bool(true)])
  .onStep((step, ctx) => {
    if (ctx?.isStart) console.log(`Starting: ${step}`);
  })
  .execute();
The Encryptable factory functions (Encryptable.uint32(...), Encryptable.bool(...), etc.) work the same way in both libraries.

3. Decrypting / Unsealing

cofhejs has a single unseal function. @cofhe/sdk splits decryption into two purpose-built methods:
  • decryptForView — returns the plaintext for UI display (no on-chain signature).
  • decryptForTx — returns the plaintext and a Threshold Network signature for on-chain verification.
cofhejs@cofhe/sdk
Functioncofhejs.unseal(sealed, type)client.decryptForView(ctHash, type) or client.decryptForTx(ctHash)
Permit handlingAutomatic (uses most recent permit)Explicit — .withPermit() / .withoutPermit()
Return valueResult<bigint | boolean | string>Direct value for view; { ctHash, decryptedValue, signature } for tx
On-chain verificationNot built indecryptForTx returns a signature for FHE.publishDecryptResult(...)

Before (cofhejs)

import { cofhejs, FheTypes } from 'cofhejs/node';

const sealedBalance = await contract.getBalance();
const result = await cofhejs.unseal(sealedBalance, FheTypes.Uint64);

if (!result.success) {
  console.error(result.error);
  return;
}

console.log(result.data); // bigint

After (@cofhe/sdk) — viewing in UI

import { FheTypes } from '@cofhe/sdk';

const ctHash = await contract.getBalance();

const balance = await client
  .decryptForView(ctHash, FheTypes.Uint64)
  .execute();

After (@cofhe/sdk) — publishing on-chain

const ctHash = await myContract.getEncryptedAmount();

const { decryptedValue, signature } = await client
  .decryptForTx(ctHash)
  .withoutPermit()
  .execute();

await myContract.publishDecryptResult(ctHash, decryptedValue, signature);

4. Permits

Permits are no longer auto-generated during initialization. All permit operations are now explicit through client.permits.
cofhejs@cofhe/sdk
Auto-generationgeneratePermit: true (default)Never — always explicit
Creationcofhejs.createPermit({ type, issuer })client.permits.createSelf(...), client.permits.createSharing(...)
Return typeResult<Permit>Direct Permit object
Active permitImplicitly used by unsealgetOrCreateSelfPermit() sets active; used automatically by decrypt methods

Before (cofhejs)

await cofhejs.initializeWithEthers({
  ethersProvider: provider,
  ethersSigner: signer,
  environment: 'TESTNET',
  // generatePermit: true  ← default
});

const result = await cofhejs.createPermit({
  type: 'self',
  issuer: wallet.address,
});

After (@cofhe/sdk)

// Create a self permit (prompts for wallet signature)
const permit = await client.permits.createSelf({
  issuer: account,
  name: 'My dApp permit',
});

// Or use the convenience method that creates one only if needed
const permit2 = await client.permits.getOrCreateSelfPermit();

// Use with decryptForView (active permit is used automatically)
const value = await client
  .decryptForView(ctHash, FheTypes.Uint32)
  .execute();

5. Error handling

Before (cofhejs)

const result = await cofhejs.encrypt([Encryptable.uint32(42n)]);

if (!result.success) {
  console.error('Failed:', result.error); // string
  return;
}

const encrypted = result.data;

After (@cofhe/sdk)

import { isCofheError, CofheErrorCode } from '@cofhe/sdk';

try {
  const encrypted = await client
    .encryptInputs([Encryptable.uint32(42n)])
    .execute();
} catch (err) {
  if (isCofheError(err)) {
    console.error(err.code);    // CofheErrorCode enum
    console.error(err.message); // human-readable message
  }
}

6. Import path changes

cofhejs@cofhe/sdk
cofhejs/node@cofhe/sdk/node
cofhejs/web@cofhe/sdk/web
N/A@cofhe/sdk (core types, Encryptable, FheTypes)
N/A@cofhe/sdk/permits
N/A@cofhe/sdk/adapters
N/A@cofhe/sdk/chains

7. Type renames

cofhejs@cofhe/sdk
CoFheInItemEncryptedItemInput
CoFheInBoolEncryptedBoolInput
CoFheInUint8EncryptedUint8Input
CoFheInUint16EncryptedUint16Input
CoFheInUint32EncryptedUint32Input
CoFheInUint64EncryptedUint64Input
CoFheInUint128EncryptedUint128Input
CoFheInAddressEncryptedAddressInput