# Configuration

Every SDK instance needs a relayer (handles FHE encryption and decryption), a signer (wallet interface), and a storage backend (persists the FHE keypair). This guide walks through each piece and assembles a working `ZamaSDK`.

## Steps

### 1. Choose your environment

The SDK ships two relayer implementations. Pick the one that matches your runtime:

| Environment                 | Relayer       | Import path                              |
| --------------------------- | ------------- | ---------------------------------------- |
| Browser (React, vanilla TS) | `RelayerWeb`  | `@zama-fhe/sdk` or `@zama-fhe/react-sdk` |
| Node.js (scripts, servers)  | `RelayerNode` | `@zama-fhe/sdk/node`                     |

`RelayerWeb` runs FHE inside a Web Worker using WASM. `RelayerNode` uses native worker threads.

{% hint style="info" %}
For local Hardhat nodes or custom testnets deployed in cleartext mode, use [`createCleartextRelayer`](https://github.com/zama-ai/sdk/blob/main/guides/local-development/README.md) instead — no KMS, no gateway, no WASM needed.
{% endhint %}

### 2. Set up a signer

The signer lets the SDK interact with the user's wallet. Choose the adapter for your Web3 library.

{% tabs %}
{% tab title="viem" %}

```ts
import { createPublicClient, createWalletClient, custom, http } from "viem";
import { sepolia } from "viem/chains";
import { ViemSigner } from "@zama-fhe/sdk/viem";

const publicClient = createPublicClient({
  chain: sepolia,
  transport: http("https://sepolia.infura.io/v3/YOUR_KEY"),
});
const walletClient = createWalletClient({
  chain: sepolia,
  transport: custom(window.ethereum!),
});

const signer = new ViemSigner({ walletClient, publicClient });
```

{% endtab %}

{% tab title="ethers" %}

```ts
import { EthersSigner } from "@zama-fhe/sdk/ethers";

// Browser — pass the raw EIP-1193 provider
const signer = new EthersSigner({ ethereum: window.ethereum! });

// Node.js — pass an ethers Signer directly
// const provider = new ethers.JsonRpcProvider(rpcUrl);
// const signer = new EthersSigner({ signer: new ethers.Wallet(privateKey, provider) });
```

{% endtab %}

{% tab title="wagmi (React)" %}

```ts
import { WagmiSigner } from "@zama-fhe/react-sdk/wagmi";

const signer = new WagmiSigner({ config: wagmiConfig });
```

{% endtab %}
{% endtabs %}

For full type information, see the [ViemSigner](https://github.com/zama-ai/sdk/blob/main/reference/sdk/ViemSigner/README.md), [EthersSigner](https://github.com/zama-ai/sdk/blob/main/reference/sdk/EthersSigner/README.md), and [WagmiSigner](https://github.com/zama-ai/sdk/blob/main/reference/sdk/WagmiSigner/README.md) reference pages. You can also implement the [GenericSigner](https://github.com/zama-ai/sdk/blob/main/reference/sdk/GenericSigner/README.md) interface for a custom wallet integration.

### 3. Configure the relayer

The relayer needs a `getChainId` callback and a transport map. Use the built-in [network presets](https://github.com/zama-ai/sdk/blob/main/reference/sdk/network-presets/README.md) (`SepoliaConfig`, `MainnetConfig`, `HardhatConfig`) so you don't have to specify contract addresses manually. Each preset provides `chainId`, `relayerUrl`, `gatewayAddress`, `aclAddress`, and `kmsVerifierAddress` for its network.

{% tabs %}
{% tab title="Browser (RelayerWeb)" %}

```ts
import { RelayerWeb, MainnetConfig, SepoliaConfig } from "@zama-fhe/sdk";

const relayer = new RelayerWeb({
  getChainId: () => signer.getChainId(),
  transports: {
    [MainnetConfig.chainId]: {
      ...MainnetConfig,
      relayerUrl: "https://your-app.com/api/relayer/1",
      network: "https://mainnet.infura.io/v3/YOUR_KEY",
    },
    [SepoliaConfig.chainId]: {
      ...SepoliaConfig,
      relayerUrl: "https://your-app.com/api/relayer/11155111",
      network: "https://sepolia.infura.io/v3/YOUR_KEY",
    },
  },
});
```

{% endtab %}

{% tab title="Node.js (RelayerNode)" %}

```ts
import { SepoliaConfig } from "@zama-fhe/sdk";
import { RelayerNode } from "@zama-fhe/sdk/node";

const relayer = new RelayerNode({
  getChainId: () => signer.getChainId(),
  poolSize: 4, // worker threads (defaults to min(CPU cores, 4))
  transports: {
    [SepoliaConfig.chainId]: {
      ...SepoliaConfig,
      network: "https://sepolia.infura.io/v3/YOUR_KEY",
      auth: { __type: "ApiKeyHeader", value: process.env.RELAYER_API_KEY },
    },
  },
});
```

{% endtab %}
{% endtabs %}

Browser apps should proxy relayer requests through a backend to keep the API key secret. See the [Authentication guide](https://github.com/zama-ai/sdk/blob/main/guides/authentication/README.md) for the full setup.

For details on multi-threaded FHE and security options, see the [RelayerWeb](https://github.com/zama-ai/sdk/blob/main/reference/sdk/RelayerWeb/README.md) and [RelayerNode](https://github.com/zama-ai/sdk/blob/main/reference/sdk/RelayerNode/README.md) reference pages.

### 4. Choose a storage backend

The FHE keypair is cached so users don't get a wallet popup on every decrypt. Pick the storage that fits your environment:

| Storage             | When to use                                         |
| ------------------- | --------------------------------------------------- |
| `indexedDBStorage`  | Browser apps — persists across reloads and sessions |
| `memoryStorage`     | Tests, scripts, throwaway sessions                  |
| `asyncLocalStorage` | Node.js servers — isolates FHE keypair per request  |

```ts
import { indexedDBStorage, memoryStorage } from "@zama-fhe/sdk";
// Node.js per-request isolation:
// import { asyncLocalStorage } from "@zama-fhe/sdk/node";
```

For full storage options see the [GenericStorage](https://github.com/zama-ai/sdk/blob/main/reference/sdk/GenericStorage/README.md) reference.

### 5. Create the SDK instance

With the three pieces ready, assemble the SDK:

{% tabs %}
{% tab title="Browser (viem)" %}

```ts
import { ZamaSDK, RelayerWeb, indexedDBStorage, MainnetConfig, SepoliaConfig } from "@zama-fhe/sdk";
import { ViemSigner } from "@zama-fhe/sdk/viem";

const signer = new ViemSigner({ walletClient, publicClient });

const sdk = new ZamaSDK({
  relayer: new RelayerWeb({
    getChainId: () => signer.getChainId(),
    transports: {
      [MainnetConfig.chainId]: {
        ...MainnetConfig,
        relayerUrl: "https://your-app.com/api/relayer/1",
        network: "https://mainnet.infura.io/v3/YOUR_KEY",
      },
      [SepoliaConfig.chainId]: {
        ...SepoliaConfig,
        relayerUrl: "https://your-app.com/api/relayer/11155111",
        network: "https://sepolia.infura.io/v3/YOUR_KEY",
      },
    },
  }),
  signer,
  storage: indexedDBStorage,
});
```

{% endtab %}

{% tab title="Node.js" %}

```ts
import { ZamaSDK, memoryStorage, SepoliaConfig } from "@zama-fhe/sdk";
import { RelayerNode } from "@zama-fhe/sdk/node";
import { ViemSigner } from "@zama-fhe/sdk/viem";

const signer = new ViemSigner({ walletClient, publicClient });

const sdk = new ZamaSDK({
  relayer: new RelayerNode({
    getChainId: () => signer.getChainId(),
    transports: {
      [SepoliaConfig.chainId]: {
        ...SepoliaConfig,
        network: "https://sepolia.infura.io/v3/YOUR_KEY",
        auth: { __type: "ApiKeyHeader", value: process.env.RELAYER_API_KEY },
      },
    },
  }),
  signer,
  storage: memoryStorage,
});
```

{% endtab %}

{% tab title="Web Extensions" %}
MV3 Chrome extensions need a second storage backend for session signatures, because the service worker can be terminated at any time and in-memory state is lost. Use `chromeSessionStorage` alongside `indexedDBStorage`:

```ts
import {
  ZamaSDK,
  RelayerWeb,
  indexedDBStorage,
  chromeSessionStorage,
  SepoliaConfig,
} from "@zama-fhe/sdk";
import { ViemSigner } from "@zama-fhe/sdk/viem";

const signer = new ViemSigner({ walletClient, publicClient });

const sdk = new ZamaSDK({
  relayer: new RelayerWeb({
    getChainId: () => signer.getChainId(),
    transports: {
      [SepoliaConfig.chainId]: {
        ...SepoliaConfig,
        relayerUrl: "https://your-app.com/api/relayer/11155111",
        network: "https://sepolia.infura.io/v3/YOUR_KEY",
      },
    },
  }),
  signer,
  storage: indexedDBStorage, // encrypted keypair — persistent
  sessionStorage: chromeSessionStorage, // wallet signature — survives SW restarts
});
```

Your `manifest.json` must include the `"storage"` permission. See the [Web Extensions guide](https://github.com/zama-ai/sdk/blob/main/guides/web-extensions/README.md) for manifest configuration, multi-context sharing, and browser close behavior.
{% endtab %}
{% endtabs %}

### 6. (Optional) Configure TTLs and event listener

You can tune how long the FHE keypair and session signatures remain valid, and subscribe to lifecycle events for debugging:

```ts
const sdk = new ZamaSDK({
  relayer,
  signer,
  storage,
  keypairTTL: 604800, // 7 days (default: 2592000 = 30 days)
  sessionTTL: 3600, // 1 hour (default: 2592000 = 30 days)
  onEvent: ({ type, tokenAddress, ...rest }) => {
    console.debug(`[zama] ${type}`, rest);
  },
});
```

Setting `sessionTTL: 0` disables session caching entirely — every operation triggers a wallet prompt. When done with the SDK, call `sdk.terminate()` to clean up the Web Worker or thread pool.

## Next steps

* [Authentication](https://github.com/zama-ai/sdk/blob/main/guides/authentication/README.md) — set up a backend proxy or use a direct API key
* [Shield Tokens](https://github.com/zama-ai/sdk/blob/main/guides/shield-tokens/README.md) — convert public ERC-20 tokens into confidential form
* [RelayerWeb reference](https://github.com/zama-ai/sdk/blob/main/reference/sdk/RelayerWeb/README.md) — multi-threading, security options, CDN configuration
* [GenericStorage reference](https://github.com/zama-ai/sdk/blob/main/reference/sdk/GenericStorage/README.md) — custom storage implementations


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.zama.org/protocol/sdk/guides/configuration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
