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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +139 -9
  2. package/dist/mt/Cargo-smJQCGjz.js +26146 -0
  3. package/dist/mt/Cargo-smJQCGjz.js.map +1 -0
  4. package/dist/{api-types.d.ts → mt/api-types.d.ts} +154 -4
  5. package/dist/mt/assets/miden_client_web.wasm +0 -0
  6. package/dist/mt/crates/miden_client_web.d.ts +4791 -0
  7. package/dist/mt/eager.js +38 -0
  8. package/dist/mt/eager.js.map +1 -0
  9. package/dist/{index.d.ts → mt/index.d.ts} +4 -2
  10. package/dist/mt/index.js +3778 -0
  11. package/dist/mt/index.js.map +1 -0
  12. package/dist/{wasm.js → mt/wasm.js} +1 -1
  13. package/dist/mt/wasm.js.map +1 -0
  14. package/dist/mt/workerHelpers.js +28 -0
  15. package/dist/mt/workers/Cargo-smJQCGjz-q4GYXDiD.js +26147 -0
  16. package/dist/mt/workers/Cargo-smJQCGjz-q4GYXDiD.js.map +1 -0
  17. package/dist/mt/workers/assets/miden_client_web.wasm +0 -0
  18. package/dist/mt/workers/web-client-methods-worker.js +26885 -0
  19. package/dist/mt/workers/web-client-methods-worker.js.map +1 -0
  20. package/dist/{workers → mt/workers}/web-client-methods-worker.module.js +17 -1
  21. package/dist/mt/workers/web-client-methods-worker.module.js.map +1 -0
  22. package/dist/mt/workers/workerHelpers.js +28 -0
  23. package/dist/{Cargo-CVlXCH_2.js → st/Cargo-CG4XszZo.js} +698 -495
  24. package/dist/st/Cargo-CG4XszZo.js.map +1 -0
  25. package/dist/st/api-types.d.ts +1140 -0
  26. package/dist/{workers → st}/assets/miden_client_web.wasm +0 -0
  27. package/dist/{crates → st/crates}/miden_client_web.d.ts +342 -196
  28. package/dist/st/docs-entry.d.ts +38 -0
  29. package/dist/{eager.js → st/eager.js} +1 -1
  30. package/dist/st/eager.js.map +1 -0
  31. package/dist/st/index.d.ts +182 -0
  32. package/dist/{index.js → st/index.js} +425 -19
  33. package/dist/st/index.js.map +1 -0
  34. package/dist/st/wasm.js +23 -0
  35. package/dist/st/wasm.js.map +1 -0
  36. package/dist/{workers/Cargo-CVlXCH_2-CWA-5vlh.js → st/workers/Cargo-CG4XszZo-S7EHAZSa.js} +698 -495
  37. package/dist/st/workers/Cargo-CG4XszZo-S7EHAZSa.js.map +1 -0
  38. package/dist/{assets → st/workers/assets}/miden_client_web.wasm +0 -0
  39. package/dist/{workers → st/workers}/web-client-methods-worker.js +715 -496
  40. package/dist/st/workers/web-client-methods-worker.js.map +1 -0
  41. package/dist/st/workers/web-client-methods-worker.module.js +575 -0
  42. package/dist/st/workers/web-client-methods-worker.module.js.map +1 -0
  43. package/js/client.js +166 -2
  44. package/js/node/napi-compat.js +15 -0
  45. package/js/node-index.js +0 -1
  46. package/js/resources/accounts.js +4 -6
  47. package/js/resources/transactions.js +134 -0
  48. package/lazy/package.json +2 -2
  49. package/mt/lazy/package.json +4 -0
  50. package/mt/package.json +4 -0
  51. package/package.json +30 -15
  52. package/dist/Cargo-CVlXCH_2.js.map +0 -1
  53. package/dist/eager.js.map +0 -1
  54. package/dist/index.js.map +0 -1
  55. package/dist/wasm.js.map +0 -1
  56. package/dist/workers/Cargo-CVlXCH_2-CWA-5vlh.js.map +0 -1
  57. package/dist/workers/web-client-methods-worker.js.map +0 -1
  58. package/dist/workers/web-client-methods-worker.module.js.map +0 -1
  59. /package/dist/{docs-entry.d.ts → mt/docs-entry.d.ts} +0 -0
@@ -1,9 +1,10 @@
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, NetworkNoteStatusInfo, 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, StorageMapEntryJs, 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-CVlXCH_2.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, NetworkNoteStatusInfo, NetworkType, Note, NoteAndArgs, NoteAndArgsArray, NoteArray, NoteAssets, NoteAttachment, 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, StorageMapEntryJs, 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, sequentialSumBench, setupLogging } from './Cargo-CG4XszZo.js';
3
3
 
