@miden-sdk/react 0.13.0

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 ADDED
@@ -0,0 +1,1031 @@
1
+ # @miden-sdk/react
2
+
3
+ React hooks library for the Miden Web Client. Provides a simple, ergonomic interface for building React applications on the Miden rollup.
4
+
5
+ ## Features
6
+
7
+ - **Easy Setup** - Single provider component handles WASM initialization and client setup
8
+ - **Sensible Defaults** - Privacy-first defaults that work out of the box
9
+ - **Auto-Sync** - Automatic background synchronization with the network
10
+ - **TypeScript First** - Full type safety with comprehensive type exports
11
+ - **Consistent Patterns** - All hooks follow predictable patterns for loading, errors, and state
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @miden-sdk/react @miden-sdk/miden-sdk
17
+ # or
18
+ yarn add @miden-sdk/react @miden-sdk/miden-sdk
19
+ # or
20
+ pnpm add @miden-sdk/react @miden-sdk/miden-sdk
21
+ ```
22
+
23
+ ## Testing
24
+
25
+ From `packages/react-sdk`:
26
+
27
+ ```bash
28
+ # Unit tests
29
+ yarn test:unit
30
+
31
+ # Integration tests (Playwright) in test/
32
+ # Build the web-client dist first:
33
+ cd ../../crates/web-client && yarn build
34
+ cd ../../packages/react-sdk
35
+ yarn playwright install --with-deps
36
+ yarn test:integration
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ Wrap your app with `MidenProvider` and start using hooks:
42
+
43
+ ```tsx
44
+ import { MidenProvider, useMiden, useCreateWallet, useAccounts } from '@miden-sdk/react';
45
+
46
+ function App() {
47
+ return (
48
+ <MidenProvider>
49
+ <Wallet />
50
+ </MidenProvider>
51
+ );
52
+ }
53
+
54
+ function Wallet() {
55
+ const { isReady } = useMiden();
56
+ const { wallets, isLoading } = useAccounts();
57
+ const { createWallet, isCreating } = useCreateWallet();
58
+
59
+ if (!isReady) return <div>Initializing Miden...</div>;
60
+ if (isLoading) return <div>Loading accounts...</div>;
61
+
62
+ return (
63
+ <div>
64
+ <h2>My Wallets ({wallets.length})</h2>
65
+ <ul>
66
+ {wallets.map(wallet => (
67
+ <li key={wallet.id().toString()}>
68
+ {wallet.id().toString()}
69
+ </li>
70
+ ))}
71
+ </ul>
72
+
73
+ <button onClick={() => createWallet()} disabled={isCreating}>
74
+ {isCreating ? 'Creating...' : 'Create Wallet'}
75
+ </button>
76
+ </div>
77
+ );
78
+ }
79
+ ```
80
+
81
+ ## Provider Configuration
82
+
83
+ The `MidenProvider` handles WASM initialization and client setup:
84
+
85
+ ```tsx
86
+ import { MidenProvider } from '@miden-sdk/react';
87
+
88
+ function App() {
89
+ return (
90
+ <MidenProvider
91
+ config={{
92
+ // RPC endpoint (defaults to testnet). You can also use 'devnet' or 'testnet'.
93
+ rpcUrl: 'devnet',
94
+
95
+ // Auto-sync interval in milliseconds (default: 15000)
96
+ // Set to 0 to disable auto-sync
97
+ autoSyncInterval: 15000,
98
+
99
+ // Optional: prover selection ('local' | 'devnet' | 'testnet' | URL)
100
+ // prover: 'local',
101
+ }}
102
+ // Optional: Custom loading component
103
+ loadingComponent={<div>Loading Miden...</div>}
104
+
105
+ // Optional: Custom error component
106
+ errorComponent={(error) => (
107
+ <div>Failed to initialize: {error.message}</div>
108
+ )}
109
+ >
110
+ <YourApp />
111
+ </MidenProvider>
112
+ );
113
+ }
114
+ ```
115
+
116
+ ## Hooks Reference
117
+
118
+ ### Core Hooks
119
+
120
+ **ID formats:** All hooks that accept account IDs or asset IDs accept both
121
+ bech32 and hex strings (auto-detected).
122
+
123
+ #### `useMiden()`
124
+
125
+ Access the Miden client instance and initialization state. This is your entry
126
+ point for low-level control (syncing, direct client access, and prover-aware
127
+ transactions) while still playing nicely with the provider lifecycle. It
128
+ centralizes readiness and error handling so you avoid sprinkling guards across
129
+ components. You also get a single place to hook sync and exclusive access
130
+ without wiring your own locks.
131
+
132
+ ```tsx
133
+ import { useMiden } from '@miden-sdk/react';
134
+
135
+ function MyComponent() {
136
+ const {
137
+ client, // WebClient instance (null if not ready)
138
+ isReady, // true when client is initialized
139
+ error, // Initialization error if any
140
+ sync, // Function to trigger manual sync
141
+ } = useMiden();
142
+
143
+ if (error) {
144
+ return <div>Error: {error.message}</div>;
145
+ }
146
+
147
+ if (!isReady) {
148
+ return <div>Initializing...</div>;
149
+ }
150
+
151
+ return <div>Connected! Block height: {/* ... */}</div>;
152
+ }
153
+ ```
154
+
155
+ #### `useMidenClient()`
156
+
157
+ Get the ready `WebClient` instance directly. It’s a convenience for advanced
158
+ flows where you want to call SDK methods yourself without re-checking readiness
159
+ every time; it throws if the client isn't ready yet. This keeps your component
160
+ logic clean by avoiding boilerplate null checks. You still benefit from the
161
+ provider lifecycle while opting into raw SDK control.
162
+
163
+ ```tsx
164
+ import { useMidenClient } from '@miden-sdk/react';
165
+
166
+ function MyComponent() {
167
+ const client = useMidenClient();
168
+ // Safe to use client here (initialized)
169
+ return <div>Client ready</div>;
170
+ }
171
+ ```
172
+
173
+ #### `useSyncState()`
174
+
175
+ Monitor network sync status and trigger manual syncs. Useful for UI indicators,
176
+ pull-to-refresh, or forcing a sync before running a transaction pipeline. It
177
+ wraps the shared sync lock so multiple components don't stampede the node. You
178
+ get consistent timestamps and error state without wiring your own timers.
179
+
180
+ ```tsx
181
+ import { useSyncState } from '@miden-sdk/react';
182
+
183
+ function SyncStatus() {
184
+ const {
185
+ syncHeight, // Current synced block height
186
+ isSyncing, // true during sync operation
187
+ lastSyncTime, // Timestamp of last successful sync
188
+ error, // Sync error if any
189
+ sync, // Function to trigger manual sync
190
+ } = useSyncState();
191
+
192
+ return (
193
+ <div>
194
+ <p>Block Height: {syncHeight}</p>
195
+ <p>Last Sync: {lastSyncTime ? new Date(lastSyncTime).toLocaleString() : 'Never'}</p>
196
+ <button onClick={sync} disabled={isSyncing}>
197
+ {isSyncing ? 'Syncing...' : 'Sync Now'}
198
+ </button>
199
+ </div>
200
+ );
201
+ }
202
+ ```
203
+
204
+ ### Account Hooks
205
+
206
+ #### `useAccounts()`
207
+
208
+ List all accounts tracked by the local client, automatically categorized into
209
+ wallets and faucets. Great for dashboards, account pickers, and quick summaries
210
+ without extra filtering logic. It hides the bit twiddling needed to recognize
211
+ faucet IDs. Refetch runs through the same sync-safe path so you avoid stale
212
+ caches or double fetches.
213
+
214
+ ```tsx
215
+ import { useAccounts } from '@miden-sdk/react';
216
+
217
+ function AccountList() {
218
+ const {
219
+ accounts, // All accounts
220
+ wallets, // Regular wallet accounts
221
+ faucets, // Faucet accounts
222
+ isLoading, // Loading state
223
+ error, // Error if fetch failed
224
+ refetch, // Function to refresh the list
225
+ } = useAccounts();
226
+
227
+ if (isLoading) return <div>Loading...</div>;
228
+
229
+ return (
230
+ <div>
231
+ <h2>Wallets ({wallets.length})</h2>
232
+ {wallets.map(w => (
233
+ <div key={w.id().toString()}>
234
+ {w.id().toString()}
235
+ </div>
236
+ ))}
237
+
238
+ <h2>Faucets ({faucets.length})</h2>
239
+ {faucets.map(f => (
240
+ <div key={f.id().toString()}>
241
+ {f.id().toString()}
242
+ </div>
243
+ ))}
244
+
245
+ <button onClick={refetch}>Refresh</button>
246
+ </div>
247
+ );
248
+ }
249
+ ```
250
+
251
+ #### `useAccount(accountId)`
252
+
253
+ Get detailed information for a single account, including assets. The hook
254
+ hydrates balances with token metadata (symbol/decimals) and keeps data fresh
255
+ after syncs. It spares you from manual vault parsing and metadata joins.
256
+ Balances update automatically with sync so UI stays in step.
257
+
258
+ ```tsx
259
+ import { useAccount } from '@miden-sdk/react';
260
+
261
+ function AccountDetails({ accountId }: { accountId: string }) {
262
+ const {
263
+ account, // Full account object
264
+ assets, // Array of { assetId, amount, symbol?, decimals? } balances
265
+ isLoading,
266
+ error,
267
+ refetch,
268
+ getBalance, // Helper to get balance for specific asset
269
+ } = useAccount(accountId);
270
+
271
+ if (isLoading) return <div>Loading...</div>;
272
+ if (!account) return <div>Account not found</div>;
273
+
274
+ // Get balance for a specific token
275
+ const usdcBalance = getBalance('0xasset123...');
276
+
277
+ return (
278
+ <div>
279
+ <h2>Account: {account.id().toString()}</h2>
280
+ <p>Nonce: {account.nonce().toString()}</p>
281
+
282
+ <h3>Assets</h3>
283
+ {assets.map(asset => (
284
+ <div key={asset.assetId}>
285
+ {asset.symbol ?? asset.assetId}: {asset.amount.toString()}
286
+ </div>
287
+ ))}
288
+
289
+ <p>USDC Balance: {usdcBalance.toString()}</p>
290
+ </div>
291
+ );
292
+ }
293
+ ```
294
+
295
+ #### `useCreateWallet()`
296
+
297
+ Create new wallet accounts. Supports storage mode, mutability, and auth scheme
298
+ so you can quickly spin up accounts for demos or customize for production needs.
299
+ It wraps ID parsing and defaults so you can start with a one-liner. The hook
300
+ also tracks creation state so you can wire UI without extra reducers.
301
+
302
+ ```tsx
303
+ import { useCreateWallet } from '@miden-sdk/react';
304
+
305
+ function CreateWalletButton() {
306
+ const {
307
+ createWallet, // Function to create wallet
308
+ wallet, // Created wallet (after success)
309
+ isCreating, // Loading state
310
+ error, // Error if creation failed
311
+ reset, // Reset state for new creation
312
+ } = useCreateWallet();
313
+
314
+ const handleCreate = async () => {
315
+ try {
316
+ // With defaults (private storage, mutable, Falcon auth)
317
+ const newWallet = await createWallet();
318
+ console.log('Created wallet:', newWallet.id().toString());
319
+
320
+ // Or with custom options
321
+ const customWallet = await createWallet({
322
+ storageMode: 'private', // 'private' | 'public' | 'network'
323
+ mutable: true, // Allow code updates
324
+ authScheme: 0, // 0 = Falcon (default), 1 = ECDSA
325
+ });
326
+ } catch (err) {
327
+ console.error('Failed to create wallet:', err);
328
+ }
329
+ };
330
+
331
+ return (
332
+ <div>
333
+ {error && (
334
+ <div>
335
+ Error: {error.message}
336
+ <button onClick={reset}>Try Again</button>
337
+ </div>
338
+ )}
339
+
340
+ <button onClick={handleCreate} disabled={isCreating}>
341
+ {isCreating ? 'Creating...' : 'Create Wallet'}
342
+ </button>
343
+
344
+ {wallet && <div>Created: {wallet.id().toString()}</div>}
345
+ </div>
346
+ );
347
+ }
348
+ ```
349
+
350
+ #### `useCreateFaucet()`
351
+
352
+ Create new faucets for minting tokens. Ideal for dev/test flows where you need
353
+ a controlled token source and quick bootstrap of balances.
354
+ It handles storage/auth defaults and returns a ready faucet object. That
355
+ removes the usual setup friction when you just want tokens to exist.
356
+
357
+ ```tsx
358
+ import { useCreateFaucet } from '@miden-sdk/react';
359
+
360
+ function CreateFaucetForm() {
361
+ const { createFaucet, faucet, isCreating, error, reset } = useCreateFaucet();
362
+
363
+ const handleCreate = async () => {
364
+ try {
365
+ const newFaucet = await createFaucet({
366
+ tokenSymbol: 'USDC', // 1-4 character symbol
367
+ decimals: 6, // Token decimals (default: 8)
368
+ maxSupply: 1000000000n * 10n**6n, // Max supply in smallest units
369
+ storageMode: 'private', // Optional (default: 'private')
370
+ authScheme: 0, // Optional (default: 0 = Falcon)
371
+ });
372
+ console.log('Created faucet:', newFaucet.id().toString());
373
+ } catch (err) {
374
+ console.error('Failed:', err);
375
+ }
376
+ };
377
+
378
+ return (
379
+ <div>
380
+ {error && <div>Error: {error.message}</div>}
381
+ <button onClick={handleCreate} disabled={isCreating}>
382
+ {isCreating ? 'Creating...' : 'Create USDC Faucet'}
383
+ </button>
384
+ </div>
385
+ );
386
+ }
387
+ ```
388
+
389
+ #### `useImportAccount()`
390
+
391
+ Import an existing account into the client. This lets you start tracking an
392
+ on-chain account by ID, or restore a private account from a file/seed.
393
+ The hook normalizes ID formats and hides SDK branching. It also exposes the
394
+ imported account so you can update UI immediately.
395
+
396
+ ```tsx
397
+ import { useImportAccount } from '@miden-sdk/react';
398
+
399
+ function ImportAccountButton({ accountId }: { accountId: string }) {
400
+ const { importAccount, account, isImporting, error, reset } = useImportAccount();
401
+
402
+ const handleImport = async () => {
403
+ await importAccount({ type: 'id', accountId });
404
+ };
405
+
406
+ return (
407
+ <button onClick={handleImport} disabled={isImporting}>
408
+ {isImporting ? 'Importing...' : 'Import Account'}
409
+ </button>
410
+ );
411
+ }
412
+ ```
413
+
414
+ ### Note Hooks
415
+
416
+ #### `useNotes(options?)`
417
+
418
+ List and filter notes (incoming transactions). Includes consumable notes and
419
+ optional summaries that bundle asset metadata so you can render balances and
420
+ labels without extra lookups. It syncs notes after successful syncs so you
421
+ don't have to wire listeners. Summaries reduce boilerplate around asset
422
+ metadata and formatting.
423
+
424
+ ```tsx
425
+ import { useNotes } from '@miden-sdk/react';
426
+
427
+ function NotesList() {
428
+ const {
429
+ notes, // All notes matching filter
430
+ consumableNotes, // Notes ready to be consumed
431
+ noteSummaries, // Summary objects with asset metadata
432
+ isLoading,
433
+ error,
434
+ refetch,
435
+ } = useNotes();
436
+
437
+ // With filtering options
438
+ const { notes: committedNotes } = useNotes({
439
+ status: 'committed', // 'all' | 'consumed' | 'committed' | 'expected' | 'processing'
440
+ accountId: '0x...', // Filter by account
441
+ });
442
+
443
+ return (
444
+ <div>
445
+ <h2>Consumable Notes ({consumableNotes.length})</h2>
446
+ {consumableNotes.map(note => (
447
+ <div key={note.id().toString()}>
448
+ {note.id().toString()}
449
+ </div>
450
+ ))}
451
+
452
+ <h2>Note Summaries</h2>
453
+ {noteSummaries.map(summary => (
454
+ <div key={summary.id}>
455
+ {summary.id} — {summary.assets.map(a => `${a.amount} ${a.symbol ?? a.assetId}`).join(', ')}
456
+ </div>
457
+ ))}
458
+ </div>
459
+ );
460
+ }
461
+ ```
462
+
463
+ #### `useAssetMetadata(assetIds)`
464
+
465
+ Fetch asset symbols/decimals for a list of asset IDs. This is the lightweight
466
+ way to enrich balances and note lists with human-friendly token info.
467
+ It batches lookups and caches results for reuse across components. That avoids
468
+ repeated RPC calls and inconsistent labels.
469
+
470
+ ```tsx
471
+ import { useAssetMetadata } from '@miden-sdk/react';
472
+
473
+ function AssetLabels({ assetIds }: { assetIds: string[] }) {
474
+ const { assetMetadata } = useAssetMetadata(assetIds);
475
+ return (
476
+ <ul>
477
+ {assetIds.map((id) => {
478
+ const meta = assetMetadata.get(id);
479
+ return (
480
+ <li key={id}>
481
+ {id} — {meta?.symbol ?? 'UNKNOWN'} ({meta?.decimals ?? 0})
482
+ </li>
483
+ );
484
+ })}
485
+ </ul>
486
+ );
487
+ }
488
+ ```
489
+
490
+ ### Transaction Hooks
491
+
492
+ All transaction hooks follow a consistent pattern with `stage` tracking:
493
+
494
+ | Stage | Description |
495
+ |-------|-------------|
496
+ | `'idle'` | Not started |
497
+ | `'executing'` | Building/executing request |
498
+ | `'proving'` | Generating ZK proof |
499
+ | `'submitting'` | Submitting to network |
500
+ | `'complete'` | Transaction confirmed |
501
+
502
+ #### `useSend()`
503
+
504
+ Send tokens from one account to another. Handles the full lifecycle (execute,
505
+ prove, submit, apply) and delivers private notes automatically when needed.
506
+ It collapses the multi-step transaction pipeline into a single call with stage
507
+ tracking. You get private note delivery without having to remember the extra
508
+ send step.
509
+
510
+ ```tsx
511
+ import { useSend } from '@miden-sdk/react';
512
+
513
+ function SendForm() {
514
+ const {
515
+ send, // Function to execute send
516
+ result, // { transactionId } after success
517
+ isLoading, // true during transaction
518
+ stage, // Current stage
519
+ error,
520
+ reset,
521
+ } = useSend();
522
+
523
+ const handleSend = async () => {
524
+ try {
525
+ const { transactionId } = await send({
526
+ from: '0xsender...', // Sender account ID
527
+ to: '0xrecipient...', // Recipient account ID
528
+ assetId: '0xtoken...', // Asset ID (token id)
529
+ amount: 100n, // Amount in smallest units
530
+
531
+ // Optional parameters
532
+ noteType: 'private', // 'private' | 'public' | 'encrypted' (default: 'private')
533
+ recallHeight: 1000, // Sender can reclaim after this block
534
+ });
535
+
536
+ console.log('Sent! TX:', transactionId);
537
+ } catch (err) {
538
+ console.error('Send failed:', err);
539
+ }
540
+ };
541
+
542
+ return (
543
+ <div>
544
+ {error && <div>Error: {error.message}</div>}
545
+
546
+ <button onClick={handleSend} disabled={isLoading}>
547
+ {isLoading ? `Sending (${stage})...` : 'Send Tokens'}
548
+ </button>
549
+
550
+ {result && <div>Success! TX: {result.transactionId}</div>}
551
+ </div>
552
+ );
553
+ }
554
+ ```
555
+
556
+ #### `useMultiSend()`
557
+
558
+ Create multiple P2ID output notes in a single transaction. This is ideal for
559
+ batched payouts or airdrops; with `noteType: 'private'`, the hook also delivers
560
+ each note to recipients via `sendPrivateNote`.
561
+ It builds the request and executes the full pipeline in one go. That means
562
+ fewer chances to handle batching incorrectly or forget private note delivery.
563
+
564
+ ```tsx
565
+ import { useMultiSend } from '@miden-sdk/react';
566
+
567
+ function MultiSendButton() {
568
+ const { sendMany, isLoading, stage } = useMultiSend();
569
+
570
+ const handleSend = async () => {
571
+ await sendMany({
572
+ from: '0xsender...',
573
+ assetId: '0xtoken...',
574
+ recipients: [
575
+ { to: '0xrec1...', amount: 100n },
576
+ { to: '0xrec2...', amount: 250n },
577
+ ],
578
+ noteType: 'public',
579
+ });
580
+ };
581
+
582
+ return (
583
+ <button onClick={handleSend} disabled={isLoading}>
584
+ {isLoading ? `Sending (${stage})...` : 'Multi-Send'}
585
+ </button>
586
+ );
587
+ }
588
+ ```
589
+
590
+ #### `useInternalTransfer()`
591
+
592
+ Create a P2ID note and immediately consume it. This is useful for transfers
593
+ between accounts you control (e.g., public → private), since the receiver must
594
+ be available in the local client to consume the note.
595
+ It abstracts the two-transaction flow and keeps both steps tied to a single UI
596
+ action. You don't have to juggle temporary note IDs or manual syncs.
597
+
598
+ ```tsx
599
+ import { useInternalTransfer } from '@miden-sdk/react';
600
+
601
+ function InternalTransferButton() {
602
+ const { transfer, isLoading, stage } = useInternalTransfer();
603
+
604
+ const handleTransfer = async () => {
605
+ await transfer({
606
+ from: '0xsender...',
607
+ to: '0xrecipient...',
608
+ assetId: '0xtoken...',
609
+ amount: 50n,
610
+ noteType: 'public',
611
+ });
612
+ };
613
+
614
+ return (
615
+ <button onClick={handleTransfer} disabled={isLoading}>
616
+ {isLoading ? `Transferring (${stage})...` : 'Transfer'}
617
+ </button>
618
+ );
619
+ }
620
+ ```
621
+
622
+ #### `useWaitForCommit()`
623
+
624
+ Wait for a transaction to be committed. Useful for gating UI transitions,
625
+ follow-up actions, or polling-driven workflows that depend on finality.
626
+ It handles sync + polling loops and the discarded/timeout edge cases. So you
627
+ can keep UI logic simple and avoid bespoke timers.
628
+
629
+ ```tsx
630
+ import { useWaitForCommit } from '@miden-sdk/react';
631
+
632
+ function WaitForTx({ txId }: { txId: string }) {
633
+ const { waitForCommit } = useWaitForCommit();
634
+
635
+ const handleWait = async () => {
636
+ await waitForCommit(txId, { timeoutMs: 10_000, intervalMs: 1_000 });
637
+ };
638
+
639
+ return <button onClick={handleWait}>Wait for Commit</button>;
640
+ }
641
+ ```
642
+
643
+ #### `useTransactionHistory()`
644
+
645
+ Query transaction history and get live state for a single transaction. You can
646
+ pass a single ID, multiple IDs, or a custom `TransactionFilter`. Results refresh
647
+ after each successful sync by default.
648
+ It hides the filter plumbing and provides a single-record convenience view.
649
+ The hook keeps lists fresh on sync so you don't wire manual refreshes.
650
+
651
+ ```tsx
652
+ import { useTransactionHistory } from '@miden-sdk/react';
653
+
654
+ function TxHistory() {
655
+ const { records, isLoading, refetch } = useTransactionHistory();
656
+
657
+ if (isLoading) return <div>Loading...</div>;
658
+
659
+ return (
660
+ <div>
661
+ <button onClick={refetch}>Refresh</button>
662
+ <ul>
663
+ {records.map((record) => (
664
+ <li key={record.id().toHex()}>{record.id().toHex()}</li>
665
+ ))}
666
+ </ul>
667
+ </div>
668
+ );
669
+ }
670
+ ```
671
+
672
+ ```tsx
673
+ import { useTransactionHistory } from '@miden-sdk/react';
674
+
675
+ function TxStatus({ txId }: { txId: string }) {
676
+ const { record, status } = useTransactionHistory({ id: txId });
677
+
678
+ if (!record) return <div>Not found</div>;
679
+
680
+ return (
681
+ <div>
682
+ {record.id().toHex()} → {status}
683
+ </div>
684
+ );
685
+ }
686
+ ```
687
+
688
+ #### `useWaitForNotes()`
689
+
690
+ Wait until an account has consumable notes. Great for mint → consume pipelines
691
+ and other flows where you want to proceed only when notes are ready.
692
+ It wraps the poll/sync loop and returns notes once the threshold is met. That
693
+ keeps workflows linear without ad-hoc sleeps.
694
+
695
+ ```tsx
696
+ import { useWaitForNotes } from '@miden-sdk/react';
697
+
698
+ function WaitForNotes({ accountId }: { accountId: string }) {
699
+ const { waitForConsumableNotes } = useWaitForNotes();
700
+
701
+ const handleWait = async () => {
702
+ const notes = await waitForConsumableNotes({
703
+ accountId,
704
+ minCount: 1,
705
+ timeoutMs: 10_000,
706
+ intervalMs: 1_000,
707
+ });
708
+ console.log('Notes ready:', notes.length);
709
+ };
710
+
711
+ return <button onClick={handleWait}>Wait for Notes</button>;
712
+ }
713
+ ```
714
+
715
+ #### `useMint()`
716
+
717
+ Mint new tokens from a faucet you control. The hook handles the full tx pipeline
718
+ and is perfect for quickly funding accounts in dev/test environments.
719
+ Defaults and ID parsing keep the call small while still letting you tune note
720
+ type. It also tracks stages so you can surface progress without extra state.
721
+
722
+ ```tsx
723
+ import { useMint } from '@miden-sdk/react';
724
+
725
+ function MintForm() {
726
+ const { mint, result, isLoading, stage, error, reset } = useMint();
727
+
728
+ const handleMint = async () => {
729
+ try {
730
+ const { transactionId } = await mint({
731
+ faucetId: '0xmyfaucet...', // Your faucet ID
732
+ targetAccountId: '0xwallet...', // Recipient wallet
733
+ amount: 1000n * 10n**8n, // Amount to mint
734
+ noteType: 'private', // Optional: 'private' | 'public' | 'encrypted'
735
+ });
736
+
737
+ console.log('Minted! TX:', transactionId);
738
+ } catch (err) {
739
+ console.error('Mint failed:', err);
740
+ }
741
+ };
742
+
743
+ return (
744
+ <button onClick={handleMint} disabled={isLoading}>
745
+ {isLoading ? `Minting (${stage})...` : 'Mint 1000 Tokens'}
746
+ </button>
747
+ );
748
+ }
749
+ ```
750
+
751
+ #### `useConsume()`
752
+
753
+ Consume notes to claim tokens sent to your account. Supports multiple note IDs
754
+ and handles proof generation and submission automatically.
755
+ It wraps the multi-step consume pipeline so you don't have to string calls
756
+ together. That prevents common snags like forgetting to sync or submit.
757
+
758
+ ```tsx
759
+ import { useConsume } from '@miden-sdk/react';
760
+
761
+ function ConsumeNotes() {
762
+ const { consume, result, isLoading, stage, error, reset } = useConsume();
763
+
764
+ const handleConsume = async (noteIds: string[]) => {
765
+ try {
766
+ const { transactionId } = await consume({
767
+ accountId: '0xmywallet...', // Your wallet ID
768
+ noteIds: noteIds, // Array of note IDs to consume
769
+ });
770
+
771
+ console.log('Consumed! TX:', transactionId);
772
+ } catch (err) {
773
+ console.error('Consume failed:', err);
774
+ }
775
+ };
776
+
777
+ return (
778
+ <button
779
+ onClick={() => handleConsume(['0xnote1...', '0xnote2...'])}
780
+ disabled={isLoading}
781
+ >
782
+ {isLoading ? `Consuming (${stage})...` : 'Claim Tokens'}
783
+ </button>
784
+ );
785
+ }
786
+ ```
787
+
788
+ #### `useSwap()`
789
+
790
+ Create atomic swap offers. Use it to build escrow-style swaps with configurable
791
+ note types for both the swap note and the payback note.
792
+ The hook hides request construction and lets you focus on trade parameters.
793
+ Stage tracking helps you provide clear UX during proof/submission.
794
+
795
+ ```tsx
796
+ import { useSwap } from '@miden-sdk/react';
797
+
798
+ function SwapForm() {
799
+ const { swap, result, isLoading, stage, error, reset } = useSwap();
800
+
801
+ const handleSwap = async () => {
802
+ try {
803
+ const { transactionId } = await swap({
804
+ accountId: '0xmywallet...',
805
+
806
+ // What you're offering
807
+ offeredFaucetId: '0xtokenA...',
808
+ offeredAmount: 100n,
809
+
810
+ // What you want in return
811
+ requestedFaucetId: '0xtokenB...',
812
+ requestedAmount: 50n,
813
+
814
+ // Optional
815
+ noteType: 'private', // Note type for swap note
816
+ paybackNoteType: 'private', // Note type for payback note
817
+ });
818
+
819
+ console.log('Swap created! TX:', transactionId);
820
+ } catch (err) {
821
+ console.error('Swap failed:', err);
822
+ }
823
+ };
824
+
825
+ return (
826
+ <button onClick={handleSwap} disabled={isLoading}>
827
+ {isLoading ? `Creating Swap (${stage})...` : 'Create Swap Offer'}
828
+ </button>
829
+ );
830
+ }
831
+ ```
832
+
833
+ #### `useTransaction()`
834
+
835
+ Execute a custom `TransactionRequest` or build one with the client. This is the
836
+ escape hatch for advanced flows not covered by higher-level hooks.
837
+ It standardizes the execute/prove/submit flow while still letting you craft the
838
+ request. You get progress and error handling without wrapping every call
839
+ yourself.
840
+
841
+ ```tsx
842
+ import { useTransaction } from '@miden-sdk/react';
843
+ import { AccountId, NoteType } from '@miden-sdk/miden-sdk';
844
+
845
+ function CustomTransactionButton({ accountId }: { accountId: string }) {
846
+ const { execute, isLoading, stage } = useTransaction();
847
+
848
+ const handleRun = async () => {
849
+ await execute({
850
+ accountId,
851
+ request: (client) =>
852
+ client.newSwapTransactionRequest(
853
+ AccountId.fromHex(accountId),
854
+ AccountId.fromHex('0xassetA'),
855
+ 10n,
856
+ AccountId.fromHex('0xassetB'),
857
+ 5n,
858
+ NoteType.Private,
859
+ NoteType.Private
860
+ ),
861
+ });
862
+ };
863
+
864
+ return (
865
+ <button onClick={handleRun} disabled={isLoading}>
866
+ {isLoading ? stage : 'Run Transaction'}
867
+ </button>
868
+ );
869
+ }
870
+ ```
871
+
872
+ ## Common Patterns
873
+
874
+ ### Error Handling
875
+
876
+ All hooks that can fail provide an `error` state and `reset` function:
877
+
878
+ ```tsx
879
+ function MyComponent() {
880
+ const { createWallet, error, reset } = useCreateWallet();
881
+
882
+ if (error) {
883
+ return (
884
+ <div>
885
+ <p>Error: {error.message}</p>
886
+ <button onClick={reset}>Try Again</button>
887
+ </div>
888
+ );
889
+ }
890
+
891
+ // ...
892
+ }
893
+ ```
894
+
895
+ ### Loading States
896
+
897
+ Query hooks provide `isLoading`, mutation hooks provide both `isLoading` and `stage`:
898
+
899
+ ```tsx
900
+ function TransactionButton() {
901
+ const { send, isLoading, stage } = useSend();
902
+
903
+ // Show detailed progress
904
+ const buttonText = isLoading
905
+ ? `${stage === 'proving' ? 'Generating proof' : 'Submitting'}...`
906
+ : 'Send';
907
+
908
+ return <button disabled={isLoading}>{buttonText}</button>;
909
+ }
910
+ ```
911
+
912
+ ### Refreshing Data
913
+
914
+ All query hooks provide a `refetch` function:
915
+
916
+ ```tsx
917
+ function AccountBalance({ accountId }) {
918
+ const { assets, refetch } = useAccount(accountId);
919
+
920
+ // Refresh after a transaction
921
+ const handleSendComplete = async () => {
922
+ await refetch();
923
+ };
924
+
925
+ return (
926
+ <div>
927
+ {/* ... */}
928
+ <button onClick={refetch}>Refresh Balance</button>
929
+ </div>
930
+ );
931
+ }
932
+ ```
933
+
934
+ ### Waiting for Client Ready
935
+
936
+ Always check `isReady` before using hooks that require the client:
937
+
938
+ ```tsx
939
+ function MyFeature() {
940
+ const { isReady } = useMiden();
941
+ const { createWallet } = useCreateWallet();
942
+
943
+ if (!isReady) {
944
+ return <div>Please wait...</div>;
945
+ }
946
+
947
+ return <button onClick={() => createWallet()}>Create Wallet</button>;
948
+ }
949
+ ```
950
+
951
+ ## Default Values
952
+
953
+ The SDK uses privacy-first defaults:
954
+
955
+ | Setting | Default | Description |
956
+ |---------|---------|-------------|
957
+ | `storageMode` | `'private'` | Account data stored off-chain |
958
+ | `mutable` | `true` | Wallet code can be updated |
959
+ | `authScheme` | `0` (Falcon) | Post-quantum secure signatures |
960
+ | `noteType` | `'private'` | Note contents encrypted |
961
+ | `decimals` | `8` | Token decimal places |
962
+ | `autoSyncInterval` | `15000` | Sync every 15 seconds |
963
+
964
+ ## TypeScript
965
+
966
+ Full TypeScript support with exported types:
967
+
968
+ ```tsx
969
+ import type {
970
+ // Configuration
971
+ MidenConfig,
972
+
973
+ // Hook options
974
+ CreateWalletOptions,
975
+ CreateFaucetOptions,
976
+ ImportAccountOptions,
977
+ SendOptions,
978
+ MultiSendRecipient,
979
+ MultiSendOptions,
980
+ InternalTransferOptions,
981
+ InternalTransferChainOptions,
982
+ InternalTransferResult,
983
+ WaitForCommitOptions,
984
+ WaitForNotesOptions,
985
+ MintOptions,
986
+ ConsumeOptions,
987
+ SwapOptions,
988
+ ExecuteTransactionOptions,
989
+ NotesFilter,
990
+
991
+ // Hook results
992
+ AccountResult,
993
+ AccountsResult,
994
+ NotesResult,
995
+ TransactionResult,
996
+
997
+ // State types
998
+ TransactionStage,
999
+ AssetBalance,
1000
+ SyncState,
1001
+ } from '@miden-sdk/react';
1002
+ ```
1003
+
1004
+ ## Examples
1005
+
1006
+ One runnable Vite example lives in `examples/`:
1007
+
1008
+ - `examples/wallet` - Minimal wallet: create account, view balances, claim notes, send tokens.
1009
+
1010
+ ```bash
1011
+ cd examples/wallet
1012
+ yarn install
1013
+ yarn dev
1014
+ ```
1015
+
1016
+ ## Requirements
1017
+
1018
+ - React 18.0 or higher
1019
+ - `@miden-sdk/miden-sdk` ^0.13.0-0
1020
+
1021
+ ## Browser Support
1022
+
1023
+ Requires browsers with WebAssembly support:
1024
+ - Chrome 57+
1025
+ - Firefox 52+
1026
+ - Safari 11+
1027
+ - Edge 16+
1028
+
1029
+ ## License
1030
+
1031
+ MIT