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) is the common chain across all products. Mainnet (1) has an entry only for fhe-disperse. The testnet faucet also supports local Anvil (31337), but canonical registry addresses exist only for Sepolia; local faucet clients pass an explicit CTTT address override and discover TTT through the wrapper. Other local chains get their addresses from .cache/local-deploys.json written by pnpm fhevm:up 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, audited by OpenZeppelin. Mainnet pending deployment + Zama KMS readiness.

fhe-airdrop: Sepolia live, audited by OpenZeppelin. Mainnet pending deployment + Zama KMS readiness.

fhe-disperse: Sepolia and Ethereum mainnet both live.

testnet-faucet: Sepolia canonical TTT/CTTT live. Local Anvil (31337) is supported for fixtures and forks with an explicit CTTT address override; no mainnet deployment.

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