Every SDK call that submits an encrypted input takes an encryptor option. The option accepts either:
- An eager instance:
Encryptor - A lazy factory:
() => Encryptor | undefined
React: always use a lazy factory#
React renders the SDK client at mount, but the wallet context that drives the encryptor only fully resolves later. Eager passes capture a stale or undefined encryptor.
import { useZamaSDK } from "@zama-fhe/react-sdk";
import { useCreateVesting } from "@tokenops/sdk/fhe-vesting/react";
export function CreateButton({ managerAddress }) {
const zamaSDK = useZamaSDK();
const create = useCreateVesting({
address: managerAddress,
// Lazy factory — the SDK calls this AT submit time, not at mount time.
encryptor: () => zamaSDK.relayer,
});
return <button onClick={() => create.mutate(args)}>Create</button>;
}The arrow function defers resolution. When the user clicks the button, the SDK calls encryptor(), which now returns the live relayer from zamaSDK.relayer. Same pattern works for Vue (read a ref) and signals (read a subscription).
Node + tests: eager is correct#
Server-side code controls its own dependency graph. The encryptor is created at startup; the SDK client is created right after; both live the lifetime of the process. Lazy resolution has no value here.
import { createSepoliaEncryptor } from "@tokenops/sdk/fhe";
import { createConfidentialVestingManagerClient } from "@tokenops/sdk/fhe-vesting";
// Server-side: the encryptor's lifetime matches the SDK client's. Eager is fine.
const encryptor = await createSepoliaEncryptor({ privateKey, rpcUrl });
const manager = createConfidentialVestingManagerClient({
publicClient,
walletClient,
address: managerAddress,
encryptor,
});Available encryptor flavours#
Three drop-in adapters ship in @tokenops/sdk/fhe:
createSepoliaEncryptorWeb — the browser flavour. Wraps Zama's RelayerWeb. Takes the user's wallet client to sign the per-encrypt authorization. The story flows on this site use it.
createSepoliaEncryptor — Node flavour. Takes a private key + RPC URL. Same interface, no wallet client.
createMockEncryptor — the test flavour. Bound to a deterministic local FHEVM (Anvil + forge-fhevm). Use under describeMockFheVesting and friends — never in production code.