@miden-sdk/miden-sdk 0.13.2 → 0.14.0-alpha

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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import loadWasm from './wasm.js';
2
- export { Account, AccountArray, AccountBuilder, AccountBuilderResult, AccountCode, AccountComponent, AccountComponentCode, AccountDelta, AccountFile, AccountHeader, AccountId, AccountIdArray, AccountInterface, AccountStorage, AccountStorageDelta, AccountStorageMode, AccountStorageRequirements, AccountType, AccountVaultDelta, Address, AdviceInputs, AdviceMap, AssetVault, AuthFalcon512RpoMultisigConfig, AuthScheme, AuthSecretKey, BasicFungibleFaucetComponent, BlockHeader, CodeBuilder, CommittedNote, ConsumableNoteRecord, Endpoint, ExecutedTransaction, Felt, FeltArray, FetchedAccount, FetchedNote, FlattenedU8Vec, ForeignAccount, ForeignAccountArray, FungibleAsset, FungibleAssetDelta, FungibleAssetDeltaItem, GetProceduresResultItem, InputNote, InputNoteRecord, InputNoteState, InputNotes, IntoUnderlyingByteSource, IntoUnderlyingSink, IntoUnderlyingSource, JsAccountUpdate, JsStateSyncUpdate, JsStorageMapEntry, JsStorageSlot, JsVaultAsset, Library, MerklePath, NetworkId, NetworkType, Note, NoteAndArgs, NoteAndArgsArray, NoteAssets, NoteAttachment, NoteAttachmentKind, NoteAttachmentScheme, NoteConsumability, NoteConsumptionStatus, NoteDetails, NoteDetailsAndTag, NoteDetailsAndTagArray, NoteExecutionHint, NoteFile, NoteFilter, NoteFilterTypes, NoteHeader, NoteId, NoteIdAndArgs, NoteIdAndArgsArray, NoteInclusionProof, NoteInputs, NoteLocation, NoteMetadata, NoteRecipient, NoteRecipientArray, NoteScript, NoteSyncInfo, NoteTag, NoteType, OutputNote, OutputNoteArray, OutputNoteRecord, OutputNoteState, OutputNotes, OutputNotesArray, Package, PartialNote, ProcedureThreshold, Program, ProvenTransaction, PublicKey, RpcClient, Rpo256, SerializedInputNoteData, SerializedOutputNoteData, SerializedTransactionData, Signature, SigningInputs, SigningInputsType, SlotAndKeys, SparseMerklePath, StorageMap, StorageSlot, StorageSlotArray, SyncSummary, TestUtils, TokenSymbol, TransactionArgs, TransactionFilter, TransactionId, TransactionProver, TransactionRecord, TransactionRequest, TransactionRequestBuilder, TransactionResult, TransactionScript, TransactionScriptInputPair, TransactionScriptInputPairArray, TransactionStatus, TransactionStoreUpdate, TransactionSummary, Word, createAuthFalcon512RpoMultisig, initSync, setupLogging } from './Cargo-e77f9a02.js';
2
+ export { Account, AccountArray, AccountBuilder, AccountBuilderResult, AccountCode, AccountComponent, AccountComponentCode, AccountDelta, AccountFile, AccountHeader, AccountId, AccountIdArray, AccountInterface, AccountProof, AccountReader, AccountStatus, AccountStorage, AccountStorageDelta, AccountStorageMode, AccountStorageRequirements, AccountVaultDelta, Address, AdviceInputs, AdviceMap, AssetVault, AuthFalcon512RpoMultisigConfig, AuthSecretKey, BasicFungibleFaucetComponent, BlockHeader, CodeBuilder, CommittedNote, ConsumableNoteRecord, Endpoint, ExecutedTransaction, Felt, FeltArray, FetchedAccount, FetchedNote, FlattenedU8Vec, ForeignAccount, ForeignAccountArray, FungibleAsset, FungibleAssetDelta, FungibleAssetDeltaItem, GetProceduresResultItem, InputNote, InputNoteRecord, InputNoteState, InputNotes, IntoUnderlyingByteSource, IntoUnderlyingSink, IntoUnderlyingSource, JsAccountUpdate, JsStateSyncUpdate, JsStorageMapEntry, JsStorageSlot, JsVaultAsset, Library, MerklePath, NetworkId, NetworkType, Note, NoteAndArgs, NoteAndArgsArray, NoteAssets, NoteAttachment, NoteAttachmentKind, NoteAttachmentScheme, NoteConsumability, NoteConsumptionStatus, NoteDetails, NoteDetailsAndTag, NoteDetailsAndTagArray, NoteExecutionHint, NoteExportFormat, NoteFile, NoteFilter, NoteFilterTypes, NoteHeader, NoteId, NoteIdAndArgs, NoteIdAndArgsArray, NoteInclusionProof, NoteLocation, NoteMetadata, NoteRecipient, NoteRecipientArray, NoteScript, NoteStorage, NoteSyncInfo, NoteTag, NoteType, OutputNote, OutputNoteArray, OutputNoteRecord, OutputNoteState, OutputNotes, OutputNotesArray, Package, PartialNote, ProcedureThreshold, Program, ProvenTransaction, PublicKey, RpcClient, Rpo256, SerializedInputNoteData, SerializedOutputNoteData, SerializedTransactionData, Signature, SigningInputs, SigningInputsType, SlotAndKeys, SparseMerklePath, StorageMap, StorageSlot, StorageSlotArray, SyncSummary, TestUtils, TokenSymbol, TransactionArgs, TransactionFilter, TransactionId, TransactionProver, TransactionRecord, TransactionRequest, TransactionRequestBuilder, TransactionResult, TransactionScript, TransactionScriptInputPair, TransactionScriptInputPairArray, TransactionStatus, TransactionStoreUpdate, TransactionSummary, WebClient, Word, createAuthFalcon512RpoMultisig, initSync, setupLogging } from './Cargo-D064yzd4.js';
3
3
 
