> 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-5.md).

# 3.5.x

Polygon presets, offline signing and decryption permits, at-rest transport-key encryption, and automatic KMS-context recovery.

This page covers the `3.5.x` line.

## 3.5.1

*Released 2026-08-27.*

### Reverted the unintended `@fhevm/sdk` upgrade

3.5.1 pins `@fhevm/sdk` back to the stable `0.13.2`, reverting the accidental `0.14.1-0` bump that shipped in 3.5.0. No SDK API changes — upgrade from 3.5.0 to restore the intended dependency.

## 3.5.0

*Released 2026-08-26.*

{% hint style="warning" %}
**Superseded by 3.5.1.** 3.5.0 unintentionally shipped a pre-release `@fhevm/sdk` (`0.14.1-0`) instead of the stable `0.13.2`. Upgrade to **3.5.1**, which reverts that dependency; the features below are otherwise unchanged.
{% endhint %}

### Polygon support

The SDK now ships built-in presets for **Polygon**: `polygon` (mainnet, chain ID 137) and `polygonAmoy` (Amoy testnet, chain ID 80002). Import either from `@zama-fhe/sdk/chains` and pass it to `createConfig` like any other chain — no manual contract-address wiring.

```ts
import { polygon, polygonAmoy } from "@zama-fhe/sdk/chains";

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

The shared Zama **testnet** relayer needs no API key, so `polygonAmoy` works as-is; the **mainnet** relayer used by `polygon` requires one — see [Authentication](/protocol/sdk/guides/authentication.md). A full `example-polygon-amoy` reference app is included in the examples. See [Configuration](/protocol/sdk/guides/configuration.md#1-pick-your-chains) for the complete preset list.

### Offline signing

A new `sdk.offline.prepare` builds an unsigned transaction that the caller signs and broadcasts out-of-process: institutional custody, HSM ceremonies, policy engines with human approval. The signer is now optional in the SDK config, so the preparing process never holds the wallet private key. Atomic call sites are unchanged; `Token.confidentialTransfer` and friends keep their online path.

```ts
const prepared = await sdk.offline.prepare({
  kind: "ConfidentialTransfer",
  from: custodyAddress,
  token,
  to: recipient,
  amount: 1_000n,
});
// { kind, from, unsignedTx } - a JSON-safe handoff to your custody platform
```

All eleven write operations are covered, including the shield legs and the two-phase unshield. See [Offline signing](/protocol/sdk/guides/offline.md) for the full guide: the handoff contract, request kinds, multi-transaction batches, and approval-delay behavior.

#### Decryption permits

The same offline model now covers decryption permits: `sdk.offline.preparePermit` builds the unsigned EIP-712 typed data for a permit — no signer required — and `sdk.permits.registerPermit` verifies and persists the signature a custody partner returns for it.

```ts
const prepared = await sdk.offline.preparePermit({ signer: custodyAddress, contracts: [cUSDT] });
// hand prepared.eip712 to your custody platform for eth_signTypedData_v4

await sdk.permits.registerPermit(prepared, signature);
```

One permit per call — unlike `grantPermit`, `preparePermit` never widens an existing permit or chunks over 10 contracts. See [Offline signing](/protocol/sdk/guides/offline.md#offline-permits) for the full workflow and [Offline reference](/protocol/sdk/api-references/sdk/offline.md#preparepermit) for the method signatures and typed errors.

### Transport key pair encrypted at rest

A new `transportKeyPairDerivationSecret` option on the `ZamaSDK` constructor encrypts the transport private key before it is written to storage. (This is key encryption, unrelated to token wrapping via `shield`/`unshield`.) It is for headless environments with no secure storage to delegate to: CLI tools, agents, bare-metal boxes. Omit it and nothing changes: storage stays plaintext, as before.

```ts
const sdk = new ZamaSDK(config, {
  transportKeyPairDerivationSecret: derivationSecret, // 32+ random bytes
});
```

Failures surface as a new `KeyWrappingError` (`KEY_WRAPPING_FAILED`); `hasPermit` and `hasDelegationPermit` return `false` instead of throwing it. See [Configuration](/protocol/sdk/guides/configuration.md#10-optional-wrap-the-transport-key-pair-at-rest-headless-environments) for which environments should use this and the setup, and [Security Model](/protocol/sdk/concepts/security-model.md#wrapped-at-rest-transportkeypairderivationsecret) for the mechanism, entropy requirement, and rotation.

### Automatic recovery from KMS context rotation

When a permit's KMS context is revoked on-chain, the SDK now re-grants the permit (one wallet prompt) and retries the decrypt instead of failing. No API change; existing decrypt and balance calls pick this up automatically. A new `RevokedKmsContextError` (`REVOKED_KMS_CONTEXT`) surfaces when the retry also fails, or when the re-grant itself fails (a signing failure attached as `cause`); on a failed re-grant the other permits of the scope are kept. See the [error reference](/protocol/sdk/api-references/sdk/errors.md#revokedkmscontexterror) for the recovery mechanics.

### Bug fixes

* **Operator transfers bind the input proof to the caller, not the token owner.** `confidentialTransferFrom` and `confidentialTransferFromAndCall` now build the encrypted-amount proof against the transaction sender (the operator), matching how the contract verifies it against `msg.sender`; previously it was bound to the `from` owner, so an operator-initiated transfer could fail proof verification. No API change — `Token.confidentialTransferFrom` / `confidentialTransferFromAndCall` and their `useConfidentialTransferFrom` / `useConfidentialTransferFromAndCall` hooks pick it up automatically. See [Operator approvals](/protocol/sdk/guides/operator-approvals.md).
* **ACL revert decoding ignores inherited property names.** Decoding a Solidity revert whose custom error is named like a built-in object member (`valueOf`, `toString`, `constructor`, …) no longer returns a spurious match — the lookup checks own properties only, so an unmapped error correctly falls through to `null` instead of surfacing a non-[`ZamaError`](/protocol/sdk/api-references/sdk/errors.md) object.


---

# 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-5.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.