4
4
  const WorkerAction = Object.freeze({
5
5
  INIT: "init",
6
6
  INIT_MOCK: "initMock",
7
+ INIT_THREAD_POOL: "initThreadPool",
7
8
  CALL_METHOD: "callMethod",
8
9
  EXECUTE_CALLBACK: "executeCallback",
9
10
  });
@@ -504,6 +505,7 @@ class AccountsResource {
504
505
  return await this.#inner.newFaucet(
505
506
  storageMode,
506
507
  type === 1 || type === "NonFungibleFaucet",
508
+ opts.name ?? opts.symbol,
507
509
  opts.symbol,
508
510
  opts.decimals,
509
511
  BigInt(opts.maxSupply),
@@ -536,11 +538,9 @@ class AccountsResource {
536
538
  if (!opts.auth)
537
539
  throw new Error("Contract creation requires an 'auth' (AuthSecretKey)");
538
540
 
539
- // Default to immutable when type is omitted (safer for contracts)
540
- const mutable = opts.type === "MutableContract" || opts.type === 3;
541
- const accountTypeEnum = mutable
542
- ? wasm.AccountType.RegularAccountUpdatableCode
543
- : wasm.AccountType.RegularAccountImmutableCode;
541
+ // The 0.15 protocol has no code-mutability distinction, so the `type`
542
+ // ("ImmutableContract" / "MutableContract") only steers routing here; the
543
+ // account's on-chain visibility is set entirely by `storageMode`.
544
544
  const storageMode = resolveStorageMode(opts.storage ?? "public", wasm);
545
545
  const authComponent =
546
546
  wasm.AccountComponent.createAuthComponentFromSecretKey(opts.auth);
@@ -555,7 +555,6 @@ class AccountsResource {
555
555
  }
556
556
 
557
557
  let builder = new wasm.AccountBuilder(opts.seed)
558
- .accountType(accountTypeEnum)
559
558
  .storageMode(storageMode)
560
559
  .withAuthComponent(authComponent);
561
560
 
@@ -866,6 +865,72 @@ class TransactionsResource {
866
865
  return { txId, result };
867
866
  }
868
867
 
868
+ /** Create a partial-swap (PSWAP) note. See {@link PswapCreateOptions}. */
869
+ async pswapCreate(opts) {
870
+ this.#client.assertNotTerminated();
871
+ const wasm = await this.#getWasm();
872
+ const { accountId, request } = await this.#buildPswapCreateRequest(
873
+ opts,
874
+ wasm
875
+ );
876
+
877
+ const { txId, result } = await this.#submitOrSubmitWithProver(
878
+ accountId,
879
+ request,
880
+ opts.prover
881
+ );
882
+
883
+ if (opts.waitForConfirmation) {
884
+ await this.waitFor(txId.toHex(), { timeout: opts.timeout });
885
+ }
886
+
887
+ return { txId, result };
888
+ }
889
+
890
+ /** Consume (fully or partially fill) a PSWAP note. See {@link PswapConsumeOptions}. */
891
+ async pswapConsume(opts) {
892
+ this.#client.assertNotTerminated();
893
+ const wasm = await this.#getWasm();
894
+ const { accountId, request } = await this.#buildPswapConsumeRequest(
895
+ opts,
896
+ wasm
897
+ );
898
+
899
+ const { txId, result } = await this.#submitOrSubmitWithProver(
900
+ accountId,
901
+ request,
902
+ opts.prover
903
+ );
904
+
905
+ if (opts.waitForConfirmation) {
906
+ await this.waitFor(txId.toHex(), { timeout: opts.timeout });
907
+ }
908
+
909
+ return { txId, result };
910
+ }
911
+
912
+ /** Cancel a PSWAP note as its creator and reclaim the offered asset. See {@link PswapCancelOptions}. */
913
+ async pswapCancel(opts) {
914
+ this.#client.assertNotTerminated();
915
+ const wasm = await this.#getWasm();
916
+ const { accountId, request } = await this.#buildPswapCancelRequest(
917
+ opts,
918
+ wasm
919
+ );
920
+
921
+ const { txId, result } = await this.#submitOrSubmitWithProver(
922
+ accountId,
923
+ request,
924
+ opts.prover
925
+ );
926
+
927
+ if (opts.waitForConfirmation) {
928
+ await this.waitFor(txId.toHex(), { timeout: opts.timeout });
929
+ }
930
+
931
+ return { txId, result };
932
+ }
933
+
869
934
  async preview(opts) {
870
935
  this.#client.assertNotTerminated();
871
936
  const wasm = await this.#getWasm();
@@ -890,6 +955,27 @@ class TransactionsResource {
890
955
  ({ accountId, request } = await this.#buildSwapRequest(opts, wasm));
891
956
  break;
892
957
  }
958
+ case "pswapCreate": {
959
+ ({ accountId, request } = await this.#buildPswapCreateRequest(
960
+ opts,
961
+ wasm
962
+ ));
963
+ break;
964
+ }
965
+ case "pswapConsume": {
966
+ ({ accountId, request } = await this.#buildPswapConsumeRequest(
967
+ opts,
968
+ wasm
969
+ ));
970
+ break;
971
+ }
972
+ case "pswapCancel": {
973
+ ({ accountId, request } = await this.#buildPswapCancelRequest(
974
+ opts,
975
+ wasm
976
+ ));
977
+ break;
978
+ }
893
979
  case "custom": {
894
980
  accountId = resolveAccountRef(opts.account, wasm);
895
981
  request = opts.request;
@@ -1176,6 +1262,53 @@ class TransactionsResource {
1176
1262
  return { accountId, request };
1177
1263
  }
1178
1264
 
1265
+ async #buildPswapCreateRequest(opts, wasm) {
1266
+ const accountId = resolveAccountRef(opts.account, wasm);
1267
+ const offeredFaucetId = resolveAccountRef(opts.offer.token, wasm);
1268
+ const requestedFaucetId = resolveAccountRef(opts.request.token, wasm);
1269
+ const noteType = resolveNoteType(opts.type, wasm);
1270
+ const paybackNoteType = resolveNoteType(
1271
+ opts.paybackType ?? opts.type,
1272
+ wasm
1273
+ );
1274
+
1275
+ const request = await this.#inner.newPswapCreateTransactionRequest(
1276
+ accountId,
1277
+ offeredFaucetId,
1278
+ BigInt(opts.offer.amount),
1279
+ requestedFaucetId,
1280
+ BigInt(opts.request.amount),
1281
+ noteType,
1282
+ paybackNoteType
1283
+ );
1284
+ return { accountId, request };
1285
+ }
1286
+
1287
+ async #buildPswapConsumeRequest(opts, wasm) {
1288
+ const accountId = resolveAccountRef(opts.account, wasm);
1289
+ const note = await this.#resolveNoteInput(opts.note);
1290
+ const noteFillAmount = opts.noteFillAmount ?? 0n;
1291
+
1292
+ const request = await this.#inner.newPswapConsumeTransactionRequest(
1293
+ note,
1294
+ accountId,
1295
+ BigInt(opts.fillAmount),
1296
+ BigInt(noteFillAmount)
1297
+ );
1298
+ return { accountId, request };
1299
+ }
1300
+
1301
+ async #buildPswapCancelRequest(opts, wasm) {
1302
+ const accountId = resolveAccountRef(opts.account, wasm);
1303
+ const note = await this.#resolveNoteInput(opts.note);
1304
+
1305
+ const request = await this.#inner.newPswapCancelTransactionRequest(
1306
+ note,
1307
+ accountId
1308
+ );
1309
+ return { accountId, request };
1310
+ }
1311
+
1179
1312
  async #resolveNoteInput(input) {