4
4
  const WorkerAction = Object.freeze({
5
5
  INIT: "init",
@@ -16,8 +16,7 @@ const CallbackType = Object.freeze({
16
16
 
17
17
  const MethodName = Object.freeze({
18
18
  CREATE_CLIENT: "createClient",
19
- NEW_WALLET: "newWallet",
20
- NEW_FAUCET: "newFaucet",
19
+ APPLY_TRANSACTION: "applyTransaction",
21
20
  EXECUTE_TRANSACTION: "executeTransaction",
22
21
  PROVE_TRANSACTION: "proveTransaction",
23
22
  SUBMIT_NEW_TRANSACTION: "submitNewTransaction",
@@ -239,13 +238,1210 @@ function releaseSyncLockWithError(dbId, error) {
239
238
  }
240
239
  }
241
240
 
241
+ /**
242
+ * Shared utility functions for the MidenClient resource classes.
243
+ * Each function accepts a `wasm` parameter (the WASM module) for constructing typed objects.
244
+ */
245
+
246
+ /**
247
+ * Resolves an AccountRef (string | Account | AccountId) to an AccountId.
248
+ *
249
+ * - Strings starting with `0x`/`0X` are parsed as hex via `AccountId.fromHex()`.
250
+ * - Other strings are parsed as bech32 via `AccountId.fromBech32()`.
251
+ * - Objects with an `.id()` method (Account) are resolved by calling `.id()`.
252
+ * - Otherwise, the value is assumed to be an AccountId pass-through.
253
+ *
254
+ * @param {string | Account | AccountId} ref - The account reference to resolve.
255
+ * @param {object} wasm - The WASM module.
256
+ * @returns {AccountId} The resolved AccountId.
257
+ */
258
+ function resolveAccountRef(ref, wasm) {
259
+ if (ref == null) {
260
+ throw new Error("Account reference cannot be null or undefined");
261
+ }
262
+ if (typeof ref === "string") {
263
+ if (ref.startsWith("0x") || ref.startsWith("0X")) {
264
+ return wasm.AccountId.fromHex(ref);
265
+ }
266
+ return wasm.AccountId.fromBech32(ref);
267
+ }
268
+ if (ref && typeof ref.id === "function") {
269
+ return ref.id();
270
+ }
271
+ return ref;
272
+ }
273
+
274
+ /**
275
+ * Resolves an AccountRef to a WASM Address object.
276
+ *
277
+ * - Strings starting with bech32 prefixes (`m`) are parsed via `Address.fromBech32()`.
278
+ * - Strings starting with `0x`/`0X` are parsed as hex AccountId, then wrapped in Address.
279
+ * - Account objects are resolved via `.id()` then wrapped in Address.
280
+ * - AccountId objects are wrapped in Address directly.
281
+ *
282
+ * @param {string | Account | AccountId} ref - The account reference to resolve.
283
+ * @param {object} wasm - The WASM module.
284
+ * @returns {Address} The resolved Address.
285
+ */
286
+ function resolveAddress(ref, wasm) {
287
+ if (ref == null) {
288
+ throw new Error("Address reference cannot be null or undefined");
289
+ }
290
+ if (typeof ref === "string") {
291
+ if (ref.startsWith("0x") || ref.startsWith("0X")) {
292
+ const accountId = wasm.AccountId.fromHex(ref);
293
+ return wasm.Address.fromAccountId(accountId, undefined);
294
+ }
295
+ return wasm.Address.fromBech32(ref);
296
+ }
297
+ if (ref && typeof ref.id === "function") {
298
+ const accountId = ref.id();
299
+ return wasm.Address.fromAccountId(accountId, undefined);
300
+ }
301
+ return wasm.Address.fromAccountId(ref, undefined);
302
+ }
303
+
304
+ /**
305
+ * Resolves a NoteVisibility string to a WASM NoteType value.
306
+ *
307
+ * @param {string | undefined} type - "public" or "private". Defaults to "public".
308
+ * @param {object} wasm - The WASM module.
309
+ * @returns {number} The NoteType enum value.
310
+ */
311
+ function resolveNoteType(type, wasm) {
312
+ if (type === "private") {
313
+ return wasm.NoteType.Private;
314
+ }
315
+ if (type === "public" || type == null) {
316
+ return wasm.NoteType.Public;
317
+ }
318
+ throw new Error(
319
+ `Unknown note type: "${type}". Expected "public" or "private".`
320
+ );
321
+ }
322
+
323
+ /**
324
+ * Resolves a storage mode string to a WASM AccountStorageMode instance.
325
+ *
326
+ * @param {string | undefined} mode - "private", "public", or "network". Defaults to "private".
327
+ * @param {object} wasm - The WASM module.
328
+ * @returns {AccountStorageMode} The storage mode instance.
329
+ */
330
+ function resolveStorageMode(mode, wasm) {
331
+ switch (mode) {
332
+ case "public":
333
+ return wasm.AccountStorageMode.public();
334
+ case "network":
335
+ return wasm.AccountStorageMode.network();
336
+ case "private":
337
+ case undefined:
338
+ case null:
339
+ return wasm.AccountStorageMode.private();
340
+ default:
341
+ throw new Error(
342
+ `Unknown storage mode: "${mode}". Expected "private", "public", or "network".`
343
+ );
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Resolves an auth scheme string to a WASM AuthScheme enum value.
349
+ *
350
+ * @param {string | undefined} scheme - "falcon" or "ecdsa". Defaults to "falcon".
351
+ * @param {object} wasm - The WASM module.
352
+ * @returns {number} The AuthScheme enum value.
353
+ */
354
+ function resolveAuthScheme(scheme, wasm) {
355
+ if (scheme === "ecdsa") {
356
+ return wasm.AuthScheme.AuthEcdsaK256Keccak;
357
+ }
358
+ if (scheme === "falcon" || scheme == null) {
359
+ return wasm.AuthScheme.AuthRpoFalcon512;
360
+ }
361
+ throw new Error(
362
+ `Unknown auth scheme: "${scheme}". Expected "falcon" or "ecdsa".`
363
+ );
364
+ }
365
+
366
+ /**
367
+ * Resolves a simplified AccountType string to a boolean `mutable` flag
368
+ * for the underlying WASM `newWallet()` / `importPublicAccountFromSeed()` calls.
369
+ *
370
+ * @param {string | undefined} accountType - "MutableWallet", "ImmutableWallet", or undefined.
371
+ * Defaults to mutable wallet when undefined.
372
+ * @returns {boolean} Whether the account code is mutable.
373
+ */
374
+ function resolveAccountMutability(accountType) {
375
+ if (accountType == null || accountType === "MutableWallet") {
376
+ return true;
377
+ }
378
+ if (accountType === "ImmutableWallet") {
379
+ return false;
380
+ }
381
+ throw new Error(
382
+ `Unknown wallet account type: "${accountType}". Expected "MutableWallet" or "ImmutableWallet".`
383
+ );
384
+ }
385
+
386
+ /**
387
+ * Hashes a seed value. Strings are hashed via SHA-256 to produce a 32-byte Uint8Array.
388
+ * Uint8Array values are passed through unchanged.
389
+ *
390
+ * @param {string | Uint8Array} seed - The seed to hash.
391
+ * @returns {Promise<Uint8Array>} The hashed seed.
392
+ */
393
+ async function hashSeed(seed) {
394
+ if (seed instanceof Uint8Array) {
395
+ return seed;
396
+ }
397
+ if (typeof seed === "string") {
398
+ const encoded = new TextEncoder().encode(seed);
399
+ const hash = await crypto.subtle.digest("SHA-256", encoded);
400
+ return new Uint8Array(hash);
401
+ }
402
+ throw new TypeError(
403
+ `Invalid seed type: expected string or Uint8Array, got ${typeof seed}`
404
+ );
405
+ }
406
+
407
+ class AccountsResource {
408
+ #inner;
409
+ #getWasm;
410
+ #client;
411
+
412
+ constructor(inner, getWasm, client) {
413
+ this.#inner = inner;
414
+ this.#getWasm = getWasm;
415
+ this.#client = client;
416
+ }
417
+
418
+ async create(opts) {
419
+ this.#client.assertNotTerminated();
420
+ const wasm = await this.#getWasm();
421
+
422
+ if (opts?.type === "FungibleFaucet") {
423
+ const storageMode = resolveStorageMode(opts.storage ?? "public", wasm);
424
+ const authScheme = resolveAuthScheme(opts.auth, wasm);
425
+ return await this.#inner.newFaucet(
426
+ storageMode,
427
+ false,
428
+ opts.symbol,
429
+ opts.decimals,
430
+ BigInt(opts.maxSupply),
431
+ authScheme
432
+ );
433
+ }
434
+
435
+ // Default: wallet (mutable or immutable based on type)
436
+ const mutable = resolveAccountMutability(opts?.type);
437
+ const storageMode = resolveStorageMode(opts?.storage ?? "private", wasm);
438
+ const authScheme = resolveAuthScheme(opts?.auth, wasm);
439
+ const seed = opts?.seed ? await hashSeed(opts.seed) : undefined;
440
+ return await this.#inner.newWallet(storageMode, mutable, authScheme, seed);
441
+ }
442
+
443
+ async get(ref) {
444
+ this.#client.assertNotTerminated();
445
+ const wasm = await this.#getWasm();
446
+ const id = resolveAccountRef(ref, wasm);
447
+ const account = await this.#inner.getAccount(id);
448
+ return account ?? null;
449
+ }
450
+
451
+ async list() {
452
+ this.#client.assertNotTerminated();
453
+ return await this.#inner.getAccounts();
454
+ }
455
+
456
+ async getDetails(ref) {
457
+ this.#client.assertNotTerminated();
458
+ const wasm = await this.#getWasm();
459
+ const id = resolveAccountRef(ref, wasm);
460
+ const account = await this.#inner.getAccount(id);
461
+ if (!account) {
462
+ throw new Error(`Account not found: ${id.toString()}`);
463
+ }
464
+ const keys = await this.#inner.getPublicKeyCommitmentsOfAccount(id);
465
+ return {
466
+ account,
467
+ vault: account.vault(),
468
+ storage: account.storage(),
469
+ code: account.code() ?? null,
470
+ keys,
471
+ };
472
+ }
473
+
474
+ async getBalance(accountRef, tokenRef) {
475
+ this.#client.assertNotTerminated();
476
+ const wasm = await this.#getWasm();
477
+ const accountId = resolveAccountRef(accountRef, wasm);
478
+ const faucetId = resolveAccountRef(tokenRef, wasm);
479
+ const reader = this.#inner.accountReader(accountId);
480
+ return await reader.getBalance(faucetId);
481
+ }
482
+
483
+ async import(input) {
484
+ this.#client.assertNotTerminated();
485
+ const wasm = await this.#getWasm();
486
+
487
+ if (typeof input === "string") {
488
+ // Import by ID (hex or bech32 string)
489
+ const id = resolveAccountRef(input, wasm);
490
+ await this.#inner.importAccountById(id);
491
+ return await this.#inner.getAccount(id);
492
+ }
493
+
494
+ if (input.file) {
495
+ // Extract accountId before importAccountFile — WASM consumes the
496
+ // AccountFile by value, invalidating the JS wrapper after the call.
497
+ const accountId =
498
+ typeof input.file.accountId === "function"
499
+ ? input.file.accountId()
500
+ : null;
501
+ await this.#inner.importAccountFile(input.file);
502
+ if (accountId) {
503
+ return await this.#inner.getAccount(accountId);
504
+ }
505
+ throw new Error(
506
+ "Could not determine account ID from AccountFile. " +
507
+ "Ensure the file contains a valid account."
508
+ );
509
+ }
510
+
511
+ if (input.seed) {
512
+ // Import public account from seed
513
+ const authScheme = resolveAuthScheme(input.auth, wasm);
514
+ const mutable = resolveAccountMutability(input.type);
515
+ return await this.#inner.importPublicAccountFromSeed(
516
+ input.seed,
517
+ mutable,
518
+ authScheme
519
+ );
520
+ }
521
+
522
+ throw new Error(
523
+ "Invalid import input: expected a string, { file }, or { seed }"
524
+ );
525
+ }
526
+
527
+ async export(ref) {
528
+ this.#client.assertNotTerminated();
529
+ const wasm = await this.#getWasm();
530
+ const id = resolveAccountRef(ref, wasm);
531
+ return await this.#inner.exportAccountFile(id);
532
+ }
533
+
534
+ async addAddress(ref, addr) {
535
+ this.#client.assertNotTerminated();
536
+ const wasm = await this.#getWasm();
537
+ const id = resolveAccountRef(ref, wasm);
538
+ const address = wasm.Address.fromBech32(addr);
539
+ await this.#inner.insertAccountAddress(id, address);
540
+ }
541
+
542
+ async removeAddress(ref, addr) {
543
+ this.#client.assertNotTerminated();
544
+ const wasm = await this.#getWasm();
545
+ const id = resolveAccountRef(ref, wasm);
546
+ const address = wasm.Address.fromBech32(addr);
547
+ await this.#inner.removeAccountAddress(id, address);
548
+ }
549
+ }
550
+
551
+ class TransactionsResource {
552
+ #inner;
553
+ #getWasm;
554
+ #client;
555
+
556
+ constructor(inner, getWasm, client) {
557
+ this.#inner = inner;
558
+ this.#getWasm = getWasm;
559
+ this.#client = client;
560
+ }
561
+
562
+ async send(opts) {
563
+ this.#client.assertNotTerminated();
564
+ const wasm = await this.#getWasm();
565
+ const { accountId, request } = await this.#buildSendRequest(opts, wasm);
566
+
567
+ const txId = await this.#submitOrSubmitWithProver(
568
+ accountId,
569
+ request,
570
+ opts.prover
571
+ );
572
+
573
+ if (opts.waitForConfirmation) {
574
+ await this.waitFor(txId.toHex(), { timeout: opts.timeout });
575
+ }
576
+
577
+ return txId;
578
+ }
579
+
580
+ async mint(opts) {
581
+ this.#client.assertNotTerminated();
582
+ const wasm = await this.#getWasm();
583
+ const { accountId, request } = await this.#buildMintRequest(opts, wasm);
584
+
585
+ const txId = await this.#submitOrSubmitWithProver(
586
+ accountId,
587
+ request,
588
+ opts.prover
589
+ );
590
+
591
+ if (opts.waitForConfirmation) {
592
+ await this.waitFor(txId.toHex(), { timeout: opts.timeout });
593
+ }
594
+
595
+ return txId;
596
+ }
597
+
598
+ async consume(opts) {
599
+ this.#client.assertNotTerminated();
600
+ const wasm = await this.#getWasm();
601
+ const { accountId, request } = await this.#buildConsumeRequest(opts, wasm);
602
+
603
+ const txId = await this.#submitOrSubmitWithProver(
604
+ accountId,
605
+ request,
606
+ opts.prover
607
+ );
608
+
609
+ if (opts.waitForConfirmation) {
610
+ await this.waitFor(txId.toHex(), { timeout: opts.timeout });
611
+ }
612
+
613
+ return txId;
614
+ }
615
+
616
+ async consumeAll(opts) {
617
+ this.#client.assertNotTerminated();
618
+ const wasm = await this.#getWasm();
619
+
620
+ // getConsumableNotes takes AccountId by value (consumed by WASM).
621
+ // Save hex so we can reconstruct for submitNewTransaction.
622
+ const accountId = resolveAccountRef(opts.account, wasm);
623
+ const accountIdHex = accountId.toString();
624
+ const consumable = await this.#inner.getConsumableNotes(accountId);
625
+
626
+ if (!consumable || consumable.length === 0) {
627
+ return { txId: null, consumed: 0, remaining: 0 };
628
+ }
629
+
630
+ const total = consumable.length;
631
+ const toConsume =
632
+ opts.maxNotes != null ? consumable.slice(0, opts.maxNotes) : consumable;
633
+
634
+ if (toConsume.length === 0) {
635
+ return { txId: null, consumed: 0, remaining: total };
636
+ }
637
+
638
+ const notes = toConsume.map((c) => c.inputNoteRecord().toNote());
639
+
640
+ const request = await this.#inner.newConsumeTransactionRequest(notes);
641
+
642
+ const txId = await this.#submitOrSubmitWithProver(
643
+ wasm.AccountId.fromHex(accountIdHex),
644
+ request,
645
+ opts.prover
646
+ );
647
+
648
+ if (opts.waitForConfirmation) {
649
+ await this.waitFor(txId.toHex(), { timeout: opts.timeout });
650
+ }
651
+
652
+ return {
653
+ txId,
654
+ consumed: toConsume.length,
655
+ remaining: total - toConsume.length,
656
+ };
657
+ }
658
+
659
+ async swap(opts) {
660
+ this.#client.assertNotTerminated();
661
+ const wasm = await this.#getWasm();
662
+ const { accountId, request } = await this.#buildSwapRequest(opts, wasm);
663
+
664
+ const txId = await this.#submitOrSubmitWithProver(
665
+ accountId,
666
+ request,
667
+ opts.prover
668
+ );
669
+
670
+ if (opts.waitForConfirmation) {
671
+ await this.waitFor(txId.toHex(), { timeout: opts.timeout });
672
+ }
673
+
674
+ return txId;
675
+ }
676
+
677
+ async preview(opts) {
678
+ this.#client.assertNotTerminated();
679
+ const wasm = await this.#getWasm();
680
+
681
+ let accountId;
682
+ let request;
683
+
684
+ switch (opts.operation) {
685
+ case "send": {
686
+ ({ accountId, request } = await this.#buildSendRequest(opts, wasm));
687
+ break;
688
+ }
689
+ case "mint": {
690
+ ({ accountId, request } = await this.#buildMintRequest(opts, wasm));
691
+ break;
692
+ }
693
+ case "consume": {
694
+ ({ accountId, request } = await this.#buildConsumeRequest(opts, wasm));
695
+ break;
696
+ }
697
+ case "swap": {
698
+ ({ accountId, request } = await this.#buildSwapRequest(opts, wasm));
699
+ break;
700
+ }
701
+ default:
702
+ throw new Error(`Unknown preview operation: ${opts.operation}`);
703
+ }
704
+
705
+ return await this.#inner.executeForSummary(accountId, request);
706
+ }
707
+
708
+ async submit(account, request, opts) {
709
+ this.#client.assertNotTerminated();
710
+ const wasm = await this.#getWasm();
711
+ const accountId = resolveAccountRef(account, wasm);
712
+ return await this.#submitOrSubmitWithProver(
713
+ accountId,
714
+ request,
715
+ opts?.prover
716
+ );
717
+ }
718
+
719
+ async list(query) {
720
+ this.#client.assertNotTerminated();
721
+ const wasm = await this.#getWasm();
722
+
723
+ let filter;
724
+ if (!query) {
725
+ filter = wasm.TransactionFilter.all();
726
+ } else if (query.status === "uncommitted") {
727
+ filter = wasm.TransactionFilter.uncommitted();
728
+ } else if (query.ids) {
729
+ const txIds = query.ids.map((id) => wasm.TransactionId.fromHex(id));
730
+ filter = wasm.TransactionFilter.ids(txIds);
731
+ } else if (query.expiredBefore !== undefined) {
732
+ filter = wasm.TransactionFilter.expiredBefore(query.expiredBefore);
733
+ } else {
734
+ filter = wasm.TransactionFilter.all();
735
+ }
736
+
737
+ return await this.#inner.getTransactions(filter);
738
+ }
739
+
740
+ /**
741
+ * Polls for transaction confirmation.
742
+ *
743
+ * @param {string} txId - Transaction ID hex string.
744
+ * @param {WaitOptions} [opts] - Polling options.
745
+ * @param {number} [opts.timeout=60000] - Wall-clock polling timeout in
746
+ * milliseconds. This is NOT a block height — it controls how long the
747
+ * client waits before giving up. Set to 0 to disable the timeout and poll
748
+ * indefinitely until the transaction is committed or discarded.
749
+ * @param {number} [opts.interval=5000] - Polling interval in ms.
750
+ * @param {function} [opts.onProgress] - Called with the current status on
751
+ * each poll iteration ("pending", "submitted", or "committed").
752
+ */
753
+ async waitFor(txId, opts) {
754
+ this.#client.assertNotTerminated();
755
+ const timeout = opts?.timeout ?? 60_000;
756
+ const interval = opts?.interval ?? 5_000;
757
+ const start = Date.now();
758
+
759
+ const wasm = await this.#getWasm();
760
+
761
+ while (true) {
762
+ const elapsed = Date.now() - start;
763
+ if (timeout > 0 && elapsed >= timeout) {
764
+ throw new Error(
765
+ `Transaction confirmation timed out after ${timeout}ms`
766
+ );
767
+ }
768
+
769
+ try {
770
+ await this.#inner.syncStateWithTimeout(0);
771
+ } catch {
772
+ // Sync may fail transiently; continue polling
773
+ }
774
+
775
+ // Recreate filter each iteration — WASM consumes it by value
776
+ const filter = wasm.TransactionFilter.ids([
777
+ wasm.TransactionId.fromHex(txId),
778
+ ]);
779
+ const txs = await this.#inner.getTransactions(filter);
780
+
781
+ if (txs && txs.length > 0) {
782
+ const tx = txs[0];
783
+ const status = tx.transactionStatus?.();
784
+
785
+ if (status) {
786
+ if (status.isCommitted()) {
787
+ opts?.onProgress?.("committed");
788
+ return;
789
+ }
790
+ if (status.isDiscarded()) {
791
+ throw new Error(`Transaction rejected: ${txId}`);
792
+ }
793
+ }
794
+
795
+ opts?.onProgress?.("submitted");
796
+ } else {
797
+ opts?.onProgress?.("pending");
798
+ }
799
+
800
+ await new Promise((resolve) => setTimeout(resolve, interval));
801
+ }
802
+ }
803
+
804
+ // ── Shared request builders ──
805
+
806
+ async #buildSendRequest(opts, wasm) {
807
+ const accountId = resolveAccountRef(opts.account, wasm);
808
+ const targetId = resolveAccountRef(opts.to, wasm);
809
+ const faucetId = resolveAccountRef(opts.token, wasm);
810
+ const noteType = resolveNoteType(opts.type, wasm);
811
+ const amount = BigInt(opts.amount);
812
+
813
+ const request = await this.#inner.newSendTransactionRequest(
814
+ accountId,
815
+ targetId,
816
+ faucetId,
817
+ noteType,
818
+ amount,
819
+ opts.reclaimAfter,
820
+ opts.timelockUntil
821
+ );
822
+ return { accountId, request };
823
+ }
824
+
825
+ async #buildMintRequest(opts, wasm) {
826
+ const accountId = resolveAccountRef(opts.account, wasm);
827
+ const targetId = resolveAccountRef(opts.to, wasm);
828
+ const noteType = resolveNoteType(opts.type, wasm);
829
+ const amount = BigInt(opts.amount);
830
+
831
+ // WASM signature: newMintTransactionRequest(target, faucet, noteType, amount)
832
+ const request = await this.#inner.newMintTransactionRequest(
833
+ targetId,
834
+ accountId,
835
+ noteType,
836
+ amount
837
+ );
838
+ return { accountId, request };
839
+ }
840
+
841
+ async #buildConsumeRequest(opts, wasm) {
842
+ const accountId = resolveAccountRef(opts.account, wasm);
843
+ const noteInputs = Array.isArray(opts.notes) ? opts.notes : [opts.notes];
844
+ const notes = await Promise.all(
845
+ noteInputs.map((input) => this.#resolveNoteInput(input))
846
+ );
847
+ const request = await this.#inner.newConsumeTransactionRequest(notes);
848
+ return { accountId, request };
849
+ }
850
+
851
+ async #buildSwapRequest(opts, wasm) {
852
+ const accountId = resolveAccountRef(opts.account, wasm);
853
+ const offeredFaucetId = resolveAccountRef(opts.offer.token, wasm);
854
+ const requestedFaucetId = resolveAccountRef(opts.request.token, wasm);
855
+ const noteType = resolveNoteType(opts.type, wasm);
856
+ const paybackNoteType = resolveNoteType(
857
+ opts.paybackType ?? opts.type,
858
+ wasm
859
+ );
860
+
861
+ const request = await this.#inner.newSwapTransactionRequest(
862
+ accountId,
863
+ offeredFaucetId,
864
+ BigInt(opts.offer.amount),
865
+ requestedFaucetId,
866
+ BigInt(opts.request.amount),
867
+ noteType,
868
+ paybackNoteType
869
+ );
870
+ return { accountId, request };
871
+ }
872
+
873
+ async #resolveNoteInput(input) {
874
+ if (typeof input === "string") {
875
+ const record = await this.#inner.getInputNote(input);
876
+ if (!record) {
877
+ throw new Error(`Note not found: ${input}`);
878
+ }
879
+ return record.toNote();
880
+ }
881
+ // InputNoteRecord — unwrap to Note
882
+ if (input && typeof input.toNote === "function") {
883
+ return input.toNote();
884
+ }
885
+ // NoteId — look up the note by its hex ID
886
+ if (input && input.constructor?.name === "NoteId") {
887
+ const hex = input.toString();
888
+ const record = await this.#inner.getInputNote(hex);
889
+ if (!record) {
890
+ throw new Error(`Note not found: ${hex}`);
891
+ }
892
+ return record.toNote();
893
+ }
894
+ // Assume it's already a Note object
895
+ return input;
896
+ }
897
+
898
+ async #submitOrSubmitWithProver(accountId, request, perCallProver) {
899
+ const prover = perCallProver ?? this.#client.defaultProver;
900
+ if (prover) {
901
+ return await this.#inner.submitNewTransactionWithProver(
902
+ accountId,
903
+ request,
904
+ prover
905
+ );
906
+ }
907
+ return await this.#inner.submitNewTransaction(accountId, request);
908
+ }
909
+ }
910
+
911
+ class NotesResource {
912
+ #inner;
913
+ #getWasm;
914
+ #client;
915
+
916
+ constructor(inner, getWasm, client) {
917
+ this.#inner = inner;
918
+ this.#getWasm = getWasm;
919
+ this.#client = client;
920
+ }
921
+
922
+ async list(query) {
923
+ this.#client.assertNotTerminated();
924
+ const wasm = await this.#getWasm();
925
+ const filter = buildNoteFilter(query, wasm);
926
+ return await this.#inner.getInputNotes(filter);
927
+ }
928
+
929
+ async get(noteId) {
930
+ this.#client.assertNotTerminated();
931
+ const result = await this.#inner.getInputNote(noteId);
932
+ return result ?? null;
933
+ }
934
+
935
+ async listSent(query) {
936
+ this.#client.assertNotTerminated();
937
+ const wasm = await this.#getWasm();
938
+ const filter = buildNoteFilter(query, wasm);
939
+ return await this.#inner.getOutputNotes(filter);
940
+ }
941
+
942
+ async listAvailable(opts) {
943
+ this.#client.assertNotTerminated();
944
+ const wasm = await this.#getWasm();
945
+ const accountId = resolveAccountRef(opts.account, wasm);
946
+ return await this.#inner.getConsumableNotes(accountId);
947
+ }
948
+
949
+ async import(noteFile) {
950
+ this.#client.assertNotTerminated();
951
+ return await this.#inner.importNoteFile(noteFile);
952
+ }
953
+
954
+ async export(noteId, opts) {
955
+ this.#client.assertNotTerminated();
956
+ const wasm = await this.#getWasm();
957
+ const format = opts?.format ?? wasm.NoteExportFormat.Full;
958
+ return await this.#inner.exportNoteFile(noteId, format);
959
+ }
960
+
961
+ async fetchPrivate(opts) {
962
+ this.#client.assertNotTerminated();
963
+ if (opts?.mode === "all") {
964
+ await this.#inner.fetchAllPrivateNotes();
965
+ } else {
966
+ await this.#inner.fetchPrivateNotes();
967
+ }
968
+ }
969
+
970
+ async sendPrivate(opts) {
971
+ this.#client.assertNotTerminated();
972
+ const wasm = await this.#getWasm();
973
+ const noteRecord = await this.#inner.getInputNote(opts.noteId);
974
+ if (!noteRecord) {
975
+ throw new Error(`Note not found: ${opts.noteId}`);
976
+ }
977
+ const note = noteRecord.toNote();
978
+ const address = resolveAddress(opts.to, wasm);
979
+ await this.#inner.sendPrivateNote(note, address);
980
+ }
981
+ }
982
+
983
+ function buildNoteFilter(query, wasm) {
984
+ if (!query) {
985
+ return new wasm.NoteFilter(wasm.NoteFilterTypes.All, undefined);
986
+ }
987
+
988
+ if (query.ids) {
989
+ const noteIds = query.ids.map((id) => wasm.NoteId.fromHex(id));
990
+ return new wasm.NoteFilter(wasm.NoteFilterTypes.List, noteIds);
991
+ }
992
+
993
+ if (query.status) {
994
+ const statusMap = {
995
+ consumed: wasm.NoteFilterTypes.Consumed,
996
+ committed: wasm.NoteFilterTypes.Committed,
997
+ expected: wasm.NoteFilterTypes.Expected,
998
+ processing: wasm.NoteFilterTypes.Processing,
999
+ unverified: wasm.NoteFilterTypes.Unverified,
1000
+ };
1001
+ const filterType = statusMap[query.status];
1002
+ if (filterType === undefined) {
1003
+ throw new Error(`Unknown note status: ${query.status}`);
1004
+ }
1005
+ return new wasm.NoteFilter(filterType, undefined);
1006
+ }
1007
+
1008
+ return new wasm.NoteFilter(wasm.NoteFilterTypes.All, undefined);
1009
+ }
1010
+
1011
+ class TagsResource {
1012
+ #inner;
1013
+ #client;
1014
+
1015
+ constructor(inner, getWasm, client) {
1016
+ this.#inner = inner;
1017
+ this.#client = client;
1018
+ }
1019
+
1020
+ async add(tag) {
1021
+ this.#client.assertNotTerminated();
1022
+ await this.#inner.addTag(String(tag));
1023
+ }
1024
+
1025
+ async remove(tag) {
1026
+ this.#client.assertNotTerminated();
1027
+ await this.#inner.removeTag(String(tag));
1028
+ }
1029
+
1030
+ async list() {
1031
+ this.#client.assertNotTerminated();
1032
+ const tags = await this.#inner.listTags();
1033
+ return Array.from(tags).map((t) => {
1034
+ const n = Number(t);
1035
+ if (Number.isNaN(n)) {
1036
+ throw new Error(`Invalid tag value: ${t}`);
1037
+ }
1038
+ return n;
1039
+ });
1040
+ }
1041
+ }
1042
+
1043
+ class SettingsResource {
1044
+ #inner;
1045
+ #client;
1046
+
1047
+ constructor(inner, _getWasm, client) {
1048
+ this.#inner = inner;
1049
+ this.#client = client;
1050
+ }
1051
+
1052
+ async get(key) {
1053
+ this.#client.assertNotTerminated();
1054
+ const value = await this.#inner.getSetting(key);
1055
+ return value === undefined ? null : value;
1056
+ }
1057
+
1058
+ async set(key, value) {
1059
+ this.#client.assertNotTerminated();
1060
+ await this.#inner.setSetting(key, value);
1061
+ }
1062
+
1063
+ async remove(key) {
1064
+ this.#client.assertNotTerminated();
1065
+ await this.#inner.removeSetting(key);
1066
+ }
1067
+
1068
+ async listKeys() {
1069
+ this.#client.assertNotTerminated();
1070
+ return await this.#inner.listSettingKeys();
1071
+ }
1072
+ }
1073
+
1074
+ /**
1075
+ * MidenClient wraps the existing proxy-wrapped WebClient with a resource-based API.
1076
+ *
1077
+ * Resource classes receive the proxy client and call its methods, handling all type
1078
+ * conversions (string -> AccountId, number -> BigInt, string -> enum).
1079
+ */
1080
+ class MidenClient {
1081
+ // Injected by index.js to resolve circular imports
1082
+ static _WasmWebClient = null;
1083
+ static _MockWasmWebClient = null;
1084
+ static _getWasmOrThrow = null;
1085
+
1086
+ #inner;
1087
+ #getWasm;
1088
+ #terminated = false;
1089
+ #defaultProver = null;
1090
+ #isMock = false;
1091
+
1092
+ constructor(inner, getWasm, defaultProver) {
1093
+ this.#inner = inner;
1094
+ this.#getWasm = getWasm;
1095
+ this.#defaultProver = defaultProver ?? null;
1096
+
1097
+ this.accounts = new AccountsResource(inner, getWasm, this);
1098
+ this.transactions = new TransactionsResource(inner, getWasm, this);
1099
+ this.notes = new NotesResource(inner, getWasm, this);
1100
+ this.tags = new TagsResource(inner, getWasm, this);
1101
+ this.settings = new SettingsResource(inner, getWasm, this);
1102
+ }
1103
+
1104
+ /**
1105
+ * Creates and initializes a new MidenClient.
1106
+ *
1107
+ * @param {ClientOptions} [options] - Client configuration options.
1108
+ * @returns {Promise<MidenClient>} A fully initialized client.
1109
+ */
1110
+ static async create(options) {
1111
+ const getWasm = MidenClient._getWasmOrThrow;
1112
+ const WebClientClass = MidenClient._WasmWebClient;
1113
+
1114
+ if (!WebClientClass || !getWasm) {
1115
+ throw new Error(
1116
+ "MidenClient not initialized. Import from the SDK package entry point."
1117
+ );
1118
+ }
1119
+
1120
+ const seed = options?.seed ? await hashSeed(options.seed) : undefined;
1121
+
1122
+ let inner;
1123
+ if (options?.keystore) {
1124
+ inner = await WebClientClass.createClientWithExternalKeystore(
1125
+ options?.rpcUrl,
1126
+ options?.noteTransportUrl,
1127
+ seed,
1128
+ options?.storeName,
1129
+ options.keystore.getKey,
1130
+ options.keystore.insertKey,
1131
+ options.keystore.sign
1132
+ );
1133
+ } else {
1134
+ inner = await WebClientClass.createClient(
1135
+ options?.rpcUrl,
1136
+ options?.noteTransportUrl,
1137
+ seed,
1138
+ options?.storeName
1139
+ );
1140
+ }
1141
+
1142
+ let defaultProver = null;
1143
+ if (options?.proverUrl) {
1144
+ const wasm = await getWasm();
1145
+ defaultProver = wasm.TransactionProver.newRemoteProver(
1146
+ options.proverUrl,
1147
+ undefined
1148
+ );
1149
+ }
1150
+
1151
+ const client = new MidenClient(inner, getWasm, defaultProver);
1152
+
1153
+ if (options?.autoSync) {
1154
+ await client.sync();
1155
+ }
1156
+
1157
+ return client;
1158
+ }
1159
+
1160
+ /**
1161
+ * Creates a client preconfigured for testnet use.
1162
+ * Defaults to autoSync: true.
1163
+ *
1164
+ * @param {object} [options] - Options (autoSync can be overridden).
1165
+ * @returns {Promise<MidenClient>} A fully initialized testnet client.
1166
+ */
1167
+ static async createTestnet(options) {
1168
+ return MidenClient.create({
1169
+ ...options,
1170
+ autoSync: options?.autoSync ?? true,
1171
+ });
1172
+ }
1173
+
1174
+ /**
1175
+ * Creates a mock client for testing.
1176
+ *
1177
+ * @param {MockOptions} [options] - Mock client options.
1178
+ * @returns {Promise<MidenClient>} A mock client.
1179
+ */
1180
+ static async createMock(options) {
1181
+ const getWasm = MidenClient._getWasmOrThrow;
1182
+ const MockWebClientClass = MidenClient._MockWasmWebClient;
1183
+
1184
+ if (!MockWebClientClass || !getWasm) {
1185
+ throw new Error(
1186
+ "MidenClient not initialized. Import from the SDK package entry point."
1187
+ );
1188
+ }
1189
+
1190
+ const seed = options?.seed ? await hashSeed(options.seed) : undefined;
1191
+
1192
+ const inner = await MockWebClientClass.createClient(
1193
+ options?.serializedMockChain,
1194
+ options?.serializedNoteTransport,
1195
+ seed
1196
+ );
1197
+
1198
+ const client = new MidenClient(inner, getWasm, null);
1199
+ client.#isMock = true;
1200
+ return client;
1201
+ }
1202
+
1203
+ /** Returns the client-level default prover (set from ClientOptions.proverUrl). */
1204
+ get defaultProver() {
1205
+ return this.#defaultProver;
1206
+ }
1207
+
1208
+ /**
1209
+ * Syncs the client state with the Miden node.
1210
+ *
1211
+ * @param {object} [opts] - Sync options.
1212
+ * @param {number} [opts.timeout] - Timeout in milliseconds (0 = no timeout).
1213
+ * @returns {Promise<SyncSummary>} The sync summary.
1214
+ */
1215
+ async sync(opts) {
1216
+ this.assertNotTerminated();
1217
+ return await this.#inner.syncStateWithTimeout(opts?.timeout ?? 0);
1218
+ }
1219
+
1220
+ /**
1221
+ * Returns the current sync height.
1222
+ *
1223
+ * @returns {Promise<number>} The current sync height.
1224
+ */
1225
+ async getSyncHeight() {
1226
+ this.assertNotTerminated();
1227
+ return await this.#inner.getSyncHeight();
1228
+ }
1229
+
1230
+ /**
1231
+ * Terminates the underlying Web Worker. After this, all method calls will throw.
1232
+ */
1233
+ terminate() {
1234
+ this.#terminated = true;
1235
+ this.#inner.terminate?.();
1236
+ }
1237
+
1238
+ [Symbol.dispose]() {
1239
+ this.terminate();
1240
+ }
1241
+
1242
+ async [Symbol.asyncDispose]() {
1243
+ this.terminate();
1244
+ }
1245
+
1246
+ /**
1247
+ * Exports the client store as a versioned snapshot.
1248
+ *
1249
+ * @returns {Promise<StoreSnapshot>} The store snapshot.
1250
+ */
1251
+ async exportStore() {
1252
+ this.assertNotTerminated();
1253
+ const data = await this.#inner.exportStore();
1254
+ return { version: 1, data };
1255
+ }
1256
+
1257
+ /**
1258
+ * Imports a previously exported store snapshot.
1259
+ *
1260
+ * @param {StoreSnapshot} snapshot - The store snapshot to import.
1261
+ */
1262
+ async importStore(snapshot) {
1263
+ this.assertNotTerminated();
1264
+ if (!snapshot || snapshot.version !== 1) {
1265
+ throw new Error(
1266
+ `Unsupported store snapshot version: ${snapshot?.version}. Expected version 1.`
1267
+ );
1268
+ }
1269
+ // Second arg is the store password (empty string = no encryption)
1270
+ await this.#inner.forceImportStore(snapshot.data, "");
1271
+ }
1272
+
1273
+ // ── Mock-only methods ──
1274
+
1275
+ /** Advances the mock chain by one block. Only available on mock clients. */
1276
+ proveBlock() {
1277
+ this.assertNotTerminated();
1278
+ this.#assertMock("proveBlock");
1279
+ return this.#inner.proveBlock();
1280
+ }
1281
+
1282
+ /** Returns true if this client uses a mock chain. */
1283
+ usesMockChain() {
1284
+ return this.#isMock;
1285
+ }
1286
+
1287
+ /** Serializes the mock chain state for snapshot/restore in tests. */
1288
+ serializeMockChain() {
1289
+ this.assertNotTerminated();
1290
+ this.#assertMock("serializeMockChain");
1291
+ return this.#inner.serializeMockChain();
1292
+ }
1293
+
1294
+ /** Serializes the mock note transport node state. */
1295
+ serializeMockNoteTransportNode() {
1296
+ this.assertNotTerminated();
1297
+ this.#assertMock("serializeMockNoteTransportNode");
1298
+ return this.#inner.serializeMockNoteTransportNode();
1299
+ }
1300
+
1301
+ // ── Internal ──
1302
+
1303
+ /** @internal Throws if the client has been terminated. */
1304
+ assertNotTerminated() {
1305
+ if (this.#terminated) {
1306
+ throw new Error("Client terminated");
1307
+ }
1308
+ }
1309
+
1310
+ #assertMock(method) {
1311
+ if (!this.#isMock) {
1312
+ throw new Error(`${method}() is only available on mock clients`);
1313
+ }
1314
+ }
1315
+ }
1316
+
1317
+ // Module-level WASM reference, set by index.js after initialization
1318
+ let _wasm = null;
1319
+ let _WebClient = null;
1320
+
1321
+ function _setWasm(wasm) {
1322
+ _wasm = wasm;
1323
+ }
1324
+
1325
+ function _setWebClient(WebClientClass) {
1326
+ _WebClient = WebClientClass;
1327
+ }
1328
+
1329
+ function getWasm() {
1330
+ if (!_wasm) {
1331
+ throw new Error(
1332
+ "WASM not initialized. Ensure the SDK is loaded before calling standalone utilities."
1333
+ );
1334
+ }
1335
+ return _wasm;
1336
+ }
1337
+
1338
+ /**
1339
+ * Creates a P2ID (Pay-to-ID) note.
1340
+ *
1341
+ * @param {NoteOptions} opts - Note creation options.
1342
+ * @returns {OutputNote} The created output note.
1343
+ */
1344
+ function createP2IDNote(opts) {
1345
+ const wasm = getWasm();
1346
+ const sender = resolveAccountRef(opts.from, wasm);
1347
+ const target = resolveAccountRef(opts.to, wasm);
1348
+ const noteAssets = buildNoteAssets(opts.assets, wasm);
1349
+ const noteType = resolveNoteType(opts.type, wasm);
1350
+ const attachment = opts.attachment
1351
+ ? new wasm.NoteAttachment(opts.attachment)
1352
+ : new wasm.NoteAttachment([]);
1353
+
1354
+ const note = wasm.Note.createP2IDNote(
1355
+ sender,
1356
+ target,
1357
+ noteAssets,
1358
+ noteType,
1359
+ attachment
1360
+ );
1361
+ return wasm.OutputNote.full(note);
1362
+ }
1363
+
1364
+ /**
1365
+ * Creates a P2IDE (Pay-to-ID with Expiration) note.
1366
+ *
1367
+ * @param {P2IDEOptions} opts - Note creation options with timelock/reclaim.
1368
+ * @returns {OutputNote} The created output note.
1369
+ */
1370
+ function createP2IDENote(opts) {
1371
+ const wasm = getWasm();
1372
+ const sender = resolveAccountRef(opts.from, wasm);
1373
+ const target = resolveAccountRef(opts.to, wasm);
1374
+ const noteAssets = buildNoteAssets(opts.assets, wasm);
1375
+ const noteType = resolveNoteType(opts.type, wasm);
1376
+ const attachment = opts.attachment
1377
+ ? new wasm.NoteAttachment(opts.attachment)
1378
+ : new wasm.NoteAttachment([]);
1379
+
1380
+ const note = wasm.Note.createP2IDENote(
1381
+ sender,
1382
+ target,
1383
+ noteAssets,
1384
+ opts.reclaimAfter,
1385
+ opts.timelockUntil,
1386
+ noteType,
1387
+ attachment
1388
+ );
1389
+ return wasm.OutputNote.full(note);
1390
+ }
1391
+
1392
+ /**
1393
+ * Builds a swap tag for note matching.
1394
+ *
1395
+ * @param {BuildSwapTagOptions} opts - Swap tag options.
1396
+ * @returns {NoteTag} The computed swap tag.
1397
+ */
1398
+ function buildSwapTag(opts) {
1399
+ const wasm = getWasm();
1400
+ if (!_WebClient || typeof _WebClient.buildSwapTag !== "function") {
1401
+ throw new Error(
1402
+ "WebClient.buildSwapTag is not available. Ensure the SDK is fully loaded."
1403
+ );
1404
+ }
1405
+ const noteType = resolveNoteType(opts.type, wasm);
1406
+ const offeredFaucetId = resolveAccountRef(opts.offer.token, wasm);
1407
+ const requestedFaucetId = resolveAccountRef(opts.request.token, wasm);
1408
+
1409
+ return _WebClient.buildSwapTag(
1410
+ noteType,
1411
+ offeredFaucetId,
1412
+ BigInt(opts.offer.amount),
1413
+ requestedFaucetId,
1414
+ BigInt(opts.request.amount)
1415
+ );
1416
+ }
1417
+
1418
+ function buildNoteAssets(assets, wasm) {
1419
+ const assetArray = Array.isArray(assets) ? assets : [assets];
1420
+ const fungibleAssets = assetArray.map((asset) => {
1421
+ const faucetId = resolveAccountRef(asset.token, wasm);
1422
+ return new wasm.FungibleAsset(faucetId, BigInt(asset.amount));
1423
+ });
1424
+ return new wasm.NoteAssets(fungibleAssets);
1425
+ }
1426
+
1427
+ const AccountType = Object.freeze({
1428
+ MutableWallet: "MutableWallet",
1429
+ ImmutableWallet: "ImmutableWallet",
1430
+ FungibleFaucet: "FungibleFaucet",
1431
+ });
1432
+
1433
+ const AuthScheme = Object.freeze({
1434
+ Falcon: "falcon",
1435
+ ECDSA: "ecdsa",
1436
+ });
1437
+
242
1438
  const buildTypedArraysExport = (exportObject) => {
243
1439
  return Object.entries(exportObject).reduce(
244
- (exports, [exportName, _export]) => {
1440
+ (exports$1, [exportName, _export]) => {
245
1441
  if (exportName.endsWith("Array")) {
246
- exports[exportName] = _export;
1442
+ exports$1[exportName] = _export;
247
1443
  }
248
- return exports;
1444
+ return exports$1;
249
1445
  },
250
1446
  {}
251
1447
  );
@@ -291,6 +1487,8 @@ const ensureWasm = async () => {
291
1487
  copyWebClientStatics(module.WebClient);
292
1488
  webClientStaticsCopied = true;
293
1489
  }
1490
+ // Set WASM module for standalone utilities
1491
+ _setWasm(module);
294
1492
  }
295
1493
  return module;
296
1494
  });
