Concept · Chains

Where the contracts live

The SDK ships a baked-in DEPLOYED_ADDRESSES export per (product, contract, chainId). No factory-deployment helpers; only DEPLOYED_ADDRESSES is valid in production code.

The shape#

DEPLOYED_ADDRESSES is a nested object indexed by product → contract → chainId. Sepolia (11155111) and mainnet (1) are the two chains with entries. Anvil (31337) is intentionally absent — local chains get their addresses from .cache/local-deploys.json written by pnpm fhevm:deploy in the SDK monorepo.

lib/addresses.ts
ts
import {
  DEPLOYED_ADDRESSES,
  getFheVestingFactoryAddress,
} from "@tokenops/sdk";
import { sepolia } from "viem/chains";

const addr = getFheVestingFactoryAddress(sepolia.id);
// → "0xA87701CE9A52D43681600583a99c85b50DbE3150"

// Or read the raw map:
const raw = DEPLOYED_ADDRESSES.fheVesting.confidentialVestingFactory[sepolia.id];

The matrix#

fhe-vesting: Sepolia live. Mainnet pending audit close + Zama KMS readiness.

fhe-airdrop: Sepolia live. Mainnet pending audit close + Zama KMS readiness.

fhe-disperse: Sepolia and Ethereum mainnet both live. Lower audit surface; the disperse singleton went mainnet first.

Always check Resources for the canonical list — it reads from this same export at build time, so the page is never out of date with the SDK.

Accessors vs raw map#

The typed accessors (getFheVestingFactoryAddress,getFheAirdropFactoryAddress,getFheDisperseSingletonAddress) collapse the "explicitly null" and "missing key" cases to undefined. Use them in product code; reach into the raw map only if you need to distinguish "known chain, not deployed yet" from "unknown chain."

Custom RPCs#

The SDK takes a viem PublicClient and WalletClient as input. You configure RPC at the viem layer; the SDK is RPC-agnostic. Public RPCs work; Alchemy / Infura / your own works the same.

See also