@zama-fhe/sdk 3.0.0-alpha.25 → 3.0.0-alpha.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -809
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/query/index.cjs +1 -1
- package/dist/cjs/query/index.cjs.map +1 -1
- package/dist/esm/{index-BZ-vGsiw.d.ts → index-DuNJ58bo.d.ts} +19 -2
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/query/index.d.ts +1 -1
- package/dist/esm/query/index.js +1 -1
- package/dist/esm/query/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @zama-fhe/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Core TypeScript SDK for building confidential dApps and backend integrations on the Zama Protocol. Use this package for browser or Node.js code outside React when you need confidential smart contract operations such as authorization, encryption, balances, transfers, shielding, and unshielding.
|
|
4
|
+
|
|
5
|
+
If you are building a React app, pair this package with `@zama-fhe/react-sdk`.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -12,839 +14,65 @@ npm install @zama-fhe/sdk
|
|
|
12
14
|
yarn add @zama-fhe/sdk
|
|
13
15
|
```
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
| Package | Version | Required? |
|
|
18
|
-
| -------- | ------- | ------------------------------------------------- |
|
|
19
|
-
| `viem` | >= 2 | Optional — for the `@zama-fhe/sdk/viem` adapter |
|
|
20
|
-
| `ethers` | >= 6 | Optional — for the `@zama-fhe/sdk/ethers` adapter |
|
|
21
|
-
|
|
22
|
-
## Module Systems (ESM & CJS)
|
|
23
|
-
|
|
24
|
-
The SDK ships both ESM and CommonJS builds. Most modern toolchains use ESM automatically — no extra configuration needed. If your project uses CommonJS (`"type": "commonjs"` in `package.json` or `"moduleResolution": "node"` in `tsconfig.json`), the `require` condition is resolved automatically.
|
|
25
|
-
|
|
26
|
-
### ESM (recommended)
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
import { ZamaSDK } from "@zama-fhe/sdk";
|
|
30
|
-
import { web } from "@zama-fhe/sdk/web";
|
|
31
|
-
import { createConfig } from "@zama-fhe/sdk/viem";
|
|
32
|
-
import { sepolia } from "@zama-fhe/sdk/chains";
|
|
33
|
-
import { ViemSigner } from "@zama-fhe/sdk/viem";
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### CommonJS
|
|
17
|
+
If you follow the viem example below, install `viem` too:
|
|
37
18
|
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
const { createConfig, EthersSigner } = require("@zama-fhe/sdk/ethers");
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add @zama-fhe/sdk viem
|
|
41
21
|
```
|
|
42
22
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
| Subpath | Description | CJS |
|
|
46
|
-
| ------------------------- | -------------------------------------------- | --- |
|
|
47
|
-
| `@zama-fhe/sdk` | Core SDK, config, relayers, RelayerWeb, etc. | Yes |
|
|
48
|
-
| `@zama-fhe/sdk/chains` | Pre-configured chain objects | Yes |
|
|
49
|
-
| `@zama-fhe/sdk/viem` | Viem adapter (ViemSigner) | Yes |
|
|
50
|
-
| `@zama-fhe/sdk/ethers` | Ethers adapter (EthersSigner) | Yes |
|
|
51
|
-
| `@zama-fhe/sdk/node` | Node.js backend (RelayerNode) | No |
|
|
52
|
-
| `@zama-fhe/sdk/query` | TanStack Query integration | Yes |
|
|
53
|
-
| `@zama-fhe/sdk/cleartext` | Cleartext testing adapter | Yes |
|
|
54
|
-
|
|
55
|
-
> **Note:** The `@zama-fhe/sdk/node` subpath is ESM-only because it relies on `node:worker_threads` which is inherently ESM-oriented.
|
|
56
|
-
|
|
57
|
-
### TypeScript configuration
|
|
58
|
-
|
|
59
|
-
The SDK works with all TypeScript `moduleResolution` modes:
|
|
60
|
-
|
|
61
|
-
- **`"bundler"` / `"node16"` / `"nodenext"`** — resolved via the `exports` field (recommended)
|
|
62
|
-
- **`"node"`** — resolved via the `typesVersions` fallback
|
|
23
|
+
Other optional peers depend on the adapter you use: `ethers` for `@zama-fhe/sdk/ethers` and `@tanstack/query-core` for `@zama-fhe/sdk/query`.
|
|
63
24
|
|
|
64
|
-
|
|
25
|
+
`@zama-fhe/sdk/node` is ESM-only because it relies on `node:worker_threads`.
|
|
65
26
|
|
|
66
|
-
|
|
27
|
+
## Minimal example
|
|
67
28
|
|
|
68
29
|
```ts
|
|
30
|
+
import { createPublicClient, createWalletClient, custom, http } from "viem";
|
|
31
|
+
import { sepolia } from "viem/chains";
|
|
69
32
|
import { ZamaSDK } from "@zama-fhe/sdk";
|
|
70
33
|
import { web } from "@zama-fhe/sdk/web";
|
|
71
34
|
import { createConfig } from "@zama-fhe/sdk/viem";
|
|
72
|
-
import { sepolia,
|
|
73
|
-
import type { FheChain } from "@zama-fhe/sdk/chains";
|
|
35
|
+
import { sepolia as sepoliaFhe, type FheChain } from "@zama-fhe/sdk/chains";
|
|
74
36
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
} as const satisfies FheChain;
|
|
79
|
-
const myMainnet = {
|
|
80
|
-
...mainnet,
|
|
81
|
-
relayerUrl: "https://your-app.com/api/relayer/1",
|
|
82
|
-
} as const satisfies FheChain;
|
|
83
|
-
|
|
84
|
-
// 1. Create the config
|
|
85
|
-
const config = createConfig({
|
|
86
|
-
chains: [mySepolia, myMainnet],
|
|
87
|
-
publicClient,
|
|
88
|
-
walletClient,
|
|
89
|
-
relayers: {
|
|
90
|
-
[mySepolia.id]: web(),
|
|
91
|
-
[myMainnet.id]: web(),
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
const sdk = new ZamaSDK(config);
|
|
96
|
-
|
|
97
|
-
// 2. Create a token instance (wrapper is auto-discovered if omitted)
|
|
98
|
-
const token = sdk.createToken("0xEncryptedERC20Address");
|
|
99
|
-
|
|
100
|
-
// 3. Shield (wrap) public tokens into confidential tokens
|
|
101
|
-
const { txHash } = await token.shield(1000n);
|
|
102
|
-
|
|
103
|
-
// 4. Check decrypted balance
|
|
104
|
-
const balance = await token.balanceOf();
|
|
105
|
-
console.log("Confidential balance:", balance);
|
|
37
|
+
const rpcUrl = "https://sepolia.infura.io/v3/YOUR_KEY";
|
|
38
|
+
const publicClient = createPublicClient({ chain: sepolia, transport: http(rpcUrl) });
|
|
39
|
+
const walletClient = createWalletClient({ chain: sepolia, transport: custom(window.ethereum!) });
|
|
106
40
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
### Browser (ethers)
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
import { ZamaSDK } from "@zama-fhe/sdk";
|
|
115
|
-
import { web } from "@zama-fhe/sdk/web";
|
|
116
|
-
import { createConfig } from "@zama-fhe/sdk/ethers";
|
|
117
|
-
import { sepolia } from "@zama-fhe/sdk/chains";
|
|
118
|
-
import type { FheChain } from "@zama-fhe/sdk/chains";
|
|
119
|
-
|
|
120
|
-
const mySepolia = {
|
|
121
|
-
...sepolia,
|
|
41
|
+
const chain = {
|
|
42
|
+
...sepoliaFhe,
|
|
43
|
+
network: rpcUrl,
|
|
122
44
|
relayerUrl: "https://your-app.com/api/relayer/11155111",
|
|
123
45
|
} as const satisfies FheChain;
|
|
124
46
|
|
|
125
47
|
const config = createConfig({
|
|
126
|
-
chains: [
|
|
127
|
-
ethereum: window.ethereum!,
|
|
128
|
-
relayers: {
|
|
129
|
-
[mySepolia.id]: web(),
|
|
130
|
-
},
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
const sdk = new ZamaSDK(config);
|
|
134
|
-
const token = sdk.createToken("0xEncryptedERC20Address");
|
|
135
|
-
const balance = await token.balanceOf();
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Node.js
|
|
139
|
-
|
|
140
|
-
```ts
|
|
141
|
-
import { ZamaSDK, memoryStorage } from "@zama-fhe/sdk";
|
|
142
|
-
import { node } from "@zama-fhe/sdk/node";
|
|
143
|
-
import { createConfig } from "@zama-fhe/sdk/viem";
|
|
144
|
-
import { sepolia, mainnet, type FheChain } from "@zama-fhe/sdk/chains";
|
|
145
|
-
|
|
146
|
-
const mySepolia = {
|
|
147
|
-
...sepolia,
|
|
148
|
-
network: "https://sepolia.infura.io/v3/YOUR_KEY",
|
|
149
|
-
auth: { __type: "ApiKeyHeader" as const, value: process.env.RELAYER_API_KEY! },
|
|
150
|
-
} as const satisfies FheChain;
|
|
151
|
-
|
|
152
|
-
const myMainnet = {
|
|
153
|
-
...mainnet,
|
|
154
|
-
network: "https://mainnet.infura.io/v3/YOUR_KEY",
|
|
155
|
-
auth: { __type: "ApiKeyHeader" as const, value: process.env.RELAYER_API_KEY! },
|
|
156
|
-
} as const satisfies FheChain;
|
|
157
|
-
|
|
158
|
-
const config = createConfig({
|
|
159
|
-
chains: [mySepolia, myMainnet],
|
|
48
|
+
chains: [chain],
|
|
160
49
|
publicClient,
|
|
161
50
|
walletClient,
|
|
162
|
-
|
|
163
|
-
relayers: {
|
|
164
|
-
[mySepolia.id]: node({ poolSize: 4 }),
|
|
165
|
-
[myMainnet.id]: node({ poolSize: 4 }),
|
|
166
|
-
},
|
|
51
|
+
relayers: { [chain.id]: web() },
|
|
167
52
|
});
|
|
168
53
|
|
|
169
54
|
const sdk = new ZamaSDK(config);
|
|
170
|
-
const token = sdk.createToken("
|
|
171
|
-
const balance = await token.balanceOf();
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
## Core Concepts
|
|
175
|
-
|
|
176
|
-
### createConfig
|
|
177
|
-
|
|
178
|
-
The recommended way to configure the SDK. Takes chains, relayers, and a signer adapter and returns a config object for `ZamaSDK`. Import `createConfig` from the adapter subpath that matches your Web3 library.
|
|
179
|
-
|
|
180
|
-
```ts
|
|
181
|
-
import { ZamaSDK } from "@zama-fhe/sdk";
|
|
182
|
-
import { web } from "@zama-fhe/sdk/web";
|
|
183
|
-
import { createConfig } from "@zama-fhe/sdk/viem";
|
|
184
|
-
import { sepolia } from "@zama-fhe/sdk/chains";
|
|
185
|
-
import type { FheChain } from "@zama-fhe/sdk/chains";
|
|
186
|
-
|
|
187
|
-
const mySepolia = { ...sepolia, relayerUrl: "/api/relayer/11155111" } as const satisfies FheChain;
|
|
188
|
-
|
|
189
|
-
const config = createConfig({
|
|
190
|
-
chains: [mySepolia],
|
|
191
|
-
publicClient,
|
|
192
|
-
walletClient, // or use createConfig from @zama-fhe/sdk/ethers with { ethereum }
|
|
193
|
-
relayers: {
|
|
194
|
-
[mySepolia.id]: web(),
|
|
195
|
-
},
|
|
196
|
-
});
|
|
197
|
-
const sdk = new ZamaSDK(config);
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Transport functions: `web()` (browser, WASM in Web Worker), `node()` from `@zama-fhe/sdk/node` (Node.js, worker threads), `cleartext()` (local dev, no FHE).
|
|
201
|
-
|
|
202
|
-
### ZamaSDK
|
|
203
|
-
|
|
204
|
-
Entry point to the SDK. Composes a relayer backend with a signer and storage layer. Manages sessions, credentials, and acts as a factory for contract instances. Accepts a config from `createConfig` or a manual config object.
|
|
205
|
-
|
|
206
|
-
```ts
|
|
207
|
-
const sdk = new ZamaSDK(config); // from createConfig
|
|
208
|
-
|
|
209
|
-
// Read-only — balances, metadata, decryption. No wrapper needed.
|
|
210
|
-
const readonlyToken = sdk.createReadonlyToken("0xTokenAddress");
|
|
211
|
-
|
|
212
|
-
// Full read/write — shield, unshield, transfer, approve.
|
|
213
|
-
// The token address IS the wrapper (encrypted ERC20 = wrapper contract).
|
|
214
|
-
const token = sdk.createToken("0xTokenAddress");
|
|
215
|
-
// Override wrapper if it differs from the token address (rare):
|
|
216
|
-
// const token = sdk.createToken("0xTokenAddress", "0xWrapperAddress");
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
The `relayer`, `signer`, and `storage` properties are public and accessible after construction. Low-level FHE operations (`encrypt`, `userDecrypt`, `publicDecrypt`, `generateKeypair`, etc.) are available via `sdk.relayer`. Call `sdk.terminate()` to clean up resources when done.
|
|
220
|
-
|
|
221
|
-
### Relayer Backends
|
|
222
|
-
|
|
223
|
-
The `RelayerSDK` interface defines the FHE operations contract. Two implementations are provided:
|
|
224
|
-
|
|
225
|
-
| Backend | Import | Environment | How it works |
|
|
226
|
-
| ------------- | -------------------- | ----------- | ------------------------------------------ |
|
|
227
|
-
| `RelayerWeb` | `@zama-fhe/sdk` | Browser | Runs WASM in a Web Worker via CDN |
|
|
228
|
-
| `RelayerNode` | `@zama-fhe/sdk/node` | Node.js | Uses `@zama-fhe/relayer-sdk/node` directly |
|
|
229
|
-
|
|
230
|
-
The `/node` sub-path also exports `NodeWorkerClient` and `NodeWorkerClientConfig` for running FHE operations in a Node.js worker thread.
|
|
231
|
-
|
|
232
|
-
You can also implement the `RelayerSDK` interface for custom backends.
|
|
233
|
-
|
|
234
|
-
### Token
|
|
235
|
-
|
|
236
|
-
Full read/write interface for a single confidential ERC-20. Extends `ReadonlyToken`. The encrypted ERC-20 contract IS the wrapper, so `wrapper` defaults to the token `address`. Pass an explicit `wrapper` only if they differ.
|
|
237
|
-
|
|
238
|
-
| Method | Description |
|
|
239
|
-
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
240
|
-
| `shield(amount, options?)` | Shield (wrap) public ERC-20 tokens. Handles approval automatically. Options: `{ approvalStrategy: "max" \| "exact" \| "skip" }` (default `"exact"`). `"skip"` bypasses approval (use when already approved). |
|
|
241
|
-
| `unshield(amount, callbacks?)` | Unwrap a specific amount and finalize in one call. Orchestrates: unwrap → wait receipt → parse event → finalizeUnwrap. Optional `UnshieldCallbacks` for progress tracking. |
|
|
242
|
-
| `unshieldAll(callbacks?)` | Unwrap the entire balance and finalize in one call. Orchestrates: unwrapAll → wait receipt → parse event → finalizeUnwrap. Optional `UnshieldCallbacks` for progress tracking. |
|
|
243
|
-
| `unwrap(amount)` | Request unwrap for a specific amount (low-level, requires manual finalization). |
|
|
244
|
-
| `unwrapAll()` | Request unwrap for the entire balance (low-level, requires manual finalization). |
|
|
245
|
-
| `resumeUnshield(unwrapTxHash, callbacks?)` | Resume an interrupted unshield from an existing unwrap tx hash. Goes straight to wait receipt → finalize. |
|
|
246
|
-
| `finalizeUnwrap(unwrapRequestIdOrAmount)` | Complete unwrap with public decryption proof. Pass `unwrapRequestId` from upgraded events, or the legacy encrypted amount handle. |
|
|
247
|
-
| `confidentialTransfer(to, amount)` | Encrypted transfer. Encrypts amount, then calls the contract. |
|
|
248
|
-
| `confidentialTransferFrom(from, to, amt)` | Operator encrypted transfer. |
|
|
249
|
-
| `approve(spender, until?)` | Set operator approval. `until` defaults to now + 1 hour. |
|
|
250
|
-
| `isApproved(spender)` | Check if a spender is an approved operator. |
|
|
251
|
-
| `approveUnderlying(amount?)` | Approve wrapper to spend underlying ERC-20. Default: max uint256. |
|
|
252
|
-
| `delegateDecryption({ delegateAddress, expirationDate? })` | Grant decryption rights to another address via the on-chain ACL. Default: permanent. ACL address resolved from relayer config. |
|
|
253
|
-
| `revokeDelegation({ delegateAddress })` | Revoke decryption delegation for this token. ACL address resolved from relayer config. |
|
|
254
|
-
| `balanceOf(owner?)` | Decrypt and return the plaintext balance. |
|
|
255
|
-
|
|
256
|
-
All write methods return a `TransactionResult` object:
|
|
257
|
-
|
|
258
|
-
```ts
|
|
259
|
-
interface TransactionResult {
|
|
260
|
-
txHash: Hex;
|
|
261
|
-
receipt: TransactionReceipt;
|
|
262
|
-
}
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
### ReadonlyToken
|
|
266
|
-
|
|
267
|
-
Read-only subset. No wrapper address needed.
|
|
268
|
-
|
|
269
|
-
| Method | Description |
|
|
270
|
-
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
|
|
271
|
-
| `balanceOf(owner?)` | Decrypt and return the plaintext balance. |
|
|
272
|
-
| `confidentialBalanceOf(owner?)` | Return the raw encrypted balance handle (no decryption). |
|
|
273
|
-
| `allow()` | Ensure FHE decrypt credentials exist (generates/signs if needed). |
|
|
274
|
-
| `allow(...tokens)` _(static)_ | Pre-authorize multiple tokens with a single wallet signature. |
|
|
275
|
-
| `isAllowed()` | Whether a session signature is currently cached for this token. |
|
|
276
|
-
| `revoke()` | Clear the session signature for the connected wallet. |
|
|
277
|
-
| `credentials.allow(...addresses)` | Pre-authorize and cache the session signature for specific token addresses. |
|
|
278
|
-
| `credentials.revoke(...addresses?)` | Clear the session signature for the connected wallet. |
|
|
279
|
-
| `credentials.isAllowed([address, ...more])` | Whether a session signature is currently cached and covers the given contracts. |
|
|
280
|
-
| `credentials.isExpired(address?)` | Whether stored credentials are past their expiration time. |
|
|
281
|
-
| `credentials.clear()` | Delete stored credentials for the connected wallet. |
|
|
282
|
-
| `decryptBalanceAs({ delegatorAddress, owner? })` | Decrypt a delegator's balance as a delegate. ACL address resolved from relayer config. |
|
|
283
|
-
| `isDelegated({ delegatorAddress, delegateAddress })` | Check if a delegation is active and unexpired. |
|
|
284
|
-
| `getDelegationExpiry({ delegatorAddress, delegateAddress })` | Raw expiry timestamp (`0n` = none, `2^64-1` = permanent). |
|
|
285
|
-
| `isConfidential()` | ERC-165 check for ERC-7984 support. |
|
|
286
|
-
| `isWrapper()` | ERC-165 check for wrapper interface. |
|
|
287
|
-
| `underlyingToken()` | Read the underlying ERC-20 address from a wrapper. |
|
|
288
|
-
| `allowance(wrapper, owner?)` | Read ERC-20 allowance of the underlying token. |
|
|
289
|
-
| `isZeroHandle(handle)` | Returns `true` if the handle is the zero sentinel. |
|
|
290
|
-
| `name()` / `symbol()` / `decimals()` | Read token metadata. |
|
|
291
|
-
|
|
292
|
-
Static methods for multi-token operations:
|
|
293
|
-
|
|
294
|
-
```ts
|
|
295
|
-
// Pre-authorize all tokens with a single wallet signature
|
|
296
|
-
const tokens = addresses.map((a) => sdk.createReadonlyToken(a));
|
|
297
|
-
await ReadonlyToken.allow(...tokens);
|
|
298
|
-
// All subsequent decrypts reuse cached credentials — no more wallet prompts
|
|
299
|
-
|
|
300
|
-
// Decrypt balances for multiple tokens in parallel
|
|
301
|
-
const { results, errors } = await ReadonlyToken.batchBalancesOf(tokens, owner);
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
Batch delegation (on `Token`):
|
|
305
|
-
|
|
306
|
-
```ts
|
|
307
|
-
const tokens = addresses.map((a) => sdk.createToken(a));
|
|
308
|
-
|
|
309
|
-
// Delegate across multiple tokens — returns Map<Address, TransactionResult | ZamaError>
|
|
310
|
-
const results = await Token.batchDelegateDecryption({
|
|
311
|
-
tokens,
|
|
312
|
-
delegateAddress: "0xDelegate",
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
// Revoke across multiple tokens
|
|
316
|
-
const results = await Token.batchRevokeDelegation(tokens, "0xDelegate");
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### Pending Unshield Persistence
|
|
320
|
-
|
|
321
|
-
The unshield flow is two-phase: unwrap tx, then finalize. If the page reloads between phases, the unwrap tx hash is lost. Use these utilities to persist it:
|
|
322
|
-
|
|
323
|
-
```ts
|
|
324
|
-
import {
|
|
325
|
-
savePendingUnshield,
|
|
326
|
-
loadPendingUnshield,
|
|
327
|
-
loadPendingUnshieldRequest,
|
|
328
|
-
clearPendingUnshield,
|
|
329
|
-
} from "@zama-fhe/sdk";
|
|
330
|
-
|
|
331
|
-
// Save before finalization.
|
|
332
|
-
// Pass unwrapRequestId from upgraded UnwrapRequested events when available.
|
|
333
|
-
const event = findUnwrapRequested(receipt.logs);
|
|
334
|
-
await savePendingUnshield(storage, wrapperAddress, unwrapTxHash, event.unwrapRequestId);
|
|
335
|
-
|
|
336
|
-
// On next load, resume with the tx hash only (works for both legacy and upgraded wrappers)
|
|
337
|
-
const pending = await loadPendingUnshield(storage, wrapperAddress);
|
|
338
|
-
if (pending) {
|
|
339
|
-
await token.resumeUnshield(pending);
|
|
340
|
-
await clearPendingUnshield(storage, wrapperAddress);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Or load the full request object to access unwrapRequestId directly
|
|
344
|
-
const request = await loadPendingUnshieldRequest(storage, wrapperAddress);
|
|
345
|
-
if (request) {
|
|
346
|
-
// request.unwrapTxHash — always present
|
|
347
|
-
// request.unwrapRequestId — present for requests from upgraded wrappers
|
|
348
|
-
await token.resumeUnshield(request.unwrapTxHash);
|
|
349
|
-
await clearPendingUnshield(storage, wrapperAddress);
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### Storage
|
|
354
|
-
|
|
355
|
-
FHE credentials (encrypted keypair + metadata) are persisted to `storage`. The wallet signature is kept in `sessionStorage` (in-memory by default) — never written to disk. Two storage roles:
|
|
356
|
-
|
|
357
|
-
**Credential storage** (`storage`) — persists encrypted keypairs:
|
|
358
|
-
|
|
359
|
-
| Storage | Use case |
|
|
360
|
-
| ------------------- | -------------------------------------------------------- |
|
|
361
|
-
| `indexedDBStorage` | Browser apps — persists across page reloads and sessions |
|
|
362
|
-
| `memoryStorage` | Tests, scripts, throwaway sessions |
|
|
363
|
-
| `asyncLocalStorage` | Node.js servers — isolate credentials per request |
|
|
364
|
-
| Custom | Implement the `GenericStorage` interface |
|
|
365
|
-
|
|
366
|
-
**Session storage** (`sessionStorage`) — holds wallet signatures for the current session:
|
|
367
|
-
|
|
368
|
-
| Storage | Use case |
|
|
369
|
-
| ---------------------- | ----------------------------------------------------------- |
|
|
370
|
-
| Default (in-memory) | Standard web apps — signature lost on reload, user re-signs |
|
|
371
|
-
| `chromeSessionStorage` | MV3 web extensions — survives service worker restarts |
|
|
372
|
-
| Custom | Implement the `GenericStorage` interface |
|
|
373
|
-
|
|
374
|
-
```ts
|
|
375
|
-
interface GenericStorage<T = unknown> {
|
|
376
|
-
get(key: string): Promise<T | null>;
|
|
377
|
-
set(key: string, value: T): Promise<void>;
|
|
378
|
-
delete(key: string): Promise<void>;
|
|
379
|
-
}
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
#### Web Extension Example
|
|
383
|
-
|
|
384
|
-
For MV3 extensions, use the built-in `chromeSessionStorage` singleton to share the wallet signature across popup, background, and content script contexts:
|
|
385
|
-
|
|
386
|
-
```ts
|
|
387
|
-
import { ZamaSDK, indexedDBStorage, chromeSessionStorage } from "@zama-fhe/sdk";
|
|
388
|
-
|
|
389
|
-
const sdk = new ZamaSDK({
|
|
390
|
-
relayer,
|
|
391
|
-
signer,
|
|
392
|
-
storage: indexedDBStorage, // encrypted keypairs (persistent)
|
|
393
|
-
sessionStorage: chromeSessionStorage, // wallet signatures (ephemeral, shared across contexts)
|
|
394
|
-
});
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
## Configuration Reference
|
|
398
|
-
|
|
399
|
-
### `ZamaSDKConfig`
|
|
400
|
-
|
|
401
|
-
| Field | Type | Description |
|
|
402
|
-
| ---------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
403
|
-
| `relayer` | `RelayerSDK` | Relayer backend (`RelayerWeb` or `RelayerNode` instance). |
|
|
404
|
-
| `signer` | `GenericSigner` | Wallet signer interface. |
|
|
405
|
-
| `storage` | `GenericStorage` | Credential storage backend. |
|
|
406
|
-
| `sessionStorage` | `GenericStorage` | Optional. Session storage for wallet signatures. Default: in-memory (lost on reload). Use `chrome.storage.session` for web extensions. |
|
|
407
|
-
| `keypairTTL` | `number` | Optional. Seconds the ML-KEM re-encryption keypair remains valid. Default: `2592000` (30 days). Must be positive. |
|
|
408
|
-
| `sessionTTL` | `number` | Optional. Seconds the session signature remains valid. Default: `2592000` (30 days). `0` = re-sign every operation. |
|
|
409
|
-
| `onEvent` | `ZamaSDKEventListener` | Optional. Structured event listener for debugging. |
|
|
410
|
-
|
|
411
|
-
#### Structured Event Listener
|
|
412
|
-
|
|
413
|
-
The `onEvent` callback receives typed events at key lifecycle points. Event payloads never contain sensitive data (amounts, keys, proofs) — only metadata useful for debugging and telemetry.
|
|
414
|
-
|
|
415
|
-
```ts
|
|
416
|
-
const sdk = new ZamaSDK({
|
|
417
|
-
relayer,
|
|
418
|
-
signer,
|
|
419
|
-
storage,
|
|
420
|
-
onEvent: ({ type, tokenAddress, ...event }) => {
|
|
421
|
-
console.debug(`[Zama] ${type}`, {
|
|
422
|
-
tokenAddress: tokenAddress?.slice(0, 10),
|
|
423
|
-
...event,
|
|
424
|
-
});
|
|
425
|
-
},
|
|
426
|
-
});
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
**Event types:**
|
|
55
|
+
const token = sdk.createToken("0xYourConfidentialToken");
|
|
430
56
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
| Credentials | `credentials:loading`, `credentials:cached`, `credentials:expired`, `credentials:creating`, `credentials:created`, `credentials:revoked`, `credentials:allowed` | `contractAddresses` |
|
|
434
|
-
| Encryption | `encrypt:start`, `encrypt:end`, `encrypt:error` | `durationMs` (end/error), `error` (error) |
|
|
435
|
-
| Decryption | `decrypt:start`, `decrypt:end`, `decrypt:error` | `durationMs` (end/error), `error` (error) |
|
|
436
|
-
| Transactions | `transaction:error` | `operation` (`"transfer"`, `"wrap"`, `"approve"`, etc.), `error` |
|
|
437
|
-
| Write confirmations | `wrap:submitted`, `transfer:submitted`, `transferFrom:submitted`, `approve:submitted`, `approveUnderlying:submitted`, `unwrap:submitted`, `finalizeUnwrap:submitted` | `txHash` |
|
|
438
|
-
| Unshield orchestration | `unshield:phase1_submitted`, `unshield:phase2_started`, `unshield:phase2_submitted` | `txHash`, `operationId` |
|
|
439
|
-
|
|
440
|
-
All events carry `tokenAddress`, `timestamp`, and an optional `operationId` (set on unshield phase events to correlate multi-step operations).
|
|
441
|
-
|
|
442
|
-
**Dispatching events to other systems:**
|
|
443
|
-
|
|
444
|
-
The `onEvent` callback is a simple function — you can bridge it to any event system:
|
|
445
|
-
|
|
446
|
-
```ts
|
|
447
|
-
// Fan out to multiple listeners with EventEmitter
|
|
448
|
-
import { EventEmitter } from "events";
|
|
449
|
-
const emitter = new EventEmitter();
|
|
450
|
-
const sdk = new ZamaSDK({
|
|
451
|
-
// ...
|
|
452
|
-
onEvent: (event) => emitter.emit(event.type, event),
|
|
453
|
-
});
|
|
454
|
-
emitter.on("encrypt:start", (e) => {
|
|
455
|
-
/* listener A */
|
|
456
|
-
});
|
|
457
|
-
emitter.on("encrypt:start", (e) => {
|
|
458
|
-
/* listener B */
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
// Bridge to DOM CustomEvent (e.g. for cross-framework communication)
|
|
462
|
-
const sdk = new ZamaSDK({
|
|
463
|
-
// ...
|
|
464
|
-
onEvent: (event) => window.dispatchEvent(new CustomEvent(event.type, { detail: event })),
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
// Collect into React state
|
|
468
|
-
const [events, setEvents] = useState<ZamaSDKEvent[]>([]);
|
|
469
|
-
const sdk = new ZamaSDK({
|
|
470
|
-
// ...
|
|
471
|
-
onEvent: (event) => setEvents((prev) => [...prev, event]),
|
|
472
|
-
});
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
### `RelayerWebConfig` (browser)
|
|
476
|
-
|
|
477
|
-
| Field | Type | Description |
|
|
478
|
-
| ------------ | ------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
479
|
-
| `getChainId` | `() => Promise<number>` | Resolve the current chain ID. Called lazily; the worker is re-initialized on chain change. |
|
|
480
|
-
| `transports` | `Record<number, FhevmInstanceConfig>` | Chain-specific configs keyed by chain ID (includes relayerUrl, network, contract addresses). |
|
|
481
|
-
| `security` | `RelayerWebSecurityConfig` | Optional. Security options (see below). |
|
|
482
|
-
| `logger` | `GenericLogger` | Optional. Logger for worker lifecycle and request timing. |
|
|
483
|
-
| `threads` | `number` | Optional. WASM thread count for parallel FHE ops (4–8 recommended). Requires COOP/COEP headers. |
|
|
484
|
-
|
|
485
|
-
#### `RelayerWebSecurityConfig`
|
|
486
|
-
|
|
487
|
-
| Field | Type | Description |
|
|
488
|
-
| ---------------- | -------------- | ------------------------------------------------------------------------------------------------ |
|
|
489
|
-
| `getCsrfToken` | `() => string` | Optional. Resolve the CSRF token before each authenticated network request. |
|
|
490
|
-
| `integrityCheck` | `boolean` | Optional. Verify SHA-384 integrity of the CDN bundle. Defaults to `true`. Set `false` for tests. |
|
|
491
|
-
|
|
492
|
-
> **Security note:** `RelayerWeb` loads FHE WASM from a CDN at runtime. The `integrityCheck` option (enabled by default) verifies the SHA-384 hash of the bundle before execution, protecting against CDN compromise or MITM attacks. Only disable it in local development or testing.
|
|
493
|
-
|
|
494
|
-
### `RelayerNodeConfig` (Node.js)
|
|
495
|
-
|
|
496
|
-
| Field | Type | Description |
|
|
497
|
-
| ------------ | ------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
498
|
-
| `getChainId` | `() => Promise<number>` | Resolve the current chain ID. Called lazily; the pool is re-initialized on chain change. |
|
|
499
|
-
| `transports` | `Record<number, FhevmInstanceConfig>` | Chain-specific configs keyed by chain ID (includes relayerUrl, network, auth, contract addresses). |
|
|
500
|
-
|
|
501
|
-
### Chain Objects
|
|
502
|
-
|
|
503
|
-
Pre-configured chain objects from `@zama-fhe/sdk/chains`. Each includes contract addresses, relayer URLs, and an `id` alias for relayer config keys:
|
|
504
|
-
|
|
505
|
-
```ts
|
|
506
|
-
import { sepolia, mainnet, hoodi, hardhat } from "@zama-fhe/sdk/chains";
|
|
507
|
-
```
|
|
508
|
-
|
|
509
|
-
| Chain | Chain ID | Description |
|
|
510
|
-
| --------- | ---------- | ------------------ |
|
|
511
|
-
| `mainnet` | `1` | Ethereum Mainnet |
|
|
512
|
-
| `sepolia` | `11155111` | Sepolia Testnet |
|
|
513
|
-
| `hoodi` | `17000` | Hoodi Testnet |
|
|
514
|
-
| `hardhat` | `31337` | Local Hardhat node |
|
|
515
|
-
|
|
516
|
-
Use with `createConfig`:
|
|
517
|
-
|
|
518
|
-
```ts
|
|
519
|
-
import { web } from "@zama-fhe/sdk/web";
|
|
520
|
-
import { createConfig } from "@zama-fhe/sdk/viem";
|
|
521
|
-
import { sepolia } from "@zama-fhe/sdk/chains";
|
|
522
|
-
import type { FheChain } from "@zama-fhe/sdk/chains";
|
|
523
|
-
|
|
524
|
-
const mySepolia = { ...sepolia, relayerUrl: "/api/relayer/11155111" } as const satisfies FheChain;
|
|
525
|
-
|
|
526
|
-
const config = createConfig({
|
|
527
|
-
chains: [mySepolia],
|
|
528
|
-
publicClient,
|
|
529
|
-
walletClient,
|
|
530
|
-
relayers: {
|
|
531
|
-
[mySepolia.id]: web(),
|
|
532
|
-
},
|
|
533
|
-
});
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
### Legacy Network Preset Configs
|
|
537
|
-
|
|
538
|
-
The legacy `SepoliaConfig`, `MainnetConfig`, and `HardhatConfig` objects are still available for manual `RelayerWeb`/`RelayerNode` construction:
|
|
539
|
-
|
|
540
|
-
```ts
|
|
541
|
-
import { SepoliaConfig, MainnetConfig, HardhatConfig } from "@zama-fhe/sdk";
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
## GenericSigner Interface
|
|
545
|
-
|
|
546
|
-
The `GenericSigner` interface has six methods. Any Web3 library can back it.
|
|
547
|
-
|
|
548
|
-
```ts
|
|
549
|
-
interface GenericSigner {
|
|
550
|
-
getChainId(): Promise<number>;
|
|
551
|
-
getAddress(): Promise<Address>;
|
|
552
|
-
signTypedData(typedData: EIP712TypedData): Promise<Hex>;
|
|
553
|
-
writeContract(config: ContractCallConfig): Promise<Hex>;
|
|
554
|
-
readContract(config: ContractCallConfig): Promise<unknown>;
|
|
555
|
-
waitForTransactionReceipt(hash: Hex): Promise<TransactionReceipt>;
|
|
556
|
-
}
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
### Built-in Adapters
|
|
560
|
-
|
|
561
|
-
**viem** — `@zama-fhe/sdk/viem`
|
|
562
|
-
|
|
563
|
-
```ts
|
|
564
|
-
import { ViemSigner } from "@zama-fhe/sdk/viem";
|
|
565
|
-
|
|
566
|
-
const signer = new ViemSigner({ walletClient, publicClient });
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
**ethers** — `@zama-fhe/sdk/ethers`
|
|
570
|
-
|
|
571
|
-
```ts
|
|
572
|
-
import { EthersSigner } from "@zama-fhe/sdk/ethers";
|
|
573
|
-
|
|
574
|
-
const signer = new EthersSigner({ signer: ethersSigner });
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
## Contract Call Builders
|
|
578
|
-
|
|
579
|
-
Every function returns a `ContractCallConfig` object (address, ABI, function name, args) that can be used with any Web3 library. These are the low-level building blocks — they map 1:1 to on-chain contract calls without any orchestration. Use them when the high-level `Token` API doesn't cover your use case.
|
|
580
|
-
|
|
581
|
-
> **High-level vs low-level:** `token.shield()` / `token.unshield()` handle the full flow (approval, encryption, receipt waiting, finalization). The contract call builders (`wrapContract()`, `unwrapContract()`, etc.) produce raw call configs for a single contract interaction.
|
|
582
|
-
|
|
583
|
-
```ts
|
|
584
|
-
interface ContractCallConfig {
|
|
585
|
-
readonly address: Address;
|
|
586
|
-
readonly abi: readonly unknown[];
|
|
587
|
-
readonly functionName: string;
|
|
588
|
-
readonly args: readonly unknown[];
|
|
589
|
-
readonly value?: bigint;
|
|
590
|
-
readonly gas?: bigint;
|
|
591
|
-
}
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
### ERC-20
|
|
595
|
-
|
|
596
|
-
| Function | Description |
|
|
597
|
-
| ------------------------------------------ | ------------------------ |
|
|
598
|
-
| `nameContract(token)` | Read token name. |
|
|
599
|
-
| `symbolContract(token)` | Read token symbol. |
|
|
600
|
-
| `decimalsContract(token)` | Read token decimals. |
|
|
601
|
-
| `balanceOfContract(token, owner)` | Read ERC-20 balance. |
|
|
602
|
-
| `allowanceContract(token, owner, spender)` | Read ERC-20 allowance. |
|
|
603
|
-
| `approveContract(token, spender, value)` | Approve ERC-20 spending. |
|
|
604
|
-
|
|
605
|
-
### Encryption (Confidential ERC-20)
|
|
606
|
-
|
|
607
|
-
| Function | Description |
|
|
608
|
-
| ----------------------------------------------------------------------- | ----------------------------------------- |
|
|
609
|
-
| `confidentialBalanceOfContract(token, user)` | Read encrypted balance handle. |
|
|
610
|
-
| `confidentialTransferContract(token, to, handle, inputProof)` | Encrypted transfer. |
|
|
611
|
-
| `confidentialTransferFromContract(token, from, to, handle, inputProof)` | Operator encrypted transfer. |
|
|
612
|
-
| `isOperatorContract(token, holder, spender)` | Check operator approval. |
|
|
613
|
-
| `setOperatorContract(token, spender, timestamp?)` | Set operator approval (default: +1 hour). |
|
|
614
|
-
| `confidentialTotalSupplyContract(token)` | Read encrypted total supply handle. |
|
|
615
|
-
| `rateContract(token)` | Read conversion rate. |
|
|
616
|
-
|
|
617
|
-
### Wrapper
|
|
618
|
-
|
|
619
|
-
| Function | Description |
|
|
620
|
-
| -------------------------------------------------------------------- | --------------------------------------------- |
|
|
621
|
-
| `wrapContract(wrapper, to, amount)` | Wrap ERC-20 tokens. |
|
|
622
|
-
| `unwrapContract(token, from, to, encryptedAmount, inputProof)` | Request unwrap with encrypted amount. |
|
|
623
|
-
| `unwrapFromBalanceContract(token, from, to, encryptedBalance)` | Request unwrap using on-chain balance handle. |
|
|
624
|
-
| `finalizeUnwrapContract(wrapper, unwrapRequestId, cleartext, proof)` | Finalize unwrap with decryption proof. |
|
|
625
|
-
| `underlyingContract(wrapper)` | Read underlying ERC-20 address. |
|
|
626
|
-
| `inferredTotalSupplyContract(wrapper)` | Read inferred plaintext total supply. |
|
|
627
|
-
| `totalSupplyContract(wrapper)` | Deprecated legacy `totalSupply()` builder. |
|
|
628
|
-
|
|
629
|
-
For transition-safe total supply reads, prefer `totalSupplyQueryOptions` or React
|
|
630
|
-
`useTotalSupply`. They detect the wrapper ERC-165 interface ID and call
|
|
631
|
-
`inferredTotalSupply()` on upgraded wrappers or legacy `totalSupply()` on pre-upgrade
|
|
632
|
-
wrappers.
|
|
633
|
-
|
|
634
|
-
### ERC-165
|
|
635
|
-
|
|
636
|
-
| Function | Description |
|
|
637
|
-
| ----------------------------------------------- | ------------------------ |
|
|
638
|
-
| `supportsInterfaceContract(token, interfaceId)` | ERC-165 interface check. |
|
|
639
|
-
|
|
640
|
-
## Library-Specific Contract Helpers
|
|
641
|
-
|
|
642
|
-
Both the `/viem` and `/ethers` sub-paths export convenience wrappers that execute contract calls directly with library-native clients.
|
|
643
|
-
|
|
644
|
-
### viem (`@zama-fhe/sdk/viem`)
|
|
645
|
-
|
|
646
|
-
```ts
|
|
647
|
-
import {
|
|
648
|
-
readConfidentialBalanceOfContract,
|
|
649
|
-
writeConfidentialTransferContract,
|
|
650
|
-
writeWrapContract,
|
|
651
|
-
// ... more
|
|
652
|
-
} from "@zama-fhe/sdk/viem";
|
|
653
|
-
|
|
654
|
-
// Read: pass a PublicClient
|
|
655
|
-
const handle = await readConfidentialBalanceOfContract(publicClient, tokenAddress, userAddress);
|
|
656
|
-
|
|
657
|
-
// Write: pass a WalletClient
|
|
658
|
-
const txHash = await writeConfidentialTransferContract(
|
|
659
|
-
walletClient,
|
|
660
|
-
tokenAddress,
|
|
661
|
-
to,
|
|
662
|
-
handle,
|
|
663
|
-
inputProof,
|
|
664
|
-
);
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
**Read helpers:** `readConfidentialBalanceOfContract`, `readUnderlyingTokenContract`, `readSupportsInterfaceContract` (legacy — prefer `sdk.registry.getConfidentialToken()`).
|
|
668
|
-
|
|
669
|
-
**Write helpers:** `writeConfidentialTransferContract`, `writeUnwrapContract`, `writeUnwrapFromBalanceContract`, `writeFinalizeUnwrapContract`, `writeSetOperatorContract`, `writeWrapContract`.
|
|
670
|
-
|
|
671
|
-
### ethers (`@zama-fhe/sdk/ethers`)
|
|
672
|
-
|
|
673
|
-
Same set of functions, but read helpers take `Provider | Signer` and write helpers take `Signer`.
|
|
674
|
-
|
|
675
|
-
```ts
|
|
676
|
-
import {
|
|
677
|
-
readConfidentialBalanceOfContract,
|
|
678
|
-
writeConfidentialTransferContract,
|
|
679
|
-
} from "@zama-fhe/sdk/ethers";
|
|
680
|
-
|
|
681
|
-
const handle = await readConfidentialBalanceOfContract(provider, tokenAddress, userAddress);
|
|
682
|
-
const txHash = await writeConfidentialTransferContract(
|
|
683
|
-
signer,
|
|
684
|
-
tokenAddress,
|
|
685
|
-
to,
|
|
686
|
-
handle,
|
|
687
|
-
inputProof,
|
|
688
|
-
);
|
|
689
|
-
```
|
|
690
|
-
|
|
691
|
-
## Event Decoders
|
|
692
|
-
|
|
693
|
-
Decode raw log entries from `eth_getLogs` into typed event objects.
|
|
694
|
-
|
|
695
|
-
### Topics
|
|
696
|
-
|
|
697
|
-
Use `TOKEN_TOPICS` as the `topics[0]` filter for `getLogs` to capture all confidential token events:
|
|
698
|
-
|
|
699
|
-
```ts
|
|
700
|
-
import { TOKEN_TOPICS } from "@zama-fhe/sdk";
|
|
701
|
-
|
|
702
|
-
const logs = await publicClient.getLogs({
|
|
703
|
-
address: tokenAddress,
|
|
704
|
-
topics: [TOKEN_TOPICS],
|
|
705
|
-
});
|
|
706
|
-
```
|
|
707
|
-
|
|
708
|
-
Individual topic hashes are accessible via the `Topics` object: `Topics.ConfidentialTransfer`, `Topics.Wrapped`, `Topics.UnwrapRequested`, `Topics.UnwrapRequestedLegacy`, `Topics.UnwrapFinalized`, `Topics.UnwrapFinalizedLegacy`, and `Topics.UnwrappedStarted`. `Topics.UnwrappedFinalized` remains as a deprecated alias for the upgraded finalized topic.
|
|
709
|
-
|
|
710
|
-
### Decoders
|
|
711
|
-
|
|
712
|
-
| Function | Returns |
|
|
713
|
-
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
714
|
-
| `decodeConfidentialTransfer(log)` | `ConfidentialTransferEvent \| null` — `{ from, to, encryptedAmountHandle }` |
|
|
715
|
-
| `decodeWrapped(log)` | `WrappedEvent \| null` — `{ to, amountIn }` |
|
|
716
|
-
| `decodeUnwrapRequested(log)` | `UnwrapRequestedEvent \| null` — `{ receiver, encryptedAmount, unwrapRequestId? }` |
|
|
717
|
-
| `decodeUnwrapFinalized(log)` | `UnwrapFinalizedEvent \| null` — `{ receiver, encryptedAmount, cleartextAmount, unwrapRequestId? }` |
|
|
718
|
-
| `decodeUnwrappedFinalized(log)` | **Deprecated.** Like `decodeUnwrapFinalized(log)` but always returns `eventName: "UnwrappedFinalized"` — not a transparent alias. Update switch statements to use `decodeUnwrapFinalized` and `"UnwrapFinalized"` instead. |
|
|
719
|
-
| `decodeUnwrappedStarted(log)` | `UnwrappedStartedEvent \| null` — `{ returnVal, requestId, txId, to, refund, requestedAmount, burnAmount }` |
|
|
720
|
-
| `decodeOnChainEvent(log)` | `OnChainEvent \| null` — tries all decoders |
|
|
721
|
-
| `decodeOnChainEvents(logs)` | `OnChainEvent[]` — batch decode, skips unrecognized logs |
|
|
722
|
-
|
|
723
|
-
### Finder Helpers
|
|
724
|
-
|
|
725
|
-
Convenience functions that decode a logs array and return the first matching event:
|
|
726
|
-
|
|
727
|
-
```ts
|
|
728
|
-
import { findWrapped, findUnwrapRequested } from "@zama-fhe/sdk";
|
|
729
|
-
|
|
730
|
-
const wrappedEvent = findWrapped(receipt.logs);
|
|
731
|
-
const unwrapEvent = findUnwrapRequested(receipt.logs);
|
|
732
|
-
```
|
|
733
|
-
|
|
734
|
-
## Error Handling
|
|
735
|
-
|
|
736
|
-
All SDK errors extend `ZamaError`. Use `instanceof` to catch specific error types:
|
|
737
|
-
|
|
738
|
-
```ts
|
|
739
|
-
import { ZamaError, SigningRejectedError, EncryptionFailedError } from "@zama-fhe/sdk";
|
|
740
|
-
|
|
741
|
-
try {
|
|
742
|
-
await token.confidentialTransfer(to, amount);
|
|
743
|
-
} catch (error) {
|
|
744
|
-
if (error instanceof SigningRejectedError) {
|
|
745
|
-
// User rejected wallet signature
|
|
746
|
-
}
|
|
747
|
-
if (error instanceof EncryptionFailedError) {
|
|
748
|
-
// FHE encryption failed
|
|
749
|
-
}
|
|
750
|
-
if (error instanceof ZamaError) {
|
|
751
|
-
// Any other SDK error — check error.code for details
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
```
|
|
755
|
-
|
|
756
|
-
### Error Classes
|
|
757
|
-
|
|
758
|
-
| Error Class | Code | Description |
|
|
759
|
-
| --------------------------- | ------------------------ | ------------------------------------------------------------------------- |
|
|
760
|
-
| `SigningRejectedError` | `SIGNING_REJECTED` | User rejected the wallet signature request. |
|
|
761
|
-
| `SigningFailedError` | `SIGNING_FAILED` | Wallet signature failed for a non-rejection reason. |
|
|
762
|
-
| `EncryptionFailedError` | `ENCRYPTION_FAILED` | FHE encryption operation failed. |
|
|
763
|
-
| `DecryptionFailedError` | `DECRYPTION_FAILED` | FHE decryption operation failed. |
|
|
764
|
-
| `ApprovalFailedError` | `APPROVAL_FAILED` | ERC-20 approval transaction failed. |
|
|
765
|
-
| `TransactionRevertedError` | `TRANSACTION_REVERTED` | On-chain transaction reverted. |
|
|
766
|
-
| `InvalidKeypairError` | `INVALID_KEYPAIR` | Relayer rejected FHE keypair (stale or expired). |
|
|
767
|
-
| `NoCiphertextError` | `NO_CIPHERTEXT` | No FHE ciphertext exists for this account (e.g. never shielded). |
|
|
768
|
-
| `RelayerRequestFailedError` | `RELAYER_REQUEST_FAILED` | Relayer HTTP error. Carries a `statusCode` property with the HTTP status. |
|
|
769
|
-
|
|
770
|
-
### `matchZamaError`
|
|
771
|
-
|
|
772
|
-
Pattern-match on error codes without `instanceof` chains. Falls through to the `_` wildcard if no handler matches. Returns `undefined` for non-SDK errors when no `_` handler is provided.
|
|
773
|
-
|
|
774
|
-
```ts
|
|
775
|
-
import { matchZamaError } from "@zama-fhe/sdk";
|
|
776
|
-
|
|
777
|
-
matchZamaError(error, {
|
|
778
|
-
SIGNING_REJECTED: () => toast("Please approve in wallet"),
|
|
779
|
-
TRANSACTION_REVERTED: (e) => toast(`Tx failed: ${e.message}`),
|
|
780
|
-
_: () => toast("Unknown error"),
|
|
781
|
-
});
|
|
782
|
-
```
|
|
783
|
-
|
|
784
|
-
**Distinguishing "no ciphertext" from "zero balance":**
|
|
785
|
-
|
|
786
|
-
```ts
|
|
787
|
-
import { NoCiphertextError, RelayerRequestFailedError } from "@zama-fhe/sdk";
|
|
788
|
-
|
|
789
|
-
try {
|
|
790
|
-
const balance = await token.balanceOf();
|
|
791
|
-
} catch (error) {
|
|
792
|
-
if (error instanceof NoCiphertextError) {
|
|
793
|
-
// Account has never shielded — show "no confidential balance" in UI
|
|
794
|
-
}
|
|
795
|
-
if (error instanceof RelayerRequestFailedError) {
|
|
796
|
-
console.error(`Relayer returned HTTP ${error.statusCode}`);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
```
|
|
800
|
-
|
|
801
|
-
### Unshield Progress Callbacks
|
|
802
|
-
|
|
803
|
-
`unshield()`, `unshieldAll()`, and `resumeUnshield()` accept optional callbacks for tracking progress through the two-phase unshield flow:
|
|
804
|
-
|
|
805
|
-
```ts
|
|
806
|
-
import type { UnshieldCallbacks } from "@zama-fhe/sdk";
|
|
807
|
-
|
|
808
|
-
const callbacks: UnshieldCallbacks = {
|
|
809
|
-
onUnwrapSubmitted: (txHash) => console.log("Unwrap tx:", txHash),
|
|
810
|
-
onFinalizing: () => console.log("Waiting for decryption proof..."),
|
|
811
|
-
onFinalizeSubmitted: (txHash) => console.log("Finalize tx:", txHash),
|
|
812
|
-
};
|
|
813
|
-
|
|
814
|
-
await token.unshield(500n, callbacks);
|
|
57
|
+
const balance = await token.balanceOf();
|
|
58
|
+
await token.confidentialTransfer("0xRecipient", 100n);
|
|
815
59
|
```
|
|
816
60
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
## RelayerSDK (Low-Level FHE)
|
|
820
|
-
|
|
821
|
-
Low-level FHE operations are available on the relayer backend via `sdk.relayer`:
|
|
822
|
-
|
|
823
|
-
| Method | Description |
|
|
824
|
-
| --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
825
|
-
| `encrypt(params)` | Encrypt values for smart contract calls. Returns `{ handles, inputProof }`. |
|
|
826
|
-
| `userDecrypt(params)` | Decrypt ciphertext handles with the user's FHE private key. |
|
|
827
|
-
| `publicDecrypt(handles)` | Public decryption (no private key needed). Returns `{ clearValues, decryptionProof }`. |
|
|
828
|
-
| `generateKeypair()` | Generate an FHE keypair. Returns `{ publicKey, privateKey }`. |
|
|
829
|
-
| `createEIP712(publicKey, contractAddresses, startTimestamp, durationDays?)` | Create EIP-712 typed data for decrypt authorization. Default duration: 7 days. |
|
|
830
|
-
| `createDelegatedUserDecryptEIP712(...)` | Create EIP-712 for delegated decryption. |
|
|
831
|
-
| `delegatedUserDecrypt(params)` | Decrypt via delegation. |
|
|
832
|
-
| `requestZKProofVerification(zkProof)` | Submit a ZK proof for on-chain verification. |
|
|
833
|
-
| `getPublicKey()` | Get the TFHE compact public key. |
|
|
834
|
-
| `getPublicParams(bits)` | Get public parameters for encryption capacity. |
|
|
835
|
-
| `terminate()` | Terminate the backend and clean up resources. |
|
|
836
|
-
|
|
837
|
-
## Constants
|
|
61
|
+
Browser apps should proxy relayer requests through their backend so the relayer API key stays server-side. See the [Authentication guide](https://github.com/zama-ai/sdk/blob/main/docs/gitbook/src/guides/authentication.md).
|
|
838
62
|
|
|
839
|
-
|
|
840
|
-
| ------------------------------------- | --------------------------------- | ---------------------------------------------------- |
|
|
841
|
-
| `ZERO_HANDLE` | `"0x0000...0000"` (32 zero bytes) | Sentinel for empty/zero encrypted values. |
|
|
842
|
-
| `ERC7984_INTERFACE_ID` | `"0x4958f2a4"` | ERC-165 interface ID for confidential tokens. |
|
|
843
|
-
| `ERC7984_WRAPPER_INTERFACE_ID` | `"0x1f1c62b2"` | ERC-165 interface ID for upgraded wrapper contracts. |
|
|
844
|
-
| `ERC7984_WRAPPER_INTERFACE_ID_LEGACY` | `"0xd04584ba"` | ERC-165 interface ID for legacy wrapper contracts. |
|
|
63
|
+
## What this package includes
|
|
845
64
|
|
|
846
|
-
|
|
65
|
+
- `ZamaSDK` is the main entry point. It creates token instances, manages sessions, and coordinates the signer, relayer, and storage layers.
|
|
66
|
+
- `Token` exposes read/write confidential token operations such as shield, confidential transfer, unwrap, and unshield.
|
|
67
|
+
- `ReadonlyToken` exposes read-only token access such as metadata, total supply, and balance decryption.
|
|
68
|
+
- Adapter-specific `createConfig` helpers are available from `@zama-fhe/sdk/viem` and `@zama-fhe/sdk/ethers`.
|
|
69
|
+
- Relayer factories are split by runtime: browser `web()` comes from `@zama-fhe/sdk/web`, Node.js `node()` comes from `@zama-fhe/sdk/node`, and local `cleartext()` comes from `@zama-fhe/sdk`.
|
|
70
|
+
- Chain presets such as `sepolia`, `mainnet`, `hoodi`, `hardhat`, and `anvil` are available from `@zama-fhe/sdk/chains`.
|
|
847
71
|
|
|
848
|
-
|
|
72
|
+
## Documentation
|
|
849
73
|
|
|
850
|
-
|
|
74
|
+
- [Official documentation](https://docs.zama.org/protocol) is the best starting point for the hosted SDK docs.
|
|
75
|
+
- [Quick start](https://github.com/zama-ai/sdk/blob/main/docs/gitbook/src/tutorials/quick-start.md) gets from installation to a working confidential transfer.
|
|
76
|
+
- [Guides](https://github.com/zama-ai/sdk/blob/main/docs/gitbook/src/guides/README.md) cover focused topics such as authentication, configuration, balances, transfers, and unshielding.
|
|
77
|
+
- [SDK reference](https://github.com/zama-ai/sdk/blob/main/docs/gitbook/src/reference/sdk/README.md) documents the full core API, including `ZamaSDK`, `Token`, `ReadonlyToken`, adapters, and helpers.
|
|
78
|
+
- [React SDK docs](https://github.com/zama-ai/sdk/blob/main/docs/gitbook/src/reference/react/README.md) cover the provider and hook layer for React apps.
|