@zama-fhe/sdk 1.0.0 → 1.0.1
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 +237 -84
- package/dist/activity-CmR2x4Bb.d.ts +969 -0
- package/dist/activity-fyiIrk3D.js +2 -0
- package/dist/activity-fyiIrk3D.js.map +1 -0
- package/dist/cleartext/index.d.ts +49 -0
- package/dist/cleartext/index.js +2 -0
- package/dist/cleartext/index.js.map +1 -0
- package/dist/eip1193-subscribe-DVgPhzbc.js +2 -0
- package/dist/eip1193-subscribe-DVgPhzbc.js.map +1 -0
- package/dist/erc165-C9GjJpLc.js +2 -0
- package/dist/erc165-C9GjJpLc.js.map +1 -0
- package/dist/errors-B0aFtthv.js +2 -0
- package/dist/errors-B0aFtthv.js.map +1 -0
- package/dist/ethers/index.d.ts +74 -30
- package/dist/ethers/index.js +1 -130
- package/dist/ethers/index.js.map +1 -1
- package/dist/index-XNCT2KaE.d.ts +27848 -0
- package/dist/index.d.ts +4685 -32883
- package/dist/index.js +1 -2861
- package/dist/index.js.map +1 -1
- package/dist/node/index.d.ts +152 -110
- package/dist/node/index.js +1 -316
- package/dist/node/index.js.map +1 -1
- package/dist/query/index.d.ts +528 -0
- package/dist/query/index.js +2 -0
- package/dist/query/index.js.map +1 -0
- package/dist/relayer-sdk-BHnGxGkv.d.ts +41 -0
- package/dist/relayer-sdk.node-worker.d.ts +1 -2
- package/dist/relayer-sdk.node-worker.js +1 -319
- package/dist/relayer-sdk.node-worker.js.map +1 -1
- package/dist/relayer-sdk.types-DQ1e-2NV.d.ts +329 -0
- package/dist/relayer-sdk.worker.js +1 -400
- package/dist/relayer-sdk.worker.js.map +1 -1
- package/dist/relayer-utils-BRMmJpoV.d.ts +51 -0
- package/dist/relayer-utils-DOqdqWEX.js +2 -0
- package/dist/relayer-utils-DOqdqWEX.js.map +1 -0
- package/dist/token.types-BCq4YUya.d.ts +419 -0
- package/dist/transfer-batcher-yW5FnZ-A.js +2 -0
- package/dist/transfer-batcher-yW5FnZ-A.js.map +1 -0
- package/dist/utils-AlGTGj_5.js +2 -0
- package/dist/utils-AlGTGj_5.js.map +1 -0
- package/dist/viem/index.d.ts +40 -20
- package/dist/viem/index.js +1 -136
- package/dist/viem/index.js.map +1 -1
- package/dist/worker.base-client-DGGSkczN.js +2 -0
- package/dist/worker.base-client-DGGSkczN.js.map +1 -0
- package/package.json +25 -13
- package/dist/chunk-AJFSZ47V.js +0 -5115
- package/dist/chunk-AJFSZ47V.js.map +0 -1
- package/dist/chunk-UE6IBC3M.js +0 -101
- package/dist/chunk-UE6IBC3M.js.map +0 -1
- package/dist/chunk-VRLLWHHL.js +0 -278
- package/dist/chunk-VRLLWHHL.js.map +0 -1
- package/dist/relayer-sdk.types-CFkzNzRy.d.ts +0 -293
- package/dist/relayer-sdk.worker.d.ts +0 -2
- package/dist/relayer-utils-D_3834H0.d.ts +0 -46
- package/dist/token.types-CRs1iJh7.d.ts +0 -447
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@ A TypeScript SDK for building privacy-preserving token applications using Fully
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
pnpm add @zama-fhe/sdk
|
|
9
|
+
# or
|
|
10
|
+
npm install @zama-fhe/sdk
|
|
11
|
+
# or
|
|
12
|
+
yarn add @zama-fhe/sdk
|
|
9
13
|
```
|
|
10
14
|
|
|
11
15
|
### Peer dependencies
|
|
@@ -21,22 +25,23 @@ pnpm add @zama-fhe/sdk
|
|
|
21
25
|
### Browser
|
|
22
26
|
|
|
23
27
|
```ts
|
|
24
|
-
import {
|
|
28
|
+
import { ZamaSDK, RelayerWeb, IndexedDBStorage } from "@zama-fhe/sdk";
|
|
25
29
|
import { ViemSigner } from "@zama-fhe/sdk/viem";
|
|
30
|
+
import { mainnet, sepolia } from "viem/chains";
|
|
26
31
|
|
|
27
32
|
// 1. Create signer and relayer
|
|
28
|
-
const signer = new ViemSigner(walletClient, publicClient);
|
|
33
|
+
const signer = new ViemSigner({ walletClient, publicClient });
|
|
29
34
|
|
|
30
|
-
const sdk = new
|
|
35
|
+
const sdk = new ZamaSDK({
|
|
31
36
|
relayer: new RelayerWeb({
|
|
32
37
|
getChainId: () => signer.getChainId(),
|
|
33
38
|
transports: {
|
|
34
|
-
[
|
|
35
|
-
relayerUrl: "https://relayer
|
|
39
|
+
[mainnet.id]: {
|
|
40
|
+
relayerUrl: "https://your-app.com/api/relayer/1",
|
|
36
41
|
network: "https://mainnet.infura.io/v3/YOUR_KEY",
|
|
37
42
|
},
|
|
38
|
-
[
|
|
39
|
-
relayerUrl: "https://relayer
|
|
43
|
+
[sepolia.id]: {
|
|
44
|
+
relayerUrl: "https://your-app.com/api/relayer/11155111",
|
|
40
45
|
network: "https://sepolia.infura.io/v3/YOUR_KEY",
|
|
41
46
|
},
|
|
42
47
|
},
|
|
@@ -51,7 +56,7 @@ const token = sdk.createToken("0xEncryptedERC20Address");
|
|
|
51
56
|
// const token = sdk.createToken("0xEncryptedERC20Address", "0xWrapperAddress");
|
|
52
57
|
|
|
53
58
|
// 3. Shield (wrap) public tokens into confidential tokens
|
|
54
|
-
const
|
|
59
|
+
const { txHash } = await token.shield(1000n);
|
|
55
60
|
|
|
56
61
|
// 4. Check decrypted balance
|
|
57
62
|
const balance = await token.balanceOf();
|
|
@@ -64,29 +69,30 @@ const transferTx = await token.confidentialTransfer("0xRecipient", 500n);
|
|
|
64
69
|
### Node.js
|
|
65
70
|
|
|
66
71
|
```ts
|
|
67
|
-
import {
|
|
68
|
-
import { RelayerNode } from "@zama-fhe/sdk/node";
|
|
72
|
+
import { ZamaSDK } from "@zama-fhe/sdk";
|
|
73
|
+
import { RelayerNode, asyncLocalStorage } from "@zama-fhe/sdk/node";
|
|
69
74
|
import { ViemSigner } from "@zama-fhe/sdk/viem";
|
|
75
|
+
import { mainnet, sepolia } from "viem/chains";
|
|
70
76
|
|
|
71
|
-
const signer = new ViemSigner(walletClient, publicClient);
|
|
77
|
+
const signer = new ViemSigner({ walletClient, publicClient });
|
|
72
78
|
|
|
73
|
-
const sdk = new
|
|
79
|
+
const sdk = new ZamaSDK({
|
|
74
80
|
relayer: new RelayerNode({
|
|
75
81
|
getChainId: () => signer.getChainId(),
|
|
76
82
|
poolSize: 4, // number of worker threads (default: min(CPUs, 4))
|
|
77
83
|
transports: {
|
|
78
|
-
[
|
|
79
|
-
relayerUrl: "https://relayer.zama.ai",
|
|
84
|
+
[mainnet.id]: {
|
|
80
85
|
network: "https://mainnet.infura.io/v3/YOUR_KEY",
|
|
86
|
+
auth: { __type: "ApiKeyHeader", value: process.env.RELAYER_API_KEY },
|
|
81
87
|
},
|
|
82
|
-
[
|
|
83
|
-
relayerUrl: "https://relayer.zama.ai",
|
|
88
|
+
[sepolia.id]: {
|
|
84
89
|
network: "https://sepolia.infura.io/v3/YOUR_KEY",
|
|
90
|
+
auth: { __type: "ApiKeyHeader", value: process.env.RELAYER_API_KEY },
|
|
85
91
|
},
|
|
86
92
|
},
|
|
87
93
|
}),
|
|
88
94
|
signer,
|
|
89
|
-
storage:
|
|
95
|
+
storage: asyncLocalStorage,
|
|
90
96
|
});
|
|
91
97
|
|
|
92
98
|
const token = sdk.createToken("0xEncryptedERC20Address");
|
|
@@ -95,15 +101,15 @@ const balance = await token.balanceOf();
|
|
|
95
101
|
|
|
96
102
|
## Core Concepts
|
|
97
103
|
|
|
98
|
-
###
|
|
104
|
+
### ZamaSDK
|
|
99
105
|
|
|
100
106
|
Entry point to the SDK. Composes a relayer backend with a signer and storage layer. Acts as a factory for token instances.
|
|
101
107
|
|
|
102
108
|
```ts
|
|
103
|
-
const sdk = new
|
|
109
|
+
const sdk = new ZamaSDK({
|
|
104
110
|
relayer, // RelayerSDK — either RelayerWeb (browser) or RelayerNode (Node.js)
|
|
105
111
|
signer, // GenericSigner
|
|
106
|
-
storage, //
|
|
112
|
+
storage, // GenericStorage
|
|
107
113
|
});
|
|
108
114
|
|
|
109
115
|
// Read-only — balances, metadata, decryption. No wrapper needed.
|
|
@@ -137,8 +143,8 @@ Full read/write interface for a single confidential ERC-20. Extends `ReadonlyTok
|
|
|
137
143
|
|
|
138
144
|
| Method | Description |
|
|
139
145
|
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
146
|
+
| `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). |
|
|
147
|
+
| `shieldETH(amount, value?)` | Shield (wrap) native ETH. `value` defaults to `amount`. Use this when the underlying token is the zero address (native ETH). |
|
|
142
148
|
| `unshield(amount, callbacks?)` | Unwrap a specific amount and finalize in one call. Orchestrates: unwrap → wait receipt → parse event → finalizeUnwrap. Optional `UnshieldCallbacks` for progress tracking. |
|
|
143
149
|
| `unshieldAll(callbacks?)` | Unwrap the entire balance and finalize in one call. Orchestrates: unwrapAll → wait receipt → parse event → finalizeUnwrap. Optional `UnshieldCallbacks` for progress tracking. |
|
|
144
150
|
| `unwrap(amount)` | Request unwrap for a specific amount (low-level, requires manual finalization). |
|
|
@@ -153,80 +159,206 @@ Full read/write interface for a single confidential ERC-20. Extends `ReadonlyTok
|
|
|
153
159
|
| `balanceOf(owner?)` | Decrypt and return the plaintext balance. |
|
|
154
160
|
| `decryptHandles(handles, owner?)` | Batch-decrypt arbitrary encrypted handles. |
|
|
155
161
|
|
|
156
|
-
All write methods return
|
|
162
|
+
All write methods return a `TransactionResult` object:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
interface TransactionResult {
|
|
166
|
+
txHash: Hex;
|
|
167
|
+
receipt: TransactionReceipt;
|
|
168
|
+
}
|
|
169
|
+
```
|
|
157
170
|
|
|
158
171
|
### ReadonlyToken
|
|
159
172
|
|
|
160
173
|
Read-only subset. No wrapper address needed.
|
|
161
174
|
|
|
162
|
-
| Method | Description
|
|
163
|
-
| ------------------------------------- |
|
|
164
|
-
| `balanceOf(owner?)` | Decrypt and return the plaintext balance.
|
|
165
|
-
| `confidentialBalanceOf(owner?)` | Return the raw encrypted balance handle (no decryption).
|
|
166
|
-
| `decryptBalance(handle, owner?)` | Decrypt a single encrypted handle.
|
|
167
|
-
| `decryptHandles(handles, owner?)` | Batch-decrypt handles in a single relayer call.
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
175
|
+
| Method | Description |
|
|
176
|
+
| ------------------------------------- | --------------------------------------------------------------------------- |
|
|
177
|
+
| `balanceOf(owner?)` | Decrypt and return the plaintext balance. |
|
|
178
|
+
| `confidentialBalanceOf(owner?)` | Return the raw encrypted balance handle (no decryption). |
|
|
179
|
+
| `decryptBalance(handle, owner?)` | Decrypt a single encrypted handle. |
|
|
180
|
+
| `decryptHandles(handles, owner?)` | Batch-decrypt handles in a single relayer call. |
|
|
181
|
+
| `allow()` | Ensure FHE decrypt credentials exist (generates/signs if needed). |
|
|
182
|
+
| `allow(...tokens)` _(static)_ | Pre-authorize multiple tokens with a single wallet signature. |
|
|
183
|
+
| `isAllowed()` | Whether a session signature is currently cached for this token. |
|
|
184
|
+
| `revoke()` | Clear the session signature for the connected wallet. |
|
|
185
|
+
| `credentials.allow(...addresses)` | Pre-authorize and cache the session signature for specific token addresses. |
|
|
186
|
+
| `credentials.revoke(...addresses?)` | Clear the session signature for the connected wallet. |
|
|
187
|
+
| `credentials.isAllowed()` | Whether a session signature is currently cached. |
|
|
188
|
+
| `credentials.isExpired(address?)` | Whether stored credentials are past their expiration time. |
|
|
189
|
+
| `credentials.clear()` | Delete stored credentials for the connected wallet. |
|
|
190
|
+
| `isConfidential()` | ERC-165 check for ERC-7984 support. |
|
|
191
|
+
| `isWrapper()` | ERC-165 check for wrapper interface. |
|
|
192
|
+
| `discoverWrapper(coordinatorAddress)` | Look up a wrapper for this token via the deployment coordinator. |
|
|
193
|
+
| `underlyingToken()` | Read the underlying ERC-20 address from a wrapper. |
|
|
194
|
+
| `allowance(wrapper, owner?)` | Read ERC-20 allowance of the underlying token. |
|
|
195
|
+
| `isZeroHandle(handle)` | Returns `true` if the handle is the zero sentinel. |
|
|
196
|
+
| `name()` / `symbol()` / `decimals()` | Read token metadata. |
|
|
177
197
|
|
|
178
198
|
Static methods for multi-token operations:
|
|
179
199
|
|
|
180
200
|
```ts
|
|
181
201
|
// Pre-authorize all tokens with a single wallet signature
|
|
182
202
|
const tokens = addresses.map((a) => sdk.createReadonlyToken(a));
|
|
183
|
-
await ReadonlyToken.
|
|
203
|
+
await ReadonlyToken.allow(...tokens);
|
|
184
204
|
// All subsequent decrypts reuse cached credentials — no more wallet prompts
|
|
185
205
|
|
|
186
206
|
// Decrypt balances for multiple tokens in parallel
|
|
187
|
-
const balances = await ReadonlyToken.
|
|
207
|
+
const balances = await ReadonlyToken.batchDecryptBalances(tokens, { owner });
|
|
188
208
|
|
|
189
209
|
// Decrypt pre-fetched handles for multiple tokens
|
|
190
|
-
const balances = await ReadonlyToken.batchDecryptBalances(tokens, handles, owner);
|
|
210
|
+
const balances = await ReadonlyToken.batchDecryptBalances(tokens, { handles, owner });
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Pending Unshield Persistence
|
|
214
|
+
|
|
215
|
+
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:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { savePendingUnshield, loadPendingUnshield, clearPendingUnshield } from "@zama-fhe/sdk";
|
|
219
|
+
|
|
220
|
+
// Save the unwrap hash before finalization
|
|
221
|
+
await savePendingUnshield(storage, wrapperAddress, unwrapTxHash);
|
|
222
|
+
|
|
223
|
+
// On next load, check for pending unshields
|
|
224
|
+
const pending = await loadPendingUnshield(storage, wrapperAddress);
|
|
225
|
+
if (pending) {
|
|
226
|
+
await token.resumeUnshield(pending);
|
|
227
|
+
await clearPendingUnshield(storage, wrapperAddress);
|
|
228
|
+
}
|
|
191
229
|
```
|
|
192
230
|
|
|
193
231
|
### Storage
|
|
194
232
|
|
|
195
|
-
FHE credentials (keypair +
|
|
233
|
+
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:
|
|
234
|
+
|
|
235
|
+
**Credential storage** (`storage`) — persists encrypted keypairs:
|
|
236
|
+
|
|
237
|
+
| Storage | Use case |
|
|
238
|
+
| ------------------- | -------------------------------------------------------- |
|
|
239
|
+
| `indexedDBStorage` | Browser apps — persists across page reloads and sessions |
|
|
240
|
+
| `memoryStorage` | Tests, scripts, throwaway sessions |
|
|
241
|
+
| `asyncLocalStorage` | Node.js servers — isolate credentials per request |
|
|
242
|
+
| Custom | Implement the `GenericStorage` interface |
|
|
243
|
+
|
|
244
|
+
**Session storage** (`sessionStorage`) — holds wallet signatures for the current session:
|
|
196
245
|
|
|
197
|
-
| Storage
|
|
198
|
-
|
|
|
199
|
-
|
|
|
200
|
-
| `
|
|
201
|
-
|
|
|
202
|
-
| Custom | Implement the `GenericStringStorage` interface. |
|
|
246
|
+
| Storage | Use case |
|
|
247
|
+
| ---------------------- | ----------------------------------------------------------- |
|
|
248
|
+
| Default (in-memory) | Standard web apps — signature lost on reload, user re-signs |
|
|
249
|
+
| `chromeSessionStorage` | MV3 web extensions — survives service worker restarts |
|
|
250
|
+
| Custom | Implement the `GenericStorage` interface |
|
|
203
251
|
|
|
204
252
|
```ts
|
|
205
|
-
interface
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
253
|
+
interface GenericStorage<T = unknown> {
|
|
254
|
+
get(key: string): Promise<T | null>;
|
|
255
|
+
set(key: string, value: T): Promise<void>;
|
|
256
|
+
delete(key: string): Promise<void>;
|
|
209
257
|
}
|
|
210
258
|
```
|
|
211
259
|
|
|
260
|
+
#### Web Extension Example
|
|
261
|
+
|
|
262
|
+
For MV3 extensions, use the built-in `chromeSessionStorage` singleton to share the wallet signature across popup, background, and content script contexts:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
import { ZamaSDK, indexedDBStorage, chromeSessionStorage } from "@zama-fhe/sdk";
|
|
266
|
+
|
|
267
|
+
const sdk = new ZamaSDK({
|
|
268
|
+
relayer,
|
|
269
|
+
signer,
|
|
270
|
+
storage: indexedDBStorage, // encrypted keypairs (persistent)
|
|
271
|
+
sessionStorage: chromeSessionStorage, // wallet signatures (ephemeral, shared across contexts)
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
212
275
|
## Configuration Reference
|
|
213
276
|
|
|
214
|
-
### `
|
|
277
|
+
### `ZamaSDKConfig`
|
|
215
278
|
|
|
216
|
-
| Field
|
|
217
|
-
|
|
|
218
|
-
| `relayer`
|
|
219
|
-
| `signer`
|
|
220
|
-
| `storage`
|
|
279
|
+
| Field | Type | Description |
|
|
280
|
+
| ---------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
281
|
+
| `relayer` | `RelayerSDK` | Relayer backend (`RelayerWeb` or `RelayerNode` instance) |
|
|
282
|
+
| `signer` | `GenericSigner` | Wallet signer interface. |
|
|
283
|
+
| `storage` | `GenericStorage` | Credential storage backend. |
|
|
284
|
+
| `sessionStorage` | `GenericStorage` | Optional. Session storage for wallet signatures. Default: in-memory (lost on reload). Use `chrome.storage.session` for web extensions. |
|
|
285
|
+
| `keypairTTL` | `number` | Optional. Seconds the ML-KEM re-encryption keypair remains valid. Default: `86400` (1 day). Must be positive. |
|
|
286
|
+
| `sessionTTL` | `number` | Optional. Seconds the session signature remains valid. Default: `2592000` (30 days). `0` = re-sign every operation. |
|
|
287
|
+
| `onEvent` | `ZamaSDKEventListener` | Optional. Structured event listener for debugging. |
|
|
288
|
+
|
|
289
|
+
#### Structured Event Listener
|
|
290
|
+
|
|
291
|
+
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.
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
const sdk = new ZamaSDK({
|
|
295
|
+
relayer,
|
|
296
|
+
signer,
|
|
297
|
+
storage,
|
|
298
|
+
onEvent: ({ type, tokenAddress, ...event }) => {
|
|
299
|
+
console.debug(`[Zama] ${type}`, {
|
|
300
|
+
tokenAddress: tokenAddress?.slice(0, 10),
|
|
301
|
+
...event,
|
|
302
|
+
});
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Event types:**
|
|
308
|
+
|
|
309
|
+
| Category | Events | Key fields |
|
|
310
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
311
|
+
| Credentials | `credentials:loading`, `credentials:cached`, `credentials:expired`, `credentials:creating`, `credentials:created`, `credentials:revoked`, `credentials:allowed` | `contractAddresses` |
|
|
312
|
+
| Encryption | `encrypt:start`, `encrypt:end`, `encrypt:error` | `durationMs` (end/error), `error` (error) |
|
|
313
|
+
| Decryption | `decrypt:start`, `decrypt:end`, `decrypt:error` | `durationMs` (end/error), `error` (error) |
|
|
314
|
+
| Transactions | `transaction:error` | `operation` (`"transfer"`, `"wrap"`, `"approve"`, etc.), `error` |
|
|
315
|
+
| Write confirmations | `wrap:submitted`, `transfer:submitted`, `transferFrom:submitted`, `approve:submitted`, `approveUnderlying:submitted`, `unwrap:submitted`, `finalizeUnwrap:submitted` | `txHash` |
|
|
316
|
+
| Unshield orchestration | `unshield:phase1_submitted`, `unshield:phase2_started`, `unshield:phase2_submitted` | `txHash`, `operationId` |
|
|
317
|
+
|
|
318
|
+
All events carry `tokenAddress`, `timestamp`, and an optional `operationId` (set on unshield phase events to correlate multi-step operations).
|
|
319
|
+
|
|
320
|
+
**Dispatching events to other systems:**
|
|
321
|
+
|
|
322
|
+
The `onEvent` callback is a simple function — you can bridge it to any event system:
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
// Fan out to multiple listeners with EventEmitter
|
|
326
|
+
import { EventEmitter } from "events";
|
|
327
|
+
const emitter = new EventEmitter();
|
|
328
|
+
const sdk = new ZamaSDK({
|
|
329
|
+
// ...
|
|
330
|
+
onEvent: (event) => emitter.emit(event.type, event),
|
|
331
|
+
});
|
|
332
|
+
emitter.on("encrypt:start", (e) => {
|
|
333
|
+
/* listener A */
|
|
334
|
+
});
|
|
335
|
+
emitter.on("encrypt:start", (e) => {
|
|
336
|
+
/* listener B */
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// Bridge to DOM CustomEvent (e.g. for cross-framework communication)
|
|
340
|
+
const sdk = new ZamaSDK({
|
|
341
|
+
// ...
|
|
342
|
+
onEvent: (event) => window.dispatchEvent(new CustomEvent(event.type, { detail: event })),
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Collect into React state
|
|
346
|
+
const [events, setEvents] = useState<ZamaSDKEvent[]>([]);
|
|
347
|
+
const sdk = new ZamaSDK({
|
|
348
|
+
// ...
|
|
349
|
+
onEvent: (event) => setEvents((prev) => [...prev, event]),
|
|
350
|
+
});
|
|
351
|
+
```
|
|
221
352
|
|
|
222
353
|
### `RelayerWebConfig` (browser)
|
|
223
354
|
|
|
224
|
-
| Field | Type | Description
|
|
225
|
-
| ------------ | ------------------------------------- |
|
|
226
|
-
| `getChainId` | `() => Promise<number>` | Resolve the current chain ID. Called lazily; the worker is re-initialized on chain change.
|
|
227
|
-
| `transports` | `Record<number, FhevmInstanceConfig>` | Chain-specific configs keyed by chain ID (includes relayerUrl, network, contract addresses).
|
|
228
|
-
| `security` | `RelayerWebSecurityConfig` | Optional. Security options (see below).
|
|
229
|
-
| `logger` | `GenericLogger` | Optional. Logger for worker lifecycle and request timing.
|
|
355
|
+
| Field | Type | Description |
|
|
356
|
+
| ------------ | ------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
357
|
+
| `getChainId` | `() => Promise<number>` | Resolve the current chain ID. Called lazily; the worker is re-initialized on chain change. |
|
|
358
|
+
| `transports` | `Record<number, FhevmInstanceConfig>` | Chain-specific configs keyed by chain ID (includes relayerUrl, network, contract addresses). |
|
|
359
|
+
| `security` | `RelayerWebSecurityConfig` | Optional. Security options (see below). |
|
|
360
|
+
| `logger` | `GenericLogger` | Optional. Logger for worker lifecycle and request timing. |
|
|
361
|
+
| `threads` | `number` | Optional. WASM thread count for parallel FHE ops (4–8 recommended). Requires COOP/COEP headers. |
|
|
230
362
|
|
|
231
363
|
#### `RelayerWebSecurityConfig`
|
|
232
364
|
|
|
@@ -235,6 +367,8 @@ interface GenericStringStorage {
|
|
|
235
367
|
| `getCsrfToken` | `() => string` | Optional. Resolve the CSRF token before each authenticated network request. |
|
|
236
368
|
| `integrityCheck` | `boolean` | Optional. Verify SHA-384 integrity of the CDN bundle. Defaults to `true`. Set `false` for tests. |
|
|
237
369
|
|
|
370
|
+
> **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.
|
|
371
|
+
|
|
238
372
|
### `RelayerNodeConfig` (Node.js)
|
|
239
373
|
|
|
240
374
|
| Field | Type | Description |
|
|
@@ -252,21 +386,26 @@ Both the main entry (`@zama-fhe/sdk`) and the `/node` sub-path re-export preset
|
|
|
252
386
|
| `MainnetConfig` | 1 | Mainnet contract addresses. |
|
|
253
387
|
| `HardhatConfig` | 31337 | Local Hardhat node addresses. |
|
|
254
388
|
|
|
255
|
-
Each preset provides contract addresses and default
|
|
389
|
+
Each preset provides contract addresses and default relayer URL. Override `network` (RPC URL) for your environment. Browser apps should override `relayerUrl` with a proxy; server-side apps add `auth`:
|
|
256
390
|
|
|
257
391
|
```ts
|
|
258
392
|
import { SepoliaConfig, MainnetConfig } from "@zama-fhe/sdk";
|
|
259
393
|
|
|
394
|
+
// Browser — proxy through your backend
|
|
260
395
|
const transports = {
|
|
261
|
-
[
|
|
396
|
+
[SepoliaConfig.chainId]: {
|
|
262
397
|
...SepoliaConfig,
|
|
263
|
-
relayerUrl: "/api/
|
|
398
|
+
relayerUrl: "https://your-app.com/api/relayer/11155111",
|
|
264
399
|
network: "https://sepolia.infura.io/v3/KEY",
|
|
265
400
|
},
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// Node.js — auth is safe server-side
|
|
404
|
+
const transports = {
|
|
405
|
+
[SepoliaConfig.chainId]: {
|
|
406
|
+
...SepoliaConfig,
|
|
407
|
+
network: "https://sepolia.infura.io/v3/KEY",
|
|
408
|
+
auth: { __type: "ApiKeyHeader", value: process.env.RELAYER_API_KEY },
|
|
270
409
|
},
|
|
271
410
|
};
|
|
272
411
|
```
|
|
@@ -279,10 +418,10 @@ The `GenericSigner` interface has six methods. Any Web3 library can back it.
|
|
|
279
418
|
interface GenericSigner {
|
|
280
419
|
getChainId(): Promise<number>;
|
|
281
420
|
getAddress(): Promise<Address>;
|
|
282
|
-
signTypedData(typedData: EIP712TypedData): Promise<
|
|
283
|
-
writeContract(config: ContractCallConfig): Promise<
|
|
421
|
+
signTypedData(typedData: EIP712TypedData): Promise<Hex>;
|
|
422
|
+
writeContract(config: ContractCallConfig): Promise<Hex>;
|
|
284
423
|
readContract(config: ContractCallConfig): Promise<unknown>;
|
|
285
|
-
waitForTransactionReceipt(hash:
|
|
424
|
+
waitForTransactionReceipt(hash: Hex): Promise<TransactionReceipt>;
|
|
286
425
|
}
|
|
287
426
|
```
|
|
288
427
|
|
|
@@ -293,7 +432,7 @@ interface GenericSigner {
|
|
|
293
432
|
```ts
|
|
294
433
|
import { ViemSigner } from "@zama-fhe/sdk/viem";
|
|
295
434
|
|
|
296
|
-
const signer = new ViemSigner(walletClient, publicClient);
|
|
435
|
+
const signer = new ViemSigner({ walletClient, publicClient });
|
|
297
436
|
```
|
|
298
437
|
|
|
299
438
|
**ethers** — `@zama-fhe/sdk/ethers`
|
|
@@ -301,14 +440,14 @@ const signer = new ViemSigner(walletClient, publicClient);
|
|
|
301
440
|
```ts
|
|
302
441
|
import { EthersSigner } from "@zama-fhe/sdk/ethers";
|
|
303
442
|
|
|
304
|
-
const signer = new EthersSigner(ethersSigner);
|
|
443
|
+
const signer = new EthersSigner({ signer: ethersSigner });
|
|
305
444
|
```
|
|
306
445
|
|
|
307
446
|
## Contract Call Builders
|
|
308
447
|
|
|
309
448
|
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.
|
|
310
449
|
|
|
311
|
-
> **High-level vs low-level:** `token.
|
|
450
|
+
> **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.
|
|
312
451
|
|
|
313
452
|
```ts
|
|
314
453
|
interface ContractCallConfig {
|
|
@@ -466,8 +605,8 @@ Individual topic hashes are accessible via the `Topics` object: `Topics.Confiden
|
|
|
466
605
|
| `decodeUnwrapRequested(log)` | `UnwrapRequestedEvent \| null` — `{ receiver, encryptedAmount }` |
|
|
467
606
|
| `decodeUnwrappedFinalized(log)` | `UnwrappedFinalizedEvent \| null` — `{ burntAmountHandle, finalizeSuccess, burnAmount, unwrapAmount, feeAmount, ... }` |
|
|
468
607
|
| `decodeUnwrappedStarted(log)` | `UnwrappedStartedEvent \| null` — `{ returnVal, requestId, txId, to, refund, requestedAmount, burnAmount }` |
|
|
469
|
-
| `
|
|
470
|
-
| `
|
|
608
|
+
| `decodeOnChainEvent(log)` | `OnChainEvent \| null` — tries all decoders |
|
|
609
|
+
| `decodeOnChainEvents(logs)` | `OnChainEvent[]` — batch decode, skips unrecognized logs |
|
|
471
610
|
|
|
472
611
|
### Finder Helpers
|
|
473
612
|
|
|
@@ -535,7 +674,7 @@ interface ActivityItem {
|
|
|
535
674
|
fee?: ActivityAmount;
|
|
536
675
|
success?: boolean;
|
|
537
676
|
metadata: ActivityLogMetadata;
|
|
538
|
-
rawEvent:
|
|
677
|
+
rawEvent: OnChainEvent;
|
|
539
678
|
}
|
|
540
679
|
|
|
541
680
|
interface ActivityLogMetadata {
|
|
@@ -547,10 +686,10 @@ interface ActivityLogMetadata {
|
|
|
547
686
|
|
|
548
687
|
## Error Handling
|
|
549
688
|
|
|
550
|
-
All SDK errors extend `
|
|
689
|
+
All SDK errors extend `ZamaError`. Use `instanceof` to catch specific error types:
|
|
551
690
|
|
|
552
691
|
```ts
|
|
553
|
-
import {
|
|
692
|
+
import { ZamaError, SigningRejectedError, EncryptionFailedError } from "@zama-fhe/sdk";
|
|
554
693
|
|
|
555
694
|
try {
|
|
556
695
|
await token.confidentialTransfer(to, amount);
|
|
@@ -561,7 +700,7 @@ try {
|
|
|
561
700
|
if (error instanceof EncryptionFailedError) {
|
|
562
701
|
// FHE encryption failed
|
|
563
702
|
}
|
|
564
|
-
if (error instanceof
|
|
703
|
+
if (error instanceof ZamaError) {
|
|
565
704
|
// Any other SDK error — check error.code for details
|
|
566
705
|
}
|
|
567
706
|
}
|
|
@@ -577,10 +716,24 @@ try {
|
|
|
577
716
|
| `DecryptionFailedError` | `DECRYPTION_FAILED` | FHE decryption operation failed. |
|
|
578
717
|
| `ApprovalFailedError` | `APPROVAL_FAILED` | ERC-20 approval transaction failed. |
|
|
579
718
|
| `TransactionRevertedError` | `TRANSACTION_REVERTED` | On-chain transaction reverted. |
|
|
580
|
-
| `
|
|
719
|
+
| `InvalidKeypairError` | `INVALID_KEYPAIR` | Relayer rejected FHE keypair (stale or expired). |
|
|
581
720
|
| `NoCiphertextError` | `NO_CIPHERTEXT` | No FHE ciphertext exists for this account (e.g. never shielded). |
|
|
582
721
|
| `RelayerRequestFailedError` | `RELAYER_REQUEST_FAILED` | Relayer HTTP error. Carries a `statusCode` property with the HTTP status. |
|
|
583
722
|
|
|
723
|
+
### `matchZamaError`
|
|
724
|
+
|
|
725
|
+
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.
|
|
726
|
+
|
|
727
|
+
```ts
|
|
728
|
+
import { matchZamaError } from "@zama-fhe/sdk";
|
|
729
|
+
|
|
730
|
+
matchZamaError(error, {
|
|
731
|
+
SIGNING_REJECTED: () => toast("Please approve in wallet"),
|
|
732
|
+
TRANSACTION_REVERTED: (e) => toast(`Tx failed: ${e.message}`),
|
|
733
|
+
_: () => toast("Unknown error"),
|
|
734
|
+
});
|
|
735
|
+
```
|
|
736
|
+
|
|
584
737
|
**Distinguishing "no ciphertext" from "zero balance":**
|
|
585
738
|
|
|
586
739
|
```ts
|