@miden-sdk/react 0.14.5 → 0.14.9

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/CLAUDE.md CHANGED
@@ -5,7 +5,7 @@
5
5
  ```bash
6
6
  npm install @miden-sdk/react @miden-sdk/miden-sdk
7
7
  # or
8
- yarn add @miden-sdk/react @miden-sdk/miden-sdk
8
+ pnpm add @miden-sdk/react @miden-sdk/miden-sdk
9
9
  ```
10
10
 
11
11
  ## Getting Started
@@ -45,36 +45,37 @@ function App() {
45
45
 
46
46
  ## Reading Data (Query Hooks)
47
47
 
48
- All query hooks return `{ data, isLoading, error, refetch }`.
48
+ Query hooks return `{ ...data, isLoading, error, refetch }` — the data fields are spread directly onto the result object, with hook-specific names (no generic `data` field).
49
49
 
50
50
  ### List Accounts
51
51
  ```tsx
52
- const { data: accounts, isLoading } = useAccounts();
52
+ const { accounts, wallets, faucets, isLoading } = useAccounts();
53
53
 
54
- // accounts.wallets - regular accounts
55
- // accounts.faucets - token faucets
56
- // accounts.all - everything
54
+ // wallets - regular accounts
55
+ // faucets - token faucets
56
+ // accounts - both, combined
57
57
  ```
58
58
 
59
59
  ### Get Account Details
60
60
  ```tsx
61
- const { data: account } = useAccount(accountId);
61
+ const { account, isLoading } = useAccount(accountId);
62
62
 
63
- // account.id, account.nonce, account.bech32id()
64
- // account.balance(faucetId) - get token balance
63
+ // account.id(), account.nonce(), account.bech32id()
64
+ // account.vault().getBalance(assetId) - get token balance
65
65
  ```
66
66
 
67
67
  ### Get Notes
68
68
  ```tsx
69
- const { data: notes } = useNotes();
69
+ const { notes, consumableNotes, noteSummaries, consumableNoteSummaries } = useNotes();
70
70
 
71
- // notes.input - incoming notes
72
- // notes.consumable - ready to claim
71
+ // notes - all input notes for this account
72
+ // consumableNotes - subset that's ready to claim
73
+ // noteSummaries / consumableNoteSummaries - same lists, projected to UI-friendly summaries
73
74
  ```
74
75
 
75
76
  ### Check Sync Status
76
77
  ```tsx
77
- const { syncHeight, isSyncing, sync } = useSyncState();
78
+ const { syncHeight, isSyncing, lastSyncTime, sync, error } = useSyncState();
78
79
 
79
80
  // Manual sync
80
81
  await sync();
@@ -82,19 +83,19 @@ await sync();
82
83
 
83
84
  ### Get Token Metadata
84
85
  ```tsx
85
- const { data: metadata } = useAssetMetadata(faucetId);
86
+ const { metadata, isLoading } = useAssetMetadata(assetId);
86
87
  // metadata.symbol, metadata.decimals
87
88
  ```
88
89
 
89
90
  ## Writing Data (Mutation Hooks)
90
91
 
91
- All mutation hooks return `{ mutate, data, isLoading, stage, error, reset }`.
92
+ Mutation hooks return `{ <action>, result, isLoading, stage, error, reset }` — the action callback is named after the hook (`send`, `consume`, `mint`, ...) and the resolved value is on `result`.
92
93
 
93
94
  **Transaction stages:** `idle` → `executing` → `proving` → `submitting` → `complete`
94
95
 
95
96
  ### Create Wallet
96
97
  ```tsx
97
- const { mutate: createWallet, isLoading } = useCreateWallet();
98
+ const { createWallet, isLoading } = useCreateWallet();
98
99
 
99
100
  const account = await createWallet({
100
101
  storageMode: "private", // "private" | "public" | "network"
@@ -103,12 +104,12 @@ const account = await createWallet({
103
104
 
104
105
  ### Send Tokens
105
106
  ```tsx
106
- const { mutate: send, stage } = useSend();
107
+ const { send, stage } = useSend();
107
108
 
108
109
  await send({
109
110
  from: senderAccountId,
110
111
  to: recipientAccountId,
111
- faucetId: tokenFaucetId,
112
+ assetId: tokenFaucetId,
112
113
  amount: 1000n,
113
114
  noteType: "private", // "private" | "public"
114
115
  });
@@ -116,20 +117,20 @@ await send({
116
117
 
117
118
  ### Send to Multiple Recipients
118
119
  ```tsx
119
- const { mutate: multiSend } = useMultiSend();
120
+ const { multiSend } = useMultiSend();
120
121
 
121
122
  await multiSend({
122
123
  from: senderAccountId,
123
- outputs: [
124
- { to: recipient1, faucetId, amount: 500n },
125
- { to: recipient2, faucetId, amount: 300n },
124
+ recipients: [
125
+ { to: recipient1, assetId, amount: 500n },
126
+ { to: recipient2, assetId, amount: 300n },
126
127
  ],
127
128
  });
128
129
  ```
129
130
 
130
131
  ### Claim Notes
131
132
  ```tsx
132
- const { mutate: consume } = useConsume();
133
+ const { consume } = useConsume();
133
134
 
134
135
  await consume({
135
136
  accountId: myAccountId,
@@ -139,7 +140,7 @@ await consume({
139
140
 
140
141
  ### Mint Tokens (Faucet Owner)
141
142
  ```tsx
142
- const { mutate: mint } = useMint();
143
+ const { mint } = useMint();
143
144
 
144
145
  await mint({
145
146
  faucetId: myFaucetId,
@@ -150,7 +151,7 @@ await mint({
150
151
 
151
152
  ### Create Faucet
152
153
  ```tsx
153
- const { mutate: createFaucet } = useCreateFaucet();
154
+ const { createFaucet } = useCreateFaucet();
154
155
 
155
156
  const faucet = await createFaucet({
156
157
  symbol: "TOKEN",
@@ -165,11 +166,11 @@ const faucet = await createFaucet({
165
166
  ### Show Transaction Progress
166
167
  ```tsx
167
168
  function SendButton() {
168
- const { mutate: send, stage, isLoading, error } = useSend();
169
+ const { send, stage, isLoading, error } = useSend();
169
170
 
170
171
  const handleSend = async () => {
171
172
  try {
172
- await send({ from, to, faucetId, amount });
173
+ await send({ from, to, assetId, amount });
173
174
  } catch (err) {
174
175
  console.error("Transaction failed:", err);
175
176
  }
@@ -207,11 +208,11 @@ const text = formatNoteSummary(summary); // "1.5 TOKEN"
207
208
 
208
209
  ### Wait for Transaction Confirmation
209
210
  ```tsx
210
- const { mutate: waitForCommit } = useWaitForCommit();
211
+ const { waitForCommit } = useWaitForCommit();
211
212
 
212
213
  // After sending
213
214
  const result = await send({ ... });
214
- await waitForCommit({ transactionId: result.transactionId });
215
+ await waitForCommit({ txId: result.txId });
215
216
  ```
216
217
 
217
218
  ### Access Client Directly
@@ -316,7 +317,8 @@ import { SignerContext } from "@miden-sdk/react";
316
317
  storeName: `mywallet_${userAddress}`, // unique per user for DB isolation
317
318
  isConnected: true,
318
319
  accountConfig: {
319
- publicKey: userPublicKeyCommitment, // Uint8Array
320
+ publicKeyCommitment: userPublicKeyCommitment, // Uint8Array
321
+ accountType: "RegularAccountUpdatableCode",
320
322
  storageMode: "private",
321
323
  },
322
324
  signCb: async (pubKey, signingInputs) => {
@@ -377,23 +379,40 @@ account.bech32id(); // "miden1qy35..."
377
379
 
378
380
  ## Hook Reference
379
381
 
380
- | Hook | Returns | Purpose |
381
- |------|---------|---------|
382
- | `useAccounts()` | `{ wallets, faucets, all }` | List all accounts |
383
- | `useAccount(id)` | `Account` | Account details + balances |
384
- | `useNotes(filter?)` | `{ input, consumable }` | Available notes |
385
- | `useSyncState()` | `{ syncHeight, sync() }` | Sync status |
386
- | `useAssetMetadata(id)` | `{ symbol, decimals }` | Token info |
387
- | `useCreateWallet()` | `Account` | Create wallet |
388
- | `useCreateFaucet()` | `Account` | Create faucet |
389
- | `useImportAccount()` | `Account` | Import account |
390
- | `useSend()` | `TransactionResult` | Send tokens |
391
- | `useMultiSend()` | `TransactionResult` | Multi-recipient send |
392
- | `useMint()` | `TransactionResult` | Mint tokens |
393
- | `useConsume()` | `TransactionResult` | Claim notes |
394
- | `useSwap()` | `TransactionResult` | Atomic swap |
395
- | `useTransaction()` | `TransactionResult` | Custom transaction |
396
- | `useCompile()` | `{ component, txScript, noteScript }` | Compile MASM into `AccountComponent` / `TransactionScript` / `NoteScript` |
382
+ Query hooks return `{ ...data, isLoading, error, refetch }`. Mutation hooks return `{ <action>, result, isLoading, stage, error, reset }`.
383
+
384
+ ### Query (read)
385
+ | Hook | Data fields | Purpose |
386
+ |------|-------------|---------|
387
+ | `useAccounts()` | `accounts`, `wallets`, `faucets` | List local accounts |
388
+ | `useAccount(id)` | `account` | Account details + balances |
389
+ | `useNotes(filter?)` | `notes`, `consumableNotes`, `noteSummaries`, `consumableNoteSummaries` | Input notes + UI summaries |
390
+ | `useNoteStream(filter?)` | streaming variant of `useNotes` | Auto-updates as notes arrive |
391
+ | `useSyncState()` | `syncHeight`, `isSyncing`, `lastSyncTime`, `sync()` | Sync status + manual trigger |
392
+ | `useSyncControl()` | `pause()`, `resume()`, `isPaused` | Pause/resume the auto-sync timer |
393
+ | `useAssetMetadata(id)` | `metadata: { symbol, decimals }` | Token info |
394
+ | `useTransactionHistory(...)` | `transactions` | Local transaction log |
395
+ | `useSessionAccount()` | `account` | The signer's connected account |
396
+ | `useWaitForNotes(...)` | resolves when matching notes appear | Pull-style note waiting |
397
+
398
+ ### Mutation (write)
399
+ | Hook | Action | Returns on success |
400
+ |------|--------|--------------------|
401
+ | `useCreateWallet()` | `createWallet({ storageMode })` | `Account` |
402
+ | `useCreateFaucet()` | `createFaucet({ symbol, decimals, ... })` | `Account` |
403
+ | `useImportAccount()` | `importAccount(...)` | `Account` |
404
+ | `useImportNote()` | `importNote(...)` | imported `InputNoteRecord` |
405
+ | `useExportNote()` | `exportNote(...)` | serialized note bytes |
406
+ | `useImportStore()` / `useExportStore()` | store import/export | bytes / `void` |
407
+ | `useSend()` | `send({ from, to, assetId, amount, noteType })` | `SendResult` (with `txId`, `note`) |
408
+ | `useMultiSend()` | `multiSend({ from, recipients })` | `TransactionResult` |
409
+ | `useMint()` | `mint({ faucetId, to, amount })` | `TransactionResult` |
410
+ | `useConsume()` | `consume({ accountId, notes })` | `TransactionResult` |
411
+ | `useSwap()` | `swap({ ... })` | `TransactionResult` |
412
+ | `useTransaction()` | `transact({ ... })` | `TransactionResult` (custom tx) |
413
+ | `useExecuteProgram()` | `execute(...)` | program output |
414
+ | `useCompile()` | `compile({ source })` | `{ component, txScript, noteScript }` |
415
+ | `useWaitForCommit()` | `waitForCommit({ txId })` | resolves when committed on-chain |
397
416
 
398
417
  ## Type Imports
399
418
 
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Miden
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -21,7 +21,7 @@ React hooks library for the Miden Web Client. Provides a simple, ergonomic inter
21
21
  ```bash
22
22
  npm install @miden-sdk/react @miden-sdk/miden-sdk
23
23
  # or
24
- yarn add @miden-sdk/react @miden-sdk/miden-sdk
24
+ pnpm add @miden-sdk/react @miden-sdk/miden-sdk
25
25
  # or
26
26
  pnpm add @miden-sdk/react @miden-sdk/miden-sdk
27
27
  ```
@@ -32,14 +32,14 @@ From `packages/react-sdk`:
32
32
 
33
33
  ```bash
34
34
  # Unit tests
35
- yarn test:unit
35
+ pnpm test:unit
36
36
 
37
37
  # Integration tests (Playwright) in test/
38
38
  # Build the web-client dist first:
39
- cd ../../crates/web-client && yarn build
39
+ cd ../../crates/web-client && pnpm build
40
40
  cd ../../packages/react-sdk
41
- yarn playwright install --with-deps
42
- yarn test:integration
41
+ pnpm exec playwright install --with-deps
42
+ pnpm test:integration
43
43
  ```
44
44
 
45
45
  ## Quick Start
@@ -119,14 +119,25 @@ function App() {
119
119
  }
120
120
  ```
121
121
 
122
- ## Next.js & SSR
122
+ ## Subpaths: Eager / Lazy × ST / MT
123
+
124
+ The React SDK ships **four** bundle variants built from a single source tree. The two axes are independent:
125
+
126
+ - **WASM init timing** — _eager_ awaits at SDK load (TLA); _lazy_ leaves init to `MidenClient.ready()` or first awaiting method.
127
+ - **WASM threading model** — _ST_ (single-threaded) loads anywhere; _MT_ (multi-threaded via `wasm-bindgen-rayon`) parallelizes proving but **requires the page to be cross-origin-isolated**.
128
+
129
+ | Subpath | SDK variant | When to use |
130
+ | ---------------------------------- | --------------------------------- | ---------------------------------------------------------- |
131
+ | `@miden-sdk/react` | eager + ST | Plain browser apps, Vite, CRA, esbuild — no host control needed. |
132
+ | `@miden-sdk/react/lazy` | lazy + ST | Next.js / SSR, Capacitor (iOS/Android WKWebView). |
133
+ | `@miden-sdk/react/mt` | eager + MT | dApps with COOP/COEP set; want fast proving and tolerate TLA. |
134
+ | `@miden-sdk/react/mt/lazy` | lazy + MT | dApps with COOP/COEP set, on Next.js or anywhere TLA can't run. |
123
135
 
124
- The React SDK is Next.js-compatible out of the box: it ships two bundle variants built from a single source tree, and both internally import `@miden-sdk/miden-sdk/lazy` the lazy entry point that does **not** use a top-level `await`. Nothing initializes WASM at module evaluation, so server-side rendering never hangs.
136
+ All four exports have identical APIs. The choice affects which `@miden-sdk/miden-sdk` variant your bundler resolves and therefore the underlying WASM behavior.
125
137
 
126
- - Default (`@miden-sdk/react`) — internally consumes the eager SDK. Use this in plain browser apps, Vite, CRA.
127
- - `@miden-sdk/react/lazy` — internally consumes the lazy SDK. Use this in Next.js (app router or pages), SSR, or inside a Capacitor iOS/Android WKWebView host (the wallet shell uses this).
138
+ ### Next.js / SSR
128
139
 
129
- Both exports have identical APIs. The choice only affects which `@miden-sdk/miden-sdk` variant your bundler ends up resolving.
140
+ The lazy variants (`/lazy`, `/mt/lazy`) do not run a top-level `await` at module evaluation. Server-side rendering never hangs on WASM init. Use these in any Next.js app or Capacitor host:
130
141
 
131
142
  ```tsx
132
143
  // Next.js: app/providers.tsx
@@ -141,6 +152,50 @@ export function Providers({ children }: { children: React.ReactNode }) {
141
152
 
142
153
  `MidenProvider` gates child rendering on `isReady`, so you don't have to await anything manually in components — hooks already fire only after WASM is initialized.
143
154
 
155
+ ### Multi-threaded proving (`/mt`, `/mt/lazy`)
156
+
157
+ The MT variants enable `wasm-bindgen-rayon` for ~3–5× faster local proving on commodity laptops. Same API surface as the ST variants. Two extra requirements compared to ST:
158
+
159
+ **1. The page must be cross-origin-isolated.** Set the host response headers:
160
+
161
+ ```
162
+ Cross-Origin-Opener-Policy: same-origin
163
+ Cross-Origin-Embedder-Policy: require-corp
164
+ ```
165
+
166
+ Without those, the browser refuses to construct `WebAssembly.Memory({ shared: true })` and the MT WASM fails to instantiate at SDK load. See the underlying [`@miden-sdk/miden-sdk` README](https://github.com/0xMiden/web-sdk/blob/main/crates/web-client/README.md#setting-cross-origin-isolation-headers) for snippets per host (Vite, Next.js, Express, browser-extension manifests). COEP also blocks cross-origin resources unless they carry CORP / proper CORS — opt in deliberately.
167
+
168
+ **2. Bring up the rayon thread pool once at startup.** Re-exported as `initThreadPool(n)`. The React SDK does NOT call this for you — consumers must `await` it before the first transaction:
169
+
170
+ ```tsx
171
+ "use client";
172
+ import { useEffect } from "react";
173
+ import { MidenProvider, useMiden } from "@miden-sdk/react/mt/lazy";
174
+ import { initThreadPool } from "@miden-sdk/miden-sdk/mt/lazy";
175
+
176
+ function ThreadPoolBoot() {
177
+ const { isReady } = useMiden();
178
+ useEffect(() => {
179
+ if (!isReady) return;
180
+ void initThreadPool(navigator.hardwareConcurrency);
181
+ }, [isReady]);
182
+ return null;
183
+ }
184
+
185
+ export function Providers({ children }: { children: React.ReactNode }) {
186
+ return (
187
+ <MidenProvider config={{ rpcUrl: "testnet" }}>
188
+ <ThreadPoolBoot />
189
+ {children}
190
+ </MidenProvider>
191
+ );
192
+ }
193
+ ```
194
+
195
+ `initThreadPool` is idempotent — calling it multiple times resolves with the existing pool. Without this call, the rayon global thread pool spawns zero workers on `wasm32` and every `par_iter(...)` falls through to a sequential loop. You'd be shipping multi-threaded WASM that runs single-threaded.
196
+
197
+ If you can't satisfy the COI requirement (third-party host, CDN that won't set headers), stay on the default `@miden-sdk/react` / `/lazy` subpaths — they ship the ST WASM and load anywhere.
198
+
144
199
  ### Constructing wasm-bindgen types directly in Next.js
145
200
 
146
201
  Hooks accept strings for account IDs, asset IDs, and note IDs (auto-detecting hex vs. bech32) and parse them internally inside async callbacks, so **most apps never touch wasm-bindgen types directly**. If you do need to (e.g. to inspect an ID's prefix/suffix, validate a pasted string, or use a low-level API like `new Felt(…)`), import from `@miden-sdk/miden-sdk/lazy` and gate the call on `isReady`:
@@ -1616,8 +1671,8 @@ One runnable Vite example lives in `examples/`:
1616
1671
 
1617
1672
  ```bash
1618
1673
  cd examples/wallet
1619
- yarn install
1620
- yarn dev
1674
+ pnpm install
1675
+ pnpm dev
1621
1676
  ```
1622
1677
 
1623
1678
  ## Requirements
package/dist/index.d.ts CHANGED
@@ -150,6 +150,22 @@ interface MidenConfig {
150
150
  proverUrls?: ProverUrls;
151
151
  /** Default timeout for remote prover requests in milliseconds. */
152
152
  proverTimeoutMs?: number | bigint;
153
+ /**
154
+ * Enable the Web Worker shim that runs WASM calls off the main thread.
155
+ * Defaults to `true` — leave it that way in browsers/extensions so the UI
156
+ * stays responsive while WASM is busy.
157
+ *
158
+ * Set to `false` when:
159
+ * - You pass a `CallbackProver` (e.g. a native iOS/Android prover via
160
+ * a Capacitor plugin). The worker boundary serializes the prover with
161
+ * `TransactionProver.serialize()`, which has no encoding for the
162
+ * callback variant and silently downgrades to `"local"` — your
163
+ * callback would never fire.
164
+ * - You're embedding the client in a single-WebView native shell
165
+ * (Capacitor host, Tauri, Electron preload), where the UI thread
166
+ * isn't competing with the WASM thread anyway.
167
+ */
168
+ useWorker?: boolean;
153
169
  }
154
170
  interface MidenState {
155
171
  client: WasmWebClient | null;
package/dist/index.mjs CHANGED
@@ -425,6 +425,7 @@ function createRemoteProver(config, fallbackTimeout) {
425
425
  }
426
426
  return TransactionProver.newRemoteProver(
427
427
  url,
428
+ /* v8 ignore next 1 — timeoutMs is always provided in tests; ?? fallbackTimeout path unreachable */
428
429
  normalizeTimeout(timeoutMs ?? fallbackTimeout)
429
430
  );
430
431
  }
@@ -570,10 +571,12 @@ function MidenProvider({
570
571
  isReady,
571
572
  resolvedConfig.prover,
572
573
  resolvedConfig.proverTimeoutMs,
574
+ /* v8 ignore next 2 — optional chain on proverUrls; tests don't pass proverUrls config */
573
575
  resolvedConfig.proverUrls?.devnet,
574
576
  resolvedConfig.proverUrls?.testnet
575
577
  ]);
576
578
  const runExclusive = useCallback(
579
+ /* v8 ignore next 3 — runExclusive callback body; only called by advanced consumers, not directly in tests */
577
580
  async (fn) => clientLockRef.current.runExclusive(fn),
578
581
  []
579
582
  );
@@ -608,6 +611,8 @@ function MidenProvider({
608
611
  signCbRef.current = signerContext?.signCb ?? null;
609
612
  }, [signerContext?.signCb]);
610
613
  const wrappedSignCb = useCallback(
614
+ /* v8 ignore next 11 — wrappedSignCb body is only called during external signer operations;
615
+ * tests don't exercise the full signing flow through MidenProvider directly. */
611
616
  async (pubKey, signingInputs) => {
612
617
  const cb = signCbRef.current;
613
618
  if (!cb) {
@@ -658,7 +663,9 @@ function MidenProvider({
658
663
  storeName,
659
664
  signerContext.getKeyCb,
660
665
  signerContext.insertKeyCb,
661
- wrappedSignCb
666
+ wrappedSignCb,
667
+ void 0,
668
+ resolvedConfig.useWorker
662
669
  );
663
670
  if (cancelled) return;
664
671
  const accountId = await initializeSignerAccount(
@@ -674,7 +681,10 @@ function MidenProvider({
674
681
  webClient = await WebClient.createClient(
675
682
  resolvedConfig.rpcUrl,
676
683
  resolvedConfig.noteTransportUrl,
677
- seed
684
+ seed,
685
+ void 0,
686
+ void 0,
687
+ resolvedConfig.useWorker
678
688
  );
679
689
  if (cancelled) return;
680
690
  }
@@ -855,7 +865,10 @@ function MultiSignerProvider({ children }) {
855
865
  () => ({ register, unregister }),
856
866
  [register, unregister]
857
867
  );
858
- const activeSigner = activeSignerName ? signersSnapshot.find((s) => s.name === activeSignerName) ?? null : null;
868
+ const activeSigner = activeSignerName ? (
869
+ /* v8 ignore next 1 — ?? null fallback; find() returns undefined only when the signer was unregistered between renders */
870
+ signersSnapshot.find((s) => s.name === activeSignerName) ?? null
871
+ ) : null;
859
872
  const stableSignCb = useCallback2(
860
873
  async (pubKey, signingInputs) => {
861
874
  const name = activeSignerName;
@@ -1307,6 +1320,7 @@ function getNoteType(type) {
1307
1320
  return NoteType.Private;
1308
1321
  case "public":
1309
1322
  return NoteType.Public;
1323
+ /* v8 ignore next 2 — TypeScript type ensures only "private"|"public"; default is unreachable */
1310
1324
  default:
1311
1325
  return NoteType.Private;
1312
1326
  }
@@ -1782,6 +1796,7 @@ function buildFilter(filter, ids, idsHex) {
1782
1796
  }
1783
1797
  return {
1784
1798
  filter: TransactionFilter2.all(),
1799
+ /* v8 ignore next 1 — idsHex is always non-null when ids is non-empty; ?? [] is a safety net */
1785
1800
  localFilterHexes: idsHex ?? []
1786
1801
  };
1787
1802
  }
@@ -2271,7 +2286,7 @@ function useSend() {
2271
2286
  txRequest,
2272
2287
  prover
2273
2288
  ) : await client.submitNewTransaction(execFromId, txRequest);
2274
- return { txId: txId.toString(), note: p2idNote };
2289
+ return { txId: txId.toHex(), note: p2idNote };
2275
2290
  });
2276
2291
  setStage("complete");
2277
2292
  setResult(returnResult);
@@ -2323,7 +2338,6 @@ function useSend() {
2323
2338
  () => client.submitProvenTransaction(provenTransaction, txResult)
2324
2339
  );
2325
2340
  const txIdHex = txResult.id().toHex();
2326
- const txIdString = txResult.id().toString();
2327
2341
  let fullNote = null;
2328
2342
  if (noteType === NoteType2.Private) {
2329
2343
  fullNote = extractFullNote(txResult);
@@ -2347,7 +2361,7 @@ function useSend() {
2347
2361
  );
2348
2362
  }
2349
2363
  const sendResult = {
2350
- txId: txIdString,
2364
+ txId: txIdHex,
2351
2365
  note: null
2352
2366
  };
2353
2367
  setStage("complete");
@@ -2459,7 +2473,11 @@ function useMultiSend() {
2459
2473
  };
2460
2474
  }
2461
2475
  );
2462
- const txRequest = new TransactionRequestBuilder2().withOwnOutputNotes(new NoteArray2(outputs.map((o) => o.note))).build();
2476
+ const ownOutputs = new NoteArray2();
2477
+ for (const o of outputs) {
2478
+ ownOutputs.push(o.note);
2479
+ }
2480
+ const txRequest = new TransactionRequestBuilder2().withOwnOutputNotes(ownOutputs).build();
2463
2481
  const txSenderId = parseAccountId(options.from);
2464
2482
  const txResult = await client.executeTransaction(txSenderId, txRequest);
2465
2483
  setStage("proving");
@@ -2476,7 +2494,6 @@ function useMultiSend() {
2476
2494
  txResult
2477
2495
  );
2478
2496
  const txIdHex = txResult.id().toHex();
2479
- const txIdString = txResult.id().toString();
2480
2497
  await client.applyTransaction(txResult, submissionHeight);
2481
2498
  const hasPrivate = outputs.some((o) => o.noteType === NoteType3.Private);
2482
2499
  if (hasPrivate) {
@@ -2494,7 +2511,7 @@ function useMultiSend() {
2494
2511
  }
2495
2512
  }
2496
2513
  }
2497
- const txSummary = { transactionId: txIdString };
2514
+ const txSummary = { transactionId: txIdHex };
2498
2515
  setStage("complete");
2499
2516
  setResult(txSummary);
2500
2517
  await sync();
@@ -2643,7 +2660,7 @@ function useMint() {
2643
2660
  txRequest,
2644
2661
  prover
2645
2662
  ) : await client.submitNewTransaction(faucetIdObj, txRequest);
2646
- return { transactionId: txId.toString() };
2663
+ return { transactionId: txId.toHex() };
2647
2664
  });
2648
2665
  setStage("complete");
2649
2666
  setResult(txResult);
@@ -2719,16 +2736,17 @@ function useConsume() {
2719
2736
  }
2720
2737
  }
2721
2738
  if (lookupIds.length > 0) {
2739
+ const lookupIdStrings = lookupIds.map((id) => id.toString());
2722
2740
  const filter = new NoteFilter3(NoteFilterTypes2.List, lookupIds);
2723
2741
  const noteRecords = await client.getInputNotes(filter);
2724
- if (noteRecords.length !== lookupIds.length) {
2742
+ if (noteRecords.length !== lookupIdStrings.length) {
2725
2743
  throw new Error("Some notes could not be found for provided IDs");
2726
2744
  }
2727
2745
  const recordById = new Map(
2728
2746
  noteRecords.map((r) => [r.id().toString(), r])
2729
2747
  );
2730
2748
  for (let j = 0; j < lookupIndices.length; j++) {
2731
- const record = recordById.get(lookupIds[j].toString());
2749
+ const record = recordById.get(lookupIdStrings[j]);
2732
2750
  if (!record) {
2733
2751
  throw new Error(
2734
2752
  "Some notes could not be found for provided IDs"
@@ -2750,7 +2768,7 @@ function useConsume() {
2750
2768
  txRequest,
2751
2769
  prover
2752
2770
  ) : await client.submitNewTransaction(accountIdObj, txRequest);
2753
- return { transactionId: txId.toString() };
2771
+ return { transactionId: txId.toHex() };
2754
2772
  });
2755
2773
  setStage("complete");
2756
2774
  setResult(txResult);
@@ -2824,7 +2842,7 @@ function useSwap() {
2824
2842
  txRequest,
2825
2843
  prover
2826
2844
  ) : await client.submitNewTransaction(accountIdObj, txRequest);
2827
- return { transactionId: txId.toString() };
2845
+ return { transactionId: txId.toHex() };
2828
2846
  });
2829
2847
  setStage("complete");
2830
2848
  setResult(txResult);
@@ -2959,7 +2977,7 @@ function useTransaction() {
2959
2977
  );
2960
2978
  }
2961
2979
  }
2962
- const txSummary = { transactionId: txId.toString() };
2980
+ const txSummary = { transactionId: txId.toHex() };
2963
2981
  setStage("complete");
2964
2982
  setResult(txSummary);
2965
2983
  await sync();
@@ -3191,13 +3209,14 @@ function useSessionAccount(options) {
3191
3209
  setSessionAccountId(walletId);
3192
3210
  localStorage.setItem(`${storagePrefix}:accountId`, walletId);
3193
3211
  }
3212
+ const resolvedWalletId = walletId;
3194
3213
  setStep("funding");
3195
- await fundRef.current(walletId);
3214
+ await fundRef.current(resolvedWalletId);
3196
3215
  if (cancelledRef.current) return;
3197
3216
  setStep("consuming");
3198
3217
  await waitAndConsume(
3199
3218
  client,
3200
- walletId,
3219
+ resolvedWalletId,
3201
3220
  pollIntervalMs,
3202
3221
  maxWaitMs,
3203
3222
  cancelledRef
package/dist/lazy.d.ts CHANGED
@@ -150,6 +150,22 @@ interface MidenConfig {
150
150
  proverUrls?: ProverUrls;
151
151
  /** Default timeout for remote prover requests in milliseconds. */
152
152
  proverTimeoutMs?: number | bigint;
153
+ /**
154
+ * Enable the Web Worker shim that runs WASM calls off the main thread.
155
+ * Defaults to `true` — leave it that way in browsers/extensions so the UI
156
+ * stays responsive while WASM is busy.
157
+ *
158
+ * Set to `false` when:
159
+ * - You pass a `CallbackProver` (e.g. a native iOS/Android prover via
160
+ * a Capacitor plugin). The worker boundary serializes the prover with
161
+ * `TransactionProver.serialize()`, which has no encoding for the
162
+ * callback variant and silently downgrades to `"local"` — your
163
+ * callback would never fire.
164
+ * - You're embedding the client in a single-WebView native shell
165
+ * (Capacitor host, Tauri, Electron preload), where the UI thread
166
+ * isn't competing with the WASM thread anyway.
167
+ */
168
+ useWorker?: boolean;
153
169
  }
154
170
  interface MidenState {
155
171
  client: WasmWebClient | null;