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