Recipe · Integration

Chain switching at runtime

The SDK is chain-agnostic, it consumes whatever viem clients wagmi has mounted. Switch chains with wagmi's primitives; the SDK rebinds automatically.

Switch via wagmi#

useSwitchChain is the wagmi primitive. It prompts the wallet UI; once resolved, every wagmi-aware hook (and every SDK hook layered on top) sees the new chain.

components/ChainSwitch.tsx
tsx
import { useSwitchChain, useChainId } from "wagmi";
import { sepolia, mainnet } from "viem/chains";

export function ChainSwitch() {
  const chainId = useChainId();
  const { switchChain, isPending } = useSwitchChain();

  return (
    <select
      value={chainId}
      onChange={(e) => switchChain({ chainId: Number(e.target.value) })}
      disabled={isPending}
    >
      <option value={sepolia.id}>Sepolia</option>
      <option value={mainnet.id}>Mainnet</option>
    </select>
  );
}

Guard your writes#

Writes that target a specific deployment (a manager address valid only on Sepolia, etc.) should explicitly gate on useChainId. Submitting a Sepolia-only contract call while the wallet is on mainnet either reverts or succeeds against a stale address, neither is a good UX.

components/CreateButton.tsx
tsx
import { useChainId } from "wagmi";
import { sepolia } from "viem/chains";
import { useCreateVesting } from "@tokenops/sdk/fhe-vesting/react";

export function CreateButton() {
  const chainId = useChainId();
  const create = useCreateVesting({ address: managerAddress });

  if (chainId !== sepolia.id) {
    return <span>Switch to Sepolia first.</span>;
  }
  // Safe to proceed, useCreateVesting hits the chain wagmi is on.
  return <button onClick={() => create.mutate(args)}>Create</button>;
}

Anvil locally#

Anvil (chain id 31337) isn't in DEPLOYED_ADDRESSES, it comes from .cache/local-deploys.json after pnpm fhevm:deploy. The docs-site reads that via /api/local-deploys; a consumer repo can replicate the pattern or hardcode addresses for tests.

See also