@miden-sdk/react 0.13.1 → 0.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +426 -0
- package/README.md +402 -6
- package/dist/index.d.mts +264 -12
- package/dist/index.d.ts +264 -12
- package/dist/index.js +1211 -408
- package/dist/index.mjs +1210 -407
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -29,6 +29,7 @@ var initialState = {
|
|
|
29
29
|
notes: [],
|
|
30
30
|
consumableNotes: [],
|
|
31
31
|
assetMetadata: /* @__PURE__ */ new Map(),
|
|
32
|
+
noteFirstSeen: /* @__PURE__ */ new Map(),
|
|
32
33
|
isLoadingAccounts: false,
|
|
33
34
|
isLoadingNotes: false
|
|
34
35
|
};
|
|
@@ -56,8 +57,74 @@ var useMidenStore = create()((set) => ({
|
|
|
56
57
|
newMap.set(accountId, account);
|
|
57
58
|
return { accountDetails: newMap };
|
|
58
59
|
}),
|
|
59
|
-
setNotes: (notes) => set(
|
|
60
|
+
setNotes: (notes) => set((state) => {
|
|
61
|
+
const now = Date.now();
|
|
62
|
+
const newFirstSeen = /* @__PURE__ */ new Map();
|
|
63
|
+
for (const note of notes) {
|
|
64
|
+
try {
|
|
65
|
+
const id = note.id().toString();
|
|
66
|
+
newFirstSeen.set(id, state.noteFirstSeen.get(id) ?? now);
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { notes, noteFirstSeen: newFirstSeen };
|
|
71
|
+
}),
|
|
72
|
+
setNotesIfChanged: (notes) => set((state) => {
|
|
73
|
+
const safeId = (n) => {
|
|
74
|
+
try {
|
|
75
|
+
return n.id().toString();
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const prevIds = /* @__PURE__ */ new Set();
|
|
81
|
+
for (const n of state.notes) {
|
|
82
|
+
const id = safeId(n);
|
|
83
|
+
if (id) prevIds.add(id);
|
|
84
|
+
}
|
|
85
|
+
const newIds = /* @__PURE__ */ new Set();
|
|
86
|
+
for (const n of notes) {
|
|
87
|
+
const id = safeId(n);
|
|
88
|
+
if (id) newIds.add(id);
|
|
89
|
+
}
|
|
90
|
+
if (prevIds.size === newIds.size && [...prevIds].every((id) => newIds.has(id))) {
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
93
|
+
const now = Date.now();
|
|
94
|
+
const newFirstSeen = /* @__PURE__ */ new Map();
|
|
95
|
+
for (const note of notes) {
|
|
96
|
+
try {
|
|
97
|
+
const id = note.id().toString();
|
|
98
|
+
newFirstSeen.set(id, state.noteFirstSeen.get(id) ?? now);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { notes, noteFirstSeen: newFirstSeen };
|
|
103
|
+
}),
|
|
60
104
|
setConsumableNotes: (consumableNotes) => set({ consumableNotes }),
|
|
105
|
+
setConsumableNotesIfChanged: (consumableNotes) => set((state) => {
|
|
106
|
+
const safeId = (n) => {
|
|
107
|
+
try {
|
|
108
|
+
return n.inputNoteRecord().id().toString();
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const prevIds = /* @__PURE__ */ new Set();
|
|
114
|
+
for (const n of state.consumableNotes) {
|
|
115
|
+
const id = safeId(n);
|
|
116
|
+
if (id) prevIds.add(id);
|
|
117
|
+
}
|
|
118
|
+
const newIds = /* @__PURE__ */ new Set();
|
|
119
|
+
for (const n of consumableNotes) {
|
|
120
|
+
const id = safeId(n);
|
|
121
|
+
if (id) newIds.add(id);
|
|
122
|
+
}
|
|
123
|
+
if (prevIds.size === newIds.size && [...prevIds].every((id) => newIds.has(id))) {
|
|
124
|
+
return {};
|
|
125
|
+
}
|
|
126
|
+
return { consumableNotes };
|
|
127
|
+
}),
|
|
61
128
|
setAssetMetadata: (assetId, metadata) => set((state) => {
|
|
62
129
|
const newMap = new Map(state.assetMetadata);
|
|
63
130
|
newMap.set(assetId, metadata);
|
|
@@ -72,6 +139,7 @@ var useAccountsStore = () => useMidenStore((state) => state.accounts);
|
|
|
72
139
|
var useNotesStore = () => useMidenStore((state) => state.notes);
|
|
73
140
|
var useConsumableNotesStore = () => useMidenStore((state) => state.consumableNotes);
|
|
74
141
|
var useAssetMetadataStore = () => useMidenStore((state) => state.assetMetadata);
|
|
142
|
+
var useNoteFirstSeenStore = () => useMidenStore((state) => state.noteFirstSeen);
|
|
75
143
|
|
|
76
144
|
// src/utils/accountParsing.ts
|
|
77
145
|
import { AccountId, Address } from "@miden-sdk/miden-sdk";
|
|
@@ -95,6 +163,19 @@ var parseAccountId = (value) => {
|
|
|
95
163
|
const normalized = normalizeAccountIdInput(value);
|
|
96
164
|
return parseAccountIdFromString(normalized);
|
|
97
165
|
};
|
|
166
|
+
function isFaucetId(accountId) {
|
|
167
|
+
try {
|
|
168
|
+
let hex = typeof accountId.toHex === "function" ? accountId.toHex() : String(accountId);
|
|
169
|
+
if (hex.startsWith("0x") || hex.startsWith("0X")) {
|
|
170
|
+
hex = hex.slice(2);
|
|
171
|
+
}
|
|
172
|
+
const firstByte = parseInt(hex.slice(0, 2), 16);
|
|
173
|
+
const accountType = firstByte >> 4 & 3;
|
|
174
|
+
return accountType === 2 || accountType === 3;
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
98
179
|
var parseAddress = (value, accountId) => {
|
|
99
180
|
const normalized = normalizeAccountIdInput(value);
|
|
100
181
|
if (isBech32Input(normalized)) {
|
|
@@ -339,19 +420,29 @@ function isPrivateStorageMode(storageMode) {
|
|
|
339
420
|
return storageMode.toString() === "private";
|
|
340
421
|
}
|
|
341
422
|
async function initializeSignerAccount(client, config) {
|
|
342
|
-
const { AccountBuilder, AccountComponent, Word } = await import("@miden-sdk/miden-sdk");
|
|
423
|
+
const { AccountBuilder, AccountComponent, Word: Word2 } = await import("@miden-sdk/miden-sdk");
|
|
343
424
|
await client.syncState();
|
|
344
|
-
const commitmentWord =
|
|
425
|
+
const commitmentWord = Word2.deserialize(config.publicKeyCommitment);
|
|
345
426
|
const seed = config.accountSeed ?? crypto.getRandomValues(new Uint8Array(32));
|
|
346
427
|
const accountType = await getAccountType(config.accountType);
|
|
347
|
-
|
|
348
|
-
const buildResult = builder.withAuthComponent(
|
|
428
|
+
let builder = new AccountBuilder(seed).withAuthComponent(
|
|
349
429
|
AccountComponent.createAuthComponentFromCommitment(
|
|
350
430
|
commitmentWord,
|
|
351
431
|
1
|
|
352
432
|
// ECDSA auth scheme (K256/Keccak)
|
|
353
433
|
)
|
|
354
|
-
).accountType(accountType).storageMode(config.storageMode).withBasicWalletComponent()
|
|
434
|
+
).accountType(accountType).storageMode(config.storageMode).withBasicWalletComponent();
|
|
435
|
+
if (config.customComponents?.length) {
|
|
436
|
+
for (const component of config.customComponents) {
|
|
437
|
+
if (component == null || typeof component.getProcedures !== "function") {
|
|
438
|
+
throw new Error(
|
|
439
|
+
"Each entry in customComponents must be an AccountComponent instance created via AccountComponent.compile(), AccountComponent.fromPackage(), or AccountComponent.fromLibrary()."
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
builder = builder.withComponent(component);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
const buildResult = builder.build();
|
|
355
446
|
const account = buildResult.account;
|
|
356
447
|
const accountId = account.id();
|
|
357
448
|
if (!isPrivateStorageMode(config.storageMode)) {
|
|
@@ -448,63 +539,91 @@ function MidenProvider({
|
|
|
448
539
|
useEffect(() => {
|
|
449
540
|
if (!signerContext && isInitializedRef.current) return;
|
|
450
541
|
if (signerContext && !signerContext.isConnected) {
|
|
451
|
-
if (client) {
|
|
542
|
+
if (useMidenStore.getState().client) {
|
|
452
543
|
useMidenStore.getState().reset();
|
|
453
544
|
setClient(null);
|
|
454
545
|
setSignerAccountId(null);
|
|
455
546
|
}
|
|
456
547
|
return;
|
|
457
548
|
}
|
|
458
|
-
|
|
459
|
-
isInitializedRef.current = true;
|
|
460
|
-
}
|
|
549
|
+
let cancelled = false;
|
|
461
550
|
const initClient = async () => {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (signerContext && signerContext.isConnected) {
|
|
467
|
-
const storeName = `MidenClientDB_${signerContext.storeName}`;
|
|
468
|
-
webClient = await WebClient.createClientWithExternalKeystore(
|
|
469
|
-
resolvedConfig.rpcUrl,
|
|
470
|
-
resolvedConfig.noteTransportUrl,
|
|
471
|
-
resolvedConfig.seed,
|
|
472
|
-
storeName,
|
|
473
|
-
void 0,
|
|
474
|
-
// getKeyCb - not needed for public accounts
|
|
475
|
-
void 0,
|
|
476
|
-
// insertKeyCb - not needed for public accounts
|
|
477
|
-
signerContext.signCb
|
|
478
|
-
);
|
|
479
|
-
const accountId = await initializeSignerAccount(
|
|
480
|
-
webClient,
|
|
481
|
-
signerContext.accountConfig
|
|
482
|
-
);
|
|
483
|
-
setSignerAccountId(accountId);
|
|
484
|
-
} else {
|
|
485
|
-
const seed = resolvedConfig.seed;
|
|
486
|
-
webClient = await WebClient.createClient(
|
|
487
|
-
resolvedConfig.rpcUrl,
|
|
488
|
-
resolvedConfig.noteTransportUrl,
|
|
489
|
-
seed
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
setClient(webClient);
|
|
551
|
+
await runExclusive(async () => {
|
|
552
|
+
if (cancelled) return;
|
|
553
|
+
setInitializing(true);
|
|
554
|
+
setConfig(resolvedConfig);
|
|
493
555
|
try {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
556
|
+
let webClient;
|
|
557
|
+
let didSignerInit = false;
|
|
558
|
+
if (signerContext && signerContext.isConnected) {
|
|
559
|
+
const storeName = `MidenClientDB_${signerContext.storeName}`;
|
|
560
|
+
webClient = await WebClient.createClientWithExternalKeystore(
|
|
561
|
+
resolvedConfig.rpcUrl,
|
|
562
|
+
resolvedConfig.noteTransportUrl,
|
|
563
|
+
resolvedConfig.seed,
|
|
564
|
+
storeName,
|
|
565
|
+
void 0,
|
|
566
|
+
// getKeyCb - not needed for public accounts
|
|
567
|
+
void 0,
|
|
568
|
+
// insertKeyCb - not needed for public accounts
|
|
569
|
+
signerContext.signCb
|
|
570
|
+
);
|
|
571
|
+
if (cancelled) return;
|
|
572
|
+
const accountId = await initializeSignerAccount(
|
|
573
|
+
webClient,
|
|
574
|
+
signerContext.accountConfig
|
|
575
|
+
);
|
|
576
|
+
if (cancelled) return;
|
|
577
|
+
setSignerAccountId(accountId);
|
|
578
|
+
didSignerInit = true;
|
|
579
|
+
} else {
|
|
580
|
+
const seed = resolvedConfig.seed;
|
|
581
|
+
webClient = await WebClient.createClient(
|
|
582
|
+
resolvedConfig.rpcUrl,
|
|
583
|
+
resolvedConfig.noteTransportUrl,
|
|
584
|
+
seed
|
|
585
|
+
);
|
|
586
|
+
if (cancelled) return;
|
|
587
|
+
}
|
|
588
|
+
if (!didSignerInit) {
|
|
589
|
+
try {
|
|
590
|
+
const summary = await webClient.syncState();
|
|
591
|
+
if (cancelled) return;
|
|
592
|
+
setSyncState({
|
|
593
|
+
syncHeight: summary.blockNum(),
|
|
594
|
+
lastSyncTime: Date.now()
|
|
595
|
+
});
|
|
596
|
+
} catch {
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (!cancelled) {
|
|
600
|
+
try {
|
|
601
|
+
const accounts = await webClient.getAccounts();
|
|
602
|
+
if (cancelled) return;
|
|
603
|
+
useMidenStore.getState().setAccounts(accounts);
|
|
604
|
+
} catch {
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (!cancelled) {
|
|
608
|
+
if (!signerContext) {
|
|
609
|
+
isInitializedRef.current = true;
|
|
610
|
+
}
|
|
611
|
+
setClient(webClient);
|
|
612
|
+
}
|
|
613
|
+
} catch (error) {
|
|
614
|
+
if (!cancelled) {
|
|
615
|
+
setInitError(
|
|
616
|
+
error instanceof Error ? error : new Error(String(error))
|
|
617
|
+
);
|
|
618
|
+
}
|
|
502
619
|
}
|
|
503
|
-
}
|
|
504
|
-
setInitError(error instanceof Error ? error : new Error(String(error)));
|
|
505
|
-
}
|
|
620
|
+
});
|
|
506
621
|
};
|
|
507
622
|
initClient();
|
|
623
|
+
return () => {
|
|
624
|
+
cancelled = true;
|
|
625
|
+
isInitializedRef.current = false;
|
|
626
|
+
};
|
|
508
627
|
}, [
|
|
509
628
|
runExclusive,
|
|
510
629
|
resolvedConfig,
|
|
@@ -513,8 +632,7 @@ function MidenProvider({
|
|
|
513
632
|
setInitError,
|
|
514
633
|
setInitializing,
|
|
515
634
|
setSyncState,
|
|
516
|
-
signerContext
|
|
517
|
-
client
|
|
635
|
+
signerContext
|
|
518
636
|
]);
|
|
519
637
|
useEffect(() => {
|
|
520
638
|
if (!isReady || !client) return;
|
|
@@ -620,16 +738,6 @@ function useAccounts() {
|
|
|
620
738
|
refetch
|
|
621
739
|
};
|
|
622
740
|
}
|
|
623
|
-
function isFaucetId(accountId) {
|
|
624
|
-
try {
|
|
625
|
-
const hex = typeof accountId.toHex === "function" ? accountId.toHex() : String(accountId);
|
|
626
|
-
const firstByte = parseInt(hex.slice(0, 2), 16);
|
|
627
|
-
const accountType = firstByte >> 4 & 3;
|
|
628
|
-
return accountType === 2 || accountType === 3;
|
|
629
|
-
} catch {
|
|
630
|
-
return false;
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
741
|
|
|
634
742
|
// src/hooks/useAccount.ts
|
|
635
743
|
import { useCallback as useCallback3, useEffect as useEffect4, useState as useState2, useMemo as useMemo3 } from "react";
|
|
@@ -656,17 +764,12 @@ var getRpcClient = (rpcUrl) => {
|
|
|
656
764
|
return null;
|
|
657
765
|
}
|
|
658
766
|
};
|
|
659
|
-
var fetchAssetMetadata = async (
|
|
767
|
+
var fetchAssetMetadata = async (rpcClient, assetId) => {
|
|
660
768
|
try {
|
|
661
769
|
const accountId = parseAccountId(assetId);
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
const fetched = await rpcClient.getAccountDetails(accountId);
|
|
666
|
-
account = fetched.account?.();
|
|
667
|
-
} catch {
|
|
668
|
-
}
|
|
669
|
-
}
|
|
770
|
+
if (!isFaucetId(accountId)) return null;
|
|
771
|
+
const fetched = await rpcClient.getAccountDetails(accountId);
|
|
772
|
+
const account = fetched.account?.();
|
|
670
773
|
if (!account) return null;
|
|
671
774
|
const faucet = BasicFungibleFaucetComponent.fromAccount(account);
|
|
672
775
|
const symbol = faucet.symbol().toString();
|
|
@@ -677,8 +780,6 @@ var fetchAssetMetadata = async (client, rpcClient, assetId) => {
|
|
|
677
780
|
}
|
|
678
781
|
};
|
|
679
782
|
function useAssetMetadata(assetIds = []) {
|
|
680
|
-
const { client, isReady, runExclusive } = useMiden();
|
|
681
|
-
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
682
783
|
const assetMetadata = useAssetMetadataStore();
|
|
683
784
|
const setAssetMetadata = useMidenStore((state) => state.setAssetMetadata);
|
|
684
785
|
const rpcUrl = useMidenStore((state) => state.config.rpcUrl);
|
|
@@ -688,32 +789,19 @@ function useAssetMetadata(assetIds = []) {
|
|
|
688
789
|
[assetIds]
|
|
689
790
|
);
|
|
690
791
|
useEffect3(() => {
|
|
691
|
-
if (!
|
|
792
|
+
if (!rpcClient || uniqueAssetIds.length === 0) return;
|
|
692
793
|
uniqueAssetIds.forEach((assetId) => {
|
|
693
794
|
const existing = assetMetadata.get(assetId);
|
|
694
795
|
const hasMetadata = existing?.symbol !== void 0 || existing?.decimals !== void 0;
|
|
695
796
|
if (hasMetadata || inflight.has(assetId)) return;
|
|
696
|
-
const promise =
|
|
697
|
-
const metadata = await fetchAssetMetadata(
|
|
698
|
-
client,
|
|
699
|
-
rpcClient,
|
|
700
|
-
assetId
|
|
701
|
-
);
|
|
797
|
+
const promise = fetchAssetMetadata(rpcClient, assetId).then((metadata) => {
|
|
702
798
|
setAssetMetadata(assetId, metadata ?? { assetId });
|
|
703
799
|
}).finally(() => {
|
|
704
800
|
inflight.delete(assetId);
|
|
705
801
|
});
|
|
706
802
|
inflight.set(assetId, promise);
|
|
707
803
|
});
|
|
708
|
-
}, [
|
|
709
|
-
client,
|
|
710
|
-
isReady,
|
|
711
|
-
uniqueAssetIds,
|
|
712
|
-
assetMetadata,
|
|
713
|
-
runExclusiveSafe,
|
|
714
|
-
setAssetMetadata,
|
|
715
|
-
rpcClient
|
|
716
|
-
]);
|
|
804
|
+
}, [uniqueAssetIds, assetMetadata, setAssetMetadata, rpcClient]);
|
|
717
805
|
return { assetMetadata };
|
|
718
806
|
}
|
|
719
807
|
|
|
@@ -815,7 +903,7 @@ function useAccount(accountId) {
|
|
|
815
903
|
|
|
816
904
|
// src/hooks/useNotes.ts
|
|
817
905
|
import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo4, useState as useState3 } from "react";
|
|
818
|
-
import { NoteFilter
|
|
906
|
+
import { NoteFilter } from "@miden-sdk/miden-sdk";
|
|
819
907
|
|
|
820
908
|
// src/utils/amounts.ts
|
|
821
909
|
var formatAssetAmount = (amount, decimals) => {
|
|
@@ -899,6 +987,86 @@ var formatNoteSummary = (summary, formatAsset = (asset) => `${formatAssetAmount(
|
|
|
899
987
|
return summary.sender ? `${assetsText} from ${summary.sender}` : assetsText;
|
|
900
988
|
};
|
|
901
989
|
|
|
990
|
+
// src/utils/accountId.ts
|
|
991
|
+
function normalizeAccountId(id) {
|
|
992
|
+
return toBech32AccountId(id);
|
|
993
|
+
}
|
|
994
|
+
function accountIdsEqual(a, b) {
|
|
995
|
+
let idA;
|
|
996
|
+
let idB;
|
|
997
|
+
try {
|
|
998
|
+
idA = parseAccountId(a);
|
|
999
|
+
idB = parseAccountId(b);
|
|
1000
|
+
return idA.toString() === idB.toString();
|
|
1001
|
+
} catch {
|
|
1002
|
+
return a === b;
|
|
1003
|
+
} finally {
|
|
1004
|
+
idA?.free?.();
|
|
1005
|
+
idB?.free?.();
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// src/utils/noteFilters.ts
|
|
1010
|
+
import {
|
|
1011
|
+
NoteFilterTypes,
|
|
1012
|
+
NoteType,
|
|
1013
|
+
TransactionFilter
|
|
1014
|
+
} from "@miden-sdk/miden-sdk";
|
|
1015
|
+
function getNoteFilterType(status) {
|
|
1016
|
+
switch (status) {
|
|
1017
|
+
case "consumed":
|
|
1018
|
+
return NoteFilterTypes.Consumed;
|
|
1019
|
+
case "committed":
|
|
1020
|
+
return NoteFilterTypes.Committed;
|
|
1021
|
+
case "expected":
|
|
1022
|
+
return NoteFilterTypes.Expected;
|
|
1023
|
+
case "processing":
|
|
1024
|
+
return NoteFilterTypes.Processing;
|
|
1025
|
+
case "all":
|
|
1026
|
+
default:
|
|
1027
|
+
return NoteFilterTypes.All;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
function getNoteType(type) {
|
|
1031
|
+
switch (type) {
|
|
1032
|
+
case "private":
|
|
1033
|
+
return NoteType.Private;
|
|
1034
|
+
case "public":
|
|
1035
|
+
return NoteType.Public;
|
|
1036
|
+
default:
|
|
1037
|
+
return NoteType.Private;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
async function waitForTransactionCommit(client, runExclusiveSafe, txIdHex, maxWaitMs = 1e4, delayMs = 1e3) {
|
|
1041
|
+
const deadline = Date.now() + maxWaitMs;
|
|
1042
|
+
const targetHex = normalizeHex(txIdHex);
|
|
1043
|
+
while (Date.now() < deadline) {
|
|
1044
|
+
await runExclusiveSafe(() => client.syncState());
|
|
1045
|
+
const records = await runExclusiveSafe(
|
|
1046
|
+
() => client.getTransactions(TransactionFilter.all())
|
|
1047
|
+
);
|
|
1048
|
+
const record = records.find(
|
|
1049
|
+
(r) => normalizeHex(r.id().toHex()) === targetHex
|
|
1050
|
+
);
|
|
1051
|
+
if (record) {
|
|
1052
|
+
const status = record.transactionStatus();
|
|
1053
|
+
if (status.isCommitted()) {
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
if (status.isDiscarded()) {
|
|
1057
|
+
throw new Error("Transaction was discarded before commit");
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1061
|
+
}
|
|
1062
|
+
throw new Error("Timeout waiting for transaction commit");
|
|
1063
|
+
}
|
|
1064
|
+
function normalizeHex(value) {
|
|
1065
|
+
const trimmed = value.trim();
|
|
1066
|
+
const normalized = trimmed.startsWith("0x") || trimmed.startsWith("0X") ? trimmed : `0x${trimmed}`;
|
|
1067
|
+
return normalized.toLowerCase();
|
|
1068
|
+
}
|
|
1069
|
+
|
|
902
1070
|
// src/hooks/useNotes.ts
|
|
903
1071
|
function useNotes(options) {
|
|
904
1072
|
const { client, isReady, runExclusive } = useMiden();
|
|
@@ -907,8 +1075,10 @@ function useNotes(options) {
|
|
|
907
1075
|
const consumableNotes = useConsumableNotesStore();
|
|
908
1076
|
const isLoadingNotes = useMidenStore((state) => state.isLoadingNotes);
|
|
909
1077
|
const setLoadingNotes = useMidenStore((state) => state.setLoadingNotes);
|
|
910
|
-
const
|
|
911
|
-
const
|
|
1078
|
+
const setNotesIfChanged = useMidenStore((state) => state.setNotesIfChanged);
|
|
1079
|
+
const setConsumableNotesIfChanged = useMidenStore(
|
|
1080
|
+
(state) => state.setConsumableNotesIfChanged
|
|
1081
|
+
);
|
|
912
1082
|
const { lastSyncTime } = useSyncStateStore();
|
|
913
1083
|
const [error, setError] = useState3(null);
|
|
914
1084
|
const refetch = useCallback4(async () => {
|
|
@@ -934,8 +1104,8 @@ function useNotes(options) {
|
|
|
934
1104
|
};
|
|
935
1105
|
}
|
|
936
1106
|
);
|
|
937
|
-
|
|
938
|
-
|
|
1107
|
+
setNotesIfChanged(fetchedNotes);
|
|
1108
|
+
setConsumableNotesIfChanged(fetchedConsumable);
|
|
939
1109
|
} catch (err) {
|
|
940
1110
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
941
1111
|
} finally {
|
|
@@ -948,8 +1118,8 @@ function useNotes(options) {
|
|
|
948
1118
|
options?.status,
|
|
949
1119
|
options?.accountId,
|
|
950
1120
|
setLoadingNotes,
|
|
951
|
-
|
|
952
|
-
|
|
1121
|
+
setNotesIfChanged,
|
|
1122
|
+
setConsumableNotesIfChanged
|
|
953
1123
|
]);
|
|
954
1124
|
useEffect5(() => {
|
|
955
1125
|
if (isReady && notes.length === 0) {
|
|
@@ -976,14 +1146,65 @@ function useNotes(options) {
|
|
|
976
1146
|
(assetId) => assetMetadata.get(assetId),
|
|
977
1147
|
[assetMetadata]
|
|
978
1148
|
);
|
|
979
|
-
const
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1149
|
+
const normalizedSender = useMemo4(() => {
|
|
1150
|
+
if (!options?.sender) return null;
|
|
1151
|
+
try {
|
|
1152
|
+
return normalizeAccountId(options.sender);
|
|
1153
|
+
} catch {
|
|
1154
|
+
return options.sender;
|
|
1155
|
+
}
|
|
1156
|
+
}, [options?.sender]);
|
|
1157
|
+
const excludeIdsKey = useMemo4(() => {
|
|
1158
|
+
if (!options?.excludeIds || options.excludeIds.length === 0) return "";
|
|
1159
|
+
return [...options.excludeIds].sort().join("\0");
|
|
1160
|
+
}, [options?.excludeIds]);
|
|
1161
|
+
const filterBySender = useCallback4(
|
|
1162
|
+
(summaries, target) => {
|
|
1163
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1164
|
+
return summaries.filter((s) => {
|
|
1165
|
+
if (!s.sender) return false;
|
|
1166
|
+
let normalized = cache.get(s.sender);
|
|
1167
|
+
if (normalized === void 0) {
|
|
1168
|
+
try {
|
|
1169
|
+
normalized = normalizeAccountId(s.sender);
|
|
1170
|
+
} catch {
|
|
1171
|
+
normalized = s.sender;
|
|
1172
|
+
}
|
|
1173
|
+
cache.set(s.sender, normalized);
|
|
1174
|
+
}
|
|
1175
|
+
return normalized === target;
|
|
1176
|
+
});
|
|
1177
|
+
},
|
|
1178
|
+
[]
|
|
986
1179
|
);
|
|
1180
|
+
const noteSummaries = useMemo4(() => {
|
|
1181
|
+
let summaries = notes.map((note) => getNoteSummary(note, getMetadata)).filter(Boolean);
|
|
1182
|
+
if (normalizedSender) {
|
|
1183
|
+
summaries = filterBySender(summaries, normalizedSender);
|
|
1184
|
+
}
|
|
1185
|
+
if (excludeIdsKey) {
|
|
1186
|
+
const excludeSet = new Set(excludeIdsKey.split("\0"));
|
|
1187
|
+
summaries = summaries.filter((s) => !excludeSet.has(s.id));
|
|
1188
|
+
}
|
|
1189
|
+
return summaries;
|
|
1190
|
+
}, [notes, getMetadata, normalizedSender, excludeIdsKey, filterBySender]);
|
|
1191
|
+
const consumableNoteSummaries = useMemo4(() => {
|
|
1192
|
+
let summaries = consumableNotes.map((note) => getNoteSummary(note, getMetadata)).filter(Boolean);
|
|
1193
|
+
if (normalizedSender) {
|
|
1194
|
+
summaries = filterBySender(summaries, normalizedSender);
|
|
1195
|
+
}
|
|
1196
|
+
if (excludeIdsKey) {
|
|
1197
|
+
const excludeSet = new Set(excludeIdsKey.split("\0"));
|
|
1198
|
+
summaries = summaries.filter((s) => !excludeSet.has(s.id));
|
|
1199
|
+
}
|
|
1200
|
+
return summaries;
|
|
1201
|
+
}, [
|
|
1202
|
+
consumableNotes,
|
|
1203
|
+
getMetadata,
|
|
1204
|
+
normalizedSender,
|
|
1205
|
+
excludeIdsKey,
|
|
1206
|
+
filterBySender
|
|
1207
|
+
]);
|
|
987
1208
|
return {
|
|
988
1209
|
notes,
|
|
989
1210
|
consumableNotes,
|
|
@@ -994,46 +1215,251 @@ function useNotes(options) {
|
|
|
994
1215
|
refetch
|
|
995
1216
|
};
|
|
996
1217
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1218
|
+
|
|
1219
|
+
// src/hooks/useNoteStream.ts
|
|
1220
|
+
import { useCallback as useCallback5, useEffect as useEffect6, useMemo as useMemo5, useRef as useRef2, useState as useState4 } from "react";
|
|
1221
|
+
import { NoteFilter as NoteFilter2 } from "@miden-sdk/miden-sdk";
|
|
1222
|
+
|
|
1223
|
+
// src/utils/noteAttachment.ts
|
|
1224
|
+
import {
|
|
1225
|
+
NoteAttachment,
|
|
1226
|
+
NoteAttachmentKind,
|
|
1227
|
+
NoteAttachmentScheme,
|
|
1228
|
+
Word
|
|
1229
|
+
} from "@miden-sdk/miden-sdk";
|
|
1230
|
+
function readNoteAttachment(note) {
|
|
1231
|
+
try {
|
|
1232
|
+
const metadata = note.metadata?.();
|
|
1233
|
+
if (!metadata) return null;
|
|
1234
|
+
const attachment = metadata.attachment?.();
|
|
1235
|
+
if (!attachment) return null;
|
|
1236
|
+
const kind = attachment.kind?.();
|
|
1237
|
+
if (!kind) return null;
|
|
1238
|
+
if (kind === NoteAttachmentKind.None) return null;
|
|
1239
|
+
if (kind === NoteAttachmentKind.Word) {
|
|
1240
|
+
const word = attachment.asWord?.();
|
|
1241
|
+
if (!word) return null;
|
|
1242
|
+
const u64s = word.toU64s();
|
|
1243
|
+
const values = Array.from(u64s).map(
|
|
1244
|
+
(v) => BigInt(v)
|
|
1245
|
+
);
|
|
1246
|
+
return { values, kind: "word" };
|
|
1247
|
+
}
|
|
1248
|
+
if (kind === NoteAttachmentKind.Array) {
|
|
1249
|
+
const arr = attachment.asArray?.();
|
|
1250
|
+
if (!arr) return null;
|
|
1251
|
+
const u64s = arr.toU64s();
|
|
1252
|
+
const values = Array.from(u64s).map(
|
|
1253
|
+
(v) => BigInt(v)
|
|
1254
|
+
);
|
|
1255
|
+
return { values, kind: "array" };
|
|
1256
|
+
}
|
|
1257
|
+
return null;
|
|
1258
|
+
} catch {
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
function createNoteAttachment(values) {
|
|
1263
|
+
const bigints = [];
|
|
1264
|
+
for (let i = 0; i < values.length; i++) {
|
|
1265
|
+
bigints.push(BigInt(values[i]));
|
|
1266
|
+
}
|
|
1267
|
+
if (bigints.length === 0) {
|
|
1268
|
+
return new NoteAttachment();
|
|
1269
|
+
}
|
|
1270
|
+
const scheme = NoteAttachmentScheme.none();
|
|
1271
|
+
if (bigints.length <= 4) {
|
|
1272
|
+
while (bigints.length < 4) {
|
|
1273
|
+
bigints.push(0n);
|
|
1274
|
+
}
|
|
1275
|
+
const word = new Word(BigUint64Array.from(bigints));
|
|
1276
|
+
return NoteAttachment.newWord(scheme, word);
|
|
1277
|
+
}
|
|
1278
|
+
while (bigints.length % 4 !== 0) {
|
|
1279
|
+
bigints.push(0n);
|
|
1280
|
+
}
|
|
1281
|
+
const words = [];
|
|
1282
|
+
for (let i = 0; i < bigints.length; i += 4) {
|
|
1283
|
+
words.push(new Word(BigUint64Array.from(bigints.slice(i, i + 4))));
|
|
1284
|
+
}
|
|
1285
|
+
const newArray = NoteAttachment["newArray"];
|
|
1286
|
+
if (typeof newArray !== "function") {
|
|
1287
|
+
throw new Error(
|
|
1288
|
+
"NoteAttachment.newArray is not available. Ensure @miden-sdk/miden-sdk >= 0.13.1."
|
|
1289
|
+
);
|
|
1010
1290
|
}
|
|
1291
|
+
return newArray.call(NoteAttachment, scheme, words);
|
|
1011
1292
|
}
|
|
1012
1293
|
|
|
1013
|
-
// src/hooks/
|
|
1014
|
-
|
|
1015
|
-
import { TransactionFilter } from "@miden-sdk/miden-sdk";
|
|
1016
|
-
function useTransactionHistory(options = {}) {
|
|
1294
|
+
// src/hooks/useNoteStream.ts
|
|
1295
|
+
function useNoteStream(options = {}) {
|
|
1017
1296
|
const { client, isReady, runExclusive } = useMiden();
|
|
1018
1297
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1298
|
+
const allNotes = useNotesStore();
|
|
1299
|
+
const noteFirstSeen = useNoteFirstSeenStore();
|
|
1019
1300
|
const { lastSyncTime } = useSyncStateStore();
|
|
1020
|
-
const
|
|
1301
|
+
const setNotesIfChanged = useMidenStore((state) => state.setNotesIfChanged);
|
|
1021
1302
|
const [isLoading, setIsLoading] = useState4(false);
|
|
1022
1303
|
const [error, setError] = useState4(null);
|
|
1023
|
-
const
|
|
1304
|
+
const handledIdsRef = useRef2(/* @__PURE__ */ new Set());
|
|
1305
|
+
const [handledVersion, setHandledVersion] = useState4(0);
|
|
1306
|
+
const status = options.status ?? "committed";
|
|
1307
|
+
const sender = options.sender ?? null;
|
|
1308
|
+
const since = options.since;
|
|
1309
|
+
const amountFilterRef = useRef2(options.amountFilter);
|
|
1310
|
+
amountFilterRef.current = options.amountFilter;
|
|
1311
|
+
const excludeIdsKey = useMemo5(() => {
|
|
1312
|
+
if (!options.excludeIds) return "";
|
|
1313
|
+
if (options.excludeIds instanceof Set)
|
|
1314
|
+
return Array.from(options.excludeIds).sort().join("\0");
|
|
1315
|
+
return [...options.excludeIds].sort().join("\0");
|
|
1316
|
+
}, [options.excludeIds]);
|
|
1317
|
+
const excludeIdSet = useMemo5(() => {
|
|
1318
|
+
if (!excludeIdsKey) return null;
|
|
1319
|
+
return new Set(excludeIdsKey.split("\0"));
|
|
1320
|
+
}, [excludeIdsKey]);
|
|
1321
|
+
const refetch = useCallback5(async () => {
|
|
1322
|
+
if (!client || !isReady) return;
|
|
1323
|
+
setIsLoading(true);
|
|
1324
|
+
setError(null);
|
|
1325
|
+
try {
|
|
1326
|
+
const filterType = getNoteFilterType(status);
|
|
1327
|
+
const filter = new NoteFilter2(filterType);
|
|
1328
|
+
const fetched = await runExclusiveSafe(
|
|
1329
|
+
() => client.getInputNotes(filter)
|
|
1330
|
+
);
|
|
1331
|
+
setNotesIfChanged(fetched);
|
|
1332
|
+
} catch (err) {
|
|
1333
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
1334
|
+
} finally {
|
|
1335
|
+
setIsLoading(false);
|
|
1336
|
+
}
|
|
1337
|
+
}, [client, isReady, runExclusiveSafe, status, setNotesIfChanged]);
|
|
1338
|
+
useEffect6(() => {
|
|
1339
|
+
if (isReady) {
|
|
1340
|
+
refetch();
|
|
1341
|
+
}
|
|
1342
|
+
}, [isReady, lastSyncTime, refetch]);
|
|
1343
|
+
const streamedNotes = useMemo5(() => {
|
|
1344
|
+
void handledVersion;
|
|
1345
|
+
const result = [];
|
|
1346
|
+
let normalizedSender = null;
|
|
1347
|
+
if (sender) {
|
|
1348
|
+
try {
|
|
1349
|
+
normalizedSender = normalizeAccountId(sender);
|
|
1350
|
+
} catch {
|
|
1351
|
+
normalizedSender = sender;
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
for (const record of allNotes) {
|
|
1355
|
+
const note = buildStreamedNote(record, noteFirstSeen);
|
|
1356
|
+
if (!note) continue;
|
|
1357
|
+
if (handledIdsRef.current.has(note.id)) continue;
|
|
1358
|
+
if (excludeIdSet && excludeIdSet.has(note.id)) continue;
|
|
1359
|
+
if (normalizedSender && note.sender !== normalizedSender) continue;
|
|
1360
|
+
if (since !== void 0 && note.firstSeenAt < since) continue;
|
|
1361
|
+
if (amountFilterRef.current && !amountFilterRef.current(note.amount))
|
|
1362
|
+
continue;
|
|
1363
|
+
result.push(note);
|
|
1364
|
+
}
|
|
1365
|
+
result.sort((a, b) => a.firstSeenAt - b.firstSeenAt);
|
|
1366
|
+
return result;
|
|
1367
|
+
}, [allNotes, noteFirstSeen, excludeIdSet, sender, since, handledVersion]);
|
|
1368
|
+
const latest = useMemo5(
|
|
1369
|
+
() => streamedNotes.length > 0 ? streamedNotes[streamedNotes.length - 1] : null,
|
|
1370
|
+
[streamedNotes]
|
|
1371
|
+
);
|
|
1372
|
+
const markHandled = useCallback5((noteId) => {
|
|
1373
|
+
handledIdsRef.current = new Set(handledIdsRef.current).add(noteId);
|
|
1374
|
+
setHandledVersion((v) => v + 1);
|
|
1375
|
+
}, []);
|
|
1376
|
+
const markAllHandled = useCallback5(() => {
|
|
1377
|
+
const newSet = new Set(handledIdsRef.current);
|
|
1378
|
+
for (const note of streamedNotes) {
|
|
1379
|
+
newSet.add(note.id);
|
|
1380
|
+
}
|
|
1381
|
+
handledIdsRef.current = newSet;
|
|
1382
|
+
setHandledVersion((v) => v + 1);
|
|
1383
|
+
}, [streamedNotes]);
|
|
1384
|
+
const snapshot = useCallback5(() => {
|
|
1385
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1386
|
+
for (const note of streamedNotes) {
|
|
1387
|
+
ids.add(note.id);
|
|
1388
|
+
}
|
|
1389
|
+
return { ids, timestamp: Date.now() };
|
|
1390
|
+
}, [streamedNotes]);
|
|
1391
|
+
return {
|
|
1392
|
+
notes: streamedNotes,
|
|
1393
|
+
latest,
|
|
1394
|
+
markHandled,
|
|
1395
|
+
markAllHandled,
|
|
1396
|
+
snapshot,
|
|
1397
|
+
isLoading,
|
|
1398
|
+
error
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
function buildStreamedNote(record, noteFirstSeen) {
|
|
1402
|
+
try {
|
|
1403
|
+
const id = record.id().toString();
|
|
1404
|
+
const metadata = record.metadata?.();
|
|
1405
|
+
const senderHex = metadata?.sender?.()?.toString?.();
|
|
1406
|
+
const sender = senderHex ? toBech32AccountId(senderHex) : "";
|
|
1407
|
+
const assets = [];
|
|
1408
|
+
let primaryAmount = 0n;
|
|
1409
|
+
try {
|
|
1410
|
+
const details = record.details();
|
|
1411
|
+
const assetsList = details?.assets?.().fungibleAssets?.() ?? [];
|
|
1412
|
+
for (const asset of assetsList) {
|
|
1413
|
+
const assetId = asset.faucetId().toString();
|
|
1414
|
+
const amount = BigInt(asset.amount());
|
|
1415
|
+
assets.push({ assetId, amount });
|
|
1416
|
+
if (primaryAmount === 0n) {
|
|
1417
|
+
primaryAmount = amount;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
} catch {
|
|
1421
|
+
}
|
|
1422
|
+
const attachmentData = readNoteAttachment(record);
|
|
1423
|
+
const attachment = attachmentData ? attachmentData.values : null;
|
|
1424
|
+
const firstSeenAt = noteFirstSeen.get(id) ?? Date.now();
|
|
1425
|
+
return {
|
|
1426
|
+
id,
|
|
1427
|
+
sender,
|
|
1428
|
+
amount: primaryAmount,
|
|
1429
|
+
assets,
|
|
1430
|
+
record,
|
|
1431
|
+
firstSeenAt,
|
|
1432
|
+
attachment
|
|
1433
|
+
};
|
|
1434
|
+
} catch {
|
|
1435
|
+
return null;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// src/hooks/useTransactionHistory.ts
|
|
1440
|
+
import { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo6, useState as useState5 } from "react";
|
|
1441
|
+
import { TransactionFilter as TransactionFilter2 } from "@miden-sdk/miden-sdk";
|
|
1442
|
+
function useTransactionHistory(options = {}) {
|
|
1443
|
+
const { client, isReady, runExclusive } = useMiden();
|
|
1444
|
+
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1445
|
+
const { lastSyncTime } = useSyncStateStore();
|
|
1446
|
+
const [records, setRecords] = useState5([]);
|
|
1447
|
+
const [isLoading, setIsLoading] = useState5(false);
|
|
1448
|
+
const [error, setError] = useState5(null);
|
|
1449
|
+
const rawIds = useMemo6(() => {
|
|
1024
1450
|
if (options.id) return [options.id];
|
|
1025
1451
|
if (options.ids && options.ids.length > 0) return options.ids;
|
|
1026
1452
|
return null;
|
|
1027
1453
|
}, [options.id, options.ids]);
|
|
1028
|
-
const idsHex =
|
|
1454
|
+
const idsHex = useMemo6(() => {
|
|
1029
1455
|
if (!rawIds) return null;
|
|
1030
1456
|
return rawIds.map(
|
|
1031
|
-
(id) =>
|
|
1457
|
+
(id) => normalizeHex2(typeof id === "string" ? id : id.toHex())
|
|
1032
1458
|
);
|
|
1033
1459
|
}, [rawIds]);
|
|
1034
1460
|
const filter = options.filter;
|
|
1035
1461
|
const refreshOnSync = options.refreshOnSync !== false;
|
|
1036
|
-
const refetch =
|
|
1462
|
+
const refetch = useCallback6(async () => {
|
|
1037
1463
|
if (!client || !isReady) return;
|
|
1038
1464
|
setIsLoading(true);
|
|
1039
1465
|
setError(null);
|
|
@@ -1047,7 +1473,7 @@ function useTransactionHistory(options = {}) {
|
|
|
1047
1473
|
() => client.getTransactions(resolvedFilter)
|
|
1048
1474
|
);
|
|
1049
1475
|
const filtered = localFilterHexes ? fetched.filter(
|
|
1050
|
-
(record2) => localFilterHexes.includes(
|
|
1476
|
+
(record2) => localFilterHexes.includes(normalizeHex2(record2.id().toHex()))
|
|
1051
1477
|
) : fetched;
|
|
1052
1478
|
setRecords(filtered);
|
|
1053
1479
|
} catch (err) {
|
|
@@ -1056,19 +1482,19 @@ function useTransactionHistory(options = {}) {
|
|
|
1056
1482
|
setIsLoading(false);
|
|
1057
1483
|
}
|
|
1058
1484
|
}, [client, isReady, runExclusiveSafe, filter, rawIds, idsHex]);
|
|
1059
|
-
|
|
1485
|
+
useEffect7(() => {
|
|
1060
1486
|
if (!isReady) return;
|
|
1061
1487
|
refetch();
|
|
1062
1488
|
}, [isReady, refetch]);
|
|
1063
|
-
|
|
1489
|
+
useEffect7(() => {
|
|
1064
1490
|
if (!isReady || !refreshOnSync || !lastSyncTime) return;
|
|
1065
1491
|
refetch();
|
|
1066
1492
|
}, [isReady, lastSyncTime, refreshOnSync, refetch]);
|
|
1067
|
-
const record =
|
|
1493
|
+
const record = useMemo6(() => {
|
|
1068
1494
|
if (!idsHex || idsHex.length !== 1) return null;
|
|
1069
|
-
return records.find((item) =>
|
|
1495
|
+
return records.find((item) => normalizeHex2(item.id().toHex()) === idsHex[0]) ?? null;
|
|
1070
1496
|
}, [records, idsHex]);
|
|
1071
|
-
const status =
|
|
1497
|
+
const status = useMemo6(() => {
|
|
1072
1498
|
if (!record) return null;
|
|
1073
1499
|
const current = record.transactionStatus();
|
|
1074
1500
|
if (current.isCommitted()) return "committed";
|
|
@@ -1090,29 +1516,29 @@ function buildFilter(filter, ids, idsHex) {
|
|
|
1090
1516
|
return { filter };
|
|
1091
1517
|
}
|
|
1092
1518
|
if (!ids || ids.length === 0) {
|
|
1093
|
-
return { filter:
|
|
1519
|
+
return { filter: TransactionFilter2.all() };
|
|
1094
1520
|
}
|
|
1095
1521
|
const allTransactionIds = ids.every((id) => typeof id !== "string");
|
|
1096
1522
|
if (allTransactionIds) {
|
|
1097
|
-
return { filter:
|
|
1523
|
+
return { filter: TransactionFilter2.ids(ids) };
|
|
1098
1524
|
}
|
|
1099
1525
|
return {
|
|
1100
|
-
filter:
|
|
1526
|
+
filter: TransactionFilter2.all(),
|
|
1101
1527
|
localFilterHexes: idsHex ?? []
|
|
1102
1528
|
};
|
|
1103
1529
|
}
|
|
1104
|
-
function
|
|
1530
|
+
function normalizeHex2(value) {
|
|
1105
1531
|
const trimmed = value.trim();
|
|
1106
1532
|
const normalized = trimmed.startsWith("0x") || trimmed.startsWith("0X") ? trimmed : `0x${trimmed}`;
|
|
1107
1533
|
return normalized.toLowerCase();
|
|
1108
1534
|
}
|
|
1109
1535
|
|
|
1110
1536
|
// src/hooks/useSyncState.ts
|
|
1111
|
-
import { useCallback as
|
|
1537
|
+
import { useCallback as useCallback7 } from "react";
|
|
1112
1538
|
function useSyncState() {
|
|
1113
1539
|
const { sync: triggerSync } = useMiden();
|
|
1114
1540
|
const syncState = useSyncStateStore();
|
|
1115
|
-
const sync =
|
|
1541
|
+
const sync = useCallback7(async () => {
|
|
1116
1542
|
await triggerSync();
|
|
1117
1543
|
}, [triggerSync]);
|
|
1118
1544
|
return {
|
|
@@ -1122,16 +1548,16 @@ function useSyncState() {
|
|
|
1122
1548
|
}
|
|
1123
1549
|
|
|
1124
1550
|
// src/hooks/useCreateWallet.ts
|
|
1125
|
-
import { useCallback as
|
|
1551
|
+
import { useCallback as useCallback8, useState as useState6 } from "react";
|
|
1126
1552
|
import { AccountStorageMode } from "@miden-sdk/miden-sdk";
|
|
1127
1553
|
function useCreateWallet() {
|
|
1128
1554
|
const { client, isReady, sync, runExclusive } = useMiden();
|
|
1129
1555
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1130
1556
|
const setAccounts = useMidenStore((state) => state.setAccounts);
|
|
1131
|
-
const [wallet, setWallet] =
|
|
1132
|
-
const [isCreating, setIsCreating] =
|
|
1133
|
-
const [error, setError] =
|
|
1134
|
-
const createWallet =
|
|
1557
|
+
const [wallet, setWallet] = useState6(null);
|
|
1558
|
+
const [isCreating, setIsCreating] = useState6(false);
|
|
1559
|
+
const [error, setError] = useState6(null);
|
|
1560
|
+
const createWallet = useCallback8(
|
|
1135
1561
|
async (options = {}) => {
|
|
1136
1562
|
if (!client || !isReady) {
|
|
1137
1563
|
throw new Error("Miden client is not ready");
|
|
@@ -1169,7 +1595,7 @@ function useCreateWallet() {
|
|
|
1169
1595
|
},
|
|
1170
1596
|
[client, isReady, runExclusive, setAccounts]
|
|
1171
1597
|
);
|
|
1172
|
-
const reset =
|
|
1598
|
+
const reset = useCallback8(() => {
|
|
1173
1599
|
setWallet(null);
|
|
1174
1600
|
setIsCreating(false);
|
|
1175
1601
|
setError(null);
|
|
@@ -1196,16 +1622,16 @@ function getStorageMode(mode) {
|
|
|
1196
1622
|
}
|
|
1197
1623
|
|
|
1198
1624
|
// src/hooks/useCreateFaucet.ts
|
|
1199
|
-
import { useCallback as
|
|
1625
|
+
import { useCallback as useCallback9, useState as useState7 } from "react";
|
|
1200
1626
|
import { AccountStorageMode as AccountStorageMode2 } from "@miden-sdk/miden-sdk";
|
|
1201
1627
|
function useCreateFaucet() {
|
|
1202
1628
|
const { client, isReady, runExclusive } = useMiden();
|
|
1203
1629
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1204
1630
|
const setAccounts = useMidenStore((state) => state.setAccounts);
|
|
1205
|
-
const [faucet, setFaucet] =
|
|
1206
|
-
const [isCreating, setIsCreating] =
|
|
1207
|
-
const [error, setError] =
|
|
1208
|
-
const createFaucet =
|
|
1631
|
+
const [faucet, setFaucet] = useState7(null);
|
|
1632
|
+
const [isCreating, setIsCreating] = useState7(false);
|
|
1633
|
+
const [error, setError] = useState7(null);
|
|
1634
|
+
const createFaucet = useCallback9(
|
|
1209
1635
|
async (options) => {
|
|
1210
1636
|
if (!client || !isReady) {
|
|
1211
1637
|
throw new Error("Miden client is not ready");
|
|
@@ -1244,7 +1670,7 @@ function useCreateFaucet() {
|
|
|
1244
1670
|
},
|
|
1245
1671
|
[client, isReady, runExclusive, setAccounts]
|
|
1246
1672
|
);
|
|
1247
|
-
const reset =
|
|
1673
|
+
const reset = useCallback9(() => {
|
|
1248
1674
|
setFaucet(null);
|
|
1249
1675
|
setIsCreating(false);
|
|
1250
1676
|
setError(null);
|
|
@@ -1271,16 +1697,16 @@ function getStorageMode2(mode) {
|
|
|
1271
1697
|
}
|
|
1272
1698
|
|
|
1273
1699
|
// src/hooks/useImportAccount.ts
|
|
1274
|
-
import { useCallback as
|
|
1700
|
+
import { useCallback as useCallback10, useState as useState8 } from "react";
|
|
1275
1701
|
import { AccountFile } from "@miden-sdk/miden-sdk";
|
|
1276
1702
|
function useImportAccount() {
|
|
1277
1703
|
const { client, isReady, runExclusive } = useMiden();
|
|
1278
1704
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1279
1705
|
const setAccounts = useMidenStore((state) => state.setAccounts);
|
|
1280
|
-
const [account, setAccount] =
|
|
1281
|
-
const [isImporting, setIsImporting] =
|
|
1282
|
-
const [error, setError] =
|
|
1283
|
-
const importAccount =
|
|
1706
|
+
const [account, setAccount] = useState8(null);
|
|
1707
|
+
const [isImporting, setIsImporting] = useState8(false);
|
|
1708
|
+
const [error, setError] = useState8(null);
|
|
1709
|
+
const importAccount = useCallback10(
|
|
1284
1710
|
async (options) => {
|
|
1285
1711
|
if (!client || !isReady) {
|
|
1286
1712
|
throw new Error("Miden client is not ready");
|
|
@@ -1375,7 +1801,7 @@ function useImportAccount() {
|
|
|
1375
1801
|
},
|
|
1376
1802
|
[client, isReady, runExclusive, setAccounts]
|
|
1377
1803
|
);
|
|
1378
|
-
const reset =
|
|
1804
|
+
const reset = useCallback10(() => {
|
|
1379
1805
|
setAccount(null);
|
|
1380
1806
|
setIsImporting(false);
|
|
1381
1807
|
setError(null);
|
|
@@ -1425,43 +1851,154 @@ function bytesEqual(left, right) {
|
|
|
1425
1851
|
}
|
|
1426
1852
|
|
|
1427
1853
|
// src/hooks/useSend.ts
|
|
1428
|
-
import { useCallback as
|
|
1429
|
-
import {
|
|
1854
|
+
import { useCallback as useCallback11, useRef as useRef3, useState as useState9 } from "react";
|
|
1855
|
+
import {
|
|
1856
|
+
FungibleAsset,
|
|
1857
|
+
Note,
|
|
1858
|
+
NoteAssets,
|
|
1859
|
+
NoteType as NoteType2,
|
|
1860
|
+
OutputNote,
|
|
1861
|
+
OutputNoteArray,
|
|
1862
|
+
TransactionRequestBuilder
|
|
1863
|
+
} from "@miden-sdk/miden-sdk";
|
|
1864
|
+
|
|
1865
|
+
// src/utils/errors.ts
|
|
1866
|
+
var MidenError = class extends Error {
|
|
1867
|
+
constructor(message, options) {
|
|
1868
|
+
super(message);
|
|
1869
|
+
this.name = "MidenError";
|
|
1870
|
+
this.code = options?.code ?? "UNKNOWN";
|
|
1871
|
+
if (options?.cause !== void 0) {
|
|
1872
|
+
this.cause = options.cause;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
};
|
|
1876
|
+
var ERROR_PATTERNS = [
|
|
1877
|
+
{
|
|
1878
|
+
test: (msg) => msg.includes("_assertClass") || msg.includes("expected instance of"),
|
|
1879
|
+
code: "WASM_CLASS_MISMATCH",
|
|
1880
|
+
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."
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
test: (msg) => msg.includes("null pointer") || msg.includes("already been freed") || msg.includes("dereferencing a null"),
|
|
1884
|
+
code: "WASM_POINTER_CONSUMED",
|
|
1885
|
+
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."
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
test: (msg) => msg.includes("not initialized") || msg.includes("Cannot read properties of null"),
|
|
1889
|
+
code: "WASM_NOT_INITIALIZED",
|
|
1890
|
+
message: "Miden client is not initialized. Ensure you are inside a <MidenProvider> and the client is ready before calling SDK methods."
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
test: (msg) => msg.includes("state commitment mismatch") || msg.includes("stale state"),
|
|
1894
|
+
code: "WASM_SYNC_REQUIRED",
|
|
1895
|
+
message: "Account state is stale. Call sync() before executing transactions, or ensure no concurrent transactions are running against the same account."
|
|
1896
|
+
}
|
|
1897
|
+
];
|
|
1898
|
+
function wrapWasmError(e) {
|
|
1899
|
+
if (e instanceof MidenError) return e;
|
|
1900
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1901
|
+
for (const pattern of ERROR_PATTERNS) {
|
|
1902
|
+
if (pattern.test(msg)) {
|
|
1903
|
+
return new MidenError(pattern.message, { cause: e, code: pattern.code });
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
if (e instanceof Error) return e;
|
|
1907
|
+
return new Error(msg);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
// src/hooks/useSend.ts
|
|
1430
1911
|
function useSend() {
|
|
1431
1912
|
const { client, isReady, sync, runExclusive, prover } = useMiden();
|
|
1432
1913
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1433
|
-
const
|
|
1434
|
-
const [
|
|
1435
|
-
const [
|
|
1436
|
-
const [
|
|
1437
|
-
const
|
|
1914
|
+
const isBusyRef = useRef3(false);
|
|
1915
|
+
const [result, setResult] = useState9(null);
|
|
1916
|
+
const [isLoading, setIsLoading] = useState9(false);
|
|
1917
|
+
const [stage, setStage] = useState9("idle");
|
|
1918
|
+
const [error, setError] = useState9(null);
|
|
1919
|
+
const send = useCallback11(
|
|
1438
1920
|
async (options) => {
|
|
1439
1921
|
if (!client || !isReady) {
|
|
1440
1922
|
throw new Error("Miden client is not ready");
|
|
1441
1923
|
}
|
|
1924
|
+
if (isBusyRef.current) {
|
|
1925
|
+
throw new MidenError(
|
|
1926
|
+
"A send is already in progress. Await the previous send before starting another.",
|
|
1927
|
+
{ code: "SEND_BUSY" }
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
isBusyRef.current = true;
|
|
1442
1931
|
setIsLoading(true);
|
|
1443
1932
|
setStage("executing");
|
|
1444
1933
|
setError(null);
|
|
1445
1934
|
try {
|
|
1935
|
+
if (!options.skipSync) {
|
|
1936
|
+
await sync();
|
|
1937
|
+
}
|
|
1446
1938
|
const noteType = getNoteType(options.noteType ?? DEFAULTS.NOTE_TYPE);
|
|
1447
|
-
|
|
1448
|
-
|
|
1939
|
+
let amount = options.amount;
|
|
1940
|
+
if (options.sendAll) {
|
|
1941
|
+
const resolvedAmount = await runExclusiveSafe(async () => {
|
|
1942
|
+
const fromId = parseAccountId(options.from);
|
|
1943
|
+
const account = await client.getAccount(fromId);
|
|
1944
|
+
if (!account) throw new Error("Account not found");
|
|
1945
|
+
const assetIdObj = parseAccountId(options.assetId);
|
|
1946
|
+
const balance = account.vault?.()?.getBalance?.(assetIdObj);
|
|
1947
|
+
if (balance === void 0 || balance === null) {
|
|
1948
|
+
throw new Error("Could not query account balance");
|
|
1949
|
+
}
|
|
1950
|
+
const bal = BigInt(balance);
|
|
1951
|
+
if (bal === 0n) {
|
|
1952
|
+
throw new Error("Account has zero balance for this asset");
|
|
1953
|
+
}
|
|
1954
|
+
return bal;
|
|
1955
|
+
});
|
|
1956
|
+
amount = resolvedAmount;
|
|
1957
|
+
}
|
|
1958
|
+
if (amount === void 0 || amount === null) {
|
|
1959
|
+
throw new Error("Amount is required (provide amount or sendAll)");
|
|
1960
|
+
}
|
|
1449
1961
|
const assetId = options.assetId ?? options.faucetId ?? null;
|
|
1450
1962
|
if (!assetId) {
|
|
1451
1963
|
throw new Error("Asset ID is required");
|
|
1452
1964
|
}
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
toAccountId,
|
|
1458
|
-
assetIdObj,
|
|
1459
|
-
noteType,
|
|
1460
|
-
options.amount,
|
|
1461
|
-
options.recallHeight ?? null,
|
|
1462
|
-
options.timelockHeight ?? null
|
|
1965
|
+
const hasAttachment = options.attachment !== void 0 && options.attachment !== null;
|
|
1966
|
+
if (hasAttachment && (options.recallHeight != null || options.timelockHeight != null)) {
|
|
1967
|
+
throw new Error(
|
|
1968
|
+
"recallHeight and timelockHeight are not supported when attachment is provided"
|
|
1463
1969
|
);
|
|
1464
|
-
|
|
1970
|
+
}
|
|
1971
|
+
const txResult = await runExclusiveSafe(async () => {
|
|
1972
|
+
const fromAccountId = parseAccountId(options.from);
|
|
1973
|
+
const toAccountId = parseAccountId(options.to);
|
|
1974
|
+
const assetIdObj = parseAccountId(assetId);
|
|
1975
|
+
let txRequest;
|
|
1976
|
+
if (hasAttachment) {
|
|
1977
|
+
const attachment = createNoteAttachment(options.attachment);
|
|
1978
|
+
const assets = new NoteAssets([
|
|
1979
|
+
new FungibleAsset(assetIdObj, amount)
|
|
1980
|
+
]);
|
|
1981
|
+
const note = Note.createP2IDNote(
|
|
1982
|
+
fromAccountId,
|
|
1983
|
+
toAccountId,
|
|
1984
|
+
assets,
|
|
1985
|
+
noteType,
|
|
1986
|
+
attachment
|
|
1987
|
+
);
|
|
1988
|
+
txRequest = new TransactionRequestBuilder().withOwnOutputNotes(new OutputNoteArray([OutputNote.full(note)])).build();
|
|
1989
|
+
} else {
|
|
1990
|
+
txRequest = client.newSendTransactionRequest(
|
|
1991
|
+
fromAccountId,
|
|
1992
|
+
toAccountId,
|
|
1993
|
+
assetIdObj,
|
|
1994
|
+
noteType,
|
|
1995
|
+
amount,
|
|
1996
|
+
options.recallHeight ?? null,
|
|
1997
|
+
options.timelockHeight ?? null
|
|
1998
|
+
);
|
|
1999
|
+
}
|
|
2000
|
+
const execAccountId = parseAccountId(options.from);
|
|
2001
|
+
return await client.executeTransaction(execAccountId, txRequest);
|
|
1465
2002
|
});
|
|
1466
2003
|
setStage("proving");
|
|
1467
2004
|
const provenTransaction = await runExclusiveSafe(
|
|
@@ -1471,22 +2008,31 @@ function useSend() {
|
|
|
1471
2008
|
const submissionHeight = await runExclusiveSafe(
|
|
1472
2009
|
() => client.submitProvenTransaction(provenTransaction, txResult)
|
|
1473
2010
|
);
|
|
2011
|
+
const txIdHex = txResult.id().toHex();
|
|
2012
|
+
const txIdString = txResult.id().toString();
|
|
2013
|
+
let fullNote = null;
|
|
2014
|
+
if (noteType === NoteType2.Private) {
|
|
2015
|
+
fullNote = extractFullNote(txResult);
|
|
2016
|
+
}
|
|
1474
2017
|
await runExclusiveSafe(
|
|
1475
2018
|
() => client.applyTransaction(txResult, submissionHeight)
|
|
1476
2019
|
);
|
|
1477
|
-
|
|
1478
|
-
await waitForTransactionCommit(client, runExclusiveSafe, txId);
|
|
1479
|
-
if (noteType === NoteType.Private) {
|
|
1480
|
-
const fullNote = extractFullNote(txResult);
|
|
2020
|
+
if (noteType === NoteType2.Private) {
|
|
1481
2021
|
if (!fullNote) {
|
|
1482
2022
|
throw new Error("Missing full note for private send");
|
|
1483
2023
|
}
|
|
1484
|
-
|
|
2024
|
+
await waitForTransactionCommit(
|
|
2025
|
+
client,
|
|
2026
|
+
runExclusiveSafe,
|
|
2027
|
+
txIdHex
|
|
2028
|
+
);
|
|
2029
|
+
const recipientAccountId = parseAccountId(options.to);
|
|
2030
|
+
const recipientAddress = parseAddress(options.to, recipientAccountId);
|
|
1485
2031
|
await runExclusiveSafe(
|
|
1486
2032
|
() => client.sendPrivateNote(fullNote, recipientAddress)
|
|
1487
2033
|
);
|
|
1488
2034
|
}
|
|
1489
|
-
const txSummary = { transactionId:
|
|
2035
|
+
const txSummary = { transactionId: txIdString };
|
|
1490
2036
|
setStage("complete");
|
|
1491
2037
|
setResult(txSummary);
|
|
1492
2038
|
await sync();
|
|
@@ -1498,11 +2044,12 @@ function useSend() {
|
|
|
1498
2044
|
throw error2;
|
|
1499
2045
|
} finally {
|
|
1500
2046
|
setIsLoading(false);
|
|
2047
|
+
isBusyRef.current = false;
|
|
1501
2048
|
}
|
|
1502
2049
|
},
|
|
1503
2050
|
[client, isReady, prover, runExclusive, sync]
|
|
1504
2051
|
);
|
|
1505
|
-
const reset =
|
|
2052
|
+
const reset = useCallback11(() => {
|
|
1506
2053
|
setResult(null);
|
|
1507
2054
|
setIsLoading(false);
|
|
1508
2055
|
setStage("idle");
|
|
@@ -1517,39 +2064,6 @@ function useSend() {
|
|
|
1517
2064
|
reset
|
|
1518
2065
|
};
|
|
1519
2066
|
}
|
|
1520
|
-
function getNoteType(type) {
|
|
1521
|
-
switch (type) {
|
|
1522
|
-
case "private":
|
|
1523
|
-
return NoteType.Private;
|
|
1524
|
-
case "public":
|
|
1525
|
-
return NoteType.Public;
|
|
1526
|
-
case "encrypted":
|
|
1527
|
-
return NoteType.Encrypted;
|
|
1528
|
-
default:
|
|
1529
|
-
return NoteType.Private;
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
async function waitForTransactionCommit(client, runExclusiveSafe, txId, maxWaitMs = 1e4, delayMs = 1e3) {
|
|
1533
|
-
let waited = 0;
|
|
1534
|
-
while (waited < maxWaitMs) {
|
|
1535
|
-
await runExclusiveSafe(() => client.syncState());
|
|
1536
|
-
const [record] = await runExclusiveSafe(
|
|
1537
|
-
() => client.getTransactions(TransactionFilter2.ids([txId]))
|
|
1538
|
-
);
|
|
1539
|
-
if (record) {
|
|
1540
|
-
const status = record.transactionStatus();
|
|
1541
|
-
if (status.isCommitted()) {
|
|
1542
|
-
return;
|
|
1543
|
-
}
|
|
1544
|
-
if (status.isDiscarded()) {
|
|
1545
|
-
throw new Error("Transaction was discarded before commit");
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
1548
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1549
|
-
waited += delayMs;
|
|
1550
|
-
}
|
|
1551
|
-
throw new Error("Timeout waiting for transaction commit");
|
|
1552
|
-
}
|
|
1553
2067
|
function extractFullNote(txResult) {
|
|
1554
2068
|
try {
|
|
1555
2069
|
const executedTx = txResult.executedTransaction?.();
|
|
@@ -1562,26 +2076,26 @@ function extractFullNote(txResult) {
|
|
|
1562
2076
|
}
|
|
1563
2077
|
|
|
1564
2078
|
// src/hooks/useMultiSend.ts
|
|
1565
|
-
import { useCallback as
|
|
2079
|
+
import { useCallback as useCallback12, useRef as useRef4, useState as useState10 } from "react";
|
|
1566
2080
|
import {
|
|
1567
|
-
FungibleAsset,
|
|
1568
|
-
Note,
|
|
1569
|
-
NoteAssets,
|
|
1570
|
-
NoteAttachment,
|
|
1571
|
-
NoteType as
|
|
1572
|
-
OutputNote,
|
|
1573
|
-
OutputNoteArray,
|
|
1574
|
-
|
|
1575
|
-
TransactionRequestBuilder
|
|
2081
|
+
FungibleAsset as FungibleAsset2,
|
|
2082
|
+
Note as Note2,
|
|
2083
|
+
NoteAssets as NoteAssets2,
|
|
2084
|
+
NoteAttachment as NoteAttachment2,
|
|
2085
|
+
NoteType as NoteType3,
|
|
2086
|
+
OutputNote as OutputNote2,
|
|
2087
|
+
OutputNoteArray as OutputNoteArray2,
|
|
2088
|
+
TransactionRequestBuilder as TransactionRequestBuilder2
|
|
1576
2089
|
} from "@miden-sdk/miden-sdk";
|
|
1577
2090
|
function useMultiSend() {
|
|
1578
2091
|
const { client, isReady, sync, runExclusive, prover } = useMiden();
|
|
1579
2092
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1580
|
-
const
|
|
1581
|
-
const [
|
|
1582
|
-
const [
|
|
1583
|
-
const [
|
|
1584
|
-
const
|
|
2093
|
+
const isBusyRef = useRef4(false);
|
|
2094
|
+
const [result, setResult] = useState10(null);
|
|
2095
|
+
const [isLoading, setIsLoading] = useState10(false);
|
|
2096
|
+
const [stage, setStage] = useState10("idle");
|
|
2097
|
+
const [error, setError] = useState10(null);
|
|
2098
|
+
const sendMany = useCallback12(
|
|
1585
2099
|
async (options) => {
|
|
1586
2100
|
if (!client || !isReady) {
|
|
1587
2101
|
throw new Error("Miden client is not ready");
|
|
@@ -1589,35 +2103,53 @@ function useMultiSend() {
|
|
|
1589
2103
|
if (options.recipients.length === 0) {
|
|
1590
2104
|
throw new Error("No recipients provided");
|
|
1591
2105
|
}
|
|
2106
|
+
if (isBusyRef.current) {
|
|
2107
|
+
throw new MidenError(
|
|
2108
|
+
"A send is already in progress. Await the previous send before starting another.",
|
|
2109
|
+
{ code: "SEND_BUSY" }
|
|
2110
|
+
);
|
|
2111
|
+
}
|
|
2112
|
+
isBusyRef.current = true;
|
|
1592
2113
|
setIsLoading(true);
|
|
1593
2114
|
setStage("executing");
|
|
1594
2115
|
setError(null);
|
|
1595
2116
|
try {
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
const
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
receiverId
|
|
1605
|
-
assets
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
2117
|
+
if (!options.skipSync) {
|
|
2118
|
+
await sync();
|
|
2119
|
+
}
|
|
2120
|
+
const noteType = getNoteType(options.noteType ?? DEFAULTS.NOTE_TYPE);
|
|
2121
|
+
const outputs = options.recipients.map(
|
|
2122
|
+
({ to, amount, attachment, noteType: recipientNoteType }) => {
|
|
2123
|
+
const iterSenderId = parseAccountId(options.from);
|
|
2124
|
+
const iterAssetId = parseAccountId(options.assetId);
|
|
2125
|
+
const receiverId = parseAccountId(to);
|
|
2126
|
+
const assets = new NoteAssets2([
|
|
2127
|
+
new FungibleAsset2(iterAssetId, amount)
|
|
2128
|
+
]);
|
|
2129
|
+
const resolvedNoteType = recipientNoteType ? getNoteType(recipientNoteType) : noteType;
|
|
2130
|
+
const noteAttachment = attachment !== void 0 && attachment !== null ? createNoteAttachment(attachment) : new NoteAttachment2();
|
|
2131
|
+
const note = Note2.createP2IDNote(
|
|
2132
|
+
iterSenderId,
|
|
2133
|
+
receiverId,
|
|
2134
|
+
assets,
|
|
2135
|
+
resolvedNoteType,
|
|
2136
|
+
noteAttachment
|
|
2137
|
+
);
|
|
2138
|
+
const recipientAddress = parseAddress(to, receiverId);
|
|
2139
|
+
return {
|
|
2140
|
+
outputNote: OutputNote2.full(note),
|
|
2141
|
+
note,
|
|
2142
|
+
recipientAddress,
|
|
2143
|
+
noteType: resolvedNoteType
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
);
|
|
2147
|
+
const txRequest = new TransactionRequestBuilder2().withOwnOutputNotes(
|
|
2148
|
+
new OutputNoteArray2(outputs.map((o) => o.outputNote))
|
|
1618
2149
|
).build();
|
|
2150
|
+
const txSenderId = parseAccountId(options.from);
|
|
1619
2151
|
const txResult = await runExclusiveSafe(
|
|
1620
|
-
() => client.executeTransaction(
|
|
2152
|
+
() => client.executeTransaction(txSenderId, txRequest)
|
|
1621
2153
|
);
|
|
1622
2154
|
setStage("proving");
|
|
1623
2155
|
const provenTransaction = await runExclusiveSafe(
|
|
@@ -1627,23 +2159,27 @@ function useMultiSend() {
|
|
|
1627
2159
|
const submissionHeight = await runExclusiveSafe(
|
|
1628
2160
|
() => client.submitProvenTransaction(provenTransaction, txResult)
|
|
1629
2161
|
);
|
|
2162
|
+
const txIdHex = txResult.id().toHex();
|
|
2163
|
+
const txIdString = txResult.id().toString();
|
|
1630
2164
|
await runExclusiveSafe(
|
|
1631
2165
|
() => client.applyTransaction(txResult, submissionHeight)
|
|
1632
2166
|
);
|
|
1633
|
-
const
|
|
1634
|
-
if (
|
|
1635
|
-
await
|
|
2167
|
+
const hasPrivate = outputs.some((o) => o.noteType === NoteType3.Private);
|
|
2168
|
+
if (hasPrivate) {
|
|
2169
|
+
await waitForTransactionCommit(
|
|
1636
2170
|
client,
|
|
1637
2171
|
runExclusiveSafe,
|
|
1638
|
-
|
|
2172
|
+
txIdHex
|
|
1639
2173
|
);
|
|
1640
2174
|
for (const output of outputs) {
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
2175
|
+
if (output.noteType === NoteType3.Private) {
|
|
2176
|
+
await runExclusiveSafe(
|
|
2177
|
+
() => client.sendPrivateNote(output.note, output.recipientAddress)
|
|
2178
|
+
);
|
|
2179
|
+
}
|
|
1644
2180
|
}
|
|
1645
2181
|
}
|
|
1646
|
-
const txSummary = { transactionId:
|
|
2182
|
+
const txSummary = { transactionId: txIdString };
|
|
1647
2183
|
setStage("complete");
|
|
1648
2184
|
setResult(txSummary);
|
|
1649
2185
|
await sync();
|
|
@@ -1655,11 +2191,12 @@ function useMultiSend() {
|
|
|
1655
2191
|
throw error2;
|
|
1656
2192
|
} finally {
|
|
1657
2193
|
setIsLoading(false);
|
|
2194
|
+
isBusyRef.current = false;
|
|
1658
2195
|
}
|
|
1659
2196
|
},
|
|
1660
2197
|
[client, isReady, prover, runExclusive, sync]
|
|
1661
2198
|
);
|
|
1662
|
-
const reset =
|
|
2199
|
+
const reset = useCallback12(() => {
|
|
1663
2200
|
setResult(null);
|
|
1664
2201
|
setIsLoading(false);
|
|
1665
2202
|
setStage("idle");
|
|
@@ -1674,82 +2211,48 @@ function useMultiSend() {
|
|
|
1674
2211
|
reset
|
|
1675
2212
|
};
|
|
1676
2213
|
}
|
|
1677
|
-
function getNoteType2(type) {
|
|
1678
|
-
switch (type) {
|
|
1679
|
-
case "private":
|
|
1680
|
-
return NoteType2.Private;
|
|
1681
|
-
case "public":
|
|
1682
|
-
return NoteType2.Public;
|
|
1683
|
-
case "encrypted":
|
|
1684
|
-
return NoteType2.Encrypted;
|
|
1685
|
-
default:
|
|
1686
|
-
return NoteType2.Private;
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
async function waitForTransactionCommit2(client, runExclusiveSafe, txId, maxWaitMs = 1e4, delayMs = 1e3) {
|
|
1690
|
-
let waited = 0;
|
|
1691
|
-
while (waited < maxWaitMs) {
|
|
1692
|
-
await runExclusiveSafe(() => client.syncState());
|
|
1693
|
-
const [record] = await runExclusiveSafe(
|
|
1694
|
-
() => client.getTransactions(TransactionFilter3.ids([txId]))
|
|
1695
|
-
);
|
|
1696
|
-
if (record) {
|
|
1697
|
-
const status = record.transactionStatus();
|
|
1698
|
-
if (status.isCommitted()) {
|
|
1699
|
-
return;
|
|
1700
|
-
}
|
|
1701
|
-
if (status.isDiscarded()) {
|
|
1702
|
-
throw new Error("Transaction was discarded before commit");
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1706
|
-
waited += delayMs;
|
|
1707
|
-
}
|
|
1708
|
-
throw new Error("Timeout waiting for transaction commit");
|
|
1709
|
-
}
|
|
1710
2214
|
|
|
1711
2215
|
// src/hooks/useInternalTransfer.ts
|
|
1712
|
-
import { useCallback as
|
|
2216
|
+
import { useCallback as useCallback13, useState as useState11 } from "react";
|
|
1713
2217
|
import {
|
|
1714
|
-
FungibleAsset as
|
|
1715
|
-
Note as
|
|
2218
|
+
FungibleAsset as FungibleAsset3,
|
|
2219
|
+
Note as Note3,
|
|
1716
2220
|
NoteAndArgs,
|
|
1717
2221
|
NoteAndArgsArray,
|
|
1718
|
-
NoteAssets as
|
|
1719
|
-
NoteAttachment as
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
TransactionRequestBuilder as TransactionRequestBuilder2
|
|
2222
|
+
NoteAssets as NoteAssets3,
|
|
2223
|
+
NoteAttachment as NoteAttachment3,
|
|
2224
|
+
OutputNote as OutputNote3,
|
|
2225
|
+
OutputNoteArray as OutputNoteArray3,
|
|
2226
|
+
TransactionRequestBuilder as TransactionRequestBuilder3
|
|
1724
2227
|
} from "@miden-sdk/miden-sdk";
|
|
1725
2228
|
function useInternalTransfer() {
|
|
1726
2229
|
const { client, isReady, sync, runExclusive, prover } = useMiden();
|
|
1727
2230
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1728
|
-
const [result, setResult] =
|
|
1729
|
-
const [isLoading, setIsLoading] =
|
|
1730
|
-
const [stage, setStage] =
|
|
1731
|
-
const [error, setError] =
|
|
1732
|
-
const transferOnce =
|
|
2231
|
+
const [result, setResult] = useState11(null);
|
|
2232
|
+
const [isLoading, setIsLoading] = useState11(false);
|
|
2233
|
+
const [stage, setStage] = useState11("idle");
|
|
2234
|
+
const [error, setError] = useState11(null);
|
|
2235
|
+
const transferOnce = useCallback13(
|
|
1733
2236
|
async (options) => {
|
|
1734
2237
|
if (!client || !isReady) {
|
|
1735
2238
|
throw new Error("Miden client is not ready");
|
|
1736
2239
|
}
|
|
1737
|
-
const noteType =
|
|
2240
|
+
const noteType = getNoteType(options.noteType ?? DEFAULTS.NOTE_TYPE);
|
|
1738
2241
|
const senderId = parseAccountId(options.from);
|
|
1739
2242
|
const receiverId = parseAccountId(options.to);
|
|
1740
2243
|
const assetId = parseAccountId(options.assetId);
|
|
1741
|
-
const assets = new
|
|
1742
|
-
new
|
|
2244
|
+
const assets = new NoteAssets3([
|
|
2245
|
+
new FungibleAsset3(assetId, options.amount)
|
|
1743
2246
|
]);
|
|
1744
|
-
const note =
|
|
2247
|
+
const note = Note3.createP2IDNote(
|
|
1745
2248
|
senderId,
|
|
1746
2249
|
receiverId,
|
|
1747
2250
|
assets,
|
|
1748
2251
|
noteType,
|
|
1749
|
-
new
|
|
2252
|
+
new NoteAttachment3()
|
|
1750
2253
|
);
|
|
1751
2254
|
const noteId = note.id().toString();
|
|
1752
|
-
const createRequest = new
|
|
2255
|
+
const createRequest = new TransactionRequestBuilder3().withOwnOutputNotes(new OutputNoteArray3([OutputNote3.full(note)])).build();
|
|
1753
2256
|
const createTxId = await runExclusiveSafe(
|
|
1754
2257
|
() => prover ? client.submitNewTransactionWithProver(
|
|
1755
2258
|
senderId,
|
|
@@ -1757,7 +2260,7 @@ function useInternalTransfer() {
|
|
|
1757
2260
|
prover
|
|
1758
2261
|
) : client.submitNewTransaction(senderId, createRequest)
|
|
1759
2262
|
);
|
|
1760
|
-
const consumeRequest = new
|
|
2263
|
+
const consumeRequest = new TransactionRequestBuilder3().withInputNotes(new NoteAndArgsArray([new NoteAndArgs(note, null)])).build();
|
|
1761
2264
|
const consumeTxId = await runExclusiveSafe(
|
|
1762
2265
|
() => prover ? client.submitNewTransactionWithProver(
|
|
1763
2266
|
receiverId,
|
|
@@ -1773,7 +2276,7 @@ function useInternalTransfer() {
|
|
|
1773
2276
|
},
|
|
1774
2277
|
[client, isReady, prover, runExclusiveSafe]
|
|
1775
2278
|
);
|
|
1776
|
-
const transfer =
|
|
2279
|
+
const transfer = useCallback13(
|
|
1777
2280
|
async (options) => {
|
|
1778
2281
|
if (!client || !isReady) {
|
|
1779
2282
|
throw new Error("Miden client is not ready");
|
|
@@ -1799,7 +2302,7 @@ function useInternalTransfer() {
|
|
|
1799
2302
|
},
|
|
1800
2303
|
[client, isReady, sync, transferOnce]
|
|
1801
2304
|
);
|
|
1802
|
-
const transferChain =
|
|
2305
|
+
const transferChain = useCallback13(
|
|
1803
2306
|
async (options) => {
|
|
1804
2307
|
if (!client || !isReady) {
|
|
1805
2308
|
throw new Error("Miden client is not ready");
|
|
@@ -1840,7 +2343,7 @@ function useInternalTransfer() {
|
|
|
1840
2343
|
},
|
|
1841
2344
|
[client, isReady, sync, transferOnce]
|
|
1842
2345
|
);
|
|
1843
|
-
const reset =
|
|
2346
|
+
const reset = useCallback13(() => {
|
|
1844
2347
|
setResult(null);
|
|
1845
2348
|
setIsLoading(false);
|
|
1846
2349
|
setStage("idle");
|
|
@@ -1856,47 +2359,35 @@ function useInternalTransfer() {
|
|
|
1856
2359
|
reset
|
|
1857
2360
|
};
|
|
1858
2361
|
}
|
|
1859
|
-
function getNoteType3(type) {
|
|
1860
|
-
switch (type) {
|
|
1861
|
-
case "private":
|
|
1862
|
-
return NoteType3.Private;
|
|
1863
|
-
case "public":
|
|
1864
|
-
return NoteType3.Public;
|
|
1865
|
-
case "encrypted":
|
|
1866
|
-
return NoteType3.Encrypted;
|
|
1867
|
-
default:
|
|
1868
|
-
return NoteType3.Private;
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
1871
2362
|
|
|
1872
2363
|
// src/hooks/useWaitForCommit.ts
|
|
1873
|
-
import { useCallback as
|
|
1874
|
-
import { TransactionFilter as
|
|
2364
|
+
import { useCallback as useCallback14 } from "react";
|
|
2365
|
+
import { TransactionFilter as TransactionFilter3 } from "@miden-sdk/miden-sdk";
|
|
1875
2366
|
function useWaitForCommit() {
|
|
1876
2367
|
const { client, isReady, runExclusive } = useMiden();
|
|
1877
2368
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1878
|
-
const waitForCommit =
|
|
2369
|
+
const waitForCommit = useCallback14(
|
|
1879
2370
|
async (txId, options) => {
|
|
1880
2371
|
if (!client || !isReady) {
|
|
1881
2372
|
throw new Error("Miden client is not ready");
|
|
1882
2373
|
}
|
|
1883
2374
|
const timeoutMs = Math.max(0, options?.timeoutMs ?? 1e4);
|
|
1884
2375
|
const intervalMs = Math.max(1, options?.intervalMs ?? 1e3);
|
|
1885
|
-
const targetHex =
|
|
2376
|
+
const targetHex = normalizeHex3(
|
|
1886
2377
|
typeof txId === "string" ? txId : txId.toHex()
|
|
1887
2378
|
);
|
|
1888
|
-
|
|
1889
|
-
while (
|
|
2379
|
+
const deadline = Date.now() + timeoutMs;
|
|
2380
|
+
while (Date.now() < deadline) {
|
|
1890
2381
|
await runExclusiveSafe(
|
|
1891
2382
|
() => client.syncState()
|
|
1892
2383
|
);
|
|
1893
2384
|
const records = await runExclusiveSafe(
|
|
1894
2385
|
() => client.getTransactions(
|
|
1895
|
-
typeof txId === "string" ?
|
|
2386
|
+
typeof txId === "string" ? TransactionFilter3.all() : TransactionFilter3.ids([txId])
|
|
1896
2387
|
)
|
|
1897
2388
|
);
|
|
1898
2389
|
const record = records.find(
|
|
1899
|
-
(item) =>
|
|
2390
|
+
(item) => normalizeHex3(item.id().toHex()) === targetHex
|
|
1900
2391
|
);
|
|
1901
2392
|
if (record) {
|
|
1902
2393
|
const status = record.transactionStatus();
|
|
@@ -1908,7 +2399,6 @@ function useWaitForCommit() {
|
|
|
1908
2399
|
}
|
|
1909
2400
|
}
|
|
1910
2401
|
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
1911
|
-
waited += intervalMs;
|
|
1912
2402
|
}
|
|
1913
2403
|
throw new Error("Timeout waiting for transaction commit");
|
|
1914
2404
|
},
|
|
@@ -1916,18 +2406,18 @@ function useWaitForCommit() {
|
|
|
1916
2406
|
);
|
|
1917
2407
|
return { waitForCommit };
|
|
1918
2408
|
}
|
|
1919
|
-
function
|
|
2409
|
+
function normalizeHex3(value) {
|
|
1920
2410
|
const trimmed = value.trim();
|
|
1921
2411
|
const normalized = trimmed.startsWith("0x") || trimmed.startsWith("0X") ? trimmed : `0x${trimmed}`;
|
|
1922
2412
|
return normalized.toLowerCase();
|
|
1923
2413
|
}
|
|
1924
2414
|
|
|
1925
2415
|
// src/hooks/useWaitForNotes.ts
|
|
1926
|
-
import { useCallback as
|
|
2416
|
+
import { useCallback as useCallback15 } from "react";
|
|
1927
2417
|
function useWaitForNotes() {
|
|
1928
2418
|
const { client, isReady, runExclusive } = useMiden();
|
|
1929
2419
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1930
|
-
const waitForConsumableNotes =
|
|
2420
|
+
const waitForConsumableNotes = useCallback15(
|
|
1931
2421
|
async (options) => {
|
|
1932
2422
|
if (!client || !isReady) {
|
|
1933
2423
|
throw new Error("Miden client is not ready");
|
|
@@ -1938,7 +2428,9 @@ function useWaitForNotes() {
|
|
|
1938
2428
|
const accountId = parseAccountId(options.accountId);
|
|
1939
2429
|
let waited = 0;
|
|
1940
2430
|
while (waited < timeoutMs) {
|
|
1941
|
-
await runExclusiveSafe(
|
|
2431
|
+
await runExclusiveSafe(
|
|
2432
|
+
() => client.syncState()
|
|
2433
|
+
);
|
|
1942
2434
|
const notes = await runExclusiveSafe(
|
|
1943
2435
|
() => client.getConsumableNotes(accountId)
|
|
1944
2436
|
);
|
|
@@ -1956,16 +2448,15 @@ function useWaitForNotes() {
|
|
|
1956
2448
|
}
|
|
1957
2449
|
|
|
1958
2450
|
// src/hooks/useMint.ts
|
|
1959
|
-
import { useCallback as
|
|
1960
|
-
import { NoteType as NoteType4 } from "@miden-sdk/miden-sdk";
|
|
2451
|
+
import { useCallback as useCallback16, useState as useState12 } from "react";
|
|
1961
2452
|
function useMint() {
|
|
1962
2453
|
const { client, isReady, sync, runExclusive, prover } = useMiden();
|
|
1963
2454
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
1964
|
-
const [result, setResult] =
|
|
1965
|
-
const [isLoading, setIsLoading] =
|
|
1966
|
-
const [stage, setStage] =
|
|
1967
|
-
const [error, setError] =
|
|
1968
|
-
const mint =
|
|
2455
|
+
const [result, setResult] = useState12(null);
|
|
2456
|
+
const [isLoading, setIsLoading] = useState12(false);
|
|
2457
|
+
const [stage, setStage] = useState12("idle");
|
|
2458
|
+
const [error, setError] = useState12(null);
|
|
2459
|
+
const mint = useCallback16(
|
|
1969
2460
|
async (options) => {
|
|
1970
2461
|
if (!client || !isReady) {
|
|
1971
2462
|
throw new Error("Miden client is not ready");
|
|
@@ -1974,7 +2465,7 @@ function useMint() {
|
|
|
1974
2465
|
setStage("executing");
|
|
1975
2466
|
setError(null);
|
|
1976
2467
|
try {
|
|
1977
|
-
const noteType =
|
|
2468
|
+
const noteType = getNoteType(options.noteType ?? DEFAULTS.NOTE_TYPE);
|
|
1978
2469
|
const targetAccountIdObj = parseAccountId(options.targetAccountId);
|
|
1979
2470
|
const faucetIdObj = parseAccountId(options.faucetId);
|
|
1980
2471
|
setStage("proving");
|
|
@@ -2007,7 +2498,7 @@ function useMint() {
|
|
|
2007
2498
|
},
|
|
2008
2499
|
[client, isReady, prover, runExclusive, sync]
|
|
2009
2500
|
);
|
|
2010
|
-
const reset =
|
|
2501
|
+
const reset = useCallback16(() => {
|
|
2011
2502
|
setResult(null);
|
|
2012
2503
|
setIsLoading(false);
|
|
2013
2504
|
setStage("idle");
|
|
@@ -2022,30 +2513,18 @@ function useMint() {
|
|
|
2022
2513
|
reset
|
|
2023
2514
|
};
|
|
2024
2515
|
}
|
|
2025
|
-
function getNoteType4(type) {
|
|
2026
|
-
switch (type) {
|
|
2027
|
-
case "private":
|
|
2028
|
-
return NoteType4.Private;
|
|
2029
|
-
case "public":
|
|
2030
|
-
return NoteType4.Public;
|
|
2031
|
-
case "encrypted":
|
|
2032
|
-
return NoteType4.Encrypted;
|
|
2033
|
-
default:
|
|
2034
|
-
return NoteType4.Private;
|
|
2035
|
-
}
|
|
2036
|
-
}
|
|
2037
2516
|
|
|
2038
2517
|
// src/hooks/useConsume.ts
|
|
2039
|
-
import { useCallback as
|
|
2040
|
-
import { NoteFilter as
|
|
2518
|
+
import { useCallback as useCallback17, useState as useState13 } from "react";
|
|
2519
|
+
import { NoteFilter as NoteFilter3, NoteFilterTypes as NoteFilterTypes2, NoteId } from "@miden-sdk/miden-sdk";
|
|
2041
2520
|
function useConsume() {
|
|
2042
2521
|
const { client, isReady, sync, runExclusive, prover } = useMiden();
|
|
2043
2522
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
2044
|
-
const [result, setResult] =
|
|
2045
|
-
const [isLoading, setIsLoading] =
|
|
2046
|
-
const [stage, setStage] =
|
|
2047
|
-
const [error, setError] =
|
|
2048
|
-
const consume =
|
|
2523
|
+
const [result, setResult] = useState13(null);
|
|
2524
|
+
const [isLoading, setIsLoading] = useState13(false);
|
|
2525
|
+
const [stage, setStage] = useState13("idle");
|
|
2526
|
+
const [error, setError] = useState13(null);
|
|
2527
|
+
const consume = useCallback17(
|
|
2049
2528
|
async (options) => {
|
|
2050
2529
|
if (!client || !isReady) {
|
|
2051
2530
|
throw new Error("Miden client is not ready");
|
|
@@ -2063,7 +2542,7 @@ function useConsume() {
|
|
|
2063
2542
|
const noteIds = options.noteIds.map(
|
|
2064
2543
|
(noteId) => NoteId.fromHex(noteId)
|
|
2065
2544
|
);
|
|
2066
|
-
const filter = new
|
|
2545
|
+
const filter = new NoteFilter3(NoteFilterTypes2.List, noteIds);
|
|
2067
2546
|
const noteRecords = await client.getInputNotes(filter);
|
|
2068
2547
|
const notes = noteRecords.map((record) => record.toNote());
|
|
2069
2548
|
if (notes.length === 0) {
|
|
@@ -2095,7 +2574,7 @@ function useConsume() {
|
|
|
2095
2574
|
},
|
|
2096
2575
|
[client, isReady, prover, runExclusive, sync]
|
|
2097
2576
|
);
|
|
2098
|
-
const reset =
|
|
2577
|
+
const reset = useCallback17(() => {
|
|
2099
2578
|
setResult(null);
|
|
2100
2579
|
setIsLoading(false);
|
|
2101
2580
|
setStage("idle");
|
|
@@ -2112,16 +2591,15 @@ function useConsume() {
|
|
|
2112
2591
|
}
|
|
2113
2592
|
|
|
2114
2593
|
// src/hooks/useSwap.ts
|
|
2115
|
-
import { useCallback as
|
|
2116
|
-
import { NoteType as NoteType5 } from "@miden-sdk/miden-sdk";
|
|
2594
|
+
import { useCallback as useCallback18, useState as useState14 } from "react";
|
|
2117
2595
|
function useSwap() {
|
|
2118
2596
|
const { client, isReady, sync, runExclusive, prover } = useMiden();
|
|
2119
2597
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
2120
|
-
const [result, setResult] =
|
|
2121
|
-
const [isLoading, setIsLoading] =
|
|
2122
|
-
const [stage, setStage] =
|
|
2123
|
-
const [error, setError] =
|
|
2124
|
-
const swap =
|
|
2598
|
+
const [result, setResult] = useState14(null);
|
|
2599
|
+
const [isLoading, setIsLoading] = useState14(false);
|
|
2600
|
+
const [stage, setStage] = useState14("idle");
|
|
2601
|
+
const [error, setError] = useState14(null);
|
|
2602
|
+
const swap = useCallback18(
|
|
2125
2603
|
async (options) => {
|
|
2126
2604
|
if (!client || !isReady) {
|
|
2127
2605
|
throw new Error("Miden client is not ready");
|
|
@@ -2130,8 +2608,8 @@ function useSwap() {
|
|
|
2130
2608
|
setStage("executing");
|
|
2131
2609
|
setError(null);
|
|
2132
2610
|
try {
|
|
2133
|
-
const noteType =
|
|
2134
|
-
const paybackNoteType =
|
|
2611
|
+
const noteType = getNoteType(options.noteType ?? DEFAULTS.NOTE_TYPE);
|
|
2612
|
+
const paybackNoteType = getNoteType(
|
|
2135
2613
|
options.paybackNoteType ?? DEFAULTS.NOTE_TYPE
|
|
2136
2614
|
);
|
|
2137
2615
|
const accountIdObj = parseAccountId(options.accountId);
|
|
@@ -2170,7 +2648,7 @@ function useSwap() {
|
|
|
2170
2648
|
},
|
|
2171
2649
|
[client, isReady, prover, runExclusive, sync]
|
|
2172
2650
|
);
|
|
2173
|
-
const reset =
|
|
2651
|
+
const reset = useCallback18(() => {
|
|
2174
2652
|
setResult(null);
|
|
2175
2653
|
setIsLoading(false);
|
|
2176
2654
|
setStage("idle");
|
|
@@ -2185,41 +2663,40 @@ function useSwap() {
|
|
|
2185
2663
|
reset
|
|
2186
2664
|
};
|
|
2187
2665
|
}
|
|
2188
|
-
function getNoteType5(type) {
|
|
2189
|
-
switch (type) {
|
|
2190
|
-
case "private":
|
|
2191
|
-
return NoteType5.Private;
|
|
2192
|
-
case "public":
|
|
2193
|
-
return NoteType5.Public;
|
|
2194
|
-
case "encrypted":
|
|
2195
|
-
return NoteType5.Encrypted;
|
|
2196
|
-
default:
|
|
2197
|
-
return NoteType5.Private;
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2200
2666
|
|
|
2201
2667
|
// src/hooks/useTransaction.ts
|
|
2202
|
-
import { useCallback as
|
|
2668
|
+
import { useCallback as useCallback19, useRef as useRef5, useState as useState15 } from "react";
|
|
2203
2669
|
function useTransaction() {
|
|
2204
2670
|
const { client, isReady, sync, runExclusive, prover } = useMiden();
|
|
2205
2671
|
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
2206
|
-
const
|
|
2207
|
-
const [
|
|
2208
|
-
const [
|
|
2209
|
-
const [
|
|
2210
|
-
const
|
|
2672
|
+
const isBusyRef = useRef5(false);
|
|
2673
|
+
const [result, setResult] = useState15(null);
|
|
2674
|
+
const [isLoading, setIsLoading] = useState15(false);
|
|
2675
|
+
const [stage, setStage] = useState15("idle");
|
|
2676
|
+
const [error, setError] = useState15(null);
|
|
2677
|
+
const execute = useCallback19(
|
|
2211
2678
|
async (options) => {
|
|
2212
2679
|
if (!client || !isReady) {
|
|
2213
2680
|
throw new Error("Miden client is not ready");
|
|
2214
2681
|
}
|
|
2682
|
+
if (isBusyRef.current) {
|
|
2683
|
+
throw new MidenError(
|
|
2684
|
+
"A transaction is already in progress. Await the previous transaction before starting another.",
|
|
2685
|
+
{ code: "SEND_BUSY" }
|
|
2686
|
+
);
|
|
2687
|
+
}
|
|
2688
|
+
isBusyRef.current = true;
|
|
2215
2689
|
setIsLoading(true);
|
|
2216
2690
|
setStage("executing");
|
|
2217
2691
|
setError(null);
|
|
2218
2692
|
try {
|
|
2693
|
+
if (!options.skipSync) {
|
|
2694
|
+
await sync();
|
|
2695
|
+
}
|
|
2696
|
+
const txRequest = await resolveRequest(options.request, client);
|
|
2219
2697
|
setStage("proving");
|
|
2220
2698
|
const txResult = await runExclusiveSafe(async () => {
|
|
2221
2699
|
const accountIdObj = resolveAccountId2(options.accountId);
|
|
2222
|
-
const txRequest = await resolveRequest(options.request, client);
|
|
2223
2700
|
const txId = prover ? await client.submitNewTransactionWithProver(
|
|
2224
2701
|
accountIdObj,
|
|
2225
2702
|
txRequest,
|
|
@@ -2238,11 +2715,12 @@ function useTransaction() {
|
|
|
2238
2715
|
throw error2;
|
|
2239
2716
|
} finally {
|
|
2240
2717
|
setIsLoading(false);
|
|
2718
|
+
isBusyRef.current = false;
|
|
2241
2719
|
}
|
|
2242
2720
|
},
|
|
2243
2721
|
[client, isReady, prover, runExclusive, sync]
|
|
2244
2722
|
);
|
|
2245
|
-
const reset =
|
|
2723
|
+
const reset = useCallback19(() => {
|
|
2246
2724
|
setResult(null);
|
|
2247
2725
|
setIsLoading(false);
|
|
2248
2726
|
setStage("idle");
|
|
@@ -2267,16 +2745,337 @@ async function resolveRequest(request, client) {
|
|
|
2267
2745
|
return request;
|
|
2268
2746
|
}
|
|
2269
2747
|
|
|
2748
|
+
// src/hooks/useSessionAccount.ts
|
|
2749
|
+
import { useCallback as useCallback20, useEffect as useEffect8, useRef as useRef6, useState as useState16 } from "react";
|
|
2750
|
+
import { AccountStorageMode as AccountStorageMode3 } from "@miden-sdk/miden-sdk";
|
|
2751
|
+
function useSessionAccount(options) {
|
|
2752
|
+
const { client, isReady, sync, runExclusive } = useMiden();
|
|
2753
|
+
const runExclusiveSafe = runExclusive ?? runExclusiveDirect;
|
|
2754
|
+
const setAccounts = useMidenStore((state) => state.setAccounts);
|
|
2755
|
+
const [sessionAccountId, setSessionAccountId] = useState16(null);
|
|
2756
|
+
const [step, setStep] = useState16("idle");
|
|
2757
|
+
const [error, setError] = useState16(null);
|
|
2758
|
+
const cancelledRef = useRef6(false);
|
|
2759
|
+
const isBusyRef = useRef6(false);
|
|
2760
|
+
const storagePrefix = options.storagePrefix ?? "miden-session";
|
|
2761
|
+
const pollIntervalMs = options.pollIntervalMs ?? 3e3;
|
|
2762
|
+
const maxWaitMs = options.maxWaitMs ?? 6e4;
|
|
2763
|
+
const storageMode = options.walletOptions?.storageMode ?? "public";
|
|
2764
|
+
const mutable = options.walletOptions?.mutable ?? DEFAULTS.WALLET_MUTABLE;
|
|
2765
|
+
const authScheme = options.walletOptions?.authScheme ?? DEFAULTS.AUTH_SCHEME;
|
|
2766
|
+
const fundRef = useRef6(options.fund);
|
|
2767
|
+
fundRef.current = options.fund;
|
|
2768
|
+
useEffect8(() => {
|
|
2769
|
+
try {
|
|
2770
|
+
const stored = localStorage.getItem(`${storagePrefix}:accountId`);
|
|
2771
|
+
const storedReady = localStorage.getItem(`${storagePrefix}:ready`);
|
|
2772
|
+
if (stored && stored.length > 0) {
|
|
2773
|
+
const validationId = parseAccountId(stored);
|
|
2774
|
+
validationId?.free?.();
|
|
2775
|
+
setSessionAccountId(stored);
|
|
2776
|
+
if (storedReady === "true") {
|
|
2777
|
+
setStep("ready");
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
} catch {
|
|
2781
|
+
localStorage.removeItem(`${storagePrefix}:accountId`);
|
|
2782
|
+
localStorage.removeItem(`${storagePrefix}:ready`);
|
|
2783
|
+
}
|
|
2784
|
+
}, [storagePrefix]);
|
|
2785
|
+
const initialize = useCallback20(async () => {
|
|
2786
|
+
if (!client || !isReady) {
|
|
2787
|
+
throw new Error("Miden client is not ready");
|
|
2788
|
+
}
|
|
2789
|
+
if (isBusyRef.current) {
|
|
2790
|
+
throw new MidenError(
|
|
2791
|
+
"Session account initialization is already in progress.",
|
|
2792
|
+
{ code: "OPERATION_BUSY" }
|
|
2793
|
+
);
|
|
2794
|
+
}
|
|
2795
|
+
isBusyRef.current = true;
|
|
2796
|
+
cancelledRef.current = false;
|
|
2797
|
+
setError(null);
|
|
2798
|
+
try {
|
|
2799
|
+
let walletId = sessionAccountId;
|
|
2800
|
+
if (!walletId) {
|
|
2801
|
+
setStep("creating");
|
|
2802
|
+
const resolvedStorageMode = getStorageMode3(storageMode);
|
|
2803
|
+
const wallet = await runExclusiveSafe(async () => {
|
|
2804
|
+
const w = await client.newWallet(
|
|
2805
|
+
resolvedStorageMode,
|
|
2806
|
+
mutable,
|
|
2807
|
+
authScheme
|
|
2808
|
+
);
|
|
2809
|
+
ensureAccountBech32(w);
|
|
2810
|
+
const accounts = await client.getAccounts();
|
|
2811
|
+
setAccounts(accounts);
|
|
2812
|
+
return w;
|
|
2813
|
+
});
|
|
2814
|
+
if (cancelledRef.current) return;
|
|
2815
|
+
walletId = wallet.id().toString();
|
|
2816
|
+
setSessionAccountId(walletId);
|
|
2817
|
+
localStorage.setItem(`${storagePrefix}:accountId`, walletId);
|
|
2818
|
+
}
|
|
2819
|
+
setStep("funding");
|
|
2820
|
+
await fundRef.current(walletId);
|
|
2821
|
+
if (cancelledRef.current) return;
|
|
2822
|
+
setStep("consuming");
|
|
2823
|
+
await waitAndConsume(
|
|
2824
|
+
client,
|
|
2825
|
+
runExclusiveSafe,
|
|
2826
|
+
walletId,
|
|
2827
|
+
pollIntervalMs,
|
|
2828
|
+
maxWaitMs,
|
|
2829
|
+
cancelledRef
|
|
2830
|
+
);
|
|
2831
|
+
if (cancelledRef.current) return;
|
|
2832
|
+
setStep("ready");
|
|
2833
|
+
localStorage.setItem(`${storagePrefix}:ready`, "true");
|
|
2834
|
+
await sync();
|
|
2835
|
+
} catch (err) {
|
|
2836
|
+
if (!cancelledRef.current) {
|
|
2837
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
2838
|
+
setError(error2);
|
|
2839
|
+
setStep("idle");
|
|
2840
|
+
throw error2;
|
|
2841
|
+
}
|
|
2842
|
+
} finally {
|
|
2843
|
+
isBusyRef.current = false;
|
|
2844
|
+
}
|
|
2845
|
+
}, [
|
|
2846
|
+
client,
|
|
2847
|
+
isReady,
|
|
2848
|
+
sync,
|
|
2849
|
+
runExclusiveSafe,
|
|
2850
|
+
sessionAccountId,
|
|
2851
|
+
storageMode,
|
|
2852
|
+
mutable,
|
|
2853
|
+
authScheme,
|
|
2854
|
+
storagePrefix,
|
|
2855
|
+
pollIntervalMs,
|
|
2856
|
+
maxWaitMs,
|
|
2857
|
+
setAccounts
|
|
2858
|
+
]);
|
|
2859
|
+
const reset = useCallback20(() => {
|
|
2860
|
+
cancelledRef.current = true;
|
|
2861
|
+
isBusyRef.current = false;
|
|
2862
|
+
setSessionAccountId(null);
|
|
2863
|
+
setStep("idle");
|
|
2864
|
+
setError(null);
|
|
2865
|
+
localStorage.removeItem(`${storagePrefix}:accountId`);
|
|
2866
|
+
localStorage.removeItem(`${storagePrefix}:ready`);
|
|
2867
|
+
}, [storagePrefix]);
|
|
2868
|
+
return {
|
|
2869
|
+
initialize,
|
|
2870
|
+
sessionAccountId,
|
|
2871
|
+
isReady: step === "ready",
|
|
2872
|
+
step,
|
|
2873
|
+
error,
|
|
2874
|
+
reset
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
2877
|
+
function getStorageMode3(mode) {
|
|
2878
|
+
switch (mode) {
|
|
2879
|
+
case "private":
|
|
2880
|
+
return AccountStorageMode3.private();
|
|
2881
|
+
case "public":
|
|
2882
|
+
return AccountStorageMode3.public();
|
|
2883
|
+
default:
|
|
2884
|
+
return AccountStorageMode3.public();
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
async function waitAndConsume(client, runExclusiveSafe, walletId, pollIntervalMs, maxWaitMs, cancelledRef) {
|
|
2888
|
+
const deadline = Date.now() + maxWaitMs;
|
|
2889
|
+
while (Date.now() < deadline) {
|
|
2890
|
+
if (cancelledRef.current) return;
|
|
2891
|
+
await runExclusiveSafe(
|
|
2892
|
+
() => client.syncState()
|
|
2893
|
+
);
|
|
2894
|
+
if (cancelledRef.current) return;
|
|
2895
|
+
const consumed = await runExclusiveSafe(async () => {
|
|
2896
|
+
const accountIdObj = parseAccountId(walletId);
|
|
2897
|
+
const consumable = await client.getConsumableNotes(accountIdObj);
|
|
2898
|
+
if (consumable.length === 0) return false;
|
|
2899
|
+
const notes = consumable.map((c) => c.inputNoteRecord().toNote());
|
|
2900
|
+
const txRequest = client.newConsumeTransactionRequest(notes);
|
|
2901
|
+
const freshAccountId = parseAccountId(walletId);
|
|
2902
|
+
await client.submitNewTransaction(freshAccountId, txRequest);
|
|
2903
|
+
return true;
|
|
2904
|
+
});
|
|
2905
|
+
if (consumed) return;
|
|
2906
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
2907
|
+
}
|
|
2908
|
+
throw new Error("Timeout waiting for session wallet funding");
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2911
|
+
// src/utils/bytes.ts
|
|
2912
|
+
function bytesToBigInt(bytes) {
|
|
2913
|
+
let result = 0n;
|
|
2914
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2915
|
+
result = result << 8n | BigInt(bytes[i]);
|
|
2916
|
+
}
|
|
2917
|
+
return result;
|
|
2918
|
+
}
|
|
2919
|
+
function bigIntToBytes(value, length) {
|
|
2920
|
+
if (value < 0n) {
|
|
2921
|
+
throw new RangeError("bigIntToBytes: value must be non-negative");
|
|
2922
|
+
}
|
|
2923
|
+
const maxValue = (1n << BigInt(length * 8)) - 1n;
|
|
2924
|
+
if (value > maxValue) {
|
|
2925
|
+
throw new RangeError(
|
|
2926
|
+
`bigIntToBytes: value ${value} does not fit in ${length} byte(s) (max ${maxValue})`
|
|
2927
|
+
);
|
|
2928
|
+
}
|
|
2929
|
+
const bytes = new Uint8Array(length);
|
|
2930
|
+
let remaining = value;
|
|
2931
|
+
for (let i = length - 1; i >= 0; i--) {
|
|
2932
|
+
bytes[i] = Number(remaining & 0xffn);
|
|
2933
|
+
remaining >>= 8n;
|
|
2934
|
+
}
|
|
2935
|
+
return bytes;
|
|
2936
|
+
}
|
|
2937
|
+
function concatBytes(...arrays) {
|
|
2938
|
+
const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
|
|
2939
|
+
const result = new Uint8Array(totalLength);
|
|
2940
|
+
let offset = 0;
|
|
2941
|
+
for (const arr of arrays) {
|
|
2942
|
+
result.set(arr, offset);
|
|
2943
|
+
offset += arr.length;
|
|
2944
|
+
}
|
|
2945
|
+
return result;
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
// src/utils/walletDetection.ts
|
|
2949
|
+
async function waitForWalletDetection(adapter, timeoutMs = 5e3) {
|
|
2950
|
+
if (adapter.readyState === "Installed") return;
|
|
2951
|
+
return new Promise((resolve, reject) => {
|
|
2952
|
+
let settled = false;
|
|
2953
|
+
const settle = () => {
|
|
2954
|
+
if (settled) return;
|
|
2955
|
+
settled = true;
|
|
2956
|
+
clearTimeout(timer);
|
|
2957
|
+
adapter.off("readyStateChange", onReady);
|
|
2958
|
+
resolve();
|
|
2959
|
+
};
|
|
2960
|
+
const timer = setTimeout(() => {
|
|
2961
|
+
if (settled) return;
|
|
2962
|
+
settled = true;
|
|
2963
|
+
adapter.off("readyStateChange", onReady);
|
|
2964
|
+
reject(
|
|
2965
|
+
new Error(
|
|
2966
|
+
`Wallet extension not detected within ${timeoutMs}ms. Is the browser extension installed and enabled?`
|
|
2967
|
+
)
|
|
2968
|
+
);
|
|
2969
|
+
}, timeoutMs);
|
|
2970
|
+
const onReady = (state) => {
|
|
2971
|
+
if (state === "Installed") settle();
|
|
2972
|
+
};
|
|
2973
|
+
adapter.on("readyStateChange", onReady);
|
|
2974
|
+
if (adapter.readyState === "Installed") settle();
|
|
2975
|
+
});
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2978
|
+
// src/utils/storage.ts
|
|
2979
|
+
async function migrateStorage(options) {
|
|
2980
|
+
if (typeof window === "undefined") return false;
|
|
2981
|
+
const versionKey = options.versionKey ?? "miden:storageVersion";
|
|
2982
|
+
const stored = localStorage.getItem(versionKey);
|
|
2983
|
+
if (stored === options.version) return false;
|
|
2984
|
+
if (options.onBeforeClear) {
|
|
2985
|
+
await options.onBeforeClear();
|
|
2986
|
+
}
|
|
2987
|
+
await clearMidenStorage();
|
|
2988
|
+
localStorage.setItem(versionKey, options.version);
|
|
2989
|
+
if (options.reloadOnClear !== false) {
|
|
2990
|
+
window.location.reload();
|
|
2991
|
+
}
|
|
2992
|
+
return true;
|
|
2993
|
+
}
|
|
2994
|
+
async function clearMidenStorage() {
|
|
2995
|
+
if (typeof indexedDB === "undefined") return;
|
|
2996
|
+
try {
|
|
2997
|
+
const databases = await indexedDB.databases();
|
|
2998
|
+
const midenDbs = databases.filter(
|
|
2999
|
+
(db) => db.name?.toLowerCase().includes("miden")
|
|
3000
|
+
);
|
|
3001
|
+
await Promise.all(
|
|
3002
|
+
midenDbs.map(
|
|
3003
|
+
(db) => new Promise((resolve, reject) => {
|
|
3004
|
+
if (!db.name) {
|
|
3005
|
+
resolve();
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
3008
|
+
const req = indexedDB.deleteDatabase(db.name);
|
|
3009
|
+
req.onsuccess = () => resolve();
|
|
3010
|
+
req.onerror = () => reject(req.error);
|
|
3011
|
+
req.onblocked = () => {
|
|
3012
|
+
console.warn(
|
|
3013
|
+
`IndexedDB "${db.name}" delete was blocked \u2014 close other tabs using this database.`
|
|
3014
|
+
);
|
|
3015
|
+
resolve();
|
|
3016
|
+
};
|
|
3017
|
+
})
|
|
3018
|
+
)
|
|
3019
|
+
);
|
|
3020
|
+
} catch {
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
function createMidenStorage(prefix) {
|
|
3024
|
+
const fullKey = (key) => `${prefix}:${key}`;
|
|
3025
|
+
return {
|
|
3026
|
+
get(key) {
|
|
3027
|
+
try {
|
|
3028
|
+
const raw = localStorage.getItem(fullKey(key));
|
|
3029
|
+
if (raw === null) return null;
|
|
3030
|
+
return JSON.parse(raw);
|
|
3031
|
+
} catch {
|
|
3032
|
+
return null;
|
|
3033
|
+
}
|
|
3034
|
+
},
|
|
3035
|
+
set(key, value) {
|
|
3036
|
+
try {
|
|
3037
|
+
localStorage.setItem(fullKey(key), JSON.stringify(value));
|
|
3038
|
+
} catch (e) {
|
|
3039
|
+
console.warn(`Failed to write localStorage key "${fullKey(key)}":`, e);
|
|
3040
|
+
}
|
|
3041
|
+
},
|
|
3042
|
+
remove(key) {
|
|
3043
|
+
localStorage.removeItem(fullKey(key));
|
|
3044
|
+
},
|
|
3045
|
+
clear() {
|
|
3046
|
+
const keysToRemove = [];
|
|
3047
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
3048
|
+
const k = localStorage.key(i);
|
|
3049
|
+
if (k?.startsWith(`${prefix}:`)) {
|
|
3050
|
+
keysToRemove.push(k);
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
keysToRemove.forEach((k) => localStorage.removeItem(k));
|
|
3054
|
+
}
|
|
3055
|
+
};
|
|
3056
|
+
}
|
|
3057
|
+
|
|
2270
3058
|
// src/index.ts
|
|
2271
3059
|
installAccountBech32();
|
|
2272
3060
|
export {
|
|
2273
3061
|
DEFAULTS,
|
|
3062
|
+
MidenError,
|
|
2274
3063
|
MidenProvider,
|
|
2275
3064
|
SignerContext,
|
|
3065
|
+
accountIdsEqual,
|
|
3066
|
+
bigIntToBytes,
|
|
3067
|
+
bytesToBigInt,
|
|
3068
|
+
clearMidenStorage,
|
|
3069
|
+
concatBytes,
|
|
3070
|
+
createMidenStorage,
|
|
3071
|
+
createNoteAttachment,
|
|
2276
3072
|
formatAssetAmount,
|
|
2277
3073
|
formatNoteSummary,
|
|
2278
3074
|
getNoteSummary,
|
|
3075
|
+
migrateStorage,
|
|
3076
|
+
normalizeAccountId,
|
|
2279
3077
|
parseAssetAmount,
|
|
3078
|
+
readNoteAttachment,
|
|
2280
3079
|
toBech32AccountId,
|
|
2281
3080
|
useAccount,
|
|
2282
3081
|
useAccounts,
|
|
@@ -2290,13 +3089,17 @@ export {
|
|
|
2290
3089
|
useMidenClient,
|
|
2291
3090
|
useMint,
|
|
2292
3091
|
useMultiSend,
|
|
3092
|
+
useNoteStream,
|
|
2293
3093
|
useNotes,
|
|
2294
3094
|
useSend,
|
|
3095
|
+
useSessionAccount,
|
|
2295
3096
|
useSigner,
|
|
2296
3097
|
useSwap,
|
|
2297
3098
|
useSyncState,
|
|
2298
3099
|
useTransaction,
|
|
2299
3100
|
useTransactionHistory,
|
|
2300
3101
|
useWaitForCommit,
|
|
2301
|
-
useWaitForNotes
|
|
3102
|
+
useWaitForNotes,
|
|
3103
|
+
waitForWalletDetection,
|
|
3104
|
+
wrapWasmError
|
|
2302
3105
|
};
|