> For the complete documentation index, see [llms.txt](https://docs.zama.org/protocol/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zama.org/protocol/sdk/changelog/v3/v3-1.md).

# 3.1.x

This page covers the `3.1.x` line.

## 3.1.0

*Released 2026-06-22.*

This is the release where the `v3` public API reached its current shape. Configuration is now a single `createConfig` call, relayers are wired in through transport factories, read access and wallet authority are separated into a provider and an optional signer, and the token surface splits into `Token` (ERC-7984) and `WrappedToken` (shield/unshield). It also introduces top-level FHE primitives for apps whose contracts use encrypted types directly.

{% hint style="danger" %}
**Breaking change: `buildRelayer` is removed from the public API.**

Relayers are no longer constructed directly. Build them with the transport factories (`web()`, `node()`, `cleartext()`) and pass them to `createConfig` in a `relayers` map keyed by chain ID. See [Configuration](/protocol/sdk/guides/configuration.md).

`createZamaConfig` was also renamed to `createConfig` — the package path already namespaces it. Config props are now flat (no `viem: {}` / `ethers: {}` wrapper objects).
{% endhint %}

### `createConfig`: one call to wire everything

`createConfig` takes your chains, relayers, provider, and optional signer and returns a single config object you hand to `ZamaSDK`. Import it from the entry point that matches your Web3 library:

```ts
import { createConfig } from "@zama-fhe/sdk/viem"; // or @zama-fhe/sdk/ethers
import { ZamaSDK } from "@zama-fhe/sdk";
import { web } from "@zama-fhe/sdk/web";
import { sepolia } from "@zama-fhe/sdk/chains";

const config = createConfig({
  chains: [sepolia],
  publicClient, // viem PublicClient — read access
  walletClient, // viem WalletClient — signing (optional, see below)
  relayers: { [sepolia.id]: web() },
});

const sdk = new ZamaSDK(config);
```

React apps build the config with `createConfig` from `@zama-fhe/react-sdk/wagmi` and pass it to `<ZamaProvider config={config}>`.

#### Migrate from `createZamaConfig` / `buildRelayer`

{% tabs %}
{% tab title="Before (3.0.x)" %}

```ts
import { createZamaConfig, buildRelayer } from "@zama-fhe/sdk";

const relayer = buildRelayer(/* ... */);
const config = createZamaConfig({
  viem: { publicClient, walletClient },
  // ...
});
```

{% endtab %}

{% tab title="After (3.1.0)" %}

```ts
import { createConfig } from "@zama-fhe/sdk/viem";
import { web } from "@zama-fhe/sdk/web";
import { sepolia } from "@zama-fhe/sdk/chains";

const config = createConfig({
  chains: [sepolia],
  publicClient,
  walletClient,
  relayers: { [sepolia.id]: web() },
});
```

{% endtab %}
{% endtabs %}

### Transport factories per chain

Relayers tell the SDK how to run FHE operations on each chain. Pick a transport per environment and map it to the chains it serves. Chain-specific data (relayer URL, network, contract addresses) comes from the chain preset, so a bare call is all most apps need.

| Transport     | Environment | Import               |
| ------------- | ----------- | -------------------- |
| `web()`       | Browser     | `@zama-fhe/sdk/web`  |
| `node()`      | Node.js     | `@zama-fhe/sdk/node` |
| `cleartext()` | Local dev   | `@zama-fhe/sdk`      |

```ts
import { web } from "@zama-fhe/sdk/web";
import { node } from "@zama-fhe/sdk/node";
import { cleartext } from "@zama-fhe/sdk";

// Multi-chain: map each chain ID to its transport.
relayers: {
  [sepolia.id]: web(),
  [hoodi.id]: web(),
}
```

Each transport factory also takes an optional `options` argument for per-chain tuning — in `3.1.0` this parameter was renamed from `relayer` to `options`. Its fields depend on the FHE backend; the internal backend migration and its current tuning options are documented under [FHE runtime and client tuning](/protocol/sdk/changelog/v3/v3-4.md#fhe-runtime-and-client-tuning).

See [Configuration](/protocol/sdk/guides/configuration.md#2-pick-a-relayer) for the full list.

### Provider / signer split and optional signer

The SDK now separates **read access** (the provider) from **wallet authority** (the signer). The provider handles contract reads and receipt polling; the signer handles signing and write transactions. Both are built for you by `createConfig` from your library's native objects.

The signer is **optional**. Omit it to build a read-only SDK — perfect for dashboards, indexers, or server-side balance displays that never write. Calling a method that needs to sign without a signer throws `SignerRequiredError`, so misuse fails loudly:

```ts
// Read-only SDK — no walletClient/signer supplied.
const config = createConfig({ chains: [sepolia], publicClient, relayers: { [sepolia.id]: web() } });
const sdk = new ZamaSDK(config);

// Reads work; writes throw SignerRequiredError.
```

The concrete adapters — `ViemProvider` / `ViemSigner` and `EthersProvider` / `EthersSigner` — are documented in the [SDK reference](/protocol/sdk/api-references/sdk.md). For custom integrations, implement [`GenericProvider`](/protocol/sdk/api-references/sdk/genericprovider.md) and [`GenericSigner`](/protocol/sdk/api-references/sdk/genericsigner.md).

### `Token` and `WrappedToken`

The token surface splits into two classes so each API only exposes what applies:

* **`Token`** — an ERC-7984 confidential token: `balanceOf`, `confidentialTransfer`, `setOperator`, and so on.
* **`WrappedToken`** — a `Token` that also wraps a public ERC-20, adding `shield`, `unshield`, and `unshieldAll`.

```ts
// A wrapper is itself the confidential token — pass the wrapper's own address.
const wrappedToken = sdk.createWrappedToken("0xWrapperAddress");

await wrappedToken.shield(1000n); // public ERC-20 → confidential
await wrappedToken.confidentialTransfer("0xRecipient", 500n);
await wrappedToken.unshield(500n); // confidential → public ERC-20

// For a plain ERC-7984 token that isn't a wrapper:
const token = sdk.createToken("0xConfidentialToken");
```

#### Explicit owner on token reads

Token reads now require an **explicit owner address** rather than implicitly using the connected account. This keeps reads deterministic and makes read-only (signer-less) usage possible:

```ts
const balance = await wrappedToken.balanceOf("0xOwnerAddress");
```

### FHE encryption and decryption primitives

For contracts that use FHE types directly — a sealed-bid auction, a confidential vote, any non-token contract storing `euint` values — the SDK exposes the underlying operations. Encryption is a top-level method; decryption is grouped under the `sdk.decryption` namespace:

```ts
// Encrypt values for a specific contract + user, ready to submit on-chain.
const { encryptedValues, inputProof } = await sdk.encrypt({
  values: [{ value: 42n, type: "euint64" }],
  contractAddress: "0xYourContract",
  userAddress: "0xYourAddress",
});

// Decrypt a publicly-decryptable value (no signature required).
const clear = await sdk.decryption.decryptPublicValues([encryptedValue]);

// Decrypt values you're entitled to under the ACL (prompts for a permit signature).
const values = await sdk.decryption.decryptValues([
  { encryptedValue, contractAddress: "0xYourContract" },
]);
```

In React, use [`useEncrypt`](/protocol/sdk/api-references/react/useencrypt.md) and [`useDecryptValues`](/protocol/sdk/api-references/react/usedecryptvalues.md) — see the [Encrypt & decrypt](/protocol/sdk/guides/encrypt-decrypt.md) guide.

### SDK-level delegation primitives

Delegated decryption — letting another account decrypt your values under the ACL — is now a first-class SDK primitive. `sdk.decryption.delegatedDecryptValues()` decrypts on behalf of a delegator, and the on-chain delegation lifecycle is managed through `sdk.delegations` and the React delegation hooks. See [Delegated decryption](/protocol/sdk/guides/delegated-decryption.md).

### Automatic ERC-1363 shield routing

`WrappedToken.shield()` now detects whether the underlying ERC-20 implements [ERC-1363](https://eips.ethereum.org/EIPS/eip-1363) (via ERC-165) and routes accordingly — `transferAndCall` for a single-transaction shield when supported, or `approve` + `wrap` otherwise. This is fully transparent: **your code doesn't change and you never pick a path**.

```ts
// Identical call regardless of the underlying token — the SDK picks the optimal path.
await wrappedToken.shield(1000n);
```

See the routing table in [Shield tokens](/protocol/sdk/guides/shield-tokens.md#shielding-paths).

### Glossary alignment (naming)

Several public field and method names were aligned with the [FHEVM glossary](https://docs.zama.org/protocol) for consistency — decrypt wording, `key`/`keypair` terminology, and the remaining `*Handle` fields. The credentials model was also replaced with a keypair vault plus a permission store internally. If you referenced these names directly, update them to the current terms; the [3.2.0 codemods](/protocol/sdk/changelog/v3/v3-2.md#upgrade-codemods) automate most of the mechanical renames.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.zama.org/protocol/sdk/changelog/v3/v3-1.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