@@ -332,6 +1530,28 @@ const getWasmOrThrow = async () => {
332
1530
  * Because of this implementation, the only breaking change for end users is in the way the
333
1531
  * web client is instantiated. Users should now use the WebClient.createClient static call.
334
1532
  */
1533
+ /**
1534
+ * Create a Proxy that forwards missing properties to the underlying WASM
1535
+ * WebClient.
1536
+ */
1537
+ function createClientProxy(instance) {
1538
+ return new Proxy(instance, {
1539
+ get(target, prop, receiver) {
1540
+ if (prop in target) {
1541
+ return Reflect.get(target, prop, receiver);
1542
+ }
1543
+ if (target.wasmWebClient && prop in target.wasmWebClient) {
1544
+ const value = target.wasmWebClient[prop];
1545
+ if (typeof value === "function") {
1546
+ return value.bind(target.wasmWebClient);
1547
+ }
1548
+ return value;
1549
+ }
1550
+ return undefined;
1551
+ },
1552
+ });
1553
+ }
1554
+
335
1555
  class WebClient {
336
1556
  /**
337
1557
  * Create a WebClient wrapper.
@@ -477,6 +1697,25 @@ class WebClient {
477
1697
  // Lazy initialize the underlying WASM WebClient when first requested.
478
1698
  this.wasmWebClient = null;
479
1699
  this.wasmWebClientPromise = null;
1700
+
1701
+ // Promise chain to serialize direct WASM calls that require exclusive
1702
+ // (&mut self) access. Without this, concurrent calls on the same client
1703
+ // would panic with "recursive use of an object detected" due to
1704
+ // wasm-bindgen's internal RefCell.
1705
+ this._wasmCallChain = Promise.resolve();
1706
+ }
1707
+
1708
+ /**
1709
+ * Serialize a WASM call that requires exclusive (&mut self) access.
1710
+ * Concurrent calls are queued and executed one at a time.
1711
+ *
1712
+ * @param {() => Promise<any>} fn - The async function to execute.
1713
+ * @returns {Promise<any>} The result of fn.
1714
+ */
1715
+ _serializeWasmCall(fn) {
1716
+ const result = this._wasmCallChain.catch(() => {}).then(fn);
1717
+ this._wasmCallChain = result.catch(() => {});
1718
+ return result;
480
1719
  }
481
1720
 
482
1721
  // TODO: This will soon conflict with some changes in main.
@@ -550,24 +1789,7 @@ class WebClient {
550
1789
  // Wait for the worker to be ready
551
1790
  await instance.ready;
552
1791
 
553
- // Return a proxy that forwards missing properties to wasmWebClient.
554
- return new Proxy(instance, {
555
- get(target, prop, receiver) {
556
- // If the property exists on the wrapper, return it.
557
- if (prop in target) {
558
- return Reflect.get(target, prop, receiver);
559
- }
560
- // Otherwise, if the wasmWebClient has it, return that.
561
- if (target.wasmWebClient && prop in target.wasmWebClient) {
562
- const value = target.wasmWebClient[prop];
563
- if (typeof value === "function") {
564
- return value.bind(target.wasmWebClient);
565
- }
566
- return value;
567
- }
568
- return undefined;
569
- },
570
- });
1792
+ return createClientProxy(instance);
571
1793
  }
572
1794
 
573
1795
  /**
@@ -625,24 +1847,7 @@ class WebClient {
625
1847
  );
626
1848
 
627
1849
  await instance.ready;
628
- // Return a proxy that forwards missing properties to wasmWebClient.
629
- return new Proxy(instance, {
630
- get(target, prop, receiver) {
631
- // If the property exists on the wrapper, return it.
632
- if (prop in target) {
633
- return Reflect.get(target, prop, receiver);
634
- }
635
- // Otherwise, if the wasmWebClient has it, return that.
636
- if (target.wasmWebClient && prop in target.wasmWebClient) {
637
- const value = target.wasmWebClient[prop];
638
- if (typeof value === "function") {
639
- return value.bind(target.wasmWebClient);
640
- }
641
- return value;
642
- }
643
- return undefined;
644
- },
645
- });
1850
+ return createClientProxy(instance);
646
1851
  }
647
1852
 
648
1853
  /**
@@ -671,30 +1876,15 @@ class WebClient {
671
1876
  // ----- Explicitly Wrapped Methods (Worker-Forwarded) -----
672
1877
 
673
1878
  async newWallet(storageMode, mutable, authSchemeId, seed) {
674
- try {
675
- if (!this.worker) {
676
- const wasmWebClient = await this.getWasmWebClient();
677
- return await wasmWebClient.newWallet(
678
- storageMode,
679
- mutable,
680
- authSchemeId,
681
- seed
682
- );
683
- }
684
- const wasm = await getWasmOrThrow();
685
- const serializedStorageMode = storageMode.asStr();
686
- const serializedAccountBytes = await this.callMethodWithWorker(
687
- MethodName.NEW_WALLET,
688
- serializedStorageMode,
1879
+ return this._serializeWasmCall(async () => {
1880
+ const wasmWebClient = await this.getWasmWebClient();
1881
+ return await wasmWebClient.newWallet(
1882
+ storageMode,
689
1883
  mutable,
690
1884
  authSchemeId,
691
1885
  seed
692
1886
  );
693
- return wasm.Account.deserialize(new Uint8Array(serializedAccountBytes));
694
- } catch (error) {
695
- console.error("INDEX.JS: Error in newWallet:", error);
696
- throw error;
697
- }
1887
+ });
698
1888
  }
699
1889
 
700
1890
  async newFaucet(
@@ -705,36 +1895,17 @@ class WebClient {
705
1895
  maxSupply,
706
1896
  authSchemeId
707
1897
  ) {
708
- try {
709
- if (!this.worker) {
710
- const wasmWebClient = await this.getWasmWebClient();
711
- return await wasmWebClient.newFaucet(
712
- storageMode,
713
- nonFungible,
714
- tokenSymbol,
715
- decimals,
716
- maxSupply,
717
- authSchemeId
718
- );
719
- }
720
- const wasm = await getWasmOrThrow();
721
- const serializedStorageMode = storageMode.asStr();
722
- const serializedMaxSupply = maxSupply.toString();
723
- const serializedAccountBytes = await this.callMethodWithWorker(
724
- MethodName.NEW_FAUCET,
725
- serializedStorageMode,
1898
+ return this._serializeWasmCall(async () => {
1899
+ const wasmWebClient = await this.getWasmWebClient();
1900
+ return await wasmWebClient.newFaucet(
1901
+ storageMode,
726
1902
  nonFungible,
727
1903
  tokenSymbol,
728
1904
  decimals,
729
- serializedMaxSupply,
1905
+ maxSupply,
730
1906
  authSchemeId
731
1907
  );
732
-
733
- return wasm.Account.deserialize(new Uint8Array(serializedAccountBytes));
734
- } catch (error) {
735
- console.error("INDEX.JS: Error in newFaucet:", error);
736
- throw error;
737
- }
1908
+ });
738
1909
  }
739
1910
 
740
1911
  async submitNewTransaction(accountId, transactionRequest) {
@@ -854,6 +2025,33 @@ class WebClient {
854
2025
  }
855
2026
  }
856
2027
 
2028
+ async applyTransaction(transactionResult, submissionHeight) {
2029
+ try {
2030
+ if (!this.worker) {
2031
+ const wasmWebClient = await this.getWasmWebClient();
2032
+ return await wasmWebClient.applyTransaction(
2033
+ transactionResult,
2034
+ submissionHeight
2035
+ );
2036
+ }
2037
+
2038
+ const wasm = await getWasmOrThrow();
2039
+ const serializedTransactionResult = transactionResult.serialize();
2040
+ const serializedUpdateBytes = await this.callMethodWithWorker(
2041
+ MethodName.APPLY_TRANSACTION,
2042
+ serializedTransactionResult,
2043
+ submissionHeight
2044
+ );
2045
+
2046
+ return wasm.TransactionStoreUpdate.deserialize(
2047
+ new Uint8Array(serializedUpdateBytes)
2048
+ );
2049
+ } catch (error) {
2050
+ console.error("INDEX.JS: Error in applyTransaction:", error);
2051
+ throw error;
2052
+ }
2053
+ }
2054
+
857
2055
  /**
858
2056
  * Syncs the client state with the node.
859
2057
  *
@@ -920,6 +2118,15 @@ class WebClient {
920
2118
  }
921
2119
  }
922
2120
 
2121
+ /**
2122
+ * Terminates the underlying Web Worker used by this WebClient instance.
2123
+ *
2124
+ * Call this method when you're done using a WebClient to free up browser
2125
+ * resources. Each WebClient instance uses a dedicated Web Worker for
2126
+ * computationally intensive operations. Terminating releases that thread.
2127
+ *
2128
+ * After calling terminate(), the WebClient should not be used.
2129
+ */
923
2130
  terminate() {
924
2131
  if (this.worker) {
925
2132
  this.worker.terminate();
@@ -973,24 +2180,7 @@ class MockWebClient extends WebClient {
973
2180
  // Wait for the worker to be ready
974
2181
  await instance.ready;
975
2182
 
976
- // Return a proxy that forwards missing properties to wasmWebClient.
977
- return new Proxy(instance, {
978
- get(target, prop, receiver) {
979
- // If the property exists on the wrapper, return it.
980
- if (prop in target) {
981
- return Reflect.get(target, prop, receiver);
982
- }
983
- // Otherwise, if the wasmWebClient has it, return that.
984
- if (target.wasmWebClient && prop in target.wasmWebClient) {
985
- const value = target.wasmWebClient[prop];
986
- if (typeof value === "function") {
987
- return value.bind(target.wasmWebClient);
988
- }
989
- return value;
990
- }
991
- return undefined;
992
- },
993
- });
2183
+ return createClientProxy(instance);
994
2184
  }
995
2185
 
996
2186
  /**
@@ -1181,5 +2371,11 @@ function copyWebClientStatics(WasmWebClient) {
1181
2371
  });
1182
2372
  }
1183
2373
 
1184
- export { MidenArrays, MockWebClient, WebClient };
2374
+ // Wire MidenClient dependencies (resolves circular import)
2375
+ MidenClient._WasmWebClient = WebClient;
2376
+ MidenClient._MockWasmWebClient = MockWebClient;
2377
+ MidenClient._getWasmOrThrow = getWasmOrThrow;
2378
+ _setWebClient(WebClient);
2379
+
2380
+ export { AccountType, AuthScheme, MidenArrays, MidenClient, MockWebClient as MockWasmWebClient, WebClient as WasmWebClient, buildSwapTag, createP2IDENote, createP2IDNote, getWasmOrThrow };
1185
2381
  //# sourceMappingURL=index.js.map