@miden-sdk/miden-sdk 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/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, 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, NoteArray, 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, NoteSyncBlock, NoteSyncInfo, NoteTag, NoteType, OutputNote, OutputNoteArray, OutputNoteRecord, OutputNoteState, OutputNotes, Package, PartialNote, Poseidon2, ProcedureThreshold, Program, ProvenTransaction, PublicKey, RpcClient, Rpo256, SerializedInputNoteData, SerializedOutputNoteData, SerializedTransactionData, Signature, SigningInputs, SigningInputsType, SlotAndKeys, SparseMerklePath, StorageMap, StorageMapEntry, StorageMapInfo, StorageMapUpdate, StorageSlot, StorageSlotArray, SyncSummary, TestUtils, TokenSymbol, TransactionArgs, TransactionFilter, TransactionId, TransactionProver, TransactionRecord, TransactionRequest, TransactionRequestBuilder, TransactionResult, TransactionScript, TransactionScriptInputPair, TransactionScriptInputPairArray, TransactionStatus, TransactionStoreUpdate, TransactionSummary, WebClient, WebKeystoreApi, Word, createAuthFalcon512RpoMultisig, exportStore, importStore, initSync, setupLogging } from './Cargo-D44KIErf.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, NoteArray, 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, NoteSyncBlock, NoteSyncInfo, NoteTag, NoteType, OutputNote, OutputNoteArray, OutputNoteRecord, OutputNoteState, OutputNotes, Package, PartialNote, Poseidon2, ProcedureThreshold, Program, ProvenTransaction, PublicKey, RpcClient, Rpo256, SerializedInputNoteData, SerializedOutputNoteData, SerializedTransactionData, Signature, SigningInputs, SigningInputsType, SlotAndKeys, SparseMerklePath, StorageMap, StorageMapEntry, StorageMapInfo, StorageMapUpdate, StorageSlot, StorageSlotArray, SyncSummary, TestUtils, TokenSymbol, TransactionArgs, TransactionFilter, TransactionId, TransactionProver, TransactionRecord, TransactionRequest, TransactionRequestBuilder, TransactionResult, TransactionScript, TransactionScriptInputPair, TransactionScriptInputPairArray, TransactionStatus, TransactionStoreUpdate, TransactionSummary, WebClient, WebKeystoreApi, Word, createAuthFalcon512RpoMultisig, exportStore, importStore, initSync, setupLogging } from './Cargo-Bwjf7IkR.js';
3
3
 
4
4
  const WorkerAction = Object.freeze({
5
5
  INIT: "init",
@@ -874,6 +874,11 @@ class TransactionsResource {
874
874
  ({ accountId, request } = await this.#buildSwapRequest(opts, wasm));
875
875
  break;
876
876
  }
877
+ case "custom": {
878
+ accountId = resolveAccountRef(opts.account, wasm);
879
+ request = opts.request;
880
+ break;
881
+ }
877
882
  default:
878
883
  throw new Error(`Unknown preview operation: ${opts.operation}`);
879
884
  }
@@ -1190,8 +1195,13 @@ class TransactionsResource {
1190
1195
  async #submitOrSubmitWithProver(accountId, request, perCallProver) {
1191
1196
  const result = await this.#inner.executeTransaction(accountId, request);
1192
1197
  const prover = perCallProver ?? this.#client.defaultProver;
1198
+ // Use proveTransactionWithProver (by-reference) when a prover is
1199
+ // provided, so the JS-side handle is NOT consumed by wasm-bindgen.
1200
+ // Passing the prover by value would transfer ownership and invalidate
1201
+ // the JS object after one call, causing silent fallback to local
1202
+ // proving on reuse.
1193
1203
  const proven = prover
1194
- ? await this.#inner.proveTransaction(result, prover)
1204
+ ? await this.#inner.proveTransactionWithProver(result, prover)
1195
1205
  : await this.#inner.proveTransaction(result);
1196
1206
  const txId = result.id();
1197
1207
  const height = await this.#inner.submitProvenTransaction(proven, result);
@@ -1630,6 +1640,32 @@ class MidenClient {
1630
1640
  });
1631
1641
  }
1632
1642
 
1643
+ /**
1644
+ * Resolves once the WASM module is initialized and safe to use.
1645
+ *
1646
+ * Idempotent and shared across callers: the underlying loader memoizes the
1647
+ * in-flight promise, so concurrent `ready()` calls await the same
1648
+ * initialization and post-init callers resolve immediately from a cached
1649
+ * module. Safe to call from `MidenProvider`, tutorial helpers, and any
1650
+ * other consumer simultaneously.
1651
+ *
1652
+ * Useful on the `/lazy` entry (e.g. Next.js / Capacitor), where no
1653
+ * top-level await runs at import time. On the default (eager) entry this
1654
+ * is redundant — importing the module already awaits WASM — but calling it
1655
+ * is still harmless.
1656
+ *
1657
+ * @returns {Promise<void>} Resolves when WASM is initialized.
1658
+ */
1659
+ static async ready() {
1660
+ const getWasm = MidenClient._getWasmOrThrow;
1661
+ if (!getWasm) {
1662
+ throw new Error(
1663
+ "MidenClient not initialized. Import from the SDK package entry point."
1664
+ );
1665
+ }
1666
+ await getWasm();
1667
+ }
1668
+
1633
1669
  /**
1634
1670
  * Creates a mock client for testing.
1635
1671
  *
@@ -1686,6 +1722,54 @@ class MidenClient {
1686
1722
  return await this.#inner.getSyncHeight();
1687
1723
  }
1688
1724
 
1725
+ /**
1726
+ * Resolves once every serialized WASM call that was already on the
1727
+ * internal `_serializeWasmCall` chain when `waitForIdle()` was called
1728
+ * (execute, submit, prove, apply, sync, or account creation) has
1729
+ * settled. Use this from callers that need to perform a non-WASM-side
1730
+ * action — e.g. clearing an in-memory auth key on wallet lock — after
1731
+ * the kernel finishes, so its auth callback doesn't race with the key
1732
+ * being cleared.
1733
+ *
1734
+ * Does NOT wait for calls enqueued after `waitForIdle()` returns —
1735
+ * intentional, so a caller can drain and proceed without being blocked
1736
+ * indefinitely by concurrent workload.
1737
+ *
1738
+ * Caveat for `syncState`: `syncStateWithTimeout` awaits the sync lock
1739
+ * (`acquireSyncLock`, which uses Web Locks) BEFORE putting its WASM
1740
+ * call onto the chain, so a `syncState` that is queued on the sync
1741
+ * lock — but has not yet begun its WASM phase — is not visible to
1742
+ * `waitForIdle` and will not be awaited. Other methods (`newWallet`,
1743
+ * `executeTransaction`, etc.) route through the chain synchronously
1744
+ * on call and are always observed.
1745
+ *
1746
+ * Safe to call at any time; returns immediately if nothing was in
1747
+ * flight.
1748
+ *
1749
+ * @returns {Promise<void>}
1750
+ */
1751
+ async waitForIdle() {
1752
+ this.assertNotTerminated();
1753
+ await this.#inner.waitForIdle();
1754
+ }
1755
+
1756
+ /**
1757
+ * Returns the raw JS value that the most recent sign-callback invocation
1758
+ * threw, or `null` if the last sign call succeeded (or no call has
1759
+ * happened yet).
1760
+ *
1761
+ * Useful for recovering structured metadata (e.g. a `reason: 'locked'`
1762
+ * property) that the kernel-level `auth::request` diagnostic would
1763
+ * otherwise erase. Call immediately after catching a failed
1764
+ * `transactions.submit` / `transactions.send` / `transactions.consume`.
1765
+ *
1766
+ * @returns {any} The raw thrown value, or `null`.
1767
+ */
1768
+ lastAuthError() {
1769
+ this.assertNotTerminated();
1770
+ return this.#inner.lastAuthError();
1771
+ }
1772
+
1689
1773
  /**
1690
1774
  * Terminates the underlying Web Worker. After this, all method calls will throw.
1691
1775
  */
@@ -1956,6 +2040,43 @@ const Linking = Object.freeze({
1956
2040
  Static: "static",
1957
2041
  });
1958
2042
 
2043
+ // Method classification sets — used by scripts/check-method-classification.js to ensure
2044
+ // every WASM export is explicitly categorised. Update when adding new WASM methods.
2045
+ //
2046
+ // Note on `SYNC_METHODS`: the classifier is "synchronous in JS" — i.e.
2047
+ // `pub fn ...` in Rust, not `pub async fn ...`. Two sub-cases:
2048
+ // 1. Factory methods that return a non-Promise value (`accountReader`
2049
+ // returns `AccountReader`; the transaction-request builders return
2050
+ // `TransactionRequestBuilder`; `createCodeBuilder` returns a builder).
2051
+ // Wrapping these in `_serializeWasmCall` would turn their return
2052
+ // value into `Promise<T>` and break callers that use the result
2053
+ // immediately (e.g. `const reader = client.accountReader(id);
2054
+ // await reader.nonce();`).
2055
+ // 2. Sync methods that still take `&mut self` in Rust (`proveBlock`,
2056
+ // `serializeMockChain`, `setDebugMode`). Safe to opt out because JS
2057
+ // is single-threaded — the event loop cannot interleave another
2058
+ // call during their synchronous execution, so the RefCell borrow
2059
+ // is always released before any other borrow can start.
2060
+ // Do NOT move a sync-in-JS method into `WRITE_METHODS` / `READ_METHODS`
2061
+ // just because it takes `&mut self` or `&self`; wrapping changes its
2062
+ // return shape and breaks every caller.
2063
+ const SYNC_METHODS = new Set([
2064
+ "accountReader",
2065
+ "buildSwapTag",
2066
+ "createCodeBuilder",
2067
+ "lastAuthError",
2068
+ "newConsumeTransactionRequest",
2069
+ "newMintTransactionRequest",
2070
+ "newSendTransactionRequest",
2071
+ "newSwapTransactionRequest",
2072
+ "proveBlock",
2073
+ "serializeMockChain",
2074
+ "serializeMockNoteTransportNode",
2075
+ "setDebugMode",
2076
+ "storeIdentifier",
2077
+ "usesMockChain",
2078
+ ]);
2079
+
1959
2080
  const MOCK_STORE_NAME = "mock_client_db";
1960
2081
 
1961
2082
  const buildTypedArraysExport = (exportObject) => {
@@ -2056,6 +2177,16 @@ const getWasmOrThrow = async () => {
2056
2177
  /**
2057
2178
  * Create a Proxy that forwards missing properties to the underlying WASM
2058
2179
  * WebClient.
2180
+ *
2181
+ * Async proxy-fallback methods (every WASM method that borrows the
2182
+ * WebClient's RefCell — reads included, since `&self` and `&mut self` both
2183
+ * trip wasm-bindgen's "recursive use of an object detected" panic if
2184
+ * another borrow is live) are routed through `_serializeWasmCall` so they
2185
+ * queue on the same chain as the explicitly-wrapped methods.
2186
+ *
2187
+ * `SYNC_METHODS` opts out: they are synchronous in JS and wrapping them
2188
+ * would change their return type to `Promise<T>`, which is a breaking
2189
+ * change for consumers that use them as plain getters or builders.
2059
2190
  */
2060
2191
  function createClientProxy(instance) {
2061
2192
  return new Proxy(instance, {
@@ -2066,7 +2197,13 @@ function createClientProxy(instance) {
2066
2197
  if (target.wasmWebClient && prop in target.wasmWebClient) {
2067
2198
  const value = target.wasmWebClient[prop];
2068
2199
  if (typeof value === "function") {
2069
- return value.bind(target.wasmWebClient);
2200
+ if (typeof prop === "string" && SYNC_METHODS.has(prop)) {
2201
+ return value.bind(target.wasmWebClient);
2202
+ }
2203
+ return (...args) =>
2204
+ target._serializeWasmCall(() =>
2205
+ value.apply(target.wasmWebClient, args)
2206
+ );
2070
2207
  }
2071
2208
  return value;
2072
2209
  }
@@ -2297,6 +2434,13 @@ class WebClient {
2297
2434
  * Serialize a WASM call that requires exclusive (&mut self) access.
2298
2435
  * Concurrent calls are queued and executed one at a time.
2299
2436
  *
2437
+ * Wraps both the direct (in-thread) path and the worker-dispatched path.
2438
+ * On the worker path this is redundant with the worker's own message queue,
2439
+ * but harmless (the chain resolves immediately on the main thread once the
2440
+ * worker's postMessage returns). On the direct path it is load-bearing —
2441
+ * without it, concurrent main-thread callers would panic with
2442
+ * "recursive use of an object detected" (wasm-bindgen's internal RefCell).
2443
+ *
2300
2444
  * @param {() => Promise<any>} fn - The async function to execute.
2301
2445
  * @returns {Promise<any>} The result of fn.
2302
2446
  */
@@ -2306,6 +2450,35 @@ class WebClient {
2306
2450
  return result;
2307
2451
  }
2308
2452
 
2453
+ /**
2454
+ * Returns a promise that resolves once every serialized WASM call that
2455
+ * was already on `_wasmCallChain` when `waitForIdle()` was called has
2456
+ * settled. Use this from callers that need to perform a non-WASM-side
2457
+ * action (e.g. clear an in-memory auth key) AFTER any in-flight
2458
+ * execute / submit / sync has completed, so the WASM kernel's auth
2459
+ * callback doesn't race with the key being cleared.
2460
+ *
2461
+ * Does NOT wait for calls enqueued after `waitForIdle()` returns —
2462
+ * this is intentional, so a caller can drain and then proceed without
2463
+ * being blocked indefinitely by a concurrent workload.
2464
+ *
2465
+ * Caveat for `syncState`: `syncStateWithTimeout` awaits
2466
+ * `acquireSyncLock` (Web Locks) BEFORE wrapping its WASM call in
2467
+ * `_serializeWasmCall`, so a sync that is queued on the sync lock but
2468
+ * has not yet reached its WASM phase is not on the chain and will not
2469
+ * be awaited. Every other serialized method (`executeTransaction`,
2470
+ * `newWallet`, `submitNewTransaction`, `proveTransaction`,
2471
+ * `applyTransaction`, and the proxy-fallback reads) routes through
2472
+ * the chain synchronously on call and is always observed.
2473
+ *
2474
+ * @returns {Promise<void>}
2475
+ */
2476
+ async waitForIdle() {
2477
+ // Chain on `_wasmCallChain`; by the time this resolves, any in-flight
2478
+ // serialized call has settled. Catch so the chain state doesn't leak.
2479
+ await this._wasmCallChain.catch(() => {});
2480
+ }
2481
+
2309
2482
  // TODO: This will soon conflict with some changes in main.
2310
2483
  // More context here:
2311
2484
  // https://github.com/0xMiden/miden-client/pull/1645?notification_referrer_id=NT_kwHOA1yg7NoAJVJlcG9zaXRvcnk7NjU5MzQzNzAyO0lzc3VlOzM3OTY4OTU1Nzk&notifications_query=is%3Aunread#discussion_r2696075480
@@ -2511,147 +2684,171 @@ class WebClient {
2511
2684
  }
2512
2685
 
2513
2686
  async submitNewTransaction(accountId, transactionRequest) {
2514
- try {
2515
- if (!this.worker) {
2516
- const wasmWebClient = await this.getWasmWebClient();
2517
- return await wasmWebClient.submitNewTransaction(
2518
- accountId,
2519
- transactionRequest
2520
- );
2521
- }
2687
+ return this._serializeWasmCall(async () => {
2688
+ try {
2689
+ if (!this.worker) {
2690
+ const wasmWebClient = await this.getWasmWebClient();
2691
+ return await wasmWebClient.submitNewTransaction(
2692
+ accountId,
2693
+ transactionRequest
2694
+ );
2695
+ }
2522
2696
 
2523
- const wasm = await getWasmOrThrow();
2524
- const serializedTransactionRequest = transactionRequest.serialize();
2525
- const result = await this.callMethodWithWorker(
2526
- MethodName.SUBMIT_NEW_TRANSACTION,
2527
- accountId.toString(),
2528
- serializedTransactionRequest
2529
- );
2697
+ const wasm = await getWasmOrThrow();
2698
+ const serializedTransactionRequest = transactionRequest.serialize();
2699
+ const result = await this.callMethodWithWorker(
2700
+ MethodName.SUBMIT_NEW_TRANSACTION,
2701
+ accountId.toString(),
2702
+ serializedTransactionRequest
2703
+ );
2530
2704
 
2531
- const transactionResult = wasm.TransactionResult.deserialize(
2532
- new Uint8Array(result.serializedTransactionResult)
2533
- );
2705
+ const transactionResult = wasm.TransactionResult.deserialize(
2706
+ new Uint8Array(result.serializedTransactionResult)
2707
+ );
2534
2708
 
2535
- return transactionResult.id();
2536
- } catch (error) {
2537
- console.error("INDEX.JS: Error in submitNewTransaction:", error);
2538
- throw error;
2539
- }
2709
+ return transactionResult.id();
2710
+ } catch (error) {
2711
+ console.error("INDEX.JS: Error in submitNewTransaction:", error);
2712
+ throw error;
2713
+ }
2714
+ });
2540
2715
  }
2541
2716
 
2542
2717
  async submitNewTransactionWithProver(accountId, transactionRequest, prover) {
2543
- try {
2544
- if (!this.worker) {
2545
- const wasmWebClient = await this.getWasmWebClient();
2546
- return await wasmWebClient.submitNewTransactionWithProver(
2547
- accountId,
2548
- transactionRequest,
2549
- prover
2550
- );
2551
- }
2718
+ return this._serializeWasmCall(async () => {
2719
+ try {
2720
+ if (!this.worker) {
2721
+ const wasmWebClient = await this.getWasmWebClient();
2722
+ return await wasmWebClient.submitNewTransactionWithProver(
2723
+ accountId,
2724
+ transactionRequest,
2725
+ prover
2726
+ );
2727
+ }
2552
2728
 
2553
- const wasm = await getWasmOrThrow();
2554
- const serializedTransactionRequest = transactionRequest.serialize();
2555
- const proverPayload = prover.serialize();
2556
- const result = await this.callMethodWithWorker(
2557
- MethodName.SUBMIT_NEW_TRANSACTION_WITH_PROVER,
2558
- accountId.toString(),
2559
- serializedTransactionRequest,
2560
- proverPayload
2561
- );
2729
+ const wasm = await getWasmOrThrow();
2730
+ const serializedTransactionRequest = transactionRequest.serialize();
2731
+ const proverPayload = prover.serialize();
2732
+ const result = await this.callMethodWithWorker(
2733
+ MethodName.SUBMIT_NEW_TRANSACTION_WITH_PROVER,
2734
+ accountId.toString(),
2735
+ serializedTransactionRequest,
2736
+ proverPayload
2737
+ );
2562
2738
 
2563
- const transactionResult = wasm.TransactionResult.deserialize(
2564
- new Uint8Array(result.serializedTransactionResult)
2565
- );
2739
+ const transactionResult = wasm.TransactionResult.deserialize(
2740
+ new Uint8Array(result.serializedTransactionResult)
2741
+ );
2566
2742
 
2567
- return transactionResult.id();
2568
- } catch (error) {
2569
- console.error(
2570
- "INDEX.JS: Error in submitNewTransactionWithProver:",
2571
- error
2572
- );
2573
- throw error;
2574
- }
2743
+ return transactionResult.id();
2744
+ } catch (error) {
2745
+ console.error(
2746
+ "INDEX.JS: Error in submitNewTransactionWithProver:",
2747
+ error
2748
+ );
2749
+ throw error;
2750
+ }
2751
+ });
2575
2752
  }
2576
2753
 
2577
2754
  async executeTransaction(accountId, transactionRequest) {
2578
- try {
2579
- if (!this.worker) {
2580
- const wasmWebClient = await this.getWasmWebClient();
2581
- return await wasmWebClient.executeTransaction(
2582
- accountId,
2583
- transactionRequest
2584
- );
2585
- }
2755
+ return this._serializeWasmCall(async () => {
2756
+ try {
2757
+ if (!this.worker) {
2758
+ const wasmWebClient = await this.getWasmWebClient();
2759
+ return await wasmWebClient.executeTransaction(
2760
+ accountId,
2761
+ transactionRequest
2762
+ );
2763
+ }
2586
2764
 
2587
- const wasm = await getWasmOrThrow();
2588
- const serializedTransactionRequest = transactionRequest.serialize();
2589
- const serializedResultBytes = await this.callMethodWithWorker(
2590
- MethodName.EXECUTE_TRANSACTION,
2591
- accountId.toString(),
2592
- serializedTransactionRequest
2593
- );
2765
+ const wasm = await getWasmOrThrow();
2766
+ const serializedTransactionRequest = transactionRequest.serialize();
2767
+ const serializedResultBytes = await this.callMethodWithWorker(
2768
+ MethodName.EXECUTE_TRANSACTION,
2769
+ accountId.toString(),
2770
+ serializedTransactionRequest
2771
+ );
2594
2772
 
2595
- return wasm.TransactionResult.deserialize(
2596
- new Uint8Array(serializedResultBytes)
2597
- );
2598
- } catch (error) {
2599
- console.error("INDEX.JS: Error in executeTransaction:", error);
2600
- throw error;
2601
- }
2773
+ return wasm.TransactionResult.deserialize(
2774
+ new Uint8Array(serializedResultBytes)
2775
+ );
2776
+ } catch (error) {
2777
+ console.error("INDEX.JS: Error in executeTransaction:", error);
2778
+ throw error;
2779
+ }
2780
+ });
2602
2781
  }
2603
2782
 
2604
2783
  async proveTransaction(transactionResult, prover) {
2605
- try {
2606
- if (!this.worker) {
2607
- const wasmWebClient = await this.getWasmWebClient();
2608
- return await wasmWebClient.proveTransaction(transactionResult, prover);
2609
- }
2784
+ return this._serializeWasmCall(async () => {
2785
+ try {
2786
+ if (!this.worker) {
2787
+ const wasmWebClient = await this.getWasmWebClient();
2788
+ return prover
2789
+ ? await wasmWebClient.proveTransactionWithProver(
2790
+ transactionResult,
2791
+ prover
2792
+ )
2793
+ : await wasmWebClient.proveTransaction(transactionResult);
2794
+ }
2610
2795
 
2611
- const wasm = await getWasmOrThrow();
2612
- const serializedTransactionResult = transactionResult.serialize();
2613
- const proverPayload = prover ? prover.serialize() : null;
2796
+ const wasm = await getWasmOrThrow();
2797
+ const serializedTransactionResult = transactionResult.serialize();
2798
+ const proverPayload = prover ? prover.serialize() : null;
2614
2799
 
2615
- const serializedProvenBytes = await this.callMethodWithWorker(
2616
- MethodName.PROVE_TRANSACTION,
2617
- serializedTransactionResult,
2618
- proverPayload
2619
- );
2800
+ const serializedProvenBytes = await this.callMethodWithWorker(
2801
+ MethodName.PROVE_TRANSACTION,
2802
+ serializedTransactionResult,
2803
+ proverPayload
2804
+ );
2620
2805
 
2621
- return wasm.ProvenTransaction.deserialize(
2622
- new Uint8Array(serializedProvenBytes)
2623
- );
2624
- } catch (error) {
2625
- console.error("INDEX.JS: Error in proveTransaction:", error);
2626
- throw error;
2627
- }
2806
+ return wasm.ProvenTransaction.deserialize(
2807
+ new Uint8Array(serializedProvenBytes)
2808
+ );
2809
+ } catch (error) {
2810
+ console.error("INDEX.JS: Error in proveTransaction:", error);
2811
+ throw error;
2812
+ }
2813
+ });
2814
+ }
2815
+
2816
+ // Delegates to `proveTransaction`, which already routes through
2817
+ // `_serializeWasmCall` and dispatches to the WASM `proveTransactionWithProver`
2818
+ // binding when `prover` is present. Kept as a wrapper (rather than elided)
2819
+ // so the method classification lint sees an explicit match for the WASM
2820
+ // method name.
2821
+ async proveTransactionWithProver(transactionResult, prover) {
2822
+ return this.proveTransaction(transactionResult, prover);
2628
2823
  }
2629
2824
 
2630
2825
  async applyTransaction(transactionResult, submissionHeight) {
2631
- try {
2632
- if (!this.worker) {
2633
- const wasmWebClient = await this.getWasmWebClient();
2634
- return await wasmWebClient.applyTransaction(
2635
- transactionResult,
2826
+ return this._serializeWasmCall(async () => {
2827
+ try {
2828
+ if (!this.worker) {
2829
+ const wasmWebClient = await this.getWasmWebClient();
2830
+ return await wasmWebClient.applyTransaction(
2831
+ transactionResult,
2832
+ submissionHeight
2833
+ );
2834
+ }
2835
+
2836
+ const wasm = await getWasmOrThrow();
2837
+ const serializedTransactionResult = transactionResult.serialize();
2838
+ const serializedUpdateBytes = await this.callMethodWithWorker(
2839
+ MethodName.APPLY_TRANSACTION,
2840
+ serializedTransactionResult,
2636
2841
  submissionHeight
2637
2842
  );
2638
- }
2639
-
2640
- const wasm = await getWasmOrThrow();
2641
- const serializedTransactionResult = transactionResult.serialize();
2642
- const serializedUpdateBytes = await this.callMethodWithWorker(
2643
- MethodName.APPLY_TRANSACTION,
2644
- serializedTransactionResult,
2645
- submissionHeight
2646
- );
2647
2843
 
2648
- return wasm.TransactionStoreUpdate.deserialize(
2649
- new Uint8Array(serializedUpdateBytes)
2650
- );
2651
- } catch (error) {
2652
- console.error("INDEX.JS: Error in applyTransaction:", error);
2653
- throw error;
2654
- }
2844
+ return wasm.TransactionStoreUpdate.deserialize(
2845
+ new Uint8Array(serializedUpdateBytes)
2846
+ );
2847
+ } catch (error) {
2848
+ console.error("INDEX.JS: Error in applyTransaction:", error);
2849
+ throw error;
2850
+ }
2851
+ });
2655
2852
  }
2656
2853
 
2657
2854
  /**
@@ -2690,21 +2887,25 @@ class WebClient {
2690
2887
  return lockHandle.coalescedResult;
2691
2888
  }
2692
2889
 
2693
- // We acquired the lock - perform the sync
2890
+ // We acquired the lock - perform the sync. Wrap the actual WASM
2891
+ // call in _serializeWasmCall so it can't race with any other
2892
+ // mutating method (executeTransaction, submitNewTransaction, etc.)
2893
+ // on the same WebClient. The outer coalescing lock stays in place
2894
+ // so concurrent syncState callers still share one in-flight sync.
2694
2895
  try {
2695
- let result;
2696
- if (!this.worker) {
2697
- const wasmWebClient = await this.getWasmWebClient();
2698
- result = await wasmWebClient.syncStateImpl();
2699
- } else {
2896
+ const result = await this._serializeWasmCall(async () => {
2897
+ if (!this.worker) {
2898
+ const wasmWebClient = await this.getWasmWebClient();
2899
+ return await wasmWebClient.syncStateImpl();
2900
+ }
2700
2901
  const wasm = await getWasmOrThrow();
2701
2902
  const serializedSyncSummaryBytes = await this.callMethodWithWorker(
2702
2903
  MethodName.SYNC_STATE
2703
2904
  );
2704
- result = wasm.SyncSummary.deserialize(
2905
+ return wasm.SyncSummary.deserialize(
2705
2906
  new Uint8Array(serializedSyncSummaryBytes)
2706
2907
  );
2707
- }
2908
+ });
2708
2909
 
2709
2910
  // Release the lock with the result
2710
2911
  releaseSyncLock(dbId, result);