1180
1313
  if (typeof input === "string") {
1181
1314
  const record = await this.#inner.getInputNote(input);
@@ -1565,6 +1698,80 @@ class MidenClient {
1565
1698
  this.keystore = new KeystoreResource(inner, this);
1566
1699
  }
1567
1700
 
1701
+ /**
1702
+ * Escape hatch: runs `fn` with exclusive access to the proxied JS
1703
+ * WebClient that backs this MidenClient.
1704
+ *
1705
+ * The proxy forwards missing properties to the underlying wasm-bindgen
1706
+ * `WebClient`, so `fn` can reach lower-level methods like
1707
+ * `executeTransaction`, `proveTransaction[WithProver]`,
1708
+ * `submitProvenTransaction`, `applyTransaction`,
1709
+ * `newSendTransactionRequest`, `newConsumeTransactionRequest`, etc.
1710
+ *
1711
+ * Intended for advanced consumers that need to split the bundled
1712
+ * execute → prove → submit → apply pipeline across contexts — for example,
1713
+ * a Chrome MV3 extension that runs `executeTransaction` in its service
1714
+ * worker, dispatches the prove step to a `chrome.offscreen` document
1715
+ * (where wasm-bindgen-rayon can spawn a real thread pool), then runs
1716
+ * `submitProvenTransaction` + `applyTransaction` back in the SW.
1717
+ *
1718
+ * The callback runs inside `_serializeWasmCall`, so the WASM RefCell is
1719
+ * held for the duration of `fn`. Concurrent SDK calls (sync, other
1720
+ * transactions, etc.) queue on the same chain and run after `fn`
1721
+ * settles. Without this serialization, raw inner-client access would
1722
+ * race the proxy's chain and trip wasm-bindgen's "recursive use of an
1723
+ * object detected" panic.
1724
+ *
1725
+ * Re-entrancy: while `fn` is running, the underlying client's
1726
+ * `_withInnerLockDepth` counter is bumped so that `_serializeWasmCall`
1727
+ * invocations made BY `fn` (or any proxy-dispatched method it calls)
1728
+ * run inline rather than enqueuing on the chain. Without this, every
1729
+ * `await inner.X(...)` inside `fn` would enqueue behind the outer
1730
+ * `_withInnerWebClient` slot which is itself awaiting `fn` —
1731
+ * a classic re-entrant-lock deadlock. The depth counter restores the
1732
+ * intent of the docstring above: the lock is held for the duration
1733
+ * of `fn`, and inner-client calls "borrow" that already-held lock
1734
+ * instead of trying to re-acquire it.
1735
+ *
1736
+ * SAFETY CONTRACT for re-entrancy: callers MUST hold an external
1737
+ * mutex preventing concurrent access to this same client instance
1738
+ * via other code paths during `fn`. The chain still serializes
1739
+ * against external callers — they queue behind the outer slot — but
1740
+ * if an external task runs during one of `fn`'s awaits and calls
1741
+ * into the SDK, it will see `_withInnerLockDepth > 0` and run
1742
+ * inline, racing wasm-bindgen's borrow check. The wallet pattern
1743
+ * (own outer mutex around `_withInnerWebClient`) satisfies this.
1744
+ *
1745
+ * Stability: marked `@internal`. The shape of the proxied client is
1746
+ * intentionally not part of the documented public API and may change
1747
+ * between SDK versions. If you depend on this method, pin the SDK
1748
+ * version and test the lower-level surface carefully on each upgrade.
1749
+ * If your use case is common enough to warrant a stable public API,
1750
+ * file an issue.
1751
+ *
1752
+ * @internal
1753
+ * @template T
1754
+ * @param {(inner: object) => Promise<T>} fn - Async callback receiving
1755
+ * the proxied JS WebClient. Must not return references that escape
1756
+ * the callback's lifetime (the lock is released on settle).
1757
+ * @returns {Promise<T>} The resolved value of `fn`.
1758
+ */
1759
+ _withInnerWebClient(fn) {
1760
+ this.assertNotTerminated();
1761
+ if (typeof fn !== "function") {
1762
+ throw new TypeError("_withInnerWebClient: fn must be a function");
1763
+ }
1764
+ const inner = this.#inner;
1765
+ return inner._serializeWasmCall(async () => {
1766
+ inner._withInnerLockDepth = (inner._withInnerLockDepth || 0) + 1;
1767
+ try {
1768
+ return await fn(inner);
1769
+ } finally {
1770
+ inner._withInnerLockDepth--;
1771
+ }
1772
+ });
1773
+ }
1774
+
1568
1775
  /**
1569
1776
  * Creates and initializes a new MidenClient.
1570
1777
  *
@@ -1593,6 +1800,13 @@ class MidenClient {
1593
1800
  const rpcUrl = resolveRpcUrl(options?.rpcUrl);
1594
1801
  const noteTransportUrl = resolveNoteTransportUrl(options?.noteTransportUrl);
1595
1802
 
1803
+ // `useWorker: false` opts out of the Web Worker shim that wraps every
1804
+ // WASM call. The shim exists to keep the main thread responsive in
1805
+ // browser/extension contexts, but it serializes the prover via
1806
+ // `TransactionProver.serialize()` — a format that has no encoding for
1807
+ // `newCallbackProver(jsFn)` and silently downgrades it to `"local"`.
1808
+ // Mobile/Tauri/native-prover consumers must pass `useWorker: false`.
1809
+ const useWorker = options?.useWorker;
1596
1810
  let inner;
1597
1811
  if (options?.keystore) {
1598
1812
  inner = await WebClientClass.createClientWithExternalKeystore(
@@ -1603,7 +1817,8 @@ class MidenClient {
1603
1817
  options.keystore.getKey,
1604
1818
  options.keystore.insertKey,
1605
1819
  options.keystore.sign,
1606
- options?.debugMode
1820
+ options?.debugMode,
1821
+ useWorker
1607
1822
  );
1608
1823
  } else {
1609
1824
  inner = await WebClientClass.createClient(
@@ -1611,7 +1826,8 @@ class MidenClient {
1611
1826
  noteTransportUrl,
1612
1827
  seed,
1613
1828
  options?.storeName,
1614
- options?.debugMode
1829
+ options?.debugMode,
1830
+ useWorker
1615
1831
  );
1616
1832
  }
1617
1833
 
@@ -1668,6 +1884,32 @@ class MidenClient {
1668
1884
  });
1669
1885
  }
1670
1886
 
1887
+ /**
1888
+ * Resolves once the WASM module is initialized and safe to use.
1889
+ *
1890
+ * Idempotent and shared across callers: the underlying loader memoizes the
1891
+ * in-flight promise, so concurrent `ready()` calls await the same
1892
+ * initialization and post-init callers resolve immediately from a cached
1893
+ * module. Safe to call from `MidenProvider`, tutorial helpers, and any
1894
+ * other consumer simultaneously.
1895
+ *
1896
+ * Useful on the `/lazy` entry (e.g. Next.js / Capacitor), where no
1897
+ * top-level await runs at import time. On the default (eager) entry this
1898
+ * is redundant — importing the module already awaits WASM — but calling it
1899
+ * is still harmless.
1900
+ *
1901
+ * @returns {Promise<void>} Resolves when WASM is initialized.
1902
+ */
1903
+ static async ready() {
1904
+ const getWasm = MidenClient._getWasmOrThrow;
1905
+ if (!getWasm) {
1906
+ throw new Error(
1907
+ "MidenClient not initialized. Import from the SDK package entry point."
1908
+ );
1909
+ }
1910
+ await getWasm();
1911
+ }
1912
+
1671
1913
  /**
1672
1914
  * Creates a mock client for testing.
1673
1915
  *
@@ -1724,6 +1966,61 @@ class MidenClient {
1724
1966
  return await this.#inner.getSyncHeight();
1725
1967
  }
1726
1968
 
1969
+ /**
1970
+ * Resolves once every serialized WASM call that was already on the
1971
+ * internal `_serializeWasmCall` chain when `waitForIdle()` was called
1972
+ * (execute, submit, prove, apply, sync, or account creation) has
1973
+ * settled. Use this from callers that need to perform a non-WASM-side
1974
+ * action — e.g. clearing an in-memory auth key on wallet lock — after
1975
+ * the kernel finishes, so its auth callback doesn't race with the key
1976
+ * being cleared.
1977
+ *
1978
+ * Does NOT wait for calls enqueued after `waitForIdle()` returns —
1979
+ * intentional, so a caller can drain and proceed without being blocked
1980
+ * indefinitely by concurrent workload.
1981
+ *
1982
+ * Caveat for `syncState`: `syncStateWithTimeout` awaits the sync lock
1983
+ * (`acquireSyncLock`, which uses Web Locks) BEFORE putting its WASM
1984
+ * call onto the chain, so a `syncState` that is queued on the sync
1985
+ * lock — but has not yet begun its WASM phase — is not visible to
1986
+ * `waitForIdle` and will not be awaited. Other methods (`newWallet`,
1987
+ * `executeTransaction`, etc.) route through the chain synchronously
1988
+ * on call and are always observed.
1989
+ *
1990
+ * Safe to call at any time; returns immediately if nothing was in
1991
+ * flight.
1992
+ *
1993
+ * @returns {Promise<void>}
1994
+ */
1995
+ async waitForIdle() {
1996
+ this.assertNotTerminated();
1997
+ await this.#inner.waitForIdle();
1998
+ }
1999
+
2000
+ /**
2001
+ * Returns the raw JS value that the most recent sign-callback invocation
2002
+ * threw, or `null` if the last sign call succeeded (or no call has
2003
+ * happened yet).
2004
+ *
2005
+ * Useful for recovering structured metadata (e.g. a `reason: 'locked'`
2006
+ * property) that the kernel-level `auth::request` diagnostic would
2007
+ * otherwise erase. Call immediately after catching a failed
2008
+ * `transactions.submit` / `transactions.send` / `transactions.consume`.
2009
+ *
2010
+ * Meaningful only with `useWorker: false`: under the worker shim the
2011
+ * sign callback fires against the worker's WASM keystore, while this
2012
+ * accessor reads the main-thread instance — which never signed — so it
2013
+ * returns `null`. Consumers that need this signal (e.g. external
2014
+ * keystores with lock-aware sign callbacks) already require
2015
+ * `useWorker: false` for the callback to be reachable at all.
2016
+ *
2017
+ * @returns {any} The raw thrown value, or `null`.
2018
+ */
2019
+ lastAuthError() {
2020
+ this.assertNotTerminated();
2021
+ return this.#inner.lastAuthError();
2022
+ }
2023
+
1727
2024
  /**
1728
2025
  * Terminates the underlying Web Worker. After this, all method calls will throw.
1729
2026
  */
@@ -2319,7 +2616,6 @@ const NoteVisibility = Object.freeze({
2319
2616
  const StorageMode = Object.freeze({
2320
2617
  Public: "public",
2321
2618
  Private: "private",
2322
- Network: "network",
2323
2619
  });
2324
2620
 
2325
2621
  const Linking = Object.freeze({
@@ -2513,6 +2809,15 @@ class WebClient {
2513
2809
  * @param {string | undefined} [logLevel] - Optional log verbosity level
2514
2810
  * ("error", "warn", "info", "debug", "trace", "off", or "none").
2515
2811
  * When set, Rust tracing output is routed to the browser console.
2812
+ * @param {boolean} [useWorker=true] - When `false`, skip the Web Worker shim
2813
+ * and call the wasm-bindgen `WebClient` directly on the current thread.
2814
+ * The worker exists to keep the main thread responsive during WASM work
2815
+ * in browser/extension contexts, but it serializes the prover argument
2816
+ * via `TransactionProver.serialize()` — a format that has no encoding
2817
+ * for `newCallbackProver(jsFn)` and silently downgrades it to `"local"`.
2818
+ * Consumers that hand a `CallbackProver` (e.g. native iOS/Android plug-in
2819
+ * provers in Capacitor apps, or any other JS-side prover bridge) need
2820
+ * `useWorker: false` so the prover handle reaches the WASM binding intact.
2516
2821
  */
2517
2822
  constructor(
2518
2823
  rpcUrl,
@@ -2522,7 +2827,8 @@ class WebClient {
2522
2827
  getKeyCb,
2523
2828
  insertKeyCb,
2524
2829
  signCb,
2525
- logLevel
2830
+ logLevel,
2831
+ useWorker = true
2526
2832
  ) {
2527
2833
  this.rpcUrl = rpcUrl;
2528
2834
  this.noteTransportUrl = noteTransportUrl;
@@ -2532,9 +2838,12 @@ class WebClient {
2532
2838
  this.insertKeyCb = insertKeyCb;
2533
2839
  this.signCb = signCb;
2534
2840
  this.logLevel = logLevel;
2841
+ this.useWorker = useWorker !== false;
2535
2842
 
2536
- // Check if Web Workers are available.
2537
- if (typeof Worker !== "undefined") {
2843
+ // Check if Web Workers are available AND the caller didn't opt out via
2844
+ // `useWorker: false`. The opt-out is load-bearing for `CallbackProver`
2845
+ // consumers — see the constructor doc above.
2846
+ if (this.useWorker && typeof Worker !== "undefined") {
2538
2847
  console.log("WebClient: Web Workers are available.");
2539
2848
  // Pick between the module and classic worker variants at runtime — see
2540
2849
  // `WebClient.workerMode` below. Both branches keep the
@@ -2648,8 +2957,12 @@ class WebClient {
2648
2957
  // Once the worker script has loaded, initialize the worker.
2649
2958
  this.loaded.then(() => this.initializeWorker());
2650
2959
  } else {
2651
- console.log("WebClient: Web Workers are not available.");
2652
- // Worker not available; set up fallback values.
2960
+ console.log(
2961
+ this.useWorker
2962
+ ? "WebClient: Web Workers are not available."
2963
+ : "WebClient: Web Worker shim disabled by caller (useWorker=false)."
2964
+ );
2965
+ // Worker not available or explicitly disabled; set up fallback values.
2653
2966
  this.worker = null;
2654
2967
  this.pendingRequests = null;
2655
2968
  this.loaded = Promise.resolve();
@@ -2665,25 +2978,99 @@ class WebClient {
2665
2978
  // would panic with "recursive use of an object detected" due to
2666
2979
  // wasm-bindgen's internal RefCell.
2667
2980
  this._wasmCallChain = Promise.resolve();
2981
+ // Depth counter for `_withInnerWebClient` re-entrancy. While > 0,
2982
+ // `_serializeWasmCall` runs its callback inline instead of queueing
2983
+ // it on the chain — see the comment on `_serializeWasmCall` for the
2984
+ // safety contract.
2985
+ this._withInnerLockDepth = 0;
2668
2986
  }
2669
2987
 
2670
2988
  /**
2671
2989
  * Serialize a WASM call that requires exclusive (&mut self) access.
2672
2990
  * Concurrent calls are queued and executed one at a time.
2673
2991
  *
2992
+ * Wraps both the direct (in-thread) path and the worker-dispatched path.
2993
+ * On the worker path this is redundant with the worker's own message queue,
2994
+ * but harmless (the chain resolves immediately on the main thread once the
2995
+ * worker's postMessage returns). On the direct path it is load-bearing —
2996
+ * without it, concurrent main-thread callers would panic with
2997
+ * "recursive use of an object detected" (wasm-bindgen's internal RefCell).
2998
+ *
2999
+ * Re-entrancy: when invoked from inside a `_withInnerWebClient(fn)`
3000
+ * callback — detected via `_withInnerLockDepth > 0` — `fn` runs inline
3001
+ * (no chain enqueue). The outer `_withInnerWebClient` invocation
3002
+ * already holds the chain via its own wrapping `_serializeWasmCall`,
3003
+ * so enqueueing the inner call would deadlock (the inner queues
3004
+ * behind the outer; the outer awaits the inner). The inline run is
3005
+ * safe because the chain still serializes against external callers
3006
+ * — they queue behind the outer call's chain slot, which only resolves
3007
+ * after `fn` (including all inline re-entries) settles. Callers of
3008
+ * `_withInnerWebClient` MUST hold an external mutex preventing
3009
+ * concurrent access via other code paths on this same instance during
3010
+ * the callback; without that, an external task running between two
3011
+ * awaits inside `fn` would race wasm-bindgen's borrow check.
3012
+ *
2674
3013
  * @param {() => Promise<any>} fn - The async function to execute.
2675
3014
  * @returns {Promise<any>} The result of fn.
2676
3015
  */
2677
3016
  _serializeWasmCall(fn) {
3017
+ if (this._withInnerLockDepth > 0) {
3018
+ return Promise.resolve().then(fn);
3019
+ }
2678
3020
  const result = this._wasmCallChain.catch(() => {}).then(fn);
2679
3021
  this._wasmCallChain = result.catch(() => {});
2680
3022
  return result;
2681
3023
  }
2682
3024
 
3025
+ /**
3026
+ * Returns a promise that resolves once every serialized WASM call that
3027
+ * was already on `_wasmCallChain` when `waitForIdle()` was called has
3028
+ * settled. Use this from callers that need to perform a non-WASM-side
3029
+ * action (e.g. clear an in-memory auth key) AFTER any in-flight
3030
+ * execute / submit / sync has completed, so the WASM kernel's auth
3031
+ * callback doesn't race with the key being cleared.
3032
+ *
3033
+ * Does NOT wait for calls enqueued after `waitForIdle()` returns —
3034
+ * this is intentional, so a caller can drain and then proceed without
3035
+ * being blocked indefinitely by a concurrent workload.
3036
+ *
3037
+ * Caveat for `syncState`: `syncStateWithTimeout` awaits
3038
+ * `acquireSyncLock` (Web Locks) BEFORE wrapping its WASM call in
3039
+ * `_serializeWasmCall`, so a sync that is queued on the sync lock but
3040
+ * has not yet reached its WASM phase is not on the chain and will not
3041
+ * be awaited. Every other serialized method (`executeTransaction`,
3042
+ * `newWallet`, `submitNewTransaction`, `proveTransaction`,
3043
+ * `applyTransaction`, and the proxy-fallback reads) routes through
3044
+ * the chain synchronously on call and is always observed.
3045
+ *
3046
+ * @returns {Promise<void>}
3047
+ */
3048
+ async waitForIdle() {
3049
+ // Chain on `_wasmCallChain`; by the time this resolves, any in-flight
3050
+ // serialized call has settled. Catch so the chain state doesn't leak.
3051
+ await this._wasmCallChain.catch(() => {});
3052
+ }
3053
+
2683
3054
  // TODO: This will soon conflict with some changes in main.
2684
3055
  // More context here:
2685
3056
  // https://github.com/0xMiden/miden-client/pull/1645?notification_referrer_id=NT_kwHOA1yg7NoAJVJlcG9zaXRvcnk7NjU5MzQzNzAyO0lzc3VlOzM3OTY4OTU1Nzk&notifications_query=is%3Aunread#discussion_r2696075480
2686
3057
  initializeWorker() {
3058
+ // Pass `numThreads` to the worker so it can call `wasm.initThreadPool(n)`
3059
+ // inside its OWN WASM instance — the SDK worker's instance is separate
3060
+ // from the main thread's, and rayon's global pool is per-instance.
3061
+ // Default: navigator.hardwareConcurrency (or 1 if unavailable for any
3062
+ // reason — e.g. the page isn't crossOriginIsolated, in which case the
3063
+ // worker will skip pool init and parallelism falls back to sequential).
3064
+ let numThreads = 1;
3065
+ try {
3066
+ if (
3067
+ typeof self !== "undefined" &&
3068
+ self.crossOriginIsolated &&
3069
+ navigator?.hardwareConcurrency
3070
+ ) {
3071
+ numThreads = navigator.hardwareConcurrency;
3072
+ }
3073
+ } catch {}
2687
3074
  this.worker.postMessage({
2688
3075
  action: WorkerAction.INIT,
2689
3076
  args: [
@@ -2695,6 +3082,7 @@ class WebClient {
2695
3082
  !!this.insertKeyCb,
2696
3083
  !!this.signCb,
2697
3084
  this.logLevel,
3085
+ numThreads,
2698
3086
  ],
2699
3087
  });
2700
3088
  }
@@ -2723,9 +3111,19 @@ class WebClient {
2723
3111
  * @param {string} seed - The seed for the account.
2724
3112
  * @param {string | undefined} network - Optional name for the store. Setting this allows multiple clients to be used in the same browser.
2725
3113
  * @param {string | undefined} logLevel - Optional log verbosity level ("error", "warn", "info", "debug", "trace", "off", or "none").
3114
+ * @param {boolean} [useWorker=true] - When `false`, bypass the Web Worker shim
3115
+ * and run WASM calls on the current thread. Required for `CallbackProver`
3116
+ * consumers (the worker path serializes the prover and loses the callback).
2726
3117
  * @returns {Promise<WebClient>} The fully initialized WebClient.
2727
3118
  */
2728
- static async createClient(rpcUrl, noteTransportUrl, seed, network, logLevel) {
3119
+ static async createClient(
3120
+ rpcUrl,
3121
+ noteTransportUrl,
3122
+ seed,
3123
+ network,
3124
+ logLevel,
3125
+ useWorker = true
3126
+ ) {
2729
3127
  // Construct the instance (synchronously).
2730
3128
  const instance = new WebClient(
2731
3129
  rpcUrl,
@@ -2735,7 +3133,8 @@ class WebClient {
2735
3133
  undefined,
2736
3134
  undefined,
2737
3135
  undefined,
2738
- logLevel
3136
+ logLevel,
3137
+ useWorker
2739
3138
  );
2740
3139
 
2741
3140
  // Set up logging on the main thread before creating the client.
@@ -2766,6 +3165,9 @@ class WebClient {
2766
3165
  * @param {Function | undefined} insertKeyCb - The insert key callback.
2767
3166
  * @param {Function | undefined} signCb - The sign callback.
2768
3167
  * @param {string | undefined} logLevel - Optional log verbosity level ("error", "warn", "info", "debug", "trace", "off", or "none").
3168
+ * @param {boolean} [useWorker=true] - When `false`, bypass the Web Worker shim
3169
+ * and run WASM calls on the current thread. Required for `CallbackProver`
3170
+ * consumers (the worker path serializes the prover and loses the callback).
2769
3171
  * @returns {Promise<WebClient>} The fully initialized WebClient.
2770
3172
  */
2771
3173
  static async createClientWithExternalKeystore(
@@ -2776,7 +3178,8 @@ class WebClient {
2776
3178
  getKeyCb,
2777
3179
  insertKeyCb,
2778
3180
  signCb,
2779
- logLevel
3181
+ logLevel,
3182
+ useWorker = true
2780
3183
  ) {
2781
3184
  // Construct the instance (synchronously).
2782
3185
  const instance = new WebClient(
@@ -2787,7 +3190,8 @@ class WebClient {
2787
3190
  getKeyCb,
2788
3191
  insertKeyCb,
2789
3192
  signCb,
2790
- logLevel
3193
+ logLevel,
3194
+ useWorker
2791
3195
  );
2792
3196
 
2793
3197
  // Set up logging on the main thread before creating the client.
@@ -2852,6 +3256,7 @@ class WebClient {
2852
3256
  async newFaucet(
2853
3257
  storageMode,
2854
3258
  nonFungible,
3259
+ tokenName,
2855
3260
  tokenSymbol,
2856
3261
  decimals,
2857
3262
  maxSupply,
@@ -2862,6 +3267,7 @@ class WebClient {
2862
3267
  return await wasmWebClient.newFaucet(
2863
3268
  storageMode,
2864
3269
  nonFungible,
3270
+ tokenName,
2865
3271
  tokenSymbol,
2866
3272
  decimals,
2867
3273
  maxSupply,