@miden-sdk/react 0.15.0-alpha.4 → 0.15.0-alpha.5

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.
@@ -0,0 +1,3860 @@
1
+ // src/types/augmentations.ts
2
+ import "@miden-sdk/miden-sdk/mt/lazy";
3
+
4
+ // src/utils/accountBech32.ts
5
+ import {
6
+ Account,
7
+ AccountInterface,
8
+ Address as Address2,
9
+ NetworkId
10
+ } from "@miden-sdk/miden-sdk/mt/lazy";
11
+
12
+ // src/store/MidenStore.ts
13
+ import { create } from "zustand";
14
+ var initialSyncState = {
15
+ syncHeight: 0,
16
+ isSyncing: false,
17
+ lastSyncTime: null,
18
+ error: null
19
+ };
20
+ function freshCachedState() {
21
+ return {
22
+ sync: { ...initialSyncState },
23
+ syncPaused: false,
24
+ accounts: [],
25
+ accountDetails: /* @__PURE__ */ new Map(),
26
+ notes: [],
27
+ consumableNotes: [],
28
+ assetMetadata: /* @__PURE__ */ new Map(),
29
+ noteFirstSeen: /* @__PURE__ */ new Map(),
30
+ isLoadingAccounts: false,
31
+ isLoadingNotes: false
32
+ };
33
+ }
34
+ function freshState() {
35
+ return {
36
+ client: null,
37
+ isReady: false,
38
+ isInitializing: false,
39
+ initError: null,
40
+ config: {},
41
+ signerConnected: null,
42
+ ...freshCachedState()
43
+ };
44
+ }
45
+ var useMidenStore = create()((set) => ({
46
+ ...freshState(),
47
+ setClient: (client) => set({
48
+ client,
49
+ isReady: client !== null,
50
+ isInitializing: false,
51
+ initError: null
52
+ }),
53
+ setInitializing: (isInitializing) => set({ isInitializing }),
54
+ setInitError: (initError) => set({
55
+ initError,
56
+ isInitializing: false,
57
+ isReady: false
58
+ }),
59
+ setConfig: (config) => set({ config }),
60
+ setSignerConnected: (signerConnected) => set({ signerConnected }),
61
+ setSyncState: (sync) => set((state) => ({
62
+ sync: { ...state.sync, ...sync }
63
+ })),
64
+ setSyncPaused: (syncPaused) => set({ syncPaused }),
65
+ setAccounts: (accounts) => set({ accounts }),
66
+ setAccountDetails: (accountId, account) => set((state) => {
67
+ const newMap = new Map(state.accountDetails);
68
+ newMap.set(accountId, account);
69
+ return { accountDetails: newMap };
70
+ }),
71
+ setNotes: (notes) => set((state) => {
72
+ const now = Date.now();
73
+ const newFirstSeen = /* @__PURE__ */ new Map();
74
+ for (const note of notes) {
75
+ try {
76
+ const id = note.id().toString();
77
+ newFirstSeen.set(id, state.noteFirstSeen.get(id) ?? now);
78
+ } catch {
79
+ }
80
+ }
81
+ return { notes, noteFirstSeen: newFirstSeen };
82
+ }),
83
+ setNotesIfChanged: (notes) => set((state) => {
84
+ const safeId = (n) => {
85
+ try {
86
+ return n.id().toString();
87
+ } catch {
88
+ return null;
89
+ }
90
+ };
91
+ const prevIds = /* @__PURE__ */ new Set();
92
+ for (const n of state.notes) {
93
+ const id = safeId(n);
94
+ if (id) prevIds.add(id);
95
+ }
96
+ const newIds = /* @__PURE__ */ new Set();
97
+ for (const n of notes) {
98
+ const id = safeId(n);
99
+ if (id) newIds.add(id);
100
+ }
101
+ if (prevIds.size === newIds.size && [...prevIds].every((id) => newIds.has(id))) {
102
+ return {};
103
+ }
104
+ const now = Date.now();
105
+ const newFirstSeen = /* @__PURE__ */ new Map();
106
+ for (const note of notes) {
107
+ try {
108
+ const id = note.id().toString();
109
+ newFirstSeen.set(id, state.noteFirstSeen.get(id) ?? now);
110
+ } catch {
111
+ }
112
+ }
113
+ return { notes, noteFirstSeen: newFirstSeen };
114
+ }),
115
+ setConsumableNotes: (consumableNotes) => set({ consumableNotes }),
116
+ setConsumableNotesIfChanged: (consumableNotes) => set((state) => {
117
+ const safeId = (n) => {
118
+ try {
119
+ return n.inputNoteRecord().id().toString();
120
+ } catch {
121
+ return null;
122
+ }
123
+ };
124
+ const prevIds = /* @__PURE__ */ new Set();
125
+ for (const n of state.consumableNotes) {
126
+ const id = safeId(n);
127
+ if (id) prevIds.add(id);
128
+ }
129
+ const newIds = /* @__PURE__ */ new Set();
130
+ for (const n of consumableNotes) {
131
+ const id = safeId(n);
132
+ if (id) newIds.add(id);
133
+ }
134
+ if (prevIds.size === newIds.size && [...prevIds].every((id) => newIds.has(id))) {
135
+ return {};
136
+ }
137
+ return { consumableNotes };
138
+ }),
139
+ setAssetMetadata: (assetId, metadata) => set((state) => {
140
+ const newMap = new Map(state.assetMetadata);
141
+ newMap.set(assetId, metadata);
142
+ return { assetMetadata: newMap };
143
+ }),
144
+ setLoadingAccounts: (isLoadingAccounts) => set({ isLoadingAccounts }),
145
+ setLoadingNotes: (isLoadingNotes) => set({ isLoadingNotes }),
146
+ resetInMemoryState: () => set(freshCachedState()),
147
+ reset: () => set(freshState())
148
+ }));
149
+ var useSyncStateStore = () => useMidenStore((state) => state.sync);
150
+ var useAccountsStore = () => useMidenStore((state) => state.accounts);
151
+ var useNotesStore = () => useMidenStore((state) => state.notes);
152
+ var useConsumableNotesStore = () => useMidenStore((state) => state.consumableNotes);
153
+ var useAssetMetadataStore = () => useMidenStore((state) => state.assetMetadata);
154
+ var useNoteFirstSeenStore = () => useMidenStore((state) => state.noteFirstSeen);
155
+
156
+ // src/utils/accountParsing.ts
157
+ import { AccountId, Address } from "@miden-sdk/miden-sdk/mt/lazy";
158
+ var normalizeAccountIdInput = (value) => value.trim().replace(/^miden:/i, "");
159
+ var isBech32Input = (value) => value.startsWith("m") || value.startsWith("M");
160
+ var normalizeHexInput = (value) => value.startsWith("0x") || value.startsWith("0X") ? value : `0x${value}`;
161
+ var parseAccountIdFromString = (value) => {
162
+ if (isBech32Input(value)) {
163
+ try {
164
+ return Address.fromBech32(value).accountId();
165
+ } catch {
166
+ return AccountId.fromBech32(value);
167
+ }
168
+ }
169
+ return AccountId.fromHex(normalizeHexInput(value));
170
+ };
171
+ var parseAccountId = (value) => {
172
+ if (typeof value === "string") {
173
+ return parseAccountIdFromString(normalizeAccountIdInput(value));
174
+ }
175
+ if (typeof value.id === "function") {
176
+ return value.id();
177
+ }
178
+ return value;
179
+ };
180
+ function isFaucetId(accountId) {
181
+ try {
182
+ let hex = typeof accountId.toHex === "function" ? accountId.toHex() : String(accountId);
183
+ if (hex.startsWith("0x") || hex.startsWith("0X")) {
184
+ hex = hex.slice(2);
185
+ }
186
+ const firstByte = parseInt(hex.slice(0, 2), 16);
187
+ const accountType = firstByte >> 4 & 3;
188
+ return accountType === 2 || accountType === 3;
189
+ } catch {
190
+ return false;
191
+ }
192
+ }
193
+ var parseAddress = (value, accountId) => {
194
+ if (typeof value !== "string") {
195
+ const resolvedId = accountId ?? parseAccountId(value);
196
+ return Address.fromAccountId(resolvedId, "BasicWallet");
197
+ }
198
+ const normalized = normalizeAccountIdInput(value);
199
+ if (isBech32Input(normalized)) {
200
+ try {
201
+ return Address.fromBech32(normalized);
202
+ } catch {
203
+ const resolvedAccountId2 = accountId ?? AccountId.fromBech32(normalized);
204
+ return Address.fromAccountId(resolvedAccountId2, "BasicWallet");
205
+ }
206
+ }
207
+ const resolvedAccountId = accountId ?? AccountId.fromHex(normalizeHexInput(normalized));
208
+ return Address.fromAccountId(resolvedAccountId, "BasicWallet");
209
+ };
210
+
211
+ // src/utils/accountBech32.ts
212
+ var inferNetworkId = () => {
213
+ const { rpcUrl } = useMidenStore.getState().config;
214
+ if (!rpcUrl) {
215
+ return NetworkId.testnet();
216
+ }
217
+ const url = rpcUrl.toLowerCase();
218
+ if (url.includes("devnet") || url.includes("mdev")) {
219
+ return NetworkId.devnet();
220
+ }
221
+ if (url.includes("mainnet")) {
222
+ return NetworkId.mainnet();
223
+ }
224
+ if (url.includes("testnet") || url.includes("mtst")) {
225
+ return NetworkId.testnet();
226
+ }
227
+ return NetworkId.testnet();
228
+ };
229
+ var toBech32FromAccountId = (id) => {
230
+ try {
231
+ const address = Address2.fromAccountId(id, "BasicWallet");
232
+ return address.toBech32(inferNetworkId());
233
+ } catch {
234
+ }
235
+ try {
236
+ const maybeBech32 = id.toBech32?.(
237
+ inferNetworkId(),
238
+ AccountInterface.BasicWallet
239
+ );
240
+ if (typeof maybeBech32 === "string") {
241
+ return maybeBech32;
242
+ }
243
+ } catch {
244
+ }
245
+ return id.toString();
246
+ };
247
+ var defineBech32 = (target) => {
248
+ try {
249
+ Object.defineProperty(target, "bech32id", {
250
+ value: function bech32id() {
251
+ try {
252
+ const id = this.id?.();
253
+ if (id) {
254
+ return toBech32FromAccountId(id);
255
+ }
256
+ } catch {
257
+ }
258
+ const fallback = typeof this.toString === "function" ? this.toString() : "";
259
+ return fallback ? toBech32AccountId(fallback) : "";
260
+ }
261
+ });
262
+ return true;
263
+ } catch {
264
+ return false;
265
+ }
266
+ };
267
+ var installAccountBech32 = () => {
268
+ const proto = Account.prototype;
269
+ if (proto.bech32id) {
270
+ return;
271
+ }
272
+ defineBech32(proto);
273
+ };
274
+ var ensureAccountBech32 = (account) => {
275
+ if (!account) {
276
+ return;
277
+ }
278
+ if (typeof account.bech32id === "function") {
279
+ return;
280
+ }
281
+ const proto = Object.getPrototypeOf(account);
282
+ if (proto?.bech32id) {
283
+ return;
284
+ }
285
+ if (proto && defineBech32(proto)) {
286
+ return;
287
+ }
288
+ defineBech32(account);
289
+ };
290
+ var toBech32AccountId = (accountId) => {
291
+ try {
292
+ const id = parseAccountId(accountId);
293
+ return toBech32FromAccountId(id);
294
+ } catch {
295
+ return accountId;
296
+ }
297
+ };
298
+
299
+ // src/context/MidenProvider.tsx
300
+ import {
301
+ createContext as createContext2,
302
+ useContext as useContext2,
303
+ useEffect,
304
+ useRef,
305
+ useCallback,
306
+ useMemo,
307
+ useState
308
+ } from "react";
309
+ import { WasmWebClient as WebClient } from "@miden-sdk/miden-sdk/mt/lazy";
310
+
311
+ // src/types/index.ts
312
+ import { AuthScheme } from "@miden-sdk/miden-sdk/mt/lazy";
313
+ var DEFAULTS = {
314
+ RPC_URL: void 0,
315
+ // Will use SDK's testnet default
316
+ AUTO_SYNC_INTERVAL: 15e3,
317
+ STORAGE_MODE: "private",
318
+ WALLET_MUTABLE: true,
319
+ AUTH_SCHEME: AuthScheme.AuthRpoFalcon512,
320
+ NOTE_TYPE: "private",
321
+ FAUCET_DECIMALS: 8
322
+ };
323
+
324
+ // src/utils/asyncLock.ts
325
+ var AsyncLock = class {
326
+ constructor() {
327
+ this.pending = Promise.resolve();
328
+ }
329
+ runExclusive(fn) {
330
+ const run = this.pending.then(fn, fn);
331
+ this.pending = run.then(
332
+ () => void 0,
333
+ () => void 0
334
+ );
335
+ return run;
336
+ }
337
+ };
338
+
339
+ // src/utils/network.ts
340
+ var RPC_URLS = {
341
+ testnet: "https://rpc.testnet.miden.io",
342
+ devnet: "https://rpc.devnet.miden.io",
343
+ localhost: "http://localhost:57291"
344
+ };
345
+ function resolveRpcUrl(rpcUrl) {
346
+ if (!rpcUrl) {
347
+ return void 0;
348
+ }
349
+ const normalized = rpcUrl.trim().toLowerCase();
350
+ if (normalized === "testnet") {
351
+ return RPC_URLS.testnet;
352
+ }
353
+ if (normalized === "devnet") {
354
+ return RPC_URLS.devnet;
355
+ }
356
+ if (normalized === "localhost" || normalized === "local") {
357
+ return RPC_URLS.localhost;
358
+ }
359
+ return rpcUrl;
360
+ }
361
+
362
+ // src/utils/prover.ts
363
+ import { TransactionProver } from "@miden-sdk/miden-sdk/mt/lazy";
364
+ var DEFAULT_PROVER_URLS = {
365
+ devnet: "https://tx-prover.devnet.miden.io",
366
+ testnet: "https://tx-prover.testnet.miden.io"
367
+ };
368
+ function resolveTransactionProver(config) {
369
+ const { prover } = config;
370
+ if (!prover) {
371
+ return null;
372
+ }
373
+ if (isFallbackConfig(prover)) {
374
+ return resolveProverTarget(prover.primary, config);
375
+ }
376
+ return resolveProverTarget(prover, config);
377
+ }
378
+ async function proveWithFallback(proveFn, config) {
379
+ const primaryProver = resolveTransactionProver(config);
380
+ try {
381
+ return await proveFn(primaryProver ?? void 0);
382
+ } catch (primaryError) {
383
+ const { prover } = config;
384
+ if (!prover || !isFallbackConfig(prover) || !prover.fallback) {
385
+ throw primaryError;
386
+ }
387
+ if (prover.disableFallback?.()) {
388
+ throw primaryError;
389
+ }
390
+ const fallbackProver = resolveProverTarget(prover.fallback, config);
391
+ prover.onFallback?.();
392
+ return await proveFn(fallbackProver ?? void 0);
393
+ }
394
+ }
395
+ function isFallbackConfig(prover) {
396
+ return typeof prover === "object" && "primary" in prover;
397
+ }
398
+ function resolveProverTarget(target, config) {
399
+ if (typeof target === "string") {
400
+ const normalized = target.trim().toLowerCase();
401
+ if (normalized === "local" || normalized === "localhost") {
402
+ return TransactionProver.newLocalProver();
403
+ }
404
+ if (normalized === "devnet" || normalized === "testnet") {
405
+ const url = config.proverUrls?.[normalized] ?? DEFAULT_PROVER_URLS[normalized];
406
+ return TransactionProver.newRemoteProver(
407
+ url,
408
+ normalizeTimeout(config.proverTimeoutMs)
409
+ );
410
+ }
411
+ return TransactionProver.newRemoteProver(
412
+ target,
413
+ normalizeTimeout(config.proverTimeoutMs)
414
+ );
415
+ }
416
+ return createRemoteProver(target, config.proverTimeoutMs);
417
+ }
418
+ function createRemoteProver(config, fallbackTimeout) {
419
+ const { url, timeoutMs } = config;
420
+ if (!url) {
421
+ throw new Error("Remote prover requires a URL");
422
+ }
423
+ return TransactionProver.newRemoteProver(
424
+ url,
425
+ normalizeTimeout(timeoutMs ?? fallbackTimeout)
426
+ );
427
+ }
428
+ function normalizeTimeout(timeoutMs) {
429
+ if (timeoutMs === void 0) {
430
+ return void 0;
431
+ }
432
+ if (timeoutMs === null) {
433
+ return null;
434
+ }
435
+ return typeof timeoutMs === "bigint" ? timeoutMs : BigInt(timeoutMs);
436
+ }
437
+
438
+ // src/context/SignerContext.ts
439
+ import { createContext, useContext } from "react";
440
+ var SignerContext = createContext(null);
441
+ function useSigner() {
442
+ return useContext(SignerContext);
443
+ }
444
+
445
+ // src/utils/signerAccount.ts
446
+ function isPrivateStorageMode(storageMode) {
447
+ return storageMode.toString() === "private";
448
+ }
449
+ async function initializeSignerAccount(client, config) {
450
+ const { AccountBuilder, AccountComponent, AuthScheme: AuthScheme2, Word: Word2 } = await import("@miden-sdk/miden-sdk/mt/lazy");
451
+ await client.syncState();
452
+ if (config.importAccountId) {
453
+ const accountId2 = parseAccountId(config.importAccountId);
454
+ try {
455
+ await client.importAccountById(accountId2);
456
+ } catch (e) {
457
+ const code = e?.code;
458
+ const isFreshAccount = code === "ACCOUNT_NOT_FOUND_ON_CHAIN";
459
+ const isAlreadyTracked = code === "ACCOUNT_ALREADY_TRACKED";
460
+ if (!isAlreadyTracked && !isFreshAccount) {
461
+ throw e;
462
+ }
463
+ }
464
+ await client.syncState();
465
+ return config.importAccountId;
466
+ }
467
+ const commitmentWord = Word2.deserialize(config.publicKeyCommitment);
468
+ const seed = config.accountSeed ?? crypto.getRandomValues(new Uint8Array(32));
469
+ let builder = new AccountBuilder(seed).withAuthComponent(
470
+ AccountComponent.createAuthComponentFromCommitment(
471
+ commitmentWord,
472
+ AuthScheme2.AuthEcdsaK256Keccak
473
+ )
474
+ ).storageMode(config.storageMode).withBasicWalletComponent();
475
+ if (config.customComponents?.length) {
476
+ for (const component of config.customComponents) {
477
+ if (component == null || typeof component.getProcedures !== "function") {
478
+ throw new Error(
479
+ "Each entry in customComponents must be an AccountComponent instance created via AccountComponent.compile(), AccountComponent.fromPackage(), or AccountComponent.fromLibrary()."
480
+ );
481
+ }
482
+ builder = builder.withComponent(component);
483
+ }
484
+ }
485
+ const buildResult = builder.build();
486
+ const account = buildResult.account;
487
+ const accountId = account.id();
488
+ if (!isPrivateStorageMode(config.storageMode)) {
489
+ try {
490
+ await client.importAccountById(accountId);
491
+ await client.syncState();
492
+ return accountId.toString();
493
+ } catch {
494
+ }
495
+ }
496
+ try {
497
+ const existing = await client.getAccount(accountId);
498
+ if (existing) {
499
+ await client.syncState();
500
+ return accountId.toString();
501
+ }
502
+ } catch {
503
+ }
504
+ await client.newAccount(account, false);
505
+ await client.syncState();
506
+ return accountId.toString();
507
+ }
508
+
509
+ // src/context/MidenProvider.tsx
510
+ import { Fragment, jsx } from "react/jsx-runtime";
511
+ var MidenContext = createContext2(null);
512
+ function MidenProvider({
513
+ children,
514
+ config = {},
515
+ loadingComponent,
516
+ errorComponent
517
+ }) {
518
+ const {
519
+ client,
520
+ isReady,
521
+ isInitializing,
522
+ initError,
523
+ signerConnected,
524
+ setClient,
525
+ setInitializing,
526
+ setInitError,
527
+ setConfig,
528
+ setSyncState,
529
+ setSignerConnected
530
+ } = useMidenStore();
531
+ const syncIntervalRef = useRef(null);
532
+ const isInitializedRef = useRef(false);
533
+ const clientLockRef = useRef(new AsyncLock());
534
+ const currentStoreNameRef = useRef(null);
535
+ const signCbRef = useRef(null);
536
+ const signerContext = useSigner();
537
+ const [signerAccountId, setSignerAccountId] = useState(null);
538
+ const resolvedConfig = useMemo(
539
+ () => ({
540
+ ...config,
541
+ rpcUrl: resolveRpcUrl(config.rpcUrl)
542
+ }),
543
+ [config]
544
+ );
545
+ const defaultProver = useMemo(
546
+ () => resolveTransactionProver(resolvedConfig),
547
+ [
548
+ resolvedConfig.prover,
549
+ resolvedConfig.proverTimeoutMs,
550
+ resolvedConfig.proverUrls?.devnet,
551
+ resolvedConfig.proverUrls?.testnet
552
+ ]
553
+ );
554
+ const runExclusive = useCallback(
555
+ async (fn) => clientLockRef.current.runExclusive(fn),
556
+ []
557
+ );
558
+ const sync = useCallback(async () => {
559
+ if (!client || !isReady) return;
560
+ const store = useMidenStore.getState();
561
+ if (store.sync.isSyncing) return;
562
+ setSyncState({ isSyncing: true, error: null });
563
+ try {
564
+ const summary = await client.syncState();
565
+ const syncHeight = summary.blockNum();
566
+ setSyncState({
567
+ syncHeight,
568
+ isSyncing: false,
569
+ lastSyncTime: Date.now(),
570
+ error: null
571
+ });
572
+ const accounts = await client.getAccounts();
573
+ useMidenStore.getState().setAccounts(accounts);
574
+ } catch (error) {
575
+ setSyncState({
576
+ isSyncing: false,
577
+ error: error instanceof Error ? error : new Error(String(error))
578
+ });
579
+ }
580
+ }, [client, isReady, setSyncState]);
581
+ const signerIsConnected = signerContext?.isConnected ?? null;
582
+ const signerStoreName = signerContext?.storeName ?? null;
583
+ const signerAccountType = signerContext?.accountConfig?.accountType ?? null;
584
+ const signerStorageMode = signerContext?.accountConfig?.storageMode?.toString() ?? null;
585
+ useEffect(() => {
586
+ signCbRef.current = signerContext?.signCb ?? null;
587
+ }, [signerContext?.signCb]);
588
+ const wrappedSignCb = useCallback(
589
+ async (pubKey, signingInputs) => {
590
+ const cb = signCbRef.current;
591
+ if (!cb) {
592
+ throw new Error("Signer is disconnected. Cannot sign.");
593
+ }
594
+ return cb(pubKey, signingInputs);
595
+ },
596
+ []
597
+ );
598
+ useEffect(() => {
599
+ if (signerIsConnected === null && isInitializedRef.current) return;
600
+ if (signerIsConnected === false) {
601
+ const store = useMidenStore.getState();
602
+ if (store.signerConnected !== false) {
603
+ setSignerConnected(false);
604
+ }
605
+ return;
606
+ }
607
+ if (signerIsConnected === true && signerStoreName !== null) {
608
+ const store = useMidenStore.getState();
609
+ if (currentStoreNameRef.current === signerStoreName && store.client !== null) {
610
+ store.client.setSignCb(wrappedSignCb);
611
+ setSignerConnected(true);
612
+ return;
613
+ }
614
+ if (currentStoreNameRef.current !== null && currentStoreNameRef.current !== signerStoreName) {
615
+ store.resetInMemoryState();
616
+ setClient(null);
617
+ setSignerAccountId(null);
618
+ }
619
+ }
620
+ let cancelled = false;
621
+ const initClient = async () => {
622
+ await runExclusive(async () => {
623
+ if (cancelled) return;
624
+ setInitializing(true);
625
+ setConfig(resolvedConfig);
626
+ try {
627
+ let webClient;
628
+ let didSignerInit = false;
629
+ if (signerContext && signerIsConnected === true) {
630
+ const storeName = `MidenClientDB_${signerContext.storeName}`;
631
+ signCbRef.current = signerContext.signCb;
632
+ webClient = await WebClient.createClientWithExternalKeystore(
633
+ resolvedConfig.rpcUrl,
634
+ resolvedConfig.noteTransportUrl,
635
+ resolvedConfig.seed,
636
+ storeName,
637
+ signerContext.getKeyCb,
638
+ signerContext.insertKeyCb,
639
+ wrappedSignCb,
640
+ void 0,
641
+ resolvedConfig.useWorker
642
+ );
643
+ if (cancelled) return;
644
+ const accountId = await initializeSignerAccount(
645
+ webClient,
646
+ signerContext.accountConfig
647
+ );
648
+ if (cancelled) return;
649
+ setSignerAccountId(accountId);
650
+ didSignerInit = true;
651
+ currentStoreNameRef.current = signerContext.storeName;
652
+ } else {
653
+ const seed = resolvedConfig.seed;
654
+ webClient = await WebClient.createClient(
655
+ resolvedConfig.rpcUrl,
656
+ resolvedConfig.noteTransportUrl,
657
+ seed,
658
+ void 0,
659
+ void 0,
660
+ resolvedConfig.useWorker
661
+ );
662
+ if (cancelled) return;
663
+ }
664
+ if (!didSignerInit) {
665
+ try {
666
+ const summary = await webClient.syncState();
667
+ if (cancelled) return;
668
+ setSyncState({
669
+ syncHeight: summary.blockNum(),
670
+ lastSyncTime: Date.now()
671
+ });
672
+ } catch {
673
+ }
674
+ }
675
+ if (!cancelled) {
676
+ try {
677
+ const accounts = await webClient.getAccounts();
678
+ if (cancelled) return;
679
+ useMidenStore.getState().setAccounts(accounts);
680
+ } catch {
681
+ }
682
+ }
683
+ if (!cancelled) {
684
+ if (!signerContext) {
685
+ isInitializedRef.current = true;
686
+ }
687
+ setClient(webClient);
688
+ if (signerIsConnected === true) {
689
+ setSignerConnected(true);
690
+ }
691
+ }
692
+ } catch (error) {
693
+ if (!cancelled) {
694
+ setInitError(
695
+ error instanceof Error ? error : new Error(String(error))
696
+ );
697
+ }
698
+ }
699
+ });
700
+ };
701
+ initClient();
702
+ return () => {
703
+ cancelled = true;
704
+ isInitializedRef.current = false;
705
+ };
706
+ }, [
707
+ runExclusive,
708
+ resolvedConfig,
709
+ setClient,
710
+ setConfig,
711
+ setInitError,
712
+ setInitializing,
713
+ setSyncState,
714
+ setSignerConnected,
715
+ signerIsConnected,
716
+ signerStoreName,
717
+ signerAccountType,
718
+ signerStorageMode,
719
+ wrappedSignCb
720
+ // Note: signerContext is intentionally NOT a dep — we use stable primitives
721
+ // (signerIsConnected, signerStoreName, signerAccountType, signerStorageMode)
722
+ // to avoid re-running when the signer provider creates a new object ref.
723
+ // signCb changes are handled by the dedicated useEffect + signCbRef above,
724
+ // not by this effect.
725
+ ]);
726
+ useEffect(() => {
727
+ if (!isReady || !client) return;
728
+ const interval = config.autoSyncInterval ?? DEFAULTS.AUTO_SYNC_INTERVAL;
729
+ if (interval <= 0) return;
730
+ syncIntervalRef.current = setInterval(() => {
731
+ if (!useMidenStore.getState().syncPaused) {
732
+ sync();
733
+ }
734
+ }, interval);
735
+ return () => {
736
+ if (syncIntervalRef.current) {
737
+ clearInterval(syncIntervalRef.current);
738
+ syncIntervalRef.current = null;
739
+ }
740
+ };
741
+ }, [isReady, client, config.autoSyncInterval, sync]);
742
+ useEffect(() => {
743
+ if (!isReady || !client) return;
744
+ const unsubscribe = client.onStateChanged?.(async () => {
745
+ try {
746
+ const accounts = await client.getAccounts();
747
+ useMidenStore.getState().setAccounts(accounts);
748
+ setSyncState({ lastSyncTime: Date.now() });
749
+ } catch {
750
+ }
751
+ });
752
+ return () => {
753
+ unsubscribe?.();
754
+ };
755
+ }, [isReady, client, setSyncState]);
756
+ if (isInitializing && loadingComponent) {
757
+ return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent });
758
+ }
759
+ if (initError && errorComponent) {
760
+ if (typeof errorComponent === "function") {
761
+ return /* @__PURE__ */ jsx(Fragment, { children: errorComponent(initError) });
762
+ }
763
+ return /* @__PURE__ */ jsx(Fragment, { children: errorComponent });
764
+ }
765
+ const contextValue = {
766
+ client,
767
+ isReady,
768
+ isInitializing,
769
+ error: initError,
770
+ sync,
771
+ runExclusive,
772
+ prover: defaultProver,
773
+ signerAccountId,
774
+ signerConnected
775
+ };
776
+ return /* @__PURE__ */ jsx(MidenContext.Provider, { value: contextValue, children });
777
+ }
778
+ function useMiden() {
779
+ const context = useContext2(MidenContext);
780
+ if (!context) {
781
+ throw new Error("useMiden must be used within a MidenProvider");
782
+ }
783
+ return context;
784
+ }
785
+ function useMidenClient() {
786
+ const { client, isReady } = useMiden();
787
+ if (!client || !isReady) {
788
+ throw new Error(
789
+ "Miden client is not ready. Make sure you are inside a MidenProvider and the client has initialized."
790
+ );
791
+ }
792
+ return client;
793
+ }
794
+
795
+ // src/context/MultiSignerProvider.tsx
796
+ import {
797
+ createContext as createContext3,
798
+ useContext as useContext3,
799
+ useEffect as useEffect2,
800
+ useRef as useRef2,
801
+ useState as useState2,
802
+ useCallback as useCallback2,
803
+ useMemo as useMemo2
804
+ } from "react";
805
+ import { jsx as jsx2 } from "react/jsx-runtime";
806
+ var MultiSignerRegistryContext = createContext3(null);
807
+ var MultiSignerContext = createContext3(null);
808
+ function MultiSignerProvider({ children }) {
809
+ const signersRef = useRef2(/* @__PURE__ */ new Map());
810
+ const [signersSnapshot, setSignersSnapshot] = useState2(
811
+ []
812
+ );
813
+ const [activeSignerName, setActiveSignerName] = useState2(null);
814
+ const activeSignerNameRef = useRef2(null);
815
+ const generationRef = useRef2(0);
816
+ const updateSnapshot = useCallback2(() => {
817
+ setSignersSnapshot(Array.from(signersRef.current.values()));
818
+ }, []);
819
+ const register = useCallback2(
820
+ (value) => {
821
+ const prev = signersRef.current.get(value.name);
822
+ signersRef.current.set(value.name, value);
823
+ if (!prev || prev.name !== value.name || prev.isConnected !== value.isConnected || prev.storeName !== value.storeName || prev.accountConfig !== value.accountConfig) {
824
+ updateSnapshot();
825
+ }
826
+ },
827
+ [updateSnapshot]
828
+ );
829
+ const unregister = useCallback2(
830
+ (name) => {
831
+ signersRef.current.delete(name);
832
+ setActiveSignerName((current) => current === name ? null : current);
833
+ updateSnapshot();
834
+ },
835
+ [updateSnapshot]
836
+ );
837
+ const registry = useMemo2(
838
+ () => ({ register, unregister }),
839
+ [register, unregister]
840
+ );
841
+ const activeSigner = activeSignerName ? signersSnapshot.find((s) => s.name === activeSignerName) ?? null : null;
842
+ const stableSignCb = useCallback2(
843
+ async (pubKey, signingInputs) => {
844
+ const name = activeSignerName;
845
+ if (!name) throw new Error("No active signer (signer was disconnected)");
846
+ const signer = signersRef.current.get(name);
847
+ if (!signer) throw new Error(`Signer "${name}" not found in registry`);
848
+ return signer.signCb(pubKey, signingInputs);
849
+ },
850
+ [activeSignerName]
851
+ );
852
+ const stableConnect = useCallback2(async () => {
853
+ const name = activeSignerName;
854
+ if (!name) throw new Error("No active signer (signer was disconnected)");
855
+ const signer = signersRef.current.get(name);
856
+ if (!signer) throw new Error(`Signer "${name}" not found in registry`);
857
+ return signer.connect();
858
+ }, [activeSignerName]);
859
+ const stableDisconnect = useCallback2(async () => {
860
+ const name = activeSignerName;
861
+ if (!name) throw new Error("No active signer (signer was disconnected)");
862
+ const signer = signersRef.current.get(name);
863
+ if (!signer) throw new Error(`Signer "${name}" not found in registry`);
864
+ return signer.disconnect();
865
+ }, [activeSignerName]);
866
+ const forwardedValue = useMemo2(() => {
867
+ if (!activeSigner) return null;
868
+ return {
869
+ name: activeSigner.name,
870
+ isConnected: activeSigner.isConnected,
871
+ storeName: activeSigner.storeName,
872
+ accountConfig: activeSigner.accountConfig,
873
+ signCb: stableSignCb,
874
+ connect: stableConnect,
875
+ disconnect: stableDisconnect
876
+ };
877
+ }, [
878
+ activeSigner?.name,
879
+ activeSigner?.isConnected,
880
+ activeSigner?.storeName,
881
+ activeSigner?.accountConfig,
882
+ stableSignCb,
883
+ stableConnect,
884
+ stableDisconnect
885
+ ]);
886
+ const setActiveName = useCallback2((name) => {
887
+ activeSignerNameRef.current = name;
888
+ setActiveSignerName(name);
889
+ }, []);
890
+ const connectSigner = useCallback2(
891
+ async (name) => {
892
+ const currentName = activeSignerNameRef.current;
893
+ const currentSigner = currentName ? signersRef.current.get(currentName) : null;
894
+ if (currentName === name && currentSigner?.isConnected) return;
895
+ const newSigner = signersRef.current.get(name);
896
+ if (!newSigner) throw new Error(`Signer "${name}" not found`);
897
+ const generation = ++generationRef.current;
898
+ setActiveName(name);
899
+ if (currentSigner?.isConnected) {
900
+ currentSigner.disconnect().catch((err) => {
901
+ console.warn("Failed to disconnect previous signer:", err);
902
+ });
903
+ }
904
+ try {
905
+ await newSigner.connect();
906
+ if (generation !== generationRef.current) return;
907
+ } catch (err) {
908
+ if (generation !== generationRef.current) return;
909
+ setActiveName(null);
910
+ throw err;
911
+ }
912
+ },
913
+ [setActiveName]
914
+ );
915
+ const disconnectSigner = useCallback2(async () => {
916
+ ++generationRef.current;
917
+ const currentName = activeSignerNameRef.current;
918
+ const signer = currentName ? signersRef.current.get(currentName) : null;
919
+ setActiveName(null);
920
+ if (signer?.isConnected) {
921
+ signer.disconnect().catch((err) => {
922
+ console.warn("Failed to disconnect signer:", err);
923
+ });
924
+ }
925
+ }, [setActiveName]);
926
+ const multiSignerValue = useMemo2(
927
+ () => ({
928
+ signers: signersSnapshot,
929
+ activeSigner,
930
+ connectSigner,
931
+ disconnectSigner
932
+ }),
933
+ [signersSnapshot, activeSigner, connectSigner, disconnectSigner]
934
+ );
935
+ return /* @__PURE__ */ jsx2(SignerContext.Provider, { value: forwardedValue, children: /* @__PURE__ */ jsx2(MultiSignerRegistryContext.Provider, { value: registry, children: /* @__PURE__ */ jsx2(MultiSignerContext.Provider, { value: multiSignerValue, children }) }) });
936
+ }
937
+ function SignerSlot() {
938
+ const signerValue = useSigner();
939
+ const registry = useContext3(MultiSignerRegistryContext);
940
+ const nameRef = useRef2();
941
+ useEffect2(() => {
942
+ if (!signerValue || !registry) return;
943
+ nameRef.current = signerValue.name;
944
+ registry.register(signerValue);
945
+ }, [signerValue, registry]);
946
+ useEffect2(() => {
947
+ return () => {
948
+ if (nameRef.current && registry) {
949
+ registry.unregister(nameRef.current);
950
+ }
951
+ };
952
+ }, [registry]);
953
+ return null;
954
+ }
955
+ function useMultiSigner() {
956
+ return useContext3(MultiSignerContext);
957
+ }
958
+
959
+ // src/hooks/useAccounts.ts
960
+ import { useCallback as useCallback3, useEffect as useEffect3 } from "react";
961
+ function useAccounts() {
962
+ const { client, isReady } = useMiden();
963
+ const accounts = useAccountsStore();
964
+ const isLoadingAccounts = useMidenStore((state) => state.isLoadingAccounts);
965
+ const setLoadingAccounts = useMidenStore((state) => state.setLoadingAccounts);
966
+ const setAccounts = useMidenStore((state) => state.setAccounts);
967
+ const refetch = useCallback3(async () => {
968
+ if (!client || !isReady) return;
969
+ setLoadingAccounts(true);
970
+ try {
971
+ const fetchedAccounts = await client.getAccounts();
972
+ setAccounts(fetchedAccounts);
973
+ } catch (error) {
974
+ console.error("Failed to fetch accounts:", error);
975
+ } finally {
976
+ setLoadingAccounts(false);
977
+ }
978
+ }, [client, isReady, setAccounts, setLoadingAccounts]);
979
+ useEffect3(() => {
980
+ if (isReady && accounts.length === 0) {
981
+ refetch();
982
+ }
983
+ }, [isReady, accounts.length, refetch]);
984
+ const wallets = [];
985
+ const faucets = [];
986
+ for (const account of accounts) {
987
+ const accountId = account.id();
988
+ if (isFaucetId(accountId)) {
989
+ faucets.push(account);
990
+ } else {
991
+ wallets.push(account);
992
+ }
993
+ }
994
+ return {
995
+ accounts,
996
+ wallets,
997
+ faucets,
998
+ isLoading: isLoadingAccounts,
999
+ error: null,
1000
+ refetch
1001
+ };
1002
+ }
1003
+
1004
+ // src/hooks/useAccount.ts
1005
+ import { useCallback as useCallback4, useEffect as useEffect5, useState as useState3, useMemo as useMemo4 } from "react";
1006
+
1007
+ // src/hooks/useAssetMetadata.ts
1008
+ import { useEffect as useEffect4, useMemo as useMemo3 } from "react";
1009
+ import {
1010
+ BasicFungibleFaucetComponent,
1011
+ Endpoint,
1012
+ RpcClient
1013
+ } from "@miden-sdk/miden-sdk/mt/lazy";
1014
+ var inflight = /* @__PURE__ */ new Map();
1015
+ var rpcClients = /* @__PURE__ */ new Map();
1016
+ var getRpcClient = (rpcUrl) => {
1017
+ const key = rpcUrl ?? "__default__";
1018
+ const existing = rpcClients.get(key);
1019
+ if (existing) return existing;
1020
+ try {
1021
+ const endpoint = rpcUrl ? new Endpoint(rpcUrl) : Endpoint.testnet();
1022
+ const client = new RpcClient(endpoint);
1023
+ rpcClients.set(key, client);
1024
+ return client;
1025
+ } catch {
1026
+ return null;
1027
+ }
1028
+ };
1029
+ var fetchAssetMetadata = async (rpcClient, assetId) => {
1030
+ try {
1031
+ const accountId = parseAccountId(assetId);
1032
+ if (!isFaucetId(accountId)) return null;
1033
+ const fetched = await rpcClient.getAccountDetails(accountId);
1034
+ const account = fetched.account?.();
1035
+ if (!account) return null;
1036
+ const faucet = BasicFungibleFaucetComponent.fromAccount(account);
1037
+ const symbol = faucet.symbol().toString();
1038
+ const decimals = faucet.decimals();
1039
+ return { assetId, symbol, decimals };
1040
+ } catch {
1041
+ return null;
1042
+ }
1043
+ };
1044
+ function useAssetMetadata(assetIds = []) {
1045
+ const assetMetadata = useAssetMetadataStore();
1046
+ const setAssetMetadata = useMidenStore((state) => state.setAssetMetadata);
1047
+ const rpcUrl = useMidenStore((state) => state.config.rpcUrl);
1048
+ const rpcClient = useMemo3(() => getRpcClient(rpcUrl), [rpcUrl]);
1049
+ const uniqueAssetIds = useMemo3(
1050
+ () => Array.from(new Set(assetIds.filter(Boolean))),
1051
+ [assetIds]
1052
+ );
1053
+ useEffect4(() => {
1054
+ if (!rpcClient || uniqueAssetIds.length === 0) return;
1055
+ uniqueAssetIds.forEach((assetId) => {
1056
+ const existing = assetMetadata.get(assetId);
1057
+ const hasMetadata = existing?.symbol !== void 0 || existing?.decimals !== void 0;
1058
+ if (hasMetadata || inflight.has(assetId)) return;
1059
+ const promise = fetchAssetMetadata(rpcClient, assetId).then((metadata) => {
1060
+ setAssetMetadata(assetId, metadata ?? { assetId });
1061
+ }).finally(() => {
1062
+ inflight.delete(assetId);
1063
+ });
1064
+ inflight.set(assetId, promise);
1065
+ });
1066
+ }, [uniqueAssetIds, assetMetadata, setAssetMetadata, rpcClient]);
1067
+ return { assetMetadata };
1068
+ }
1069
+
1070
+ // src/hooks/useAccount.ts
1071
+ function useAccount(accountId) {
1072
+ const { client, isReady } = useMiden();
1073
+ const accountDetails = useMidenStore((state) => state.accountDetails);
1074
+ const setAccountDetails = useMidenStore((state) => state.setAccountDetails);
1075
+ const { lastSyncTime } = useSyncStateStore();
1076
+ const [isLoading, setIsLoading] = useState3(false);
1077
+ const [error, setError] = useState3(null);
1078
+ const accountIdStr = useMemo4(() => {
1079
+ if (!accountId) return void 0;
1080
+ if (typeof accountId === "string") return accountId;
1081
+ return parseAccountId(accountId).toString();
1082
+ }, [accountId]);
1083
+ const account = accountIdStr ? accountDetails.get(accountIdStr) ?? null : null;
1084
+ const refetch = useCallback4(async () => {
1085
+ if (!client || !isReady || !accountIdStr) return;
1086
+ setIsLoading(true);
1087
+ setError(null);
1088
+ try {
1089
+ const accountIdObj = parseAccountId(accountIdStr);
1090
+ const fetchedAccount = await client.getAccount(accountIdObj);
1091
+ if (fetchedAccount) {
1092
+ ensureAccountBech32(fetchedAccount);
1093
+ setAccountDetails(accountIdStr, fetchedAccount);
1094
+ }
1095
+ } catch (err) {
1096
+ setError(err instanceof Error ? err : new Error(String(err)));
1097
+ } finally {
1098
+ setIsLoading(false);
1099
+ }
1100
+ }, [client, isReady, accountIdStr, setAccountDetails]);
1101
+ useEffect5(() => {
1102
+ if (isReady && accountIdStr && !account) {
1103
+ refetch();
1104
+ }
1105
+ }, [isReady, accountIdStr, account, refetch]);
1106
+ useEffect5(() => {
1107
+ if (!isReady || !accountIdStr || !lastSyncTime) return;
1108
+ refetch();
1109
+ }, [isReady, accountIdStr, lastSyncTime, refetch]);
1110
+ const rawAssets = useMemo4(() => {
1111
+ if (!account) return [];
1112
+ try {
1113
+ const vault = account.vault();
1114
+ const assetsList = [];
1115
+ const vaultAssets = vault.fungibleAssets();
1116
+ for (const asset of vaultAssets) {
1117
+ assetsList.push({
1118
+ assetId: asset.faucetId().toString(),
1119
+ amount: asset.amount()
1120
+ });
1121
+ }
1122
+ return assetsList;
1123
+ } catch {
1124
+ return [];
1125
+ }
1126
+ }, [account]);
1127
+ const assetIds = useMemo4(
1128
+ () => rawAssets.map((asset) => asset.assetId),
1129
+ [rawAssets]
1130
+ );
1131
+ const { assetMetadata } = useAssetMetadata(assetIds);
1132
+ const assets = useMemo4(
1133
+ () => rawAssets.map((asset) => {
1134
+ const metadata = assetMetadata.get(asset.assetId);
1135
+ return {
1136
+ ...asset,
1137
+ symbol: metadata?.symbol,
1138
+ decimals: metadata?.decimals
1139
+ };
1140
+ }),
1141
+ [rawAssets, assetMetadata]
1142
+ );
1143
+ const getBalance = useCallback4(
1144
+ (assetId) => {
1145
+ const asset = assets.find((a) => a.assetId === assetId);
1146
+ return asset?.amount ?? 0n;
1147
+ },
1148
+ [assets]
1149
+ );
1150
+ return {
1151
+ account,
1152
+ assets,
1153
+ isLoading,
1154
+ error,
1155
+ refetch,
1156
+ getBalance
1157
+ };
1158
+ }
1159
+
1160
+ // src/hooks/useNotes.ts
1161
+ import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo5, useState as useState4 } from "react";
1162
+ import { NoteFilter } from "@miden-sdk/miden-sdk/mt/lazy";
1163
+
1164
+ // src/utils/amounts.ts
1165
+ var formatAssetAmount = (amount, decimals) => {
1166
+ const amt = BigInt(amount);
1167
+ if (!decimals || decimals <= 0) {
1168
+ return amt.toString();
1169
+ }
1170
+ const factor = 10n ** BigInt(decimals);
1171
+ const whole = amt / factor;
1172
+ const fraction = amt % factor;
1173
+ if (fraction === 0n) {
1174
+ return whole.toString();
1175
+ }
1176
+ const fractionText = fraction.toString().padStart(decimals, "0").replace(/0+$/, "");
1177
+ return `${whole.toString()}.${fractionText}`;
1178
+ };
1179
+ var parseAssetAmount = (input, decimals) => {
1180
+ const value = input.trim();
1181
+ if (!value) {
1182
+ throw new Error("Amount is required");
1183
+ }
1184
+ if (!decimals || decimals <= 0) {
1185
+ if (value.includes(".")) {
1186
+ throw new Error("Amount must be a whole number");
1187
+ }
1188
+ return BigInt(value);
1189
+ }
1190
+ const [wholeText, fractionText = ""] = value.split(".");
1191
+ if (value.split(".").length > 2) {
1192
+ throw new Error("Amount has too many decimal points");
1193
+ }
1194
+ const normalizedWhole = wholeText.length ? wholeText : "0";
1195
+ if (fractionText.length > decimals) {
1196
+ throw new Error("Amount has too many decimal places");
1197
+ }
1198
+ const paddedFraction = fractionText.padEnd(decimals, "0");
1199
+ const factor = 10n ** BigInt(decimals);
1200
+ return BigInt(normalizedWhole) * factor + BigInt(paddedFraction || "0");
1201
+ };
1202
+
1203
+ // src/utils/notes.ts
1204
+ var resolveNoteInput = async (input, client) => {
1205
+ if (typeof input === "string") {
1206
+ const record2 = await client.getInputNote(input);
1207
+ if (!record2) {
1208
+ throw new Error(`Note not found: ${input}`);
1209
+ }
1210
+ return record2.toNote();
1211
+ }
1212
+ if (typeof input.toNote === "function") {
1213
+ return input.toNote();
1214
+ }
1215
+ if (typeof input.id === "function") {
1216
+ return input;
1217
+ }
1218
+ const hex = input.toString();
1219
+ const record = await client.getInputNote(hex);
1220
+ if (!record) {
1221
+ throw new Error(`Note not found: ${hex}`);
1222
+ }
1223
+ return record.toNote();
1224
+ };
1225
+ var getInputNoteRecord = (note) => {
1226
+ const maybeConsumable = note;
1227
+ if (typeof maybeConsumable.inputNoteRecord === "function") {
1228
+ return maybeConsumable.inputNoteRecord();
1229
+ }
1230
+ return note;
1231
+ };
1232
+ var getNoteSummary = (note, getAssetMetadata) => {
1233
+ try {
1234
+ const record = getInputNoteRecord(note);
1235
+ const rawId = record.id();
1236
+ if (!rawId) return null;
1237
+ const id = rawId.toString();
1238
+ const assets = [];
1239
+ try {
1240
+ const details = record.details();
1241
+ const assetsList = details?.assets?.().fungibleAssets?.() ?? [];
1242
+ for (const asset of assetsList) {
1243
+ const assetId = asset.faucetId().toString();
1244
+ const metadata2 = getAssetMetadata?.(assetId);
1245
+ assets.push({
1246
+ assetId,
1247
+ amount: BigInt(asset.amount()),
1248
+ symbol: metadata2?.symbol,
1249
+ decimals: metadata2?.decimals
1250
+ });
1251
+ }
1252
+ } catch {
1253
+ }
1254
+ const metadata = record.metadata?.();
1255
+ const senderHex = metadata?.sender?.()?.toString?.();
1256
+ const sender = senderHex ? toBech32AccountId(senderHex) : void 0;
1257
+ return { id, assets, sender };
1258
+ } catch {
1259
+ return null;
1260
+ }
1261
+ };
1262
+ var formatNoteSummary = (summary, formatAsset = (asset) => `${formatAssetAmount(asset.amount, asset.decimals)} ${asset.symbol ?? asset.assetId}`) => {
1263
+ if (!summary.assets.length) {
1264
+ return summary.id;
1265
+ }
1266
+ const assetsText = summary.assets.map(formatAsset).join(" + ");
1267
+ return summary.sender ? `${assetsText} from ${summary.sender}` : assetsText;
1268
+ };
1269
+
1270
+ // src/utils/accountId.ts
1271
+ function normalizeAccountId(id) {
1272
+ return toBech32AccountId(id);
1273
+ }
1274
+ function accountIdsEqual(a, b) {
1275
+ let idA;
1276
+ let idB;
1277
+ try {
1278
+ idA = parseAccountId(a);
1279
+ idB = parseAccountId(b);
1280
+ return idA.toString() === idB.toString();
1281
+ } catch {
1282
+ return a === b;
1283
+ } finally {
1284
+ idA?.free?.();
1285
+ idB?.free?.();
1286
+ }
1287
+ }
1288
+
1289
+ // src/utils/noteFilters.ts
1290
+ import {
1291
+ NoteFilterTypes,
1292
+ NoteType,
1293
+ TransactionFilter
1294
+ } from "@miden-sdk/miden-sdk/mt/lazy";
1295
+ function getNoteFilterType(status) {
1296
+ switch (status) {
1297
+ case "consumed":
1298
+ return NoteFilterTypes.Consumed;
1299
+ case "committed":
1300
+ return NoteFilterTypes.Committed;
1301
+ case "expected":
1302
+ return NoteFilterTypes.Expected;
1303
+ case "processing":
1304
+ return NoteFilterTypes.Processing;
1305
+ case "all":
1306
+ default:
1307
+ return NoteFilterTypes.All;
1308
+ }
1309
+ }
1310
+ function getNoteType(type) {
1311
+ switch (type) {
1312
+ case "private":
1313
+ return NoteType.Private;
1314
+ case "public":
1315
+ return NoteType.Public;
1316
+ default:
1317
+ return NoteType.Private;
1318
+ }
1319
+ }
1320
+ async function waitForTransactionCommit(client, runExclusiveSafe, txIdHex, maxWaitMs = 1e4, delayMs = 1e3) {
1321
+ const deadline = Date.now() + maxWaitMs;
1322
+ const targetHex = normalizeHex(txIdHex);
1323
+ while (Date.now() < deadline) {
1324
+ await runExclusiveSafe(() => client.syncState());
1325
+ const records = await runExclusiveSafe(
1326
+ () => client.getTransactions(TransactionFilter.all())
1327
+ );
1328
+ const record = records.find(
1329
+ (r) => normalizeHex(r.id().toHex()) === targetHex
1330
+ );
1331
+ if (record) {
1332
+ const status = record.transactionStatus();
1333
+ if (status.isCommitted()) {
1334
+ return;
1335
+ }
1336
+ if (status.isDiscarded()) {
1337
+ throw new Error("Transaction was discarded before commit");
1338
+ }
1339
+ }
1340
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
1341
+ }
1342
+ throw new Error("Timeout waiting for transaction commit");
1343
+ }
1344
+ function normalizeHex(value) {
1345
+ const trimmed = value.trim();
1346
+ const normalized = trimmed.startsWith("0x") || trimmed.startsWith("0X") ? trimmed : `0x${trimmed}`;
1347
+ return normalized.toLowerCase();
1348
+ }
1349
+
1350
+ // src/hooks/useNotes.ts
1351
+ function useNotes(options) {
1352
+ const { client, isReady } = useMiden();
1353
+ const notes = useNotesStore();
1354
+ const consumableNotes = useConsumableNotesStore();
1355
+ const isLoadingNotes = useMidenStore((state) => state.isLoadingNotes);
1356
+ const setLoadingNotes = useMidenStore((state) => state.setLoadingNotes);
1357
+ const setNotesIfChanged = useMidenStore((state) => state.setNotesIfChanged);
1358
+ const setConsumableNotesIfChanged = useMidenStore(
1359
+ (state) => state.setConsumableNotesIfChanged
1360
+ );
1361
+ const { lastSyncTime } = useSyncStateStore();
1362
+ const [error, setError] = useState4(null);
1363
+ const refetch = useCallback5(async () => {
1364
+ if (!client || !isReady) return;
1365
+ setLoadingNotes(true);
1366
+ setError(null);
1367
+ try {
1368
+ const filterType = getNoteFilterType(options?.status);
1369
+ const filter = new NoteFilter(filterType);
1370
+ const fetchedNotes = await client.getInputNotes(filter);
1371
+ let fetchedConsumable;
1372
+ if (options?.accountId) {
1373
+ const accountIdObj = parseAccountId(options.accountId);
1374
+ fetchedConsumable = await client.getConsumableNotes(accountIdObj);
1375
+ } else {
1376
+ fetchedConsumable = await client.getConsumableNotes();
1377
+ }
1378
+ setNotesIfChanged(fetchedNotes);
1379
+ setConsumableNotesIfChanged(fetchedConsumable);
1380
+ } catch (err) {
1381
+ setError(err instanceof Error ? err : new Error(String(err)));
1382
+ } finally {
1383
+ setLoadingNotes(false);
1384
+ }
1385
+ }, [
1386
+ client,
1387
+ isReady,
1388
+ options?.status,
1389
+ options?.accountId,
1390
+ setLoadingNotes,
1391
+ setNotesIfChanged,
1392
+ setConsumableNotesIfChanged
1393
+ ]);
1394
+ useEffect6(() => {
1395
+ if (isReady && notes.length === 0) {
1396
+ refetch();
1397
+ }
1398
+ }, [isReady, notes.length, refetch]);
1399
+ useEffect6(() => {
1400
+ if (!isReady || !lastSyncTime) return;
1401
+ refetch();
1402
+ }, [isReady, lastSyncTime, refetch]);
1403
+ const noteAssetIds = useMemo5(() => {
1404
+ const ids = /* @__PURE__ */ new Set();
1405
+ const collect = (note) => {
1406
+ const summary = getNoteSummary(note);
1407
+ if (!summary) return;
1408
+ summary.assets.forEach((asset) => ids.add(asset.assetId));
1409
+ };
1410
+ notes.forEach(collect);
1411
+ consumableNotes.forEach(collect);
1412
+ return Array.from(ids);
1413
+ }, [notes, consumableNotes]);
1414
+ const { assetMetadata } = useAssetMetadata(noteAssetIds);
1415
+ const getMetadata = useCallback5(
1416
+ (assetId) => assetMetadata.get(assetId),
1417
+ [assetMetadata]
1418
+ );
1419
+ const normalizedSender = useMemo5(() => {
1420
+ if (!options?.sender) return null;
1421
+ try {
1422
+ return normalizeAccountId(options.sender);
1423
+ } catch {
1424
+ return options.sender;
1425
+ }
1426
+ }, [options?.sender]);
1427
+ const excludeIdsKey = useMemo5(() => {
1428
+ if (!options?.excludeIds || options.excludeIds.length === 0) return "";
1429
+ return [...options.excludeIds].sort().join("\0");
1430
+ }, [options?.excludeIds]);
1431
+ const filterBySender = useCallback5(
1432
+ (summaries, target) => {
1433
+ const cache = /* @__PURE__ */ new Map();
1434
+ return summaries.filter((s) => {
1435
+ if (!s.sender) return false;
1436
+ let normalized = cache.get(s.sender);
1437
+ if (normalized === void 0) {
1438
+ try {
1439
+ normalized = normalizeAccountId(s.sender);
1440
+ } catch {
1441
+ normalized = s.sender;
1442
+ }
1443
+ cache.set(s.sender, normalized);
1444
+ }
1445
+ return normalized === target;
1446
+ });
1447
+ },
1448
+ []
1449
+ );
1450
+ const noteSummaries = useMemo5(() => {
1451
+ let summaries = notes.map((note) => getNoteSummary(note, getMetadata)).filter(Boolean);
1452
+ if (normalizedSender) {
1453
+ summaries = filterBySender(summaries, normalizedSender);
1454
+ }
1455
+ if (excludeIdsKey) {
1456
+ const excludeSet = new Set(excludeIdsKey.split("\0"));
1457
+ summaries = summaries.filter((s) => !excludeSet.has(s.id));
1458
+ }
1459
+ return summaries;
1460
+ }, [notes, getMetadata, normalizedSender, excludeIdsKey, filterBySender]);
1461
+ const consumableNoteSummaries = useMemo5(() => {
1462
+ let summaries = consumableNotes.map((note) => getNoteSummary(note, getMetadata)).filter(Boolean);
1463
+ if (normalizedSender) {
1464
+ summaries = filterBySender(summaries, normalizedSender);
1465
+ }
1466
+ if (excludeIdsKey) {
1467
+ const excludeSet = new Set(excludeIdsKey.split("\0"));
1468
+ summaries = summaries.filter((s) => !excludeSet.has(s.id));
1469
+ }
1470
+ return summaries;
1471
+ }, [
1472
+ consumableNotes,
1473
+ getMetadata,
1474
+ normalizedSender,
1475
+ excludeIdsKey,
1476
+ filterBySender
1477
+ ]);
1478
+ return {
1479
+ notes,
1480
+ consumableNotes,
1481
+ noteSummaries,
1482
+ consumableNoteSummaries,
1483
+ isLoading: isLoadingNotes,
1484
+ error,
1485
+ refetch
1486
+ };
1487
+ }
1488
+
1489
+ // src/hooks/useNoteStream.ts
1490
+ import { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo6, useRef as useRef3, useState as useState5 } from "react";
1491
+ import { NoteFilter as NoteFilter2 } from "@miden-sdk/miden-sdk/mt/lazy";
1492
+
1493
+ // src/utils/noteAttachment.ts
1494
+ import {
1495
+ NoteAttachment,
1496
+ NoteAttachmentScheme,
1497
+ Word
1498
+ } from "@miden-sdk/miden-sdk/mt/lazy";
1499
+ function readNoteAttachment(note) {
1500
+ try {
1501
+ const attachments = note.attachments?.();
1502
+ if (!attachments || attachments.length === 0) return null;
1503
+ const words = attachments[0].toWords();
1504
+ if (words.length === 0) return null;
1505
+ const values = [];
1506
+ for (const word of words) {
1507
+ for (const value of word.toU64s()) {
1508
+ values.push(BigInt(value));
1509
+ }
1510
+ }
1511
+ if (values.every((value) => value === 0n)) return null;
1512
+ return { values, kind: words.length === 1 ? "word" : "array" };
1513
+ } catch {
1514
+ return null;
1515
+ }
1516
+ }
1517
+ function createNoteAttachment(values) {
1518
+ const bigints = [];
1519
+ for (let i = 0; i < values.length; i++) {
1520
+ bigints.push(BigInt(values[i]));
1521
+ }
1522
+ if (bigints.length === 0) {
1523
+ return emptyAttachment();
1524
+ }
1525
+ const scheme = NoteAttachmentScheme.none();
1526
+ if (bigints.length <= 4) {
1527
+ while (bigints.length < 4) {
1528
+ bigints.push(0n);
1529
+ }
1530
+ const word = new Word(BigUint64Array.from(bigints));
1531
+ return NoteAttachment.fromWord(scheme, word);
1532
+ }
1533
+ while (bigints.length % 4 !== 0) {
1534
+ bigints.push(0n);
1535
+ }
1536
+ const words = [];
1537
+ for (let i = 0; i < bigints.length; i += 4) {
1538
+ words.push(new Word(BigUint64Array.from(bigints.slice(i, i + 4))));
1539
+ }
1540
+ return NoteAttachment.fromWords(scheme, words);
1541
+ }
1542
+ function emptyAttachment() {
1543
+ const scheme = NoteAttachmentScheme.none();
1544
+ const word = new Word(BigUint64Array.from([0n, 0n, 0n, 0n]));
1545
+ return NoteAttachment.fromWord(scheme, word);
1546
+ }
1547
+
1548
+ // src/hooks/useNoteStream.ts
1549
+ function useNoteStream(options = {}) {
1550
+ const { client, isReady } = useMiden();
1551
+ const allNotes = useNotesStore();
1552
+ const noteFirstSeen = useNoteFirstSeenStore();
1553
+ const { lastSyncTime } = useSyncStateStore();
1554
+ const setNotesIfChanged = useMidenStore((state) => state.setNotesIfChanged);
1555
+ const [isLoading, setIsLoading] = useState5(false);
1556
+ const [error, setError] = useState5(null);
1557
+ const handledIdsRef = useRef3(/* @__PURE__ */ new Set());
1558
+ const [handledVersion, setHandledVersion] = useState5(0);
1559
+ const status = options.status ?? "committed";
1560
+ const sender = options.sender ?? null;
1561
+ const since = options.since;
1562
+ const amountFilterRef = useRef3(options.amountFilter);
1563
+ amountFilterRef.current = options.amountFilter;
1564
+ const excludeIdsKey = useMemo6(() => {
1565
+ if (!options.excludeIds) return "";
1566
+ if (options.excludeIds instanceof Set)
1567
+ return Array.from(options.excludeIds).sort().join("\0");
1568
+ return [...options.excludeIds].sort().join("\0");
1569
+ }, [options.excludeIds]);
1570
+ const excludeIdSet = useMemo6(() => {
1571
+ if (!excludeIdsKey) return null;
1572
+ return new Set(excludeIdsKey.split("\0"));
1573
+ }, [excludeIdsKey]);
1574
+ const refetch = useCallback6(async () => {
1575
+ if (!client || !isReady) return;
1576
+ setIsLoading(true);
1577
+ setError(null);
1578
+ try {
1579
+ const filterType = getNoteFilterType(status);
1580
+ const filter = new NoteFilter2(filterType);
1581
+ const fetched = await client.getInputNotes(filter);
1582
+ setNotesIfChanged(fetched);
1583
+ } catch (err) {
1584
+ setError(err instanceof Error ? err : new Error(String(err)));
1585
+ } finally {
1586
+ setIsLoading(false);
1587
+ }
1588
+ }, [client, isReady, status, setNotesIfChanged]);
1589
+ useEffect7(() => {
1590
+ if (isReady) {
1591
+ refetch();
1592
+ }
1593
+ }, [isReady, lastSyncTime, refetch]);
1594
+ const streamedNotes = useMemo6(() => {
1595
+ void handledVersion;
1596
+ const result = [];
1597
+ let normalizedSender = null;
1598
+ if (sender) {
1599
+ try {
1600
+ normalizedSender = normalizeAccountId(sender);
1601
+ } catch {
1602
+ normalizedSender = sender;
1603
+ }
1604
+ }
1605
+ for (const record of allNotes) {
1606
+ const note = buildStreamedNote(record, noteFirstSeen);
1607
+ if (!note) continue;
1608
+ if (handledIdsRef.current.has(note.id)) continue;
1609
+ if (excludeIdSet && excludeIdSet.has(note.id)) continue;
1610
+ if (normalizedSender && note.sender !== normalizedSender) continue;
1611
+ if (since !== void 0 && note.firstSeenAt < since) continue;
1612
+ if (amountFilterRef.current && !amountFilterRef.current(note.amount))
1613
+ continue;
1614
+ result.push(note);
1615
+ }
1616
+ result.sort((a, b) => a.firstSeenAt - b.firstSeenAt);
1617
+ return result;
1618
+ }, [allNotes, noteFirstSeen, excludeIdSet, sender, since, handledVersion]);
1619
+ const latest = useMemo6(
1620
+ () => streamedNotes.length > 0 ? streamedNotes[streamedNotes.length - 1] : null,
1621
+ [streamedNotes]
1622
+ );
1623
+ const markHandled = useCallback6((noteId) => {
1624
+ handledIdsRef.current = new Set(handledIdsRef.current).add(noteId);
1625
+ setHandledVersion((v) => v + 1);
1626
+ }, []);
1627
+ const markAllHandled = useCallback6(() => {
1628
+ const newSet = new Set(handledIdsRef.current);
1629
+ for (const note of streamedNotes) {
1630
+ newSet.add(note.id);
1631
+ }
1632
+ handledIdsRef.current = newSet;
1633
+ setHandledVersion((v) => v + 1);
1634
+ }, [streamedNotes]);
1635
+ const snapshot = useCallback6(() => {
1636
+ const ids = /* @__PURE__ */ new Set();
1637
+ for (const note of streamedNotes) {
1638
+ ids.add(note.id);
1639
+ }
1640
+ return { ids, timestamp: Date.now() };
1641
+ }, [streamedNotes]);
1642
+ return {
1643
+ notes: streamedNotes,
1644
+ latest,
1645
+ markHandled,
1646
+ markAllHandled,
1647
+ snapshot,
1648
+ isLoading,
1649
+ error
1650
+ };
1651
+ }
1652
+ function buildStreamedNote(record, noteFirstSeen) {
1653
+ try {
1654
+ const rawId = record.id();
1655
+ if (!rawId) return null;
1656
+ const id = rawId.toString();
1657
+ const metadata = record.metadata?.();
1658
+ const senderHex = metadata?.sender?.()?.toString?.();
1659
+ const sender = senderHex ? toBech32AccountId(senderHex) : "";
1660
+ const assets = [];
1661
+ let primaryAmount = 0n;
1662
+ try {
1663
+ const details = record.details();
1664
+ const assetsList = details?.assets?.().fungibleAssets?.() ?? [];
1665
+ for (const asset of assetsList) {
1666
+ const assetId = asset.faucetId().toString();
1667
+ const amount = BigInt(asset.amount());
1668
+ assets.push({ assetId, amount });
1669
+ if (primaryAmount === 0n) {
1670
+ primaryAmount = amount;
1671
+ }
1672
+ }
1673
+ } catch {
1674
+ }
1675
+ const attachmentData = readNoteAttachment(record);
1676
+ const attachment = attachmentData ? attachmentData.values : null;
1677
+ const firstSeenAt = noteFirstSeen.get(id) ?? Date.now();
1678
+ return {
1679
+ id,
1680
+ sender,
1681
+ amount: primaryAmount,
1682
+ assets,
1683
+ record,
1684
+ firstSeenAt,
1685
+ attachment
1686
+ };
1687
+ } catch {
1688
+ return null;
1689
+ }
1690
+ }
1691
+
1692
+ // src/hooks/useTransactionHistory.ts
1693
+ import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo7, useState as useState6 } from "react";
1694
+ import { TransactionFilter as TransactionFilter2 } from "@miden-sdk/miden-sdk/mt/lazy";
1695
+ function useTransactionHistory(options = {}) {
1696
+ const { client, isReady } = useMiden();
1697
+ const { lastSyncTime } = useSyncStateStore();
1698
+ const [records, setRecords] = useState6([]);
1699
+ const [isLoading, setIsLoading] = useState6(false);
1700
+ const [error, setError] = useState6(null);
1701
+ const rawIds = useMemo7(() => {
1702
+ if (options.id) return [options.id];
1703
+ if (options.ids && options.ids.length > 0) return options.ids;
1704
+ return null;
1705
+ }, [options.id, options.ids]);
1706
+ const idsHex = useMemo7(() => {
1707
+ if (!rawIds) return null;
1708
+ return rawIds.map(
1709
+ (id) => normalizeHex2(typeof id === "string" ? id : id.toHex())
1710
+ );
1711
+ }, [rawIds]);
1712
+ const filter = options.filter;
1713
+ const refreshOnSync = options.refreshOnSync !== false;
1714
+ const refetch = useCallback7(async () => {
1715
+ if (!client || !isReady) return;
1716
+ setIsLoading(true);
1717
+ setError(null);
1718
+ try {
1719
+ const { filter: resolvedFilter, localFilterHexes } = buildFilter(
1720
+ filter,
1721
+ rawIds,
1722
+ idsHex
1723
+ );
1724
+ const fetched = await client.getTransactions(resolvedFilter);
1725
+ const filtered = localFilterHexes ? fetched.filter(
1726
+ (record2) => localFilterHexes.includes(normalizeHex2(record2.id().toHex()))
1727
+ ) : fetched;
1728
+ setRecords(filtered);
1729
+ } catch (err) {
1730
+ setError(err instanceof Error ? err : new Error(String(err)));
1731
+ } finally {
1732
+ setIsLoading(false);
1733
+ }
1734
+ }, [client, isReady, filter, rawIds, idsHex]);
1735
+ useEffect8(() => {
1736
+ if (!isReady) return;
1737
+ refetch();
1738
+ }, [isReady, refetch]);
1739
+ useEffect8(() => {
1740
+ if (!isReady || !refreshOnSync || !lastSyncTime) return;
1741
+ refetch();
1742
+ }, [isReady, lastSyncTime, refreshOnSync, refetch]);
1743
+ const record = useMemo7(() => {
1744
+ if (!idsHex || idsHex.length !== 1) return null;
1745
+ return records.find((item) => normalizeHex2(item.id().toHex()) === idsHex[0]) ?? null;
1746
+ }, [records, idsHex]);
1747
+ const status = useMemo7(() => {
1748
+ if (!record) return null;
1749
+ const current = record.transactionStatus();
1750
+ if (current.isCommitted()) return "committed";
1751
+ if (current.isDiscarded()) return "discarded";
1752
+ if (current.isPending()) return "pending";
1753
+ return null;
1754
+ }, [record]);
1755
+ return {
1756
+ records,
1757
+ record,
1758
+ status,
1759
+ isLoading,
1760
+ error,
1761
+ refetch
1762
+ };
1763
+ }
1764
+ function buildFilter(filter, ids, idsHex) {
1765
+ if (filter) {
1766
+ return { filter };
1767
+ }
1768
+ if (!ids || ids.length === 0) {
1769
+ return { filter: TransactionFilter2.all() };
1770
+ }
1771
+ const allTransactionIds = ids.every((id) => typeof id !== "string");
1772
+ if (allTransactionIds) {
1773
+ return { filter: TransactionFilter2.ids(ids) };
1774
+ }
1775
+ return {
1776
+ filter: TransactionFilter2.all(),
1777
+ localFilterHexes: idsHex ?? []
1778
+ };
1779
+ }
1780
+ function normalizeHex2(value) {
1781
+ const trimmed = value.trim();
1782
+ const normalized = trimmed.startsWith("0x") || trimmed.startsWith("0X") ? trimmed : `0x${trimmed}`;
1783
+ return normalized.toLowerCase();
1784
+ }
1785
+
1786
+ // src/hooks/useSyncState.ts
1787
+ import { useCallback as useCallback8 } from "react";
1788
+ function useSyncState() {
1789
+ const { sync: triggerSync } = useMiden();
1790
+ const syncState = useSyncStateStore();
1791
+ const sync = useCallback8(async () => {
1792
+ await triggerSync();
1793
+ }, [triggerSync]);
1794
+ return {
1795
+ ...syncState,
1796
+ sync
1797
+ };
1798
+ }
1799
+
1800
+ // src/hooks/useCreateWallet.ts
1801
+ import { useCallback as useCallback9, useState as useState7 } from "react";
1802
+ import { AccountStorageMode } from "@miden-sdk/miden-sdk/mt/lazy";
1803
+
1804
+ // src/utils/runExclusive.ts
1805
+ var runExclusiveDirect = async (fn) => fn();
1806
+
1807
+ // src/hooks/useCreateWallet.ts
1808
+ function useCreateWallet() {
1809
+ const { client, isReady, runExclusive } = useMiden();
1810
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
1811
+ const setAccounts = useMidenStore((state) => state.setAccounts);
1812
+ const [wallet, setWallet] = useState7(null);
1813
+ const [isCreating, setIsCreating] = useState7(false);
1814
+ const [error, setError] = useState7(null);
1815
+ const createWallet = useCallback9(
1816
+ async (options = {}) => {
1817
+ if (!client || !isReady) {
1818
+ throw new Error("Miden client is not ready");
1819
+ }
1820
+ setIsCreating(true);
1821
+ setError(null);
1822
+ try {
1823
+ const storageMode = getStorageMode(
1824
+ options.storageMode ?? DEFAULTS.STORAGE_MODE
1825
+ );
1826
+ const mutable = options.mutable ?? DEFAULTS.WALLET_MUTABLE;
1827
+ const authScheme = options.authScheme ?? DEFAULTS.AUTH_SCHEME;
1828
+ const newWallet = await runExclusiveSafe(async () => {
1829
+ const createdWallet = await client.newWallet(
1830
+ storageMode,
1831
+ mutable,
1832
+ authScheme,
1833
+ options.initSeed
1834
+ );
1835
+ ensureAccountBech32(createdWallet);
1836
+ const accounts = await client.getAccounts();
1837
+ setAccounts(accounts);
1838
+ return createdWallet;
1839
+ });
1840
+ setWallet(newWallet);
1841
+ return newWallet;
1842
+ } catch (err) {
1843
+ const error2 = err instanceof Error ? err : new Error(String(err));
1844
+ setError(error2);
1845
+ throw error2;
1846
+ } finally {
1847
+ setIsCreating(false);
1848
+ }
1849
+ },
1850
+ [client, isReady, runExclusive, setAccounts]
1851
+ );
1852
+ const reset = useCallback9(() => {
1853
+ setWallet(null);
1854
+ setIsCreating(false);
1855
+ setError(null);
1856
+ }, []);
1857
+ return {
1858
+ createWallet,
1859
+ wallet,
1860
+ isCreating,
1861
+ error,
1862
+ reset
1863
+ };
1864
+ }
1865
+ function getStorageMode(mode) {
1866
+ switch (mode) {
1867
+ case "private":
1868
+ return AccountStorageMode.private();
1869
+ case "public":
1870
+ return AccountStorageMode.public();
1871
+ default:
1872
+ return AccountStorageMode.private();
1873
+ }
1874
+ }
1875
+
1876
+ // src/hooks/useCreateFaucet.ts
1877
+ import { useCallback as useCallback10, useState as useState8 } from "react";
1878
+ import { AccountStorageMode as AccountStorageMode2 } from "@miden-sdk/miden-sdk/mt/lazy";
1879
+ function useCreateFaucet() {
1880
+ const { client, isReady, runExclusive } = useMiden();
1881
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
1882
+ const setAccounts = useMidenStore((state) => state.setAccounts);
1883
+ const [faucet, setFaucet] = useState8(null);
1884
+ const [isCreating, setIsCreating] = useState8(false);
1885
+ const [error, setError] = useState8(null);
1886
+ const createFaucet = useCallback10(
1887
+ async (options) => {
1888
+ if (!client || !isReady) {
1889
+ throw new Error("Miden client is not ready");
1890
+ }
1891
+ setIsCreating(true);
1892
+ setError(null);
1893
+ try {
1894
+ const storageMode = getStorageMode2(
1895
+ options.storageMode ?? DEFAULTS.STORAGE_MODE
1896
+ );
1897
+ const decimals = options.decimals ?? DEFAULTS.FAUCET_DECIMALS;
1898
+ const authScheme = options.authScheme ?? DEFAULTS.AUTH_SCHEME;
1899
+ const newFaucet = await runExclusiveSafe(async () => {
1900
+ const createdFaucet = await client.newFaucet(
1901
+ storageMode,
1902
+ false,
1903
+ // nonFungible - currently only fungible faucets supported
1904
+ options.tokenName ?? options.tokenSymbol,
1905
+ options.tokenSymbol,
1906
+ decimals,
1907
+ BigInt(options.maxSupply),
1908
+ authScheme
1909
+ );
1910
+ const accounts = await client.getAccounts();
1911
+ setAccounts(accounts);
1912
+ return createdFaucet;
1913
+ });
1914
+ setFaucet(newFaucet);
1915
+ return newFaucet;
1916
+ } catch (err) {
1917
+ const error2 = err instanceof Error ? err : new Error(String(err));
1918
+ setError(error2);
1919
+ throw error2;
1920
+ } finally {
1921
+ setIsCreating(false);
1922
+ }
1923
+ },
1924
+ [client, isReady, runExclusive, setAccounts]
1925
+ );
1926
+ const reset = useCallback10(() => {
1927
+ setFaucet(null);
1928
+ setIsCreating(false);
1929
+ setError(null);
1930
+ }, []);
1931
+ return {
1932
+ createFaucet,
1933
+ faucet,
1934
+ isCreating,
1935
+ error,
1936
+ reset
1937
+ };
1938
+ }
1939
+ function getStorageMode2(mode) {
1940
+ switch (mode) {
1941
+ case "private":
1942
+ return AccountStorageMode2.private();
1943
+ case "public":
1944
+ return AccountStorageMode2.public();
1945
+ default:
1946
+ return AccountStorageMode2.private();
1947
+ }
1948
+ }
1949
+
1950
+ // src/hooks/useImportAccount.ts
1951
+ import { useCallback as useCallback11, useState as useState9 } from "react";
1952
+ import { AccountFile } from "@miden-sdk/miden-sdk/mt/lazy";
1953
+
1954
+ // src/utils/errors.ts
1955
+ var MidenError = class extends Error {
1956
+ constructor(message, options) {
1957
+ super(message);
1958
+ this.name = "MidenError";
1959
+ this.code = options?.code ?? "UNKNOWN";
1960
+ if (options?.cause !== void 0) {
1961
+ this.cause = options.cause;
1962
+ }
1963
+ }
1964
+ };
1965
+ var ERROR_PATTERNS = [
1966
+ {
1967
+ test: (msg) => msg.includes("_assertClass") || msg.includes("expected instance of"),
1968
+ code: "WASM_CLASS_MISMATCH",
1969
+ message: "WASM class identity mismatch. This usually means multiple copies of @miden-sdk/miden-sdk are bundled. Ensure your bundler deduplicates the package. For Vite: add resolve.dedupe and optimizeDeps.exclude for @miden-sdk/miden-sdk."
1970
+ },
1971
+ {
1972
+ test: (msg) => msg.includes("null pointer") || msg.includes("already been freed") || msg.includes("dereferencing a null"),
1973
+ code: "WASM_POINTER_CONSUMED",
1974
+ message: "WASM object was already consumed. Some WASM-bound objects can only be passed once \u2014 if you need to reuse a value, create a fresh instance before each call."
1975
+ },
1976
+ {
1977
+ test: (msg) => msg.includes("not initialized") || msg.includes("Cannot read properties of null"),
1978
+ code: "WASM_NOT_INITIALIZED",
1979
+ message: "Miden client is not initialized. Ensure you are inside a <MidenProvider> and the client is ready before calling SDK methods."
1980
+ },
1981
+ {
1982
+ test: (msg) => msg.includes("state commitment mismatch") || msg.includes("stale state"),
1983
+ code: "WASM_SYNC_REQUIRED",
1984
+ message: "Account state is stale. Call sync() before executing transactions, or ensure no concurrent transactions are running against the same account."
1985
+ }
1986
+ ];
1987
+ function assertSignerConnected(signerConnected) {
1988
+ if (signerConnected === false) {
1989
+ throw new Error(
1990
+ "Signer is disconnected. Reconnect your wallet to perform transactions."
1991
+ );
1992
+ }
1993
+ }
1994
+ function wrapWasmError(e) {
1995
+ if (e instanceof MidenError) return e;
1996
+ const msg = e instanceof Error ? e.message : String(e);
1997
+ for (const pattern of ERROR_PATTERNS) {
1998
+ if (pattern.test(msg)) {
1999
+ return new MidenError(pattern.message, { cause: e, code: pattern.code });
2000
+ }
2001
+ }
2002
+ if (e instanceof Error) return e;
2003
+ return new Error(msg);
2004
+ }
2005
+
2006
+ // src/hooks/useImportAccount.ts
2007
+ function useImportAccount() {
2008
+ const { client, isReady, signerConnected } = useMiden();
2009
+ const setAccounts = useMidenStore((state) => state.setAccounts);
2010
+ const [account, setAccount] = useState9(null);
2011
+ const [isImporting, setIsImporting] = useState9(false);
2012
+ const [error, setError] = useState9(null);
2013
+ const importAccount = useCallback11(
2014
+ async (options) => {
2015
+ if (!client || !isReady) {
2016
+ throw new Error("Miden client is not ready");
2017
+ }
2018
+ assertSignerConnected(signerConnected);
2019
+ setIsImporting(true);
2020
+ setError(null);
2021
+ try {
2022
+ let accountsAfter = null;
2023
+ const imported = await (async () => {
2024
+ switch (options.type) {
2025
+ case "file": {
2026
+ const accountsBefore = await client.getAccounts();
2027
+ const accountFile = await resolveAccountFile(options.file);
2028
+ const accountFileWithAccount = accountFile;
2029
+ const fileBytes = getAccountFileBytes(
2030
+ accountFileWithAccount,
2031
+ options.file
2032
+ );
2033
+ const accountFromFile = typeof accountFileWithAccount.account === "function" ? accountFileWithAccount.account() : null;
2034
+ const accountIdFromFile = accountFromFile === null && typeof accountFileWithAccount.accountId === "function" ? accountFileWithAccount.accountId() : null;
2035
+ try {
2036
+ await client.importAccountFile(accountFile);
2037
+ } catch (err) {
2038
+ const message = err instanceof Error ? err.message : String(err);
2039
+ if (!message.includes("already being tracked")) {
2040
+ throw err;
2041
+ }
2042
+ }
2043
+ accountsAfter = await client.getAccounts();
2044
+ if (accountFromFile) {
2045
+ return accountFromFile;
2046
+ }
2047
+ const beforeIds = new Set(
2048
+ accountsBefore.map((account2) => account2.id().toString())
2049
+ );
2050
+ const newAccountHeader = accountsAfter.find(
2051
+ (account2) => !beforeIds.has(account2.id().toString())
2052
+ );
2053
+ const accountId = accountIdFromFile ?? newAccountHeader?.id();
2054
+ if (accountId) {
2055
+ const fetchedAccount = await client.getAccount(accountId);
2056
+ if (fetchedAccount) {
2057
+ return fetchedAccount;
2058
+ }
2059
+ }
2060
+ if (fileBytes) {
2061
+ for (const header of accountsAfter) {
2062
+ const exported = await client.exportAccountFile(header.id());
2063
+ const exportedBytes = getAccountFileBytes(exported, exported);
2064
+ if (exportedBytes && bytesEqual(exportedBytes, fileBytes)) {
2065
+ const fetchedAccount = await client.getAccount(header.id());
2066
+ if (fetchedAccount) {
2067
+ return fetchedAccount;
2068
+ }
2069
+ }
2070
+ }
2071
+ }
2072
+ throw new Error("Account not found after import");
2073
+ }
2074
+ case "id": {
2075
+ const accountId = parseAccountId(options.accountId);
2076
+ await client.importAccountById(accountId);
2077
+ const fetchedAccount = await client.getAccount(accountId);
2078
+ if (!fetchedAccount) {
2079
+ throw new Error("Account not found after import");
2080
+ }
2081
+ return fetchedAccount;
2082
+ }
2083
+ case "seed": {
2084
+ const mutable = options.mutable ?? DEFAULTS.WALLET_MUTABLE;
2085
+ const authScheme = options.authScheme ?? DEFAULTS.AUTH_SCHEME;
2086
+ return await client.importPublicAccountFromSeed(
2087
+ options.seed,
2088
+ mutable,
2089
+ authScheme
2090
+ );
2091
+ }
2092
+ }
2093
+ })();
2094
+ ensureAccountBech32(imported);
2095
+ const accounts = accountsAfter ?? await client.getAccounts();
2096
+ setAccounts(accounts);
2097
+ setAccount(imported);
2098
+ return imported;
2099
+ } catch (err) {
2100
+ const error2 = err instanceof Error ? err : new Error(String(err));
2101
+ setError(error2);
2102
+ throw error2;
2103
+ } finally {
2104
+ setIsImporting(false);
2105
+ }
2106
+ },
2107
+ [client, isReady, setAccounts, signerConnected]
2108
+ );
2109
+ const reset = useCallback11(() => {
2110
+ setAccount(null);
2111
+ setIsImporting(false);
2112
+ setError(null);
2113
+ }, []);
2114
+ return {
2115
+ importAccount,
2116
+ account,
2117
+ isImporting,
2118
+ error,
2119
+ reset
2120
+ };
2121
+ }
2122
+ async function resolveAccountFile(file) {
2123
+ if (file instanceof Uint8Array) {
2124
+ return AccountFile.deserialize(file);
2125
+ }
2126
+ if (file instanceof ArrayBuffer) {
2127
+ return AccountFile.deserialize(new Uint8Array(file));
2128
+ }
2129
+ return file;
2130
+ }
2131
+ function getAccountFileBytes(accountFile, original) {
2132
+ if (original instanceof Uint8Array) {
2133
+ return original;
2134
+ }
2135
+ if (original instanceof ArrayBuffer) {
2136
+ return new Uint8Array(original);
2137
+ }
2138
+ if (typeof accountFile.serialize === "function") {
2139
+ return accountFile.serialize();
2140
+ }
2141
+ return null;
2142
+ }
2143
+ function bytesEqual(left, right) {
2144
+ if (left.length !== right.length) {
2145
+ return false;
2146
+ }
2147
+ for (let i = 0; i < left.length; i += 1) {
2148
+ if (left[i] !== right[i]) {
2149
+ return false;
2150
+ }
2151
+ }
2152
+ return true;
2153
+ }
2154
+
2155
+ // src/hooks/useSend.ts
2156
+ import { useCallback as useCallback12, useRef as useRef4, useState as useState10 } from "react";
2157
+ import {
2158
+ FungibleAsset,
2159
+ Note,
2160
+ NoteAssets,
2161
+ NoteType as NoteType2,
2162
+ NoteArray,
2163
+ TransactionRequestBuilder
2164
+ } from "@miden-sdk/miden-sdk/mt/lazy";
2165
+ function useSend() {
2166
+ const { client, isReady, sync, runExclusive, prover } = useMiden();
2167
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
2168
+ const isBusyRef = useRef4(false);
2169
+ const [result, setResult] = useState10(null);
2170
+ const [isLoading, setIsLoading] = useState10(false);
2171
+ const [stage, setStage] = useState10("idle");
2172
+ const [error, setError] = useState10(null);
2173
+ const send = useCallback12(
2174
+ async (options) => {
2175
+ if (!client || !isReady) {
2176
+ throw new Error("Miden client is not ready");
2177
+ }
2178
+ if (isBusyRef.current) {
2179
+ throw new MidenError(
2180
+ "A send is already in progress. Await the previous send before starting another.",
2181
+ { code: "SEND_BUSY" }
2182
+ );
2183
+ }
2184
+ isBusyRef.current = true;
2185
+ setIsLoading(true);
2186
+ setStage("executing");
2187
+ setError(null);
2188
+ try {
2189
+ if (!options.skipSync) {
2190
+ await sync();
2191
+ }
2192
+ const noteType = getNoteType(options.noteType ?? DEFAULTS.NOTE_TYPE);
2193
+ let amount = options.amount;
2194
+ if (options.sendAll) {
2195
+ const resolvedAmount = await runExclusiveSafe(async () => {
2196
+ const fromId = parseAccountId(options.from);
2197
+ const account = await client.getAccount(fromId);
2198
+ if (!account) throw new Error("Account not found");
2199
+ const assetIdObj = parseAccountId(options.assetId);
2200
+ const balance = account.vault?.()?.getBalance?.(assetIdObj);
2201
+ if (balance === void 0 || balance === null) {
2202
+ throw new Error("Could not query account balance");
2203
+ }
2204
+ const bal = BigInt(balance);
2205
+ if (bal === 0n) {
2206
+ throw new Error("Account has zero balance for this asset");
2207
+ }
2208
+ return bal;
2209
+ });
2210
+ amount = resolvedAmount;
2211
+ }
2212
+ if (amount === void 0 || amount === null) {
2213
+ throw new Error("Amount is required (provide amount or sendAll)");
2214
+ }
2215
+ amount = BigInt(amount);
2216
+ const assetId = options.assetId ?? options.faucetId ?? null;
2217
+ if (!assetId) {
2218
+ throw new Error("Asset ID is required");
2219
+ }
2220
+ const hasAttachment = options.attachment !== void 0 && options.attachment !== null;
2221
+ if (hasAttachment && (options.recallHeight != null || options.timelockHeight != null)) {
2222
+ throw new Error(
2223
+ "recallHeight and timelockHeight are not supported when attachment is provided"
2224
+ );
2225
+ }
2226
+ if (options.returnNote === true) {
2227
+ const returnResult = await runExclusiveSafe(async () => {
2228
+ const fromId = parseAccountId(options.from);
2229
+ const toId = parseAccountId(options.to);
2230
+ const assetObj = parseAccountId(assetId);
2231
+ const assets = new NoteAssets([
2232
+ new FungibleAsset(assetObj, BigInt(amount))
2233
+ ]);
2234
+ const p2idNote = Note.createP2IDNote(
2235
+ fromId,
2236
+ toId,
2237
+ assets,
2238
+ noteType,
2239
+ emptyAttachment()
2240
+ );
2241
+ const ownOutputs = new NoteArray();
2242
+ ownOutputs.push(p2idNote);
2243
+ const txRequest = new TransactionRequestBuilder().withOwnOutputNotes(ownOutputs).build();
2244
+ const execFromId = parseAccountId(options.from);
2245
+ const txId = prover ? await client.submitNewTransactionWithProver(
2246
+ execFromId,
2247
+ txRequest,
2248
+ prover
2249
+ ) : await client.submitNewTransaction(execFromId, txRequest);
2250
+ return { txId: txId.toHex(), note: p2idNote };
2251
+ });
2252
+ setStage("complete");
2253
+ setResult(returnResult);
2254
+ await sync();
2255
+ return returnResult;
2256
+ }
2257
+ const txResult = await runExclusiveSafe(async () => {
2258
+ const fromAccountId = parseAccountId(options.from);
2259
+ const toAccountId = parseAccountId(options.to);
2260
+ const assetIdObj = parseAccountId(assetId);
2261
+ let txRequest;
2262
+ if (hasAttachment) {
2263
+ const attachment = createNoteAttachment(options.attachment);
2264
+ const assets = new NoteAssets([
2265
+ new FungibleAsset(assetIdObj, amount)
2266
+ ]);
2267
+ const note = Note.createP2IDNote(
2268
+ fromAccountId,
2269
+ toAccountId,
2270
+ assets,
2271
+ noteType,
2272
+ attachment
2273
+ );
2274
+ txRequest = new TransactionRequestBuilder().withOwnOutputNotes(new NoteArray([note])).build();
2275
+ } else {
2276
+ txRequest = await client.newSendTransactionRequest(
2277
+ fromAccountId,
2278
+ toAccountId,
2279
+ assetIdObj,
2280
+ noteType,
2281
+ amount,
2282
+ options.recallHeight ?? null,
2283
+ options.timelockHeight ?? null
2284
+ );
2285
+ }
2286
+ const execAccountId = parseAccountId(options.from);
2287
+ return await client.executeTransaction(execAccountId, txRequest);
2288
+ });
2289
+ setStage("proving");
2290
+ const proverConfig = useMidenStore.getState().config;
2291
+ const provenTransaction = await proveWithFallback(
2292
+ (resolvedProver) => runExclusiveSafe(
2293
+ () => client.proveTransaction(txResult, resolvedProver)
2294
+ ),
2295
+ proverConfig
2296
+ );
2297
+ setStage("submitting");
2298
+ const submissionHeight = await runExclusiveSafe(
2299
+ () => client.submitProvenTransaction(provenTransaction, txResult)
2300
+ );
2301
+ const txIdHex = txResult.id().toHex();
2302
+ let fullNote = null;
2303
+ if (noteType === NoteType2.Private) {
2304
+ fullNote = extractFullNote(txResult);
2305
+ }
2306
+ await runExclusiveSafe(
2307
+ () => client.applyTransaction(txResult, submissionHeight)
2308
+ );
2309
+ if (noteType === NoteType2.Private) {
2310
+ if (!fullNote) {
2311
+ throw new Error("Missing full note for private send");
2312
+ }
2313
+ await waitForTransactionCommit(
2314
+ client,
2315
+ runExclusiveSafe,
2316
+ txIdHex
2317
+ );
2318
+ const recipientAccountId = parseAccountId(options.to);
2319
+ const recipientAddress = parseAddress(options.to, recipientAccountId);
2320
+ await runExclusiveSafe(
2321
+ () => client.sendPrivateNote(fullNote, recipientAddress)
2322
+ );
2323
+ }
2324
+ const sendResult = {
2325
+ txId: txIdHex,
2326
+ note: null
2327
+ };
2328
+ setStage("complete");
2329
+ setResult(sendResult);
2330
+ await sync();
2331
+ return sendResult;
2332
+ } catch (err) {
2333
+ const error2 = err instanceof Error ? err : new Error(String(err));
2334
+ setError(error2);
2335
+ setStage("idle");
2336
+ throw error2;
2337
+ } finally {
2338
+ setIsLoading(false);
2339
+ isBusyRef.current = false;
2340
+ }
2341
+ },
2342
+ [client, isReady, prover, runExclusive, sync]
2343
+ );
2344
+ const reset = useCallback12(() => {
2345
+ setResult(null);
2346
+ setIsLoading(false);
2347
+ setStage("idle");
2348
+ setError(null);
2349
+ }, []);
2350
+ return {
2351
+ send,
2352
+ result,
2353
+ isLoading,
2354
+ stage,
2355
+ error,
2356
+ reset
2357
+ };
2358
+ }
2359
+ function extractFullNote(txResult) {
2360
+ try {
2361
+ const executedTx = txResult.executedTransaction?.();
2362
+ const notes = executedTx?.outputNotes?.().notes?.() ?? [];
2363
+ const note = notes[0];
2364
+ return note?.intoFull?.() ?? null;
2365
+ } catch {
2366
+ return null;
2367
+ }
2368
+ }
2369
+
2370
+ // src/hooks/useMultiSend.ts
2371
+ import { useCallback as useCallback13, useRef as useRef5, useState as useState11 } from "react";
2372
+ import {
2373
+ FungibleAsset as FungibleAsset2,
2374
+ Note as Note2,
2375
+ NoteAssets as NoteAssets2,
2376
+ NoteType as NoteType3,
2377
+ NoteArray as NoteArray2,
2378
+ TransactionRequestBuilder as TransactionRequestBuilder2
2379
+ } from "@miden-sdk/miden-sdk/mt/lazy";
2380
+ function useMultiSend() {
2381
+ const { client, isReady, sync, prover, signerConnected } = useMiden();
2382
+ const isBusyRef = useRef5(false);
2383
+ const [result, setResult] = useState11(null);
2384
+ const [isLoading, setIsLoading] = useState11(false);
2385
+ const [stage, setStage] = useState11("idle");
2386
+ const [error, setError] = useState11(null);
2387
+ const sendMany = useCallback13(
2388
+ async (options) => {
2389
+ if (!client || !isReady) {
2390
+ throw new Error("Miden client is not ready");
2391
+ }
2392
+ assertSignerConnected(signerConnected);
2393
+ if (options.recipients.length === 0) {
2394
+ throw new Error("No recipients provided");
2395
+ }
2396
+ if (isBusyRef.current) {
2397
+ throw new MidenError(
2398
+ "A send is already in progress. Await the previous send before starting another.",
2399
+ { code: "SEND_BUSY" }
2400
+ );
2401
+ }
2402
+ isBusyRef.current = true;
2403
+ setIsLoading(true);
2404
+ setStage("executing");
2405
+ setError(null);
2406
+ try {
2407
+ if (!options.skipSync) {
2408
+ await sync();
2409
+ }
2410
+ const noteType = getNoteType(options.noteType ?? DEFAULTS.NOTE_TYPE);
2411
+ const outputs = options.recipients.map(
2412
+ ({ to, amount, attachment, noteType: recipientNoteType }) => {
2413
+ const iterSenderId = parseAccountId(options.from);
2414
+ const iterAssetId = parseAccountId(options.assetId);
2415
+ const receiverId = parseAccountId(to);
2416
+ const assets = new NoteAssets2([
2417
+ new FungibleAsset2(iterAssetId, BigInt(amount))
2418
+ ]);
2419
+ const resolvedNoteType = recipientNoteType ? getNoteType(recipientNoteType) : noteType;
2420
+ const noteAttachment = attachment !== void 0 && attachment !== null ? createNoteAttachment(attachment) : emptyAttachment();
2421
+ const note = Note2.createP2IDNote(
2422
+ iterSenderId,
2423
+ receiverId,
2424
+ assets,
2425
+ resolvedNoteType,
2426
+ noteAttachment
2427
+ );
2428
+ const recipientAddress = parseAddress(to, receiverId);
2429
+ return {
2430
+ note,
2431
+ recipientAddress,
2432
+ noteType: resolvedNoteType
2433
+ };
2434
+ }
2435
+ );
2436
+ const ownOutputs = new NoteArray2();
2437
+ for (const o of outputs) {
2438
+ ownOutputs.push(o.note);
2439
+ }
2440
+ const txRequest = new TransactionRequestBuilder2().withOwnOutputNotes(ownOutputs).build();
2441
+ const txSenderId = parseAccountId(options.from);
2442
+ const txResult = await client.executeTransaction(txSenderId, txRequest);
2443
+ setStage("proving");
2444
+ const proverConfig = useMidenStore.getState().config;
2445
+ const provenTransaction = await proveWithFallback(
2446
+ (resolvedProver) => runExclusiveDirect(
2447
+ () => client.proveTransaction(txResult, resolvedProver)
2448
+ ),
2449
+ proverConfig
2450
+ );
2451
+ setStage("submitting");
2452
+ const submissionHeight = await client.submitProvenTransaction(
2453
+ provenTransaction,
2454
+ txResult
2455
+ );
2456
+ const txIdHex = txResult.id().toHex();
2457
+ await client.applyTransaction(txResult, submissionHeight);
2458
+ const hasPrivate = outputs.some((o) => o.noteType === NoteType3.Private);
2459
+ if (hasPrivate) {
2460
+ await waitForTransactionCommit(
2461
+ client,
2462
+ runExclusiveDirect,
2463
+ txIdHex
2464
+ );
2465
+ for (const output of outputs) {
2466
+ if (output.noteType === NoteType3.Private) {
2467
+ await client.sendPrivateNote(
2468
+ output.note,
2469
+ output.recipientAddress
2470
+ );
2471
+ }
2472
+ }
2473
+ }
2474
+ const txSummary = { transactionId: txIdHex };
2475
+ setStage("complete");
2476
+ setResult(txSummary);
2477
+ await sync();
2478
+ return txSummary;
2479
+ } catch (err) {
2480
+ const error2 = err instanceof Error ? err : new Error(String(err));
2481
+ setError(error2);
2482
+ setStage("idle");
2483
+ throw error2;
2484
+ } finally {
2485
+ setIsLoading(false);
2486
+ isBusyRef.current = false;
2487
+ }
2488
+ },
2489
+ [client, isReady, prover, signerConnected, sync]
2490
+ );
2491
+ const reset = useCallback13(() => {
2492
+ setResult(null);
2493
+ setIsLoading(false);
2494
+ setStage("idle");
2495
+ setError(null);
2496
+ }, []);
2497
+ return {
2498
+ sendMany,
2499
+ result,
2500
+ isLoading,
2501
+ stage,
2502
+ error,
2503
+ reset
2504
+ };
2505
+ }
2506
+
2507
+ // src/hooks/useWaitForCommit.ts
2508
+ import { useCallback as useCallback14 } from "react";
2509
+ import { TransactionFilter as TransactionFilter3 } from "@miden-sdk/miden-sdk/mt/lazy";
2510
+ function useWaitForCommit() {
2511
+ const { client, isReady } = useMiden();
2512
+ const waitForCommit = useCallback14(
2513
+ async (txId, options) => {
2514
+ if (!client || !isReady) {
2515
+ throw new Error("Miden client is not ready");
2516
+ }
2517
+ const timeoutMs = Math.max(0, options?.timeoutMs ?? 1e4);
2518
+ const intervalMs = Math.max(1, options?.intervalMs ?? 1e3);
2519
+ const targetHex = normalizeHex3(
2520
+ typeof txId === "string" ? txId : txId.toHex()
2521
+ );
2522
+ const deadline = Date.now() + timeoutMs;
2523
+ while (Date.now() < deadline) {
2524
+ await client.syncState();
2525
+ const records = await client.getTransactions(
2526
+ typeof txId === "string" ? TransactionFilter3.all() : TransactionFilter3.ids([txId])
2527
+ );
2528
+ const record = records.find(
2529
+ (item) => normalizeHex3(item.id().toHex()) === targetHex
2530
+ );
2531
+ if (record) {
2532
+ const status = record.transactionStatus();
2533
+ if (status.isCommitted()) {
2534
+ return;
2535
+ }
2536
+ if (status.isDiscarded()) {
2537
+ throw new Error("Transaction was discarded before commit");
2538
+ }
2539
+ }
2540
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
2541
+ }
2542
+ throw new Error("Timeout waiting for transaction commit");
2543
+ },
2544
+ [client, isReady]
2545
+ );
2546
+ return { waitForCommit };
2547
+ }
2548
+ function normalizeHex3(value) {
2549
+ const trimmed = value.trim();
2550
+ const normalized = trimmed.startsWith("0x") || trimmed.startsWith("0X") ? trimmed : `0x${trimmed}`;
2551
+ return normalized.toLowerCase();
2552
+ }
2553
+
2554
+ // src/hooks/useWaitForNotes.ts
2555
+ import { useCallback as useCallback15 } from "react";
2556
+ function useWaitForNotes() {
2557
+ const { client, isReady, runExclusive } = useMiden();
2558
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
2559
+ const waitForConsumableNotes = useCallback15(
2560
+ async (options) => {
2561
+ if (!client || !isReady) {
2562
+ throw new Error("Miden client is not ready");
2563
+ }
2564
+ const timeoutMs = Math.max(0, options.timeoutMs ?? 1e4);
2565
+ const intervalMs = Math.max(1, options.intervalMs ?? 1e3);
2566
+ const minCount = Math.max(1, options.minCount ?? 1);
2567
+ const accountId = parseAccountId(options.accountId);
2568
+ let waited = 0;
2569
+ while (waited < timeoutMs) {
2570
+ await runExclusiveSafe(
2571
+ () => client.syncState()
2572
+ );
2573
+ const consumable = await runExclusiveSafe(
2574
+ () => client.getConsumableNotes(accountId)
2575
+ );
2576
+ if (consumable.length >= minCount) {
2577
+ return consumable;
2578
+ }
2579
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
2580
+ waited += intervalMs;
2581
+ }
2582
+ throw new Error("Timeout waiting for consumable notes");
2583
+ },
2584
+ [client, isReady, runExclusiveSafe]
2585
+ );
2586
+ return { waitForConsumableNotes };
2587
+ }
2588
+
2589
+ // src/hooks/useMint.ts
2590
+ import { useCallback as useCallback16, useState as useState12 } from "react";
2591
+ function useMint() {
2592
+ const { client, isReady, sync, runExclusive, prover } = useMiden();
2593
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
2594
+ const [result, setResult] = useState12(null);
2595
+ const [isLoading, setIsLoading] = useState12(false);
2596
+ const [stage, setStage] = useState12("idle");
2597
+ const [error, setError] = useState12(null);
2598
+ const mint = useCallback16(
2599
+ async (options) => {
2600
+ if (!client || !isReady) {
2601
+ throw new Error("Miden client is not ready");
2602
+ }
2603
+ setIsLoading(true);
2604
+ setStage("executing");
2605
+ setError(null);
2606
+ try {
2607
+ const noteType = getNoteType(options.noteType ?? DEFAULTS.NOTE_TYPE);
2608
+ const targetAccountIdObj = parseAccountId(options.targetAccountId);
2609
+ const faucetIdObj = parseAccountId(options.faucetId);
2610
+ setStage("proving");
2611
+ const txResult = await runExclusiveSafe(async () => {
2612
+ const txRequest = await client.newMintTransactionRequest(
2613
+ targetAccountIdObj,
2614
+ faucetIdObj,
2615
+ noteType,
2616
+ BigInt(options.amount)
2617
+ );
2618
+ const txId = prover ? await client.submitNewTransactionWithProver(
2619
+ faucetIdObj,
2620
+ txRequest,
2621
+ prover
2622
+ ) : await client.submitNewTransaction(faucetIdObj, txRequest);
2623
+ return { transactionId: txId.toHex() };
2624
+ });
2625
+ setStage("complete");
2626
+ setResult(txResult);
2627
+ await sync();
2628
+ return txResult;
2629
+ } catch (err) {
2630
+ const error2 = err instanceof Error ? err : new Error(String(err));
2631
+ setError(error2);
2632
+ setStage("idle");
2633
+ throw error2;
2634
+ } finally {
2635
+ setIsLoading(false);
2636
+ }
2637
+ },
2638
+ [client, isReady, prover, runExclusive, sync]
2639
+ );
2640
+ const reset = useCallback16(() => {
2641
+ setResult(null);
2642
+ setIsLoading(false);
2643
+ setStage("idle");
2644
+ setError(null);
2645
+ }, []);
2646
+ return {
2647
+ mint,
2648
+ result,
2649
+ isLoading,
2650
+ stage,
2651
+ error,
2652
+ reset
2653
+ };
2654
+ }
2655
+
2656
+ // src/hooks/useConsume.ts
2657
+ import { useCallback as useCallback17, useState as useState13 } from "react";
2658
+ import { NoteFilter as NoteFilter3, NoteFilterTypes as NoteFilterTypes2, NoteId } from "@miden-sdk/miden-sdk/mt/lazy";
2659
+ function useConsume() {
2660
+ const { client, isReady, sync, runExclusive, prover } = useMiden();
2661
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
2662
+ const [result, setResult] = useState13(null);
2663
+ const [isLoading, setIsLoading] = useState13(false);
2664
+ const [stage, setStage] = useState13("idle");
2665
+ const [error, setError] = useState13(null);
2666
+ const consume = useCallback17(
2667
+ async (options) => {
2668
+ if (!client || !isReady) {
2669
+ throw new Error("Miden client is not ready");
2670
+ }
2671
+ if (options.notes.length === 0) {
2672
+ throw new Error("No notes provided");
2673
+ }
2674
+ setIsLoading(true);
2675
+ setStage("executing");
2676
+ setError(null);
2677
+ try {
2678
+ const accountIdObj = parseAccountId(options.accountId);
2679
+ setStage("proving");
2680
+ const txResult = await runExclusiveSafe(async () => {
2681
+ const resolved = new Array(options.notes.length);
2682
+ const lookupIndices = [];
2683
+ const lookupIds = [];
2684
+ for (let i = 0; i < options.notes.length; i++) {
2685
+ const item = options.notes[i];
2686
+ if (typeof item === "string") {
2687
+ lookupIndices.push(i);
2688
+ lookupIds.push(NoteId.fromHex(item));
2689
+ } else if (item !== null && typeof item === "object" && typeof item.toNote === "function") {
2690
+ resolved[i] = item.toNote();
2691
+ } else if (item !== null && typeof item === "object" && typeof item.id === "function") {
2692
+ resolved[i] = item;
2693
+ } else {
2694
+ lookupIndices.push(i);
2695
+ lookupIds.push(item);
2696
+ }
2697
+ }
2698
+ if (lookupIds.length > 0) {
2699
+ const lookupIdStrings = lookupIds.map((id) => id.toString());
2700
+ const filter = new NoteFilter3(NoteFilterTypes2.List, lookupIds);
2701
+ const noteRecords = await client.getInputNotes(filter);
2702
+ if (noteRecords.length !== lookupIdStrings.length) {
2703
+ throw new Error("Some notes could not be found for provided IDs");
2704
+ }
2705
+ const recordById = new Map(
2706
+ noteRecords.map((r) => {
2707
+ const id = r.id();
2708
+ if (!id) {
2709
+ throw new Error(
2710
+ "getInputNotes returned a record without a note id"
2711
+ );
2712
+ }
2713
+ return [id.toString(), r];
2714
+ })
2715
+ );
2716
+ for (let j = 0; j < lookupIndices.length; j++) {
2717
+ const record = recordById.get(lookupIdStrings[j]);
2718
+ if (!record) {
2719
+ throw new Error(
2720
+ "Some notes could not be found for provided IDs"
2721
+ );
2722
+ }
2723
+ resolved[lookupIndices[j]] = record.toNote();
2724
+ }
2725
+ }
2726
+ const notes = resolved;
2727
+ const txRequest = client.newConsumeTransactionRequest(notes);
2728
+ const txId = prover ? await client.submitNewTransactionWithProver(
2729
+ accountIdObj,
2730
+ txRequest,
2731
+ prover
2732
+ ) : await client.submitNewTransaction(accountIdObj, txRequest);
2733
+ return { transactionId: txId.toHex() };
2734
+ });
2735
+ setStage("complete");
2736
+ setResult(txResult);
2737
+ await sync();
2738
+ return txResult;
2739
+ } catch (err) {
2740
+ const error2 = err instanceof Error ? err : new Error(String(err));
2741
+ setError(error2);
2742
+ setStage("idle");
2743
+ throw error2;
2744
+ } finally {
2745
+ setIsLoading(false);
2746
+ }
2747
+ },
2748
+ [client, isReady, prover, runExclusive, sync]
2749
+ );
2750
+ const reset = useCallback17(() => {
2751
+ setResult(null);
2752
+ setIsLoading(false);
2753
+ setStage("idle");
2754
+ setError(null);
2755
+ }, []);
2756
+ return {
2757
+ consume,
2758
+ result,
2759
+ isLoading,
2760
+ stage,
2761
+ error,
2762
+ reset
2763
+ };
2764
+ }
2765
+
2766
+ // src/hooks/useSwap.ts
2767
+ import { useCallback as useCallback18, useState as useState14 } from "react";
2768
+ function useSwap() {
2769
+ const { client, isReady, sync, runExclusive, prover } = useMiden();
2770
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
2771
+ const [result, setResult] = useState14(null);
2772
+ const [isLoading, setIsLoading] = useState14(false);
2773
+ const [stage, setStage] = useState14("idle");
2774
+ const [error, setError] = useState14(null);
2775
+ const swap = useCallback18(
2776
+ async (options) => {
2777
+ if (!client || !isReady) {
2778
+ throw new Error("Miden client is not ready");
2779
+ }
2780
+ setIsLoading(true);
2781
+ setStage("executing");
2782
+ setError(null);
2783
+ try {
2784
+ const noteType = getNoteType(options.noteType ?? DEFAULTS.NOTE_TYPE);
2785
+ const paybackNoteType = getNoteType(
2786
+ options.paybackNoteType ?? DEFAULTS.NOTE_TYPE
2787
+ );
2788
+ const accountIdObj = parseAccountId(options.accountId);
2789
+ const offeredFaucetIdObj = parseAccountId(options.offeredFaucetId);
2790
+ const requestedFaucetIdObj = parseAccountId(options.requestedFaucetId);
2791
+ setStage("proving");
2792
+ const txResult = await runExclusiveSafe(async () => {
2793
+ const txRequest = await client.newSwapTransactionRequest(
2794
+ accountIdObj,
2795
+ offeredFaucetIdObj,
2796
+ BigInt(options.offeredAmount),
2797
+ requestedFaucetIdObj,
2798
+ BigInt(options.requestedAmount),
2799
+ noteType,
2800
+ paybackNoteType
2801
+ );
2802
+ const txId = prover ? await client.submitNewTransactionWithProver(
2803
+ accountIdObj,
2804
+ txRequest,
2805
+ prover
2806
+ ) : await client.submitNewTransaction(accountIdObj, txRequest);
2807
+ return { transactionId: txId.toHex() };
2808
+ });
2809
+ setStage("complete");
2810
+ setResult(txResult);
2811
+ await sync();
2812
+ return txResult;
2813
+ } catch (err) {
2814
+ const error2 = err instanceof Error ? err : new Error(String(err));
2815
+ setError(error2);
2816
+ setStage("idle");
2817
+ throw error2;
2818
+ } finally {
2819
+ setIsLoading(false);
2820
+ }
2821
+ },
2822
+ [client, isReady, prover, runExclusive, sync]
2823
+ );
2824
+ const reset = useCallback18(() => {
2825
+ setResult(null);
2826
+ setIsLoading(false);
2827
+ setStage("idle");
2828
+ setError(null);
2829
+ }, []);
2830
+ return {
2831
+ swap,
2832
+ result,
2833
+ isLoading,
2834
+ stage,
2835
+ error,
2836
+ reset
2837
+ };
2838
+ }
2839
+
2840
+ // src/hooks/usePswapCreate.ts
2841
+ import { useCallback as useCallback19, useState as useState15 } from "react";
2842
+ function usePswapCreate() {
2843
+ const { client, isReady, sync, runExclusive, prover } = useMiden();
2844
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
2845
+ const [result, setResult] = useState15(null);
2846
+ const [isLoading, setIsLoading] = useState15(false);
2847
+ const [stage, setStage] = useState15("idle");
2848
+ const [error, setError] = useState15(null);
2849
+ const pswapCreate = useCallback19(
2850
+ async (options) => {
2851
+ if (!client || !isReady) {
2852
+ throw new Error("Miden client is not ready");
2853
+ }
2854
+ const offeredAmount = BigInt(options.offeredAmount);
2855
+ const requestedAmount = BigInt(options.requestedAmount);
2856
+ if (offeredAmount <= 0n) {
2857
+ throw new Error("offeredAmount must be greater than 0");
2858
+ }
2859
+ if (requestedAmount <= 0n) {
2860
+ throw new Error("requestedAmount must be greater than 0");
2861
+ }
2862
+ setIsLoading(true);
2863
+ setStage("executing");
2864
+ setError(null);
2865
+ try {
2866
+ const noteType = getNoteType(options.noteType ?? DEFAULTS.NOTE_TYPE);
2867
+ const paybackNoteType = getNoteType(
2868
+ options.paybackNoteType ?? DEFAULTS.NOTE_TYPE
2869
+ );
2870
+ const accountIdObj = parseAccountId(options.accountId);
2871
+ const offeredFaucetIdObj = parseAccountId(options.offeredFaucetId);
2872
+ const requestedFaucetIdObj = parseAccountId(options.requestedFaucetId);
2873
+ setStage("proving");
2874
+ const txResult = await runExclusiveSafe(async () => {
2875
+ const txRequest = await client.newPswapCreateTransactionRequest(
2876
+ accountIdObj,
2877
+ offeredFaucetIdObj,
2878
+ offeredAmount,
2879
+ requestedFaucetIdObj,
2880
+ requestedAmount,
2881
+ noteType,
2882
+ paybackNoteType
2883
+ );
2884
+ const txId = prover ? await client.submitNewTransactionWithProver(
2885
+ accountIdObj,
2886
+ txRequest,
2887
+ prover
2888
+ ) : await client.submitNewTransaction(accountIdObj, txRequest);
2889
+ return { transactionId: txId.toHex() };
2890
+ });
2891
+ setStage("complete");
2892
+ setResult(txResult);
2893
+ await sync();
2894
+ return txResult;
2895
+ } catch (err) {
2896
+ const error2 = err instanceof Error ? err : new Error(String(err));
2897
+ setError(error2);
2898
+ setStage("idle");
2899
+ throw error2;
2900
+ } finally {
2901
+ setIsLoading(false);
2902
+ }
2903
+ },
2904
+ [client, isReady, prover, runExclusive, sync]
2905
+ );
2906
+ const reset = useCallback19(() => {
2907
+ setResult(null);
2908
+ setIsLoading(false);
2909
+ setStage("idle");
2910
+ setError(null);
2911
+ }, []);
2912
+ return {
2913
+ pswapCreate,
2914
+ result,
2915
+ isLoading,
2916
+ stage,
2917
+ error,
2918
+ reset
2919
+ };
2920
+ }
2921
+
2922
+ // src/hooks/usePswapConsume.ts
2923
+ import { useCallback as useCallback20, useState as useState16 } from "react";
2924
+ function usePswapConsume() {
2925
+ const { client, isReady, sync, runExclusive, prover } = useMiden();
2926
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
2927
+ const [result, setResult] = useState16(null);
2928
+ const [isLoading, setIsLoading] = useState16(false);
2929
+ const [stage, setStage] = useState16("idle");
2930
+ const [error, setError] = useState16(null);
2931
+ const pswapConsume = useCallback20(
2932
+ async (options) => {
2933
+ if (!client || !isReady) {
2934
+ throw new Error("Miden client is not ready");
2935
+ }
2936
+ const fillAmount = BigInt(options.fillAmount);
2937
+ const noteFillAmount = BigInt(options.noteFillAmount ?? 0);
2938
+ if (fillAmount <= 0n) {
2939
+ throw new Error("fillAmount must be greater than 0");
2940
+ }
2941
+ if (noteFillAmount < 0n) {
2942
+ throw new Error("noteFillAmount must not be negative");
2943
+ }
2944
+ setIsLoading(true);
2945
+ setStage("executing");
2946
+ setError(null);
2947
+ try {
2948
+ const accountIdObj = parseAccountId(options.accountId);
2949
+ setStage("proving");
2950
+ const txResult = await runExclusiveSafe(async () => {
2951
+ const note = await resolveNoteInput(options.note, client);
2952
+ const txRequest = await client.newPswapConsumeTransactionRequest(
2953
+ note,
2954
+ accountIdObj,
2955
+ fillAmount,
2956
+ noteFillAmount
2957
+ );
2958
+ const txId = prover ? await client.submitNewTransactionWithProver(
2959
+ accountIdObj,
2960
+ txRequest,
2961
+ prover
2962
+ ) : await client.submitNewTransaction(accountIdObj, txRequest);
2963
+ return { transactionId: txId.toHex() };
2964
+ });
2965
+ setStage("complete");
2966
+ setResult(txResult);
2967
+ await sync();
2968
+ return txResult;
2969
+ } catch (err) {
2970
+ const error2 = err instanceof Error ? err : new Error(String(err));
2971
+ setError(error2);
2972
+ setStage("idle");
2973
+ throw error2;
2974
+ } finally {
2975
+ setIsLoading(false);
2976
+ }
2977
+ },
2978
+ [client, isReady, prover, runExclusive, sync]
2979
+ );
2980
+ const reset = useCallback20(() => {
2981
+ setResult(null);
2982
+ setIsLoading(false);
2983
+ setStage("idle");
2984
+ setError(null);
2985
+ }, []);
2986
+ return {
2987
+ pswapConsume,
2988
+ result,
2989
+ isLoading,
2990
+ stage,
2991
+ error,
2992
+ reset
2993
+ };
2994
+ }
2995
+
2996
+ // src/hooks/usePswapCancel.ts
2997
+ import { useCallback as useCallback21, useState as useState17 } from "react";
2998
+ function usePswapCancel() {
2999
+ const { client, isReady, sync, runExclusive, prover } = useMiden();
3000
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
3001
+ const [result, setResult] = useState17(null);
3002
+ const [isLoading, setIsLoading] = useState17(false);
3003
+ const [stage, setStage] = useState17("idle");
3004
+ const [error, setError] = useState17(null);
3005
+ const pswapCancel = useCallback21(
3006
+ async (options) => {
3007
+ if (!client || !isReady) {
3008
+ throw new Error("Miden client is not ready");
3009
+ }
3010
+ setIsLoading(true);
3011
+ setStage("executing");
3012
+ setError(null);
3013
+ try {
3014
+ const accountIdObj = parseAccountId(options.accountId);
3015
+ setStage("proving");
3016
+ const txResult = await runExclusiveSafe(async () => {
3017
+ const note = await resolveNoteInput(options.note, client);
3018
+ const txRequest = await client.newPswapCancelTransactionRequest(
3019
+ note,
3020
+ accountIdObj
3021
+ );
3022
+ const txId = prover ? await client.submitNewTransactionWithProver(
3023
+ accountIdObj,
3024
+ txRequest,
3025
+ prover
3026
+ ) : await client.submitNewTransaction(accountIdObj, txRequest);
3027
+ return { transactionId: txId.toHex() };
3028
+ });
3029
+ setStage("complete");
3030
+ setResult(txResult);
3031
+ await sync();
3032
+ return txResult;
3033
+ } catch (err) {
3034
+ const error2 = err instanceof Error ? err : new Error(String(err));
3035
+ setError(error2);
3036
+ setStage("idle");
3037
+ throw error2;
3038
+ } finally {
3039
+ setIsLoading(false);
3040
+ }
3041
+ },
3042
+ [client, isReady, prover, runExclusive, sync]
3043
+ );
3044
+ const reset = useCallback21(() => {
3045
+ setResult(null);
3046
+ setIsLoading(false);
3047
+ setStage("idle");
3048
+ setError(null);
3049
+ }, []);
3050
+ return {
3051
+ pswapCancel,
3052
+ result,
3053
+ isLoading,
3054
+ stage,
3055
+ error,
3056
+ reset
3057
+ };
3058
+ }
3059
+
3060
+ // src/hooks/useTransaction.ts
3061
+ import { useCallback as useCallback22, useRef as useRef6, useState as useState18 } from "react";
3062
+
3063
+ // src/utils/transactions.ts
3064
+ import { NoteType as NoteType4, TransactionFilter as TransactionFilter4 } from "@miden-sdk/miden-sdk/mt/lazy";
3065
+ async function waitForTransactionCommit2(client, runExclusiveSafe, txId, maxWaitMs = 1e4, delayMs = 1e3) {
3066
+ let waited = 0;
3067
+ while (waited < maxWaitMs) {
3068
+ await runExclusiveSafe(() => client.syncState());
3069
+ const [record] = await runExclusiveSafe(
3070
+ () => client.getTransactions(TransactionFilter4.ids([txId]))
3071
+ );
3072
+ if (record) {
3073
+ const status = record.transactionStatus();
3074
+ if (status.isCommitted()) {
3075
+ return;
3076
+ }
3077
+ if (status.isDiscarded()) {
3078
+ throw new Error("Transaction was discarded before commit");
3079
+ }
3080
+ }
3081
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
3082
+ waited += delayMs;
3083
+ }
3084
+ throw new Error("Timeout waiting for transaction commit");
3085
+ }
3086
+ function extractFullNotes(txResult) {
3087
+ try {
3088
+ const executedTx = txResult.executedTransaction?.();
3089
+ const notes = executedTx?.outputNotes?.().notes?.() ?? [];
3090
+ const result = [];
3091
+ for (const note of notes) {
3092
+ if (note.noteType?.() === NoteType4.Private) {
3093
+ const full = note.intoFull?.();
3094
+ if (full) result.push(full);
3095
+ }
3096
+ }
3097
+ return result;
3098
+ } catch {
3099
+ return [];
3100
+ }
3101
+ }
3102
+
3103
+ // src/hooks/useTransaction.ts
3104
+ function useTransaction() {
3105
+ const { client, isReady, sync, runExclusive } = useMiden();
3106
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
3107
+ const isBusyRef = useRef6(false);
3108
+ const [result, setResult] = useState18(null);
3109
+ const [isLoading, setIsLoading] = useState18(false);
3110
+ const [stage, setStage] = useState18("idle");
3111
+ const [error, setError] = useState18(null);
3112
+ const execute = useCallback22(
3113
+ async (options) => {
3114
+ if (!client || !isReady) {
3115
+ throw new Error("Miden client is not ready");
3116
+ }
3117
+ if (isBusyRef.current) {
3118
+ throw new MidenError(
3119
+ "A transaction is already in progress. Await the previous transaction before starting another.",
3120
+ { code: "SEND_BUSY" }
3121
+ );
3122
+ }
3123
+ isBusyRef.current = true;
3124
+ setIsLoading(true);
3125
+ setStage("executing");
3126
+ setError(null);
3127
+ try {
3128
+ if (!options.skipSync) {
3129
+ await sync();
3130
+ }
3131
+ const txRequest = await resolveRequest(options.request, client);
3132
+ const txResult = await runExclusiveSafe(() => {
3133
+ const accountIdObj = parseAccountId(options.accountId);
3134
+ return client.executeTransaction(accountIdObj, txRequest);
3135
+ });
3136
+ setStage("proving");
3137
+ const proverConfig = useMidenStore.getState().config;
3138
+ const provenTransaction = await proveWithFallback(
3139
+ (resolvedProver) => runExclusiveSafe(
3140
+ () => client.proveTransaction(txResult, resolvedProver)
3141
+ ),
3142
+ proverConfig
3143
+ );
3144
+ setStage("submitting");
3145
+ const submissionHeight = await runExclusiveSafe(
3146
+ () => client.submitProvenTransaction(provenTransaction, txResult)
3147
+ );
3148
+ await runExclusiveSafe(
3149
+ () => client.applyTransaction(txResult, submissionHeight)
3150
+ );
3151
+ const txId = txResult.id();
3152
+ if (options.privateNoteTarget != null) {
3153
+ await waitForTransactionCommit2(client, runExclusiveSafe, txId);
3154
+ const targetAddress = parseAddress(options.privateNoteTarget);
3155
+ const fullNotes = extractFullNotes(txResult);
3156
+ for (const note of fullNotes) {
3157
+ await runExclusiveSafe(
3158
+ () => client.sendPrivateNote(note, targetAddress)
3159
+ );
3160
+ }
3161
+ }
3162
+ const txSummary = { transactionId: txId.toHex() };
3163
+ setStage("complete");
3164
+ setResult(txSummary);
3165
+ await sync();
3166
+ return txSummary;
3167
+ } catch (err) {
3168
+ const error2 = err instanceof Error ? err : new Error(String(err));
3169
+ setError(error2);
3170
+ setStage("idle");
3171
+ throw error2;
3172
+ } finally {
3173
+ setIsLoading(false);
3174
+ isBusyRef.current = false;
3175
+ }
3176
+ },
3177
+ [client, isReady, runExclusive, sync]
3178
+ );
3179
+ const reset = useCallback22(() => {
3180
+ setResult(null);
3181
+ setIsLoading(false);
3182
+ setStage("idle");
3183
+ setError(null);
3184
+ }, []);
3185
+ return {
3186
+ execute,
3187
+ result,
3188
+ isLoading,
3189
+ stage,
3190
+ error,
3191
+ reset
3192
+ };
3193
+ }
3194
+ async function resolveRequest(request, client) {
3195
+ if (typeof request === "function") {
3196
+ return await request(client);
3197
+ }
3198
+ return request;
3199
+ }
3200
+
3201
+ // src/hooks/useExecuteProgram.ts
3202
+ import { useCallback as useCallback23, useRef as useRef7, useState as useState19 } from "react";
3203
+ import {
3204
+ AdviceInputs,
3205
+ ForeignAccount,
3206
+ ForeignAccountArray,
3207
+ AccountStorageRequirements
3208
+ } from "@miden-sdk/miden-sdk/mt/lazy";
3209
+ function isForeignAccountWrapper(fa) {
3210
+ return fa !== null && typeof fa === "object" && "id" in fa && typeof fa.id !== "function";
3211
+ }
3212
+ function useExecuteProgram() {
3213
+ const { client, isReady, sync, runExclusive } = useMiden();
3214
+ const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
3215
+ const isBusyRef = useRef7(false);
3216
+ const [result, setResult] = useState19(null);
3217
+ const [isLoading, setIsLoading] = useState19(false);
3218
+ const [error, setError] = useState19(null);
3219
+ const execute = useCallback23(
3220
+ async (options) => {
3221
+ if (!client || !isReady) {
3222
+ throw new Error("Miden client is not ready");
3223
+ }
3224
+ if (isBusyRef.current) {
3225
+ throw new MidenError(
3226
+ "A program execution is already in progress. Await the previous call before starting another.",
3227
+ { code: "OPERATION_BUSY" }
3228
+ );
3229
+ }
3230
+ isBusyRef.current = true;
3231
+ setIsLoading(true);
3232
+ setError(null);
3233
+ try {
3234
+ if (!options.skipSync) {
3235
+ await sync();
3236
+ }
3237
+ const programResult = await runExclusiveSafe(async () => {
3238
+ const accountIdObj = parseAccountId(options.accountId);
3239
+ const adviceInputs = options.adviceInputs ?? new AdviceInputs();
3240
+ let foreignAccountsArray;
3241
+ if (options.foreignAccounts?.length) {
3242
+ const accounts = options.foreignAccounts.map((fa) => {
3243
+ const wrapper = isForeignAccountWrapper(fa);
3244
+ const id = parseAccountId(wrapper ? fa.id : fa);
3245
+ const storage = wrapper && fa.storage ? fa.storage : new AccountStorageRequirements();
3246
+ return ForeignAccount.public(id, storage);
3247
+ });
3248
+ foreignAccountsArray = new ForeignAccountArray(accounts);
3249
+ } else {
3250
+ foreignAccountsArray = new ForeignAccountArray();
3251
+ }
3252
+ const feltArray = await client.executeProgram(
3253
+ accountIdObj,
3254
+ options.script,
3255
+ adviceInputs,
3256
+ foreignAccountsArray
3257
+ );
3258
+ const stack = [];
3259
+ const len = feltArray.length();
3260
+ for (let i = 0; i < len; i++) {
3261
+ stack.push(feltArray.get(i).asInt());
3262
+ }
3263
+ return { stack };
3264
+ });
3265
+ setResult(programResult);
3266
+ return programResult;
3267
+ } catch (err) {
3268
+ const error2 = err instanceof Error ? err : new Error(String(err));
3269
+ setError(error2);
3270
+ throw error2;
3271
+ } finally {
3272
+ setIsLoading(false);
3273
+ isBusyRef.current = false;
3274
+ }
3275
+ },
3276
+ [client, isReady, runExclusive, sync]
3277
+ );
3278
+ const reset = useCallback23(() => {
3279
+ setResult(null);
3280
+ setIsLoading(false);
3281
+ setError(null);
3282
+ }, []);
3283
+ return {
3284
+ execute,
3285
+ result,
3286
+ isLoading,
3287
+ error,
3288
+ reset
3289
+ };
3290
+ }
3291
+
3292
+ // src/hooks/useCompile.ts
3293
+ import { useCallback as useCallback24, useMemo as useMemo8 } from "react";
3294
+ import { CompilerResource, getWasmOrThrow } from "@miden-sdk/miden-sdk/mt/lazy";
3295
+ function useCompile() {
3296
+ const { client, isReady } = useMiden();
3297
+ const resource = useMemo8(
3298
+ () => client && isReady ? new CompilerResource(client, getWasmOrThrow) : null,
3299
+ [client, isReady]
3300
+ );
3301
+ const requireResource = useCallback24(() => {
3302
+ if (!resource) {
3303
+ throw new Error("Miden client is not ready");
3304
+ }
3305
+ return resource;
3306
+ }, [resource]);
3307
+ const component = useCallback24(
3308
+ async (options) => requireResource().component(options),
3309
+ [requireResource]
3310
+ );
3311
+ const txScript = useCallback24(
3312
+ async (options) => requireResource().txScript(options),
3313
+ [requireResource]
3314
+ );
3315
+ const noteScript = useCallback24(
3316
+ async (options) => requireResource().noteScript(options),
3317
+ [requireResource]
3318
+ );
3319
+ return { component, txScript, noteScript, isReady };
3320
+ }
3321
+
3322
+ // src/hooks/useSessionAccount.ts
3323
+ import { useCallback as useCallback25, useEffect as useEffect9, useRef as useRef8, useState as useState20 } from "react";
3324
+ import { AccountStorageMode as AccountStorageMode3 } from "@miden-sdk/miden-sdk/mt/lazy";
3325
+ function useSessionAccount(options) {
3326
+ const { client, isReady, sync } = useMiden();
3327
+ const setAccounts = useMidenStore((state) => state.setAccounts);
3328
+ const [sessionAccountId, setSessionAccountId] = useState20(null);
3329
+ const [step, setStep] = useState20("idle");
3330
+ const [error, setError] = useState20(null);
3331
+ const cancelledRef = useRef8(false);
3332
+ const isBusyRef = useRef8(false);
3333
+ const storagePrefix = options.storagePrefix ?? "miden-session";
3334
+ const pollIntervalMs = options.pollIntervalMs ?? 3e3;
3335
+ const maxWaitMs = options.maxWaitMs ?? 6e4;
3336
+ const storageMode = options.walletOptions?.storageMode ?? "public";
3337
+ const mutable = options.walletOptions?.mutable ?? DEFAULTS.WALLET_MUTABLE;
3338
+ const authScheme = options.walletOptions?.authScheme ?? DEFAULTS.AUTH_SCHEME;
3339
+ const fundRef = useRef8(options.fund);
3340
+ fundRef.current = options.fund;
3341
+ useEffect9(() => {
3342
+ try {
3343
+ const stored = localStorage.getItem(`${storagePrefix}:accountId`);
3344
+ const storedReady = localStorage.getItem(`${storagePrefix}:ready`);
3345
+ if (stored && stored.length > 0) {
3346
+ const validationId = parseAccountId(stored);
3347
+ validationId?.free?.();
3348
+ setSessionAccountId(stored);
3349
+ if (storedReady === "true") {
3350
+ setStep("ready");
3351
+ }
3352
+ }
3353
+ } catch {
3354
+ localStorage.removeItem(`${storagePrefix}:accountId`);
3355
+ localStorage.removeItem(`${storagePrefix}:ready`);
3356
+ }
3357
+ }, [storagePrefix]);
3358
+ const initialize = useCallback25(async () => {
3359
+ if (!client || !isReady) {
3360
+ throw new Error("Miden client is not ready");
3361
+ }
3362
+ if (isBusyRef.current) {
3363
+ throw new MidenError(
3364
+ "Session account initialization is already in progress.",
3365
+ { code: "OPERATION_BUSY" }
3366
+ );
3367
+ }
3368
+ isBusyRef.current = true;
3369
+ cancelledRef.current = false;
3370
+ setError(null);
3371
+ try {
3372
+ let walletId = sessionAccountId;
3373
+ if (!walletId) {
3374
+ setStep("creating");
3375
+ const resolvedStorageMode = getStorageMode3(storageMode);
3376
+ const wallet = await client.newWallet(
3377
+ resolvedStorageMode,
3378
+ mutable,
3379
+ authScheme
3380
+ );
3381
+ ensureAccountBech32(wallet);
3382
+ const accounts = await client.getAccounts();
3383
+ setAccounts(accounts);
3384
+ if (cancelledRef.current) return;
3385
+ walletId = wallet.id().toString();
3386
+ setSessionAccountId(walletId);
3387
+ localStorage.setItem(`${storagePrefix}:accountId`, walletId);
3388
+ }
3389
+ setStep("funding");
3390
+ await fundRef.current(walletId);
3391
+ if (cancelledRef.current) return;
3392
+ setStep("consuming");
3393
+ await waitAndConsume(
3394
+ client,
3395
+ walletId,
3396
+ pollIntervalMs,
3397
+ maxWaitMs,
3398
+ cancelledRef
3399
+ );
3400
+ if (cancelledRef.current) return;
3401
+ setStep("ready");
3402
+ localStorage.setItem(`${storagePrefix}:ready`, "true");
3403
+ await sync();
3404
+ } catch (err) {
3405
+ if (!cancelledRef.current) {
3406
+ const error2 = err instanceof Error ? err : new Error(String(err));
3407
+ setError(error2);
3408
+ setStep("idle");
3409
+ throw error2;
3410
+ }
3411
+ } finally {
3412
+ isBusyRef.current = false;
3413
+ }
3414
+ }, [
3415
+ client,
3416
+ isReady,
3417
+ sync,
3418
+ sessionAccountId,
3419
+ storageMode,
3420
+ mutable,
3421
+ authScheme,
3422
+ storagePrefix,
3423
+ pollIntervalMs,
3424
+ maxWaitMs,
3425
+ setAccounts
3426
+ ]);
3427
+ const reset = useCallback25(() => {
3428
+ cancelledRef.current = true;
3429
+ isBusyRef.current = false;
3430
+ setSessionAccountId(null);
3431
+ setStep("idle");
3432
+ setError(null);
3433
+ localStorage.removeItem(`${storagePrefix}:accountId`);
3434
+ localStorage.removeItem(`${storagePrefix}:ready`);
3435
+ }, [storagePrefix]);
3436
+ return {
3437
+ initialize,
3438
+ sessionAccountId,
3439
+ isReady: step === "ready",
3440
+ step,
3441
+ error,
3442
+ reset
3443
+ };
3444
+ }
3445
+ function getStorageMode3(mode) {
3446
+ switch (mode) {
3447
+ case "private":
3448
+ return AccountStorageMode3.private();
3449
+ case "public":
3450
+ return AccountStorageMode3.public();
3451
+ default:
3452
+ return AccountStorageMode3.public();
3453
+ }
3454
+ }
3455
+ async function waitAndConsume(client, walletId, pollIntervalMs, maxWaitMs, cancelledRef) {
3456
+ const deadline = Date.now() + maxWaitMs;
3457
+ while (Date.now() < deadline) {
3458
+ if (cancelledRef.current) return;
3459
+ await client.syncState();
3460
+ if (cancelledRef.current) return;
3461
+ const accountIdObj = parseAccountId(walletId);
3462
+ const consumable = await client.getConsumableNotes(accountIdObj);
3463
+ if (consumable.length > 0) {
3464
+ const notes = consumable.map((c) => c.inputNoteRecord().toNote());
3465
+ const txRequest = client.newConsumeTransactionRequest(notes);
3466
+ const freshAccountId = parseAccountId(walletId);
3467
+ await client.submitNewTransaction(freshAccountId, txRequest);
3468
+ return;
3469
+ }
3470
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
3471
+ }
3472
+ throw new Error("Timeout waiting for session wallet funding");
3473
+ }
3474
+
3475
+ // src/hooks/useExportStore.ts
3476
+ import { useCallback as useCallback26, useState as useState21 } from "react";
3477
+ import { exportStore as sdkExportStore } from "@miden-sdk/miden-sdk/mt/lazy";
3478
+ function useExportStore() {
3479
+ const { client, isReady, runExclusive } = useMiden();
3480
+ const [isExporting, setIsExporting] = useState21(false);
3481
+ const [error, setError] = useState21(null);
3482
+ const exportStore = useCallback26(async () => {
3483
+ if (!client || !isReady) {
3484
+ throw new Error("Miden client is not ready");
3485
+ }
3486
+ setIsExporting(true);
3487
+ setError(null);
3488
+ try {
3489
+ const storeName = await client.storeIdentifier();
3490
+ const snapshot = await runExclusive(() => sdkExportStore(storeName));
3491
+ return snapshot;
3492
+ } catch (err) {
3493
+ const error2 = err instanceof Error ? err : new Error(String(err));
3494
+ setError(error2);
3495
+ throw error2;
3496
+ } finally {
3497
+ setIsExporting(false);
3498
+ }
3499
+ }, [client, isReady, runExclusive]);
3500
+ const reset = useCallback26(() => {
3501
+ setIsExporting(false);
3502
+ setError(null);
3503
+ }, []);
3504
+ return {
3505
+ exportStore,
3506
+ isExporting,
3507
+ error,
3508
+ reset
3509
+ };
3510
+ }
3511
+
3512
+ // src/hooks/useImportStore.ts
3513
+ import { useCallback as useCallback27, useState as useState22 } from "react";
3514
+ import { importStore as sdkImportStore } from "@miden-sdk/miden-sdk/mt/lazy";
3515
+ function useImportStore() {
3516
+ const { client, isReady, runExclusive, sync } = useMiden();
3517
+ const [isImporting, setIsImporting] = useState22(false);
3518
+ const [error, setError] = useState22(null);
3519
+ const importStore = useCallback27(
3520
+ async (storeDump, storeName, options) => {
3521
+ if (!client || !isReady) {
3522
+ throw new Error("Miden client is not ready");
3523
+ }
3524
+ setIsImporting(true);
3525
+ setError(null);
3526
+ try {
3527
+ await runExclusive(() => sdkImportStore(storeName, storeDump));
3528
+ if (!options?.skipSync) {
3529
+ await sync();
3530
+ }
3531
+ } catch (err) {
3532
+ const error2 = err instanceof Error ? err : new Error(String(err));
3533
+ setError(error2);
3534
+ throw error2;
3535
+ } finally {
3536
+ setIsImporting(false);
3537
+ }
3538
+ },
3539
+ [client, isReady, runExclusive, sync]
3540
+ );
3541
+ const reset = useCallback27(() => {
3542
+ setIsImporting(false);
3543
+ setError(null);
3544
+ }, []);
3545
+ return {
3546
+ importStore,
3547
+ isImporting,
3548
+ error,
3549
+ reset
3550
+ };
3551
+ }
3552
+
3553
+ // src/hooks/useImportNote.ts
3554
+ import { useCallback as useCallback28, useState as useState23 } from "react";
3555
+ import { NoteFile } from "@miden-sdk/miden-sdk/mt/lazy";
3556
+ function useImportNote() {
3557
+ const { client, isReady, runExclusive, sync } = useMiden();
3558
+ const [isImporting, setIsImporting] = useState23(false);
3559
+ const [error, setError] = useState23(null);
3560
+ const importNote = useCallback28(
3561
+ async (noteBytes) => {
3562
+ if (!client || !isReady) {
3563
+ throw new Error("Miden client is not ready");
3564
+ }
3565
+ setIsImporting(true);
3566
+ setError(null);
3567
+ try {
3568
+ const noteFile = NoteFile.deserialize(noteBytes);
3569
+ const noteId = await runExclusive(
3570
+ () => client.importNoteFile(noteFile)
3571
+ );
3572
+ await sync();
3573
+ return noteId.toString();
3574
+ } catch (err) {
3575
+ const error2 = err instanceof Error ? err : new Error(String(err));
3576
+ setError(error2);
3577
+ throw error2;
3578
+ } finally {
3579
+ setIsImporting(false);
3580
+ }
3581
+ },
3582
+ [client, isReady, runExclusive, sync]
3583
+ );
3584
+ const reset = useCallback28(() => {
3585
+ setIsImporting(false);
3586
+ setError(null);
3587
+ }, []);
3588
+ return {
3589
+ importNote,
3590
+ isImporting,
3591
+ error,
3592
+ reset
3593
+ };
3594
+ }
3595
+
3596
+ // src/hooks/useExportNote.ts
3597
+ import { useCallback as useCallback29, useState as useState24 } from "react";
3598
+ import { NoteExportFormat } from "@miden-sdk/miden-sdk/mt/lazy";
3599
+ function useExportNote() {
3600
+ const { client, isReady, runExclusive } = useMiden();
3601
+ const [isExporting, setIsExporting] = useState24(false);
3602
+ const [error, setError] = useState24(null);
3603
+ const exportNote = useCallback29(
3604
+ async (noteId) => {
3605
+ if (!client || !isReady) {
3606
+ throw new Error("Miden client is not ready");
3607
+ }
3608
+ setIsExporting(true);
3609
+ setError(null);
3610
+ try {
3611
+ const noteFile = await runExclusive(
3612
+ () => client.exportNoteFile(noteId, NoteExportFormat.Full)
3613
+ );
3614
+ return noteFile.serialize();
3615
+ } catch (err) {
3616
+ const error2 = err instanceof Error ? err : new Error(String(err));
3617
+ setError(error2);
3618
+ throw error2;
3619
+ } finally {
3620
+ setIsExporting(false);
3621
+ }
3622
+ },
3623
+ [client, isReady, runExclusive]
3624
+ );
3625
+ const reset = useCallback29(() => {
3626
+ setIsExporting(false);
3627
+ setError(null);
3628
+ }, []);
3629
+ return {
3630
+ exportNote,
3631
+ isExporting,
3632
+ error,
3633
+ reset
3634
+ };
3635
+ }
3636
+
3637
+ // src/hooks/useSyncControl.ts
3638
+ import { useCallback as useCallback30 } from "react";
3639
+ function useSyncControl() {
3640
+ const isPaused = useMidenStore((state) => state.syncPaused);
3641
+ const setSyncPaused = useMidenStore((state) => state.setSyncPaused);
3642
+ const pauseSync = useCallback30(() => setSyncPaused(true), [setSyncPaused]);
3643
+ const resumeSync = useCallback30(() => setSyncPaused(false), [setSyncPaused]);
3644
+ return {
3645
+ pauseSync,
3646
+ resumeSync,
3647
+ isPaused
3648
+ };
3649
+ }
3650
+
3651
+ // src/utils/bytes.ts
3652
+ function bytesToBigInt(bytes) {
3653
+ let result = 0n;
3654
+ for (let i = 0; i < bytes.length; i++) {
3655
+ result = result << 8n | BigInt(bytes[i]);
3656
+ }
3657
+ return result;
3658
+ }
3659
+ function bigIntToBytes(value, length) {
3660
+ if (value < 0n) {
3661
+ throw new RangeError("bigIntToBytes: value must be non-negative");
3662
+ }
3663
+ const maxValue = (1n << BigInt(length * 8)) - 1n;
3664
+ if (value > maxValue) {
3665
+ throw new RangeError(
3666
+ `bigIntToBytes: value ${value} does not fit in ${length} byte(s) (max ${maxValue})`
3667
+ );
3668
+ }
3669
+ const bytes = new Uint8Array(length);
3670
+ let remaining = value;
3671
+ for (let i = length - 1; i >= 0; i--) {
3672
+ bytes[i] = Number(remaining & 0xffn);
3673
+ remaining >>= 8n;
3674
+ }
3675
+ return bytes;
3676
+ }
3677
+ function concatBytes(...arrays) {
3678
+ const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
3679
+ const result = new Uint8Array(totalLength);
3680
+ let offset = 0;
3681
+ for (const arr of arrays) {
3682
+ result.set(arr, offset);
3683
+ offset += arr.length;
3684
+ }
3685
+ return result;
3686
+ }
3687
+
3688
+ // src/utils/walletDetection.ts
3689
+ async function waitForWalletDetection(adapter, timeoutMs = 5e3) {
3690
+ if (adapter.readyState === "Installed") return;
3691
+ return new Promise((resolve, reject) => {
3692
+ let settled = false;
3693
+ const settle = () => {
3694
+ if (settled) return;
3695
+ settled = true;
3696
+ clearTimeout(timer);
3697
+ adapter.off("readyStateChange", onReady);
3698
+ resolve();
3699
+ };
3700
+ const timer = setTimeout(() => {
3701
+ if (settled) return;
3702
+ settled = true;
3703
+ adapter.off("readyStateChange", onReady);
3704
+ reject(
3705
+ new Error(
3706
+ `Wallet extension not detected within ${timeoutMs}ms. Is the browser extension installed and enabled?`
3707
+ )
3708
+ );
3709
+ }, timeoutMs);
3710
+ const onReady = (state) => {
3711
+ if (state === "Installed") settle();
3712
+ };
3713
+ adapter.on("readyStateChange", onReady);
3714
+ if (adapter.readyState === "Installed") settle();
3715
+ });
3716
+ }
3717
+
3718
+ // src/utils/storage.ts
3719
+ async function migrateStorage(options) {
3720
+ if (typeof window === "undefined") return false;
3721
+ const versionKey = options.versionKey ?? "miden:storageVersion";
3722
+ const stored = localStorage.getItem(versionKey);
3723
+ if (stored === options.version) return false;
3724
+ if (options.onBeforeClear) {
3725
+ await options.onBeforeClear();
3726
+ }
3727
+ await clearMidenStorage();
3728
+ localStorage.setItem(versionKey, options.version);
3729
+ if (options.reloadOnClear !== false) {
3730
+ window.location.reload();
3731
+ }
3732
+ return true;
3733
+ }
3734
+ async function clearMidenStorage() {
3735
+ if (typeof indexedDB === "undefined") return;
3736
+ try {
3737
+ const databases = await indexedDB.databases();
3738
+ const midenDbs = databases.filter(
3739
+ (db) => db.name?.toLowerCase().includes("miden")
3740
+ );
3741
+ await Promise.all(
3742
+ midenDbs.map(
3743
+ (db) => new Promise((resolve, reject) => {
3744
+ if (!db.name) {
3745
+ resolve();
3746
+ return;
3747
+ }
3748
+ const req = indexedDB.deleteDatabase(db.name);
3749
+ req.onsuccess = () => resolve();
3750
+ req.onerror = () => reject(req.error);
3751
+ req.onblocked = () => {
3752
+ console.warn(
3753
+ `IndexedDB "${db.name}" delete was blocked \u2014 close other tabs using this database.`
3754
+ );
3755
+ resolve();
3756
+ };
3757
+ })
3758
+ )
3759
+ );
3760
+ } catch {
3761
+ }
3762
+ }
3763
+ function createMidenStorage(prefix) {
3764
+ const fullKey = (key) => `${prefix}:${key}`;
3765
+ return {
3766
+ get(key) {
3767
+ try {
3768
+ const raw = localStorage.getItem(fullKey(key));
3769
+ if (raw === null) return null;
3770
+ return JSON.parse(raw);
3771
+ } catch {
3772
+ return null;
3773
+ }
3774
+ },
3775
+ set(key, value) {
3776
+ try {
3777
+ localStorage.setItem(fullKey(key), JSON.stringify(value));
3778
+ } catch (e) {
3779
+ console.warn(`Failed to write localStorage key "${fullKey(key)}":`, e);
3780
+ }
3781
+ },
3782
+ remove(key) {
3783
+ localStorage.removeItem(fullKey(key));
3784
+ },
3785
+ clear() {
3786
+ const keysToRemove = [];
3787
+ for (let i = 0; i < localStorage.length; i++) {
3788
+ const k = localStorage.key(i);
3789
+ if (k?.startsWith(`${prefix}:`)) {
3790
+ keysToRemove.push(k);
3791
+ }
3792
+ }
3793
+ keysToRemove.forEach((k) => localStorage.removeItem(k));
3794
+ }
3795
+ };
3796
+ }
3797
+
3798
+ // src/index.ts
3799
+ installAccountBech32();
3800
+ export {
3801
+ AuthScheme,
3802
+ DEFAULTS,
3803
+ MidenError,
3804
+ MidenProvider,
3805
+ MultiSignerProvider,
3806
+ SignerContext,
3807
+ SignerSlot,
3808
+ accountIdsEqual,
3809
+ bigIntToBytes,
3810
+ bytesToBigInt,
3811
+ clearMidenStorage,
3812
+ concatBytes,
3813
+ createMidenStorage,
3814
+ createNoteAttachment,
3815
+ ensureAccountBech32,
3816
+ formatAssetAmount,
3817
+ formatNoteSummary,
3818
+ getNoteSummary,
3819
+ installAccountBech32,
3820
+ migrateStorage,
3821
+ normalizeAccountId,
3822
+ parseAssetAmount,
3823
+ readNoteAttachment,
3824
+ toBech32AccountId,
3825
+ useAccount,
3826
+ useAccounts,
3827
+ useAssetMetadata,
3828
+ useCompile,
3829
+ useConsume,
3830
+ useCreateFaucet,
3831
+ useCreateWallet,
3832
+ useExecuteProgram,
3833
+ useExportNote,
3834
+ useExportStore,
3835
+ useImportAccount,
3836
+ useImportNote,
3837
+ useImportStore,
3838
+ useMiden,
3839
+ useMidenClient,
3840
+ useMint,
3841
+ useMultiSend,
3842
+ useMultiSigner,
3843
+ useNoteStream,
3844
+ useNotes,
3845
+ usePswapCancel,
3846
+ usePswapConsume,
3847
+ usePswapCreate,
3848
+ useSend,
3849
+ useSessionAccount,
3850
+ useSigner,
3851
+ useSwap,
3852
+ useSyncControl,
3853
+ useSyncState,
3854
+ useTransaction,
3855
+ useTransactionHistory,
3856
+ useWaitForCommit,
3857
+ useWaitForNotes,
3858
+ waitForWalletDetection,
3859
+ wrapWasmError
3860
+ };