Why eager works here#
The Pitfall #3 lazy-factory pattern exists because React + Vue + signals contexts can resolve the encryptor at different times than the SDK client. Node owns its own dependency graph: the private key loads, the RPC URL resolves, the encryptor stands up, the SDK consumes it, all in deterministic order at startup. No mismatch possible.
server/index.ts
ts
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";
import { createSepoliaEncryptor } from "@tokenops/sdk/fhe";
const account = privateKeyToAccount(process.env.SEPOLIA_PRIVATE_KEY as `0x${string}`);
const publicClient = createPublicClient({
chain: sepolia,
transport: http(process.env.SEPOLIA_RPC_URL),
});
const walletClient = createWalletClient({
account,
chain: sepolia,
transport: http(process.env.SEPOLIA_RPC_URL),
});
// Eager, the encryptor's lifetime matches the SDK client's. Lazy
// factories add no value server-side.
const encryptor = await createSepoliaEncryptor({
publicClient,
walletClient,
});
import { createConfidentialVestingManagerClient } from "@tokenops/sdk/fhe-vesting";
const manager = createConfidentialVestingManagerClient({
publicClient,
walletClient,
address: process.env.MANAGER_ADDRESS as `0x${string}`,
encryptor,
});
// Now any manager call is encryptor-aware:
await manager.createVesting({
params: {
recipient: "0x...",
startTimestamp: Math.floor(Date.now() / 1000),
endTimestamp: Math.floor(Date.now() / 1000) + 30 * 24 * 3600,
cliffSeconds: 0,
releaseIntervalSecs: 3600,
timelockSeconds: 0,
initialUnlockBps: 0,
cliffAmountBps: 0,
isRevocable: false,
},
amount: 50_000n,
});Use cases#
- Server-side admin signer for
useSignClaimAuthorization: sign the EIP-712 message off-chain, hand the signature to the frontend. - Indexer / worker: open vestings in bulk from a CSV upload, batch with
useBatchCreateVesting's headless equivalent on the manager client. - Test suite: hit a real Sepolia factory under vitest with
describeSepoliaFheVestingFullfrom the SDK's test helpers.