@provablehq/sdk 0.10.5 → 0.11.0

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 (75) hide show
  1. package/dist/mainnet/browser.cjs +1027 -418
  2. package/dist/mainnet/browser.cjs.map +1 -1
  3. package/dist/mainnet/browser.d.cts +6 -4
  4. package/dist/mainnet/browser.d.ts +6 -4
  5. package/dist/mainnet/browser.js +1021 -427
  6. package/dist/mainnet/browser.js.map +1 -1
  7. package/dist/mainnet/keys/keystore/indexeddb.d.cts +60 -0
  8. package/dist/mainnet/keys/keystore/indexeddb.d.ts +60 -0
  9. package/dist/mainnet/keys/provider/memory.d.cts +1 -1
  10. package/dist/mainnet/keys/provider/memory.d.ts +1 -1
  11. package/dist/mainnet/models/record-scanner/error.d.cts +1 -1
  12. package/dist/mainnet/models/record-scanner/error.d.ts +1 -1
  13. package/dist/mainnet/models/record-scanner/registrationResult.d.cts +1 -1
  14. package/dist/mainnet/models/record-scanner/registrationResult.d.ts +1 -1
  15. package/dist/mainnet/network-client.d.cts +22 -2
  16. package/dist/mainnet/network-client.d.ts +22 -2
  17. package/dist/mainnet/node.cjs +15 -0
  18. package/dist/mainnet/node.cjs.map +1 -1
  19. package/dist/mainnet/node.js +2 -2
  20. package/dist/mainnet/program-manager.d.cts +61 -1
  21. package/dist/mainnet/program-manager.d.ts +61 -1
  22. package/dist/mainnet/record-scanner.d.cts +13 -10
  23. package/dist/mainnet/record-scanner.d.ts +13 -10
  24. package/dist/mainnet/utils/index.d.cts +3 -0
  25. package/dist/mainnet/utils/index.d.ts +3 -0
  26. package/dist/mainnet/utils/logger.d.cts +34 -0
  27. package/dist/mainnet/utils/logger.d.ts +34 -0
  28. package/dist/mainnet/wasm-Bnb_v1_O.js +3 -0
  29. package/dist/mainnet/wasm-Bnb_v1_O.js.map +1 -0
  30. package/dist/mainnet/wasm.cjs +251 -0
  31. package/dist/mainnet/wasm.cjs.map +1 -0
  32. package/dist/mainnet/wasm.d.cts +1 -1
  33. package/dist/mainnet/wasm.d.ts +1 -1
  34. package/dist/testnet/browser.cjs +1027 -418
  35. package/dist/testnet/browser.cjs.map +1 -1
  36. package/dist/testnet/browser.d.cts +6 -4
  37. package/dist/testnet/browser.d.ts +6 -4
  38. package/dist/testnet/browser.js +1021 -427
  39. package/dist/testnet/browser.js.map +1 -1
  40. package/dist/testnet/keys/keystore/indexeddb.d.cts +60 -0
  41. package/dist/testnet/keys/keystore/indexeddb.d.ts +60 -0
  42. package/dist/testnet/keys/provider/memory.d.cts +1 -1
  43. package/dist/testnet/keys/provider/memory.d.ts +1 -1
  44. package/dist/testnet/models/record-scanner/error.d.cts +1 -1
  45. package/dist/testnet/models/record-scanner/error.d.ts +1 -1
  46. package/dist/testnet/models/record-scanner/registrationResult.d.cts +1 -1
  47. package/dist/testnet/models/record-scanner/registrationResult.d.ts +1 -1
  48. package/dist/testnet/network-client.d.cts +22 -2
  49. package/dist/testnet/network-client.d.ts +22 -2
  50. package/dist/testnet/node.cjs +15 -0
  51. package/dist/testnet/node.cjs.map +1 -1
  52. package/dist/testnet/node.js +2 -2
  53. package/dist/testnet/program-manager.d.cts +61 -1
  54. package/dist/testnet/program-manager.d.ts +61 -1
  55. package/dist/testnet/record-scanner.d.cts +13 -10
  56. package/dist/testnet/record-scanner.d.ts +13 -10
  57. package/dist/testnet/utils/index.d.cts +3 -0
  58. package/dist/testnet/utils/index.d.ts +3 -0
  59. package/dist/testnet/utils/logger.d.cts +34 -0
  60. package/dist/testnet/utils/logger.d.ts +34 -0
  61. package/dist/testnet/wasm-BCrMb35a.js +3 -0
  62. package/dist/testnet/wasm-BCrMb35a.js.map +1 -0
  63. package/dist/testnet/wasm.cjs +251 -0
  64. package/dist/testnet/wasm.cjs.map +1 -0
  65. package/dist/testnet/wasm.d.cts +1 -1
  66. package/dist/testnet/wasm.d.ts +1 -1
  67. package/package.json +2 -2
  68. package/dist/mainnet/models/record-scanner/registrationRequest.d.cts +0 -13
  69. package/dist/mainnet/models/record-scanner/registrationRequest.d.ts +0 -13
  70. package/dist/testnet/models/record-scanner/registrationRequest.d.cts +0 -13
  71. package/dist/testnet/models/record-scanner/registrationRequest.d.ts +0 -13
  72. /package/dist/mainnet/{utils.d.cts → utils/utils.d.cts} +0 -0
  73. /package/dist/mainnet/{utils.d.ts → utils/utils.d.ts} +0 -0
  74. /package/dist/testnet/{utils.d.cts → utils/utils.d.cts} +0 -0
  75. /package/dist/testnet/{utils.d.ts → utils/utils.d.ts} +0 -0
@@ -5,6 +5,431 @@ var mainnet_js = require('@provablehq/wasm/mainnet.js');
5
5
  var nobleSodium = require('@serenity-kit/noble-sodium');
6
6
  var base = require('@scure/base');
7
7
 
8
+ const LEVEL_PRIORITY = {
9
+ silent: 0,
10
+ error: 1,
11
+ warn: 2,
12
+ info: 3,
13
+ debug: 4,
14
+ };
15
+ let currentLevel = "info";
16
+ /**
17
+ * Set the SDK log level. Levels are hierarchical:
18
+ * - "silent" — suppress all SDK logging
19
+ * - "error" — only errors
20
+ * - "warn" — errors + warnings
21
+ * - "info" — errors + warnings + info (default)
22
+ * - "debug" — everything including debug traces
23
+ *
24
+ * This controls both TS-side and WASM-side logging. WASM log calls
25
+ * (e.g., "Loading program", "Executing program") are silenced when
26
+ * the level is below "info".
27
+ */
28
+ function setLogLevel(level) {
29
+ currentLevel = level;
30
+ Promise.resolve().then(function () { return require('./wasm.cjs'); })
31
+ .then(({ setWasmLogLevel }) => setWasmLogLevel(LEVEL_PRIORITY[level]))
32
+ .catch(() => { }); // WASM not loaded yet — will use default level (info)
33
+ }
34
+ /** Returns the current SDK log level. */
35
+ function getLogLevel() {
36
+ return currentLevel;
37
+ }
38
+ function shouldLog(level) {
39
+ return LEVEL_PRIORITY[level] <= LEVEL_PRIORITY[currentLevel];
40
+ }
41
+ /**
42
+ * SDK logger object following console.log/warn/error/debug syntax.
43
+ * Respects the current log level set via setLogLevel().
44
+ */
45
+ const logger = {
46
+ log(...args) {
47
+ if (shouldLog("info"))
48
+ console.log(...args);
49
+ },
50
+ warn(...args) {
51
+ if (shouldLog("warn"))
52
+ console.warn(...args);
53
+ },
54
+ error(...args) {
55
+ if (shouldLog("error"))
56
+ console.error(...args);
57
+ },
58
+ debug(...args) {
59
+ if (shouldLog("debug"))
60
+ console.debug(...args);
61
+ },
62
+ };
63
+
64
+ /**
65
+ * Error thrown when a key locator is invalid for filesystem use.
66
+ * Used to prevent path traversal and other filesystem injection when deriving paths from locators.
67
+ *
68
+ * @extends Error
69
+ */
70
+ class InvalidLocatorError extends Error {
71
+ locator;
72
+ reason;
73
+ /**
74
+ * @param message - Human-readable description of the validation failure.
75
+ * @param locator - The invalid locator string that failed validation.
76
+ * @param reason - Machine-readable reason code for the failure.
77
+ */
78
+ constructor(message, locator, reason) {
79
+ super(message);
80
+ this.locator = locator;
81
+ this.reason = reason;
82
+ this.name = "InvalidLocatorError";
83
+ Object.setPrototypeOf(this, InvalidLocatorError.prototype);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Error thrown when there is a mismatch between expected and actual key metadata.
89
+ * This can occur during verification of either the checksum or size of a key.
90
+ *
91
+ * @extends Error
92
+ */
93
+ class KeyVerificationError extends Error {
94
+ locator;
95
+ field;
96
+ expected;
97
+ actual;
98
+ /**
99
+ * Creates a new KeyVerificationError instance (error.name is "ChecksumMismatchError").
100
+ *
101
+ * @param {string} locator - The key locator where the mismatch occurred.
102
+ * @param {"checksum" | "size"} field - The field that failed verification (either "checksum" or "size").
103
+ * @param {string} expected - The expected value of the field.
104
+ * @param {string} actual - The actual value encountered.
105
+ */
106
+ constructor(locator, field, expected, actual) {
107
+ super(`Key verification ${locator} ${field} mismatch: expected ${expected}, got ${actual}`);
108
+ this.locator = locator;
109
+ this.field = field;
110
+ this.expected = expected;
111
+ this.actual = actual;
112
+ this.name = "ChecksumMismatchError";
113
+ Object.setPrototypeOf(this, KeyVerificationError.prototype);
114
+ }
115
+ }
116
+ /**
117
+ * Computes the SHA-256 checksum of a given set of bytes.
118
+ *
119
+ * @param {Uint8Array} bytes - The bytes to compute the checksum of.
120
+ */
121
+ async function sha256Hex(bytes) {
122
+ const hash = await crypto.subtle.digest("SHA-256", bytes);
123
+ return Array.from(new Uint8Array(hash))
124
+ .map((b) => b.toString(16).padStart(2, "0"))
125
+ .join("");
126
+ }
127
+
128
+ /**
129
+ * In-memory implementation of KeyVerifier that stores and verifies key fingerprints.
130
+ * Provides functionality to compute and verify cryptographic checksums of keys, storing them
131
+ * in memory for subsequent verification. This implementation is primarily used for testing
132
+ * and development purposes where persistence is not required.
133
+ *
134
+ * Key features:
135
+ * - Computes SHA-256 checksums and sizes for key bytes.
136
+ * - Stores key fingerprints in memory using string locators.
137
+ * - Verifies key bytes against stored or provided fingerprints.
138
+ *
139
+ * @implements {KeyVerifier}
140
+ */
141
+ class MemKeyVerifier {
142
+ keyStore = {};
143
+ /**
144
+ * Computes and optionally stores key metadata. If a keyFingerprint is provided, this function will verify the computed checksum against it before storing it.
145
+ *
146
+ * @param {KeyMetadata} keyMetadata - Object containing key bytes and optional verification data.
147
+ * @throws {KeyVerificationError} When provided keyFingerprint doesn't match computed values.
148
+ * @returns {Promise<KeyFingerprint>} Computed key metadata.
149
+ */
150
+ async computeKeyMetadata(keyMetadata) {
151
+ // Compute the metadata from the key bytes
152
+ const computedFingerprint = {
153
+ checksum: await sha256Hex(keyMetadata.keyBytes),
154
+ size: keyMetadata.keyBytes.length
155
+ };
156
+ // If a KeyFingerprint is provided, verify it matches computed values.
157
+ if (keyMetadata.fingerprint) {
158
+ if (keyMetadata.fingerprint.size !== computedFingerprint.size) {
159
+ throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "size", String(keyMetadata.fingerprint.size), String(computedFingerprint.size));
160
+ }
161
+ if (keyMetadata.fingerprint.checksum !== computedFingerprint.checksum) {
162
+ throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "checksum", keyMetadata.fingerprint.checksum, computedFingerprint.checksum);
163
+ }
164
+ }
165
+ // If locator is provided, store only the fingerprint
166
+ if (keyMetadata.locator) {
167
+ this.keyStore[keyMetadata.locator] = computedFingerprint;
168
+ }
169
+ return computedFingerprint;
170
+ }
171
+ /**
172
+ * Verifies key bytes against stored or provided metadata. Follows a priority verification scheme:
173
+ * 1. If KeyFingerprint is provided in the metadata, this method verifies against that first.
174
+ * 2. If a locator is provided, attempts to verify against stored fingerprint.
175
+ * 3. If neither is available, throws an error.
176
+ *
177
+ * @param {KeyMetadata} keyMetadata - Object containing the key bytes and optional verification metadata.
178
+ * @throws {Error} When neither fingerprint nor valid locator is provided for verification.
179
+ * @throws {KeyVerificationError} When size or checksum verification fails.
180
+ * @returns {Promise<void>} Promise that resolves when verification succeeds.
181
+ */
182
+ async verifyKeyBytes(keyMetadata) {
183
+ if (!keyMetadata.keyBytes) {
184
+ throw new Error("Key bytes must be provided for verification.");
185
+ }
186
+ // Compute the fingerprint for the provided bytes.
187
+ const computedFingerprint = await this.computeKeyMetadata({
188
+ keyBytes: keyMetadata.keyBytes
189
+ });
190
+ // Determine which fingerprint to verify against.
191
+ let fingerprintToVerify;
192
+ if (keyMetadata.fingerprint) {
193
+ // If a key fingerprint is provided, use it.
194
+ fingerprintToVerify = keyMetadata.fingerprint;
195
+ }
196
+ else if (keyMetadata.locator) {
197
+ // Otherwise try to get stored fingerprint by locator.
198
+ fingerprintToVerify = this.keyStore[keyMetadata.locator];
199
+ }
200
+ if (!fingerprintToVerify) {
201
+ throw new Error("Either fingerprint or a valid locator must be provided for verification.");
202
+ }
203
+ // Verify the key size.
204
+ if (fingerprintToVerify.size !== computedFingerprint.size) {
205
+ throw new KeyVerificationError(keyMetadata.locator || "", "size", String(fingerprintToVerify.size), String(computedFingerprint.size));
206
+ }
207
+ // Verify the key checksum.
208
+ if (fingerprintToVerify.checksum !== computedFingerprint.checksum) {
209
+ throw new KeyVerificationError(keyMetadata.locator || "", "checksum", fingerprintToVerify.checksum, computedFingerprint.checksum);
210
+ }
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Browser-compatible {@link KeyStore} backed by IndexedDB.
216
+ *
217
+ * This is the browser counterpart to {@link LocalFileKeyStore} (which requires Node.js `fs`).
218
+ * It persists proving and verifying keys across page reloads and browser sessions using the
219
+ * IndexedDB API available in all modern browsers and Web Workers.
220
+ *
221
+ * **Environment**: Browser / Web Worker only. Instantiating this class is safe in any
222
+ * environment, but the first IndexedDB operation will reject with a clear error when
223
+ * `globalThis.indexedDB` is not available (e.g., Node.js, SSR contexts). Guard your
224
+ * imports accordingly if you bundle for server-side rendering.
225
+ *
226
+ * **Security**: Keys are stored as plaintext `Uint8Array` in IndexedDB. Any script
227
+ * running on the same origin — including scripts injected via XSS — can read the stored
228
+ * proving and verifying keys. Do **not** use this keystore for data that must remain
229
+ * confidential under an XSS-capable adversary; encrypt sensitive material at the
230
+ * application layer before persisting, or use an in-memory store.
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * import { IndexedDBKeyStore, ProgramManager } from "@provablehq/sdk";
235
+ *
236
+ * const keyStore = new IndexedDBKeyStore();
237
+ * const pm = new ProgramManager();
238
+ * pm.setKeyStore(keyStore);
239
+ * // Keys synthesized during execution are now cached in IndexedDB
240
+ * // and reloaded automatically on subsequent runs.
241
+ * ```
242
+ */
243
+ class IndexedDBKeyStore {
244
+ dbName;
245
+ storeName = "keys";
246
+ keyVerifier = new MemKeyVerifier();
247
+ dbPromise = null;
248
+ /**
249
+ * @param dbName IndexedDB database name. Defaults to `"aleo-keystore"`.
250
+ */
251
+ constructor(dbName = "aleo-keystore") {
252
+ this.dbName = dbName;
253
+ }
254
+ // -------------------------------------------------------
255
+ // IndexedDB helpers
256
+ // -------------------------------------------------------
257
+ /** Opens (or creates) the database, returning a cached promise. */
258
+ openDB() {
259
+ if (this.dbPromise)
260
+ return this.dbPromise;
261
+ // Env check sits outside the Promise constructor so a failure doesn't
262
+ // poison the cache; a later call (e.g. after a polyfill loads) can retry.
263
+ if (typeof indexedDB === "undefined") {
264
+ return Promise.reject(new Error("IndexedDBKeyStore requires a browser or Web Worker environment: " +
265
+ "`indexedDB` is not defined. If you are in Node.js or an SSR " +
266
+ "context, use LocalFileKeyStore or an in-memory key provider instead."));
267
+ }
268
+ this.dbPromise = new Promise((resolve, reject) => {
269
+ const request = indexedDB.open(this.dbName, 1);
270
+ request.onupgradeneeded = () => {
271
+ const db = request.result;
272
+ if (!db.objectStoreNames.contains(this.storeName)) {
273
+ db.createObjectStore(this.storeName, { keyPath: "locator" });
274
+ }
275
+ };
276
+ request.onsuccess = () => resolve(request.result);
277
+ request.onerror = () => {
278
+ this.dbPromise = null;
279
+ reject(request.error);
280
+ };
281
+ request.onblocked = () => {
282
+ this.dbPromise = null;
283
+ reject(new DOMException("Database open blocked", "AbortError"));
284
+ };
285
+ });
286
+ return this.dbPromise;
287
+ }
288
+ /** Runs a single read-write transaction and returns the request result. */
289
+ async tx(mode, fn) {
290
+ const db = await this.openDB();
291
+ return new Promise((resolve, reject) => {
292
+ const txn = db.transaction(this.storeName, mode);
293
+ const store = txn.objectStore(this.storeName);
294
+ const req = fn(store);
295
+ let result;
296
+ req.onsuccess = () => { result = req.result; };
297
+ txn.oncomplete = () => resolve(result);
298
+ txn.onerror = () => reject(txn.error);
299
+ txn.onabort = () => reject(txn.error ?? new DOMException("Transaction aborted", "AbortError"));
300
+ });
301
+ }
302
+ // -------------------------------------------------------
303
+ // Locator serialization (mirrors LocalFileKeyStore)
304
+ // -------------------------------------------------------
305
+ validateComponent(value, label) {
306
+ if (value === "" || value === ".") {
307
+ throw new InvalidLocatorError(`KeyLocator ${label} must not be empty or "." (got "${value}")`, value, "reserved_name");
308
+ }
309
+ if (value.includes("..")) {
310
+ throw new InvalidLocatorError(`KeyLocator ${label} must not contain ".." (got "${value}")`, value, "path_traversal");
311
+ }
312
+ if (value.includes("/") || value.includes("\\") || value.includes("\0")) {
313
+ throw new InvalidLocatorError(`KeyLocator ${label} must not contain path separators or null bytes (got "${value}")`, value, "path_separator");
314
+ }
315
+ }
316
+ validateNonNegative(value, label) {
317
+ if (!Number.isInteger(value) || value < 0) {
318
+ throw new InvalidLocatorError(`KeyLocator ${label} must be a non-negative integer (got ${value})`, String(value), "negative_value");
319
+ }
320
+ }
321
+ serializeLocator(locator) {
322
+ this.validateComponent(locator.program, "program");
323
+ this.validateComponent(locator.functionName, "functionName");
324
+ this.validateComponent(locator.network, "network");
325
+ this.validateNonNegative(locator.edition, "edition");
326
+ this.validateNonNegative(locator.amendment, "amendment");
327
+ const base = `${locator.program}.${locator.functionName}.e${locator.edition}.a${locator.amendment}.${locator.network}.${locator.keyType}`;
328
+ if (locator.keyType === "translation") {
329
+ this.validateComponent(locator.recordName, "recordName");
330
+ this.validateNonNegative(locator.recordInputPosition, "recordInputPosition");
331
+ return `${base}.${locator.recordName}.${locator.recordInputPosition}`;
332
+ }
333
+ return base;
334
+ }
335
+ checksumToFingerprint(checksum, keyBytes) {
336
+ if (!checksum)
337
+ return undefined;
338
+ return { checksum, size: keyBytes.length };
339
+ }
340
+ // -------------------------------------------------------
341
+ // KeyStore interface
342
+ // -------------------------------------------------------
343
+ async getKeyBytes(locator) {
344
+ const key = this.serializeLocator(locator);
345
+ const record = await this.tx("readonly", (store) => store.get(key));
346
+ if (!record)
347
+ return null;
348
+ const fingerprint = this.checksumToFingerprint(locator.checksum, record.bytes) ?? record.metadata;
349
+ if (fingerprint) {
350
+ await this.keyVerifier.verifyKeyBytes({
351
+ keyBytes: record.bytes,
352
+ locator: key,
353
+ fingerprint,
354
+ });
355
+ }
356
+ return record.bytes;
357
+ }
358
+ async getProvingKey(locator) {
359
+ const bytes = await this.getKeyBytes(locator);
360
+ if (!bytes)
361
+ return null;
362
+ return mainnet_js.ProvingKey.fromBytes(bytes);
363
+ }
364
+ async getVerifyingKey(locator) {
365
+ const bytes = await this.getKeyBytes(locator);
366
+ if (!bytes)
367
+ return null;
368
+ return mainnet_js.VerifyingKey.fromBytes(bytes);
369
+ }
370
+ async setKeys(proverLocator, verifierLocator, keys) {
371
+ const proverKey = this.serializeLocator(proverLocator);
372
+ const verifierKey = this.serializeLocator(verifierLocator);
373
+ const [provingKey, verifyingKey] = keys;
374
+ const [provingKeyBytes, verifyingKeyBytes] = [
375
+ provingKey.toBytes(),
376
+ verifyingKey.toBytes(),
377
+ ];
378
+ const [proverFingerprint, verifierFingerprint] = await Promise.all([
379
+ this.keyVerifier.computeKeyMetadata({
380
+ keyBytes: provingKeyBytes,
381
+ locator: proverKey,
382
+ fingerprint: this.checksumToFingerprint(proverLocator.checksum, provingKeyBytes),
383
+ }),
384
+ this.keyVerifier.computeKeyMetadata({
385
+ keyBytes: verifyingKeyBytes,
386
+ locator: verifierKey,
387
+ fingerprint: this.checksumToFingerprint(verifierLocator.checksum, verifyingKeyBytes),
388
+ }),
389
+ ]);
390
+ const proverRecord = { locator: proverKey, bytes: provingKeyBytes, metadata: proverFingerprint };
391
+ const verifierRecord = { locator: verifierKey, bytes: verifyingKeyBytes, metadata: verifierFingerprint };
392
+ // Write both in a single transaction for atomicity.
393
+ const db = await this.openDB();
394
+ await new Promise((resolve, reject) => {
395
+ const txn = db.transaction(this.storeName, "readwrite");
396
+ const store = txn.objectStore(this.storeName);
397
+ store.put(proverRecord);
398
+ store.put(verifierRecord);
399
+ txn.oncomplete = () => resolve();
400
+ txn.onerror = () => reject(txn.error);
401
+ txn.onabort = () => reject(txn.error ?? new DOMException("Transaction aborted", "AbortError"));
402
+ });
403
+ }
404
+ async setKeyBytes(keyBytes, locator) {
405
+ const key = this.serializeLocator(locator);
406
+ const computedMetadata = await this.keyVerifier.computeKeyMetadata({
407
+ keyBytes,
408
+ locator: key,
409
+ fingerprint: this.checksumToFingerprint(locator.checksum, keyBytes),
410
+ });
411
+ const record = { locator: key, bytes: keyBytes, metadata: computedMetadata };
412
+ await this.tx("readwrite", (store) => store.put(record));
413
+ }
414
+ async getKeyMetadata(locator) {
415
+ const key = this.serializeLocator(locator);
416
+ const record = await this.tx("readonly", (store) => store.get(key));
417
+ return record?.metadata ?? null;
418
+ }
419
+ async has(locator) {
420
+ const key = this.serializeLocator(locator);
421
+ const count = await this.tx("readonly", (store) => store.count(IDBKeyRange.only(key)));
422
+ return count > 0;
423
+ }
424
+ async delete(locator) {
425
+ const key = this.serializeLocator(locator);
426
+ await this.tx("readwrite", (store) => store.delete(key));
427
+ }
428
+ async clear() {
429
+ await this.tx("readwrite", (store) => store.clear());
430
+ }
431
+ }
432
+
8
433
  /**
9
434
  * Encrypt an authorization with a cryptobox X25519 public key (libsodium-compatible wire format).
10
435
  *
@@ -164,7 +589,7 @@ class Account {
164
589
  this._privateKey = this.privateKeyFromParams(params);
165
590
  }
166
591
  catch (e) {
167
- console.error("Wrong parameter", e);
592
+ logger.error("Wrong parameter", e);
168
593
  throw new Error("Wrong Parameter");
169
594
  }
170
595
  this._viewKey = mainnet_js.ViewKey.from_private_key(this._privateKey);
@@ -635,7 +1060,7 @@ function environment() {
635
1060
  }
636
1061
  }
637
1062
  function logAndThrow(message) {
638
- console.error(message);
1063
+ logger.error(message);
639
1064
  throw new Error(message);
640
1065
  }
641
1066
  /** Default transport — wraps global fetch to avoid illegal-invocation errors in browsers. */
@@ -696,7 +1121,7 @@ async function retryWithBackoff(fn, { maxAttempts = 5, baseDelay = 100, jitter,
696
1121
  const jitterAmount = jitter ?? baseDelay;
697
1122
  const actualJitter = Math.floor(Math.random() * jitterAmount);
698
1123
  const delay = baseDelay * 2 ** (attempt - 1) + actualJitter;
699
- console.warn(`Retry ${attempt}/${maxAttempts} failed. Retrying in ${delay}ms...`);
1124
+ logger.warn(`Retry ${attempt}/${maxAttempts} failed. Retrying in ${delay}ms...`);
700
1125
  await new Promise((res) => setTimeout(res, delay));
701
1126
  }
702
1127
  }
@@ -865,7 +1290,7 @@ class AleoNetworkClient {
865
1290
  else {
866
1291
  this.headers = {
867
1292
  // This is replaced by the actual version by a Rollup plugin
868
- "X-Aleo-SDK-Version": "0.10.5",
1293
+ "X-Aleo-SDK-Version": "0.11.0",
869
1294
  "X-Aleo-environment": environment(),
870
1295
  };
871
1296
  }
@@ -881,7 +1306,7 @@ class AleoNetworkClient {
881
1306
  else {
882
1307
  this.headers = {
883
1308
  // This is replaced by the actual version by a Rollup plugin
884
- "X-Aleo-SDK-Version": "0.10.5",
1309
+ "X-Aleo-SDK-Version": "0.11.0",
885
1310
  "X-Aleo-environment": environment(),
886
1311
  };
887
1312
  }
@@ -1197,7 +1622,7 @@ class AleoNetworkClient {
1197
1622
  continue;
1198
1623
  }
1199
1624
  catch (error) {
1200
- console.log("Found unspent record!");
1625
+ logger.log("Found unspent record!");
1201
1626
  }
1202
1627
  }
1203
1628
  // Add the record to the list of records if the user did not specify amounts.
@@ -1257,14 +1682,14 @@ class AleoNetworkClient {
1257
1682
  }
1258
1683
  catch (error) {
1259
1684
  // If there is an error fetching blocks, log it and keep searching
1260
- console.warn("Error fetching blocks in range: " +
1685
+ logger.warn("Error fetching blocks in range: " +
1261
1686
  start.toString() +
1262
1687
  "-" +
1263
1688
  end.toString());
1264
- console.warn("Error: ", error);
1689
+ logger.warn("Error: ", error);
1265
1690
  failures += 1;
1266
1691
  if (failures > 10) {
1267
- console.warn("10 failures fetching records reached. Returning records fetched so far");
1692
+ logger.warn("10 failures fetching records reached. Returning records fetched so far");
1268
1693
  return records;
1269
1694
  }
1270
1695
  }
@@ -1714,6 +2139,30 @@ class AleoNetworkClient {
1714
2139
  this.ctx = {};
1715
2140
  }
1716
2141
  }
2142
+ /**
2143
+ * Returns the current edition and amendment count for a program.
2144
+ *
2145
+ * @param {string} programId - The program ID (e.g. "hello_hello.aleo")
2146
+ * @returns {{ program_id: string, edition: number, amendment_count: number }}
2147
+ *
2148
+ * @example
2149
+ * const networkClient = new AleoNetworkClient("https://api.provable.com/v2");
2150
+ * const info = await networkClient.getProgramAmendmentCount("hello_hello.aleo");
2151
+ * console.log(info.edition, info.amendment_count);
2152
+ */
2153
+ async getProgramAmendmentCount(programId) {
2154
+ try {
2155
+ this.ctx = { "X-ALEO-METHOD": "getProgramAmendmentCount" };
2156
+ const raw = await this.fetchRaw("/programs/" + programId + "/amendment_count");
2157
+ return JSON.parse(raw);
2158
+ }
2159
+ catch (error) {
2160
+ throw new Error(`Error fetching amendment count for ${programId}: ${error}`);
2161
+ }
2162
+ finally {
2163
+ this.ctx = {};
2164
+ }
2165
+ }
1717
2166
  /**
1718
2167
  * Returns a program object from a program ID or program source code.
1719
2168
  *
@@ -2304,7 +2753,7 @@ class AleoNetworkClient {
2304
2753
  };
2305
2754
  }
2306
2755
  /**
2307
- * Parses a /prove or /prove/encrypted response. Returns a result object (never throws for 200/400/500/503).
2756
+ * Parses a /prove/authorization or /prove/request response. Returns a result object (never throws for 200/400/500/503).
2308
2757
  */
2309
2758
  async handleProvingResponse(response) {
2310
2759
  // Get the proving response text.
@@ -2365,14 +2814,12 @@ class AleoNetworkClient {
2365
2814
  async submitProvingRequestSafe(options) {
2366
2815
  // Attempt to get the Prover URI first from the options, then from any configured globally, or third try the main configured host.
2367
2816
  const proverUri = (options.url ?? this.proverUri) ?? this.host;
2368
- const provingRequestString = options.provingRequest instanceof mainnet_js.ProvingRequest
2369
- ? options.provingRequest.toString()
2370
- : options.provingRequest;
2371
2817
  // Try to get JWT data to access the Provable API.
2372
2818
  const apiKey = options.apiKey ?? this.apiKey;
2373
2819
  const consumerId = options.consumerId ?? this.consumerId;
2374
2820
  let jwtData = options.jwtData ?? this.jwtData;
2375
- // Check to see if the JWT needs refreshing.
2821
+ // Check to see if the JWT needs refreshing. Runs before parsing the
2822
+ // proving request so JWT refresh errors propagate as they always have.
2376
2823
  const isExpired = jwtData && Date.now() >= jwtData.expiration - FIVE_MINUTES;
2377
2824
  if (!jwtData || isExpired) {
2378
2825
  if (apiKey && consumerId) {
@@ -2381,7 +2828,7 @@ class AleoNetworkClient {
2381
2828
  options.jwtData = jwtData;
2382
2829
  }
2383
2830
  else {
2384
- console.warn('JWT or both apiKey and consumerId are required when using the Provable API');
2831
+ logger.warn('JWT or both apiKey and consumerId are required when using the Provable API');
2385
2832
  }
2386
2833
  }
2387
2834
  // Create the necessary headers to hit the provable api.
@@ -2393,59 +2840,58 @@ class AleoNetworkClient {
2393
2840
  if (jwtData?.jwt) {
2394
2841
  headers["Authorization"] = jwtData.jwt;
2395
2842
  }
2396
- // Encapsulate the requests in a locally scoped function that can be run with a retry closure.
2397
- const runRequest = async () => {
2398
- // If DPS privacy is set, call invoke the encrypted flow.
2399
- if (options.dpsPrivacy) {
2400
- // Get an ephemeral public key from a DPS service.
2401
- const pubKeyResponse = await get(proverUri + "/pubkey", {
2402
- headers,
2403
- credentials: "include",
2404
- }, this.transport);
2405
- // Encrypt the provingRequest.
2406
- const pubkey = parseJSON(await pubKeyResponse.text());
2407
- const ciphertext = encryptProvingRequest(pubkey.public_key, mainnet_js.ProvingRequest.fromString(provingRequestString));
2408
- // Form the expected query a DPS service expects (including the key_id).
2409
- const payload = {
2410
- key_id: pubkey.key_id,
2411
- ciphertext: ciphertext,
2412
- };
2413
- // We're in node, attempt to set the cookie manually.
2414
- const cookie = isNode() ? pubKeyResponse.headers.get("set-cookie") : undefined;
2415
- // Send the encrypted proving request to the DPS service.
2416
- const res = await this.transport(`${proverUri}/prove/encrypted`, {
2417
- method: "POST",
2418
- body: JSON.stringify(payload),
2419
- headers: {
2420
- ...headers,
2421
- ...(cookie ? { Cookie: cookie } : {})
2422
- },
2423
- credentials: "include",
2424
- });
2425
- // Properly handle the proving response.
2426
- return this.handleProvingResponse(res);
2427
- }
2428
- // If encrypted usage is not specified use the unencrypted endpoint.
2429
- const proveEndpoint = proverUri.endsWith("/prove")
2430
- ? proverUri
2431
- : proverUri + "/prove";
2432
- const res = await this.transport(proveEndpoint, {
2433
- method: "POST",
2434
- body: provingRequestString,
2843
+ // Send the proving request encrypted (libsodium-compatible sealed box).
2844
+ // Used by both `/prove/authorization` (Authorization variant) and
2845
+ // `/prove/request` (Request variant). The legacy plaintext `/prove`
2846
+ // route is no longer used.
2847
+ const sendEncrypted = async (endpoint, provingRequestObj) => {
2848
+ // Get an ephemeral public key from the DPS.
2849
+ const pubKeyResponse = await get(proverUri + "/pubkey", {
2435
2850
  headers,
2851
+ credentials: "include",
2852
+ }, this.transport);
2853
+ // Encrypt the provingRequest using the ephemeral pubkey.
2854
+ const pubkey = parseJSON(await pubKeyResponse.text());
2855
+ const ciphertext = encryptProvingRequest(pubkey.public_key, provingRequestObj);
2856
+ const payload = {
2857
+ key_id: pubkey.key_id,
2858
+ ciphertext: ciphertext,
2859
+ };
2860
+ // We're in node, attempt to set the cookie manually.
2861
+ const cookie = isNode() ? pubKeyResponse.headers.get("set-cookie") : undefined;
2862
+ const res = await this.transport(`${proverUri}${endpoint}`, {
2863
+ method: "POST",
2864
+ body: JSON.stringify(payload),
2865
+ headers: {
2866
+ ...headers,
2867
+ ...(cookie ? { Cookie: cookie } : {})
2868
+ },
2869
+ credentials: "include",
2436
2870
  });
2437
- // Properly handle the proving response.
2438
2871
  return this.handleProvingResponse(res);
2439
2872
  };
2873
+ // Parse the proving request once, up front, so the variant the SDK
2874
+ // routes on matches the bytes it will eventually send (a Request-
2875
+ // variant string must hit /prove/request, not /prove/authorization).
2876
+ // A malformed input string throws synchronously — the safe-API
2877
+ // contract only promises to return `{ ok: false, ... }` for HTTP
2878
+ // failures (400/500/503); a parse error is a caller-side bug and
2879
+ // surfacing it as a fake 500 would mislead callers debugging it.
2880
+ // `options.dpsPrivacy` is ignored; the legacy plaintext `/prove`
2881
+ // route is deprecated.
2882
+ const provingRequestObj = options.provingRequest instanceof mainnet_js.ProvingRequest
2883
+ ? options.provingRequest
2884
+ : mainnet_js.ProvingRequest.fromString(options.provingRequest);
2885
+ const endpoint = provingRequestObj.kind() === "request"
2886
+ ? "/prove/request"
2887
+ : "/prove/authorization";
2440
2888
  try {
2441
- // Run the request with retries.
2442
2889
  return await retryWithBackoff(async () => {
2443
- // Run the encrypted or non-encrypted flow as specified by the flags.
2444
- const result = await runRequest();
2890
+ const result = await sendEncrypted(endpoint, provingRequestObj);
2445
2891
  if (result.ok) {
2446
2892
  return result;
2447
2893
  }
2448
- // If 500s are hit responses are returned, attempt retries.
2894
+ // Retry on 500/503; surface 400 verbatim.
2449
2895
  if (result.status === 500 || result.status === 503) {
2450
2896
  const err = new Error(result.error.message);
2451
2897
  err.status = result.status;
@@ -2455,7 +2901,7 @@ class AleoNetworkClient {
2455
2901
  });
2456
2902
  }
2457
2903
  catch (err) {
2458
- // If an error is returned, provide usable information to the caller.
2904
+ // HTTP failure inside the retry loop convert to a result object.
2459
2905
  const e = err;
2460
2906
  return {
2461
2907
  ok: false,
@@ -2511,10 +2957,10 @@ class AleoNetworkClient {
2511
2957
  let text = "";
2512
2958
  try {
2513
2959
  text = await res.text();
2514
- console.warn("Response text from server:", text);
2960
+ logger.warn("Response text from server:", text);
2515
2961
  }
2516
2962
  catch (err) {
2517
- console.warn("Failed to read response text:", err);
2963
+ logger.warn("Failed to read response text:", err);
2518
2964
  }
2519
2965
  // If the transaction ID is malformed (e.g. invalid checksum, wrong length),
2520
2966
  // the API returns a 4XX with "Invalid URL" — we treat this as a fatal error and stop polling.
@@ -2525,7 +2971,7 @@ class AleoNetworkClient {
2525
2971
  return reject(new Error(`Malformed transaction ID: ${text}`));
2526
2972
  }
2527
2973
  // Log and continue polling for 404s or 5XX errors in case a tx doesn't exist yet
2528
- console.warn("Non-OK response (retrying):", res.status, text);
2974
+ logger.warn("Non-OK response (retrying):", res.status, text);
2529
2975
  return;
2530
2976
  }
2531
2977
  const data = parseJSON(await res.text());
@@ -2539,7 +2985,7 @@ class AleoNetworkClient {
2539
2985
  }
2540
2986
  }
2541
2987
  catch (err) {
2542
- console.error("Polling error:", err);
2988
+ logger.error("Polling error:", err);
2543
2989
  }
2544
2990
  }, checkInterval);
2545
2991
  });
@@ -2547,7 +2993,7 @@ class AleoNetworkClient {
2547
2993
  }
2548
2994
 
2549
2995
  /**
2550
- * Error thrown when a record scanner request fails (e.g. /register, /register/encrypted).
2996
+ * Error thrown when a record scanner request fails (e.g. /register/encrypted).
2551
2997
  * Includes HTTP status so callers can handle 422 vs 500 etc.
2552
2998
  */
2553
2999
  class RecordScannerRequestError extends Error {
@@ -2707,7 +3153,7 @@ class AleoKeyProvider {
2707
3153
  * @returns {FunctionKeyPair} Proving and verifying keys for the specified program
2708
3154
  */
2709
3155
  getKeys(keyId) {
2710
- console.debug(`Checking if key exists in cache. KeyId: ${keyId}`);
3156
+ logger.debug(`Checking if key exists in cache. KeyId: ${keyId}`);
2711
3157
  if (this.cache.has(keyId)) {
2712
3158
  const [provingKeyBytes, verifyingKeyBytes] = (this.cache.get(keyId));
2713
3159
  return [
@@ -2808,9 +3254,9 @@ class AleoKeyProvider {
2808
3254
  ];
2809
3255
  }
2810
3256
  else {
2811
- console.debug("Fetching proving keys from url " + proverUrl);
3257
+ logger.debug("Fetching proving keys from url " + proverUrl);
2812
3258
  const provingKey = (mainnet_js.ProvingKey.fromBytes(await this.fetchBytes(proverUrl)));
2813
- console.debug("Fetching verifying keys " + verifierUrl);
3259
+ logger.debug("Fetching verifying keys " + verifierUrl);
2814
3260
  const verifyingKey = (await this.getVerifyingKey(verifierUrl));
2815
3261
  this.cache.set(cacheKey, [
2816
3262
  provingKey.toBytes(),
@@ -2850,7 +3296,7 @@ class AleoKeyProvider {
2850
3296
  return mainnet_js.ProvingKey.fromBytes(value[0]);
2851
3297
  }
2852
3298
  else {
2853
- console.debug("Fetching proving keys from url " + proverUrl);
3299
+ logger.debug("Fetching proving keys from url " + proverUrl);
2854
3300
  const provingKey = (mainnet_js.ProvingKey.fromBytes(await this.fetchBytes(proverUrl)));
2855
3301
  return provingKey;
2856
3302
  }
@@ -3023,172 +3469,22 @@ class AleoKeyProvider {
3023
3469
  try {
3024
3470
  /// Try to fetch the verifying key from the network as a string
3025
3471
  const response = await get(verifierUri, undefined, this.transport);
3026
- const text = await response.text();
3027
- return mainnet_js.VerifyingKey.fromString(text);
3028
- }
3029
- catch (e) {
3030
- /// If that fails, try to fetch the verifying key from the network as bytes
3031
- try {
3032
- return (mainnet_js.VerifyingKey.fromBytes(await this.fetchBytes(verifierUri)));
3033
- }
3034
- catch (inner) {
3035
- throw new Error("Invalid verifying key. Error: " + inner.message);
3036
- }
3037
- }
3038
- }
3039
- }
3040
- unBondPublicKeys() {
3041
- return this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.unbond_public);
3042
- }
3043
- }
3044
-
3045
- /**
3046
- * Error thrown when there is a mismatch between expected and actual key metadata.
3047
- * This can occur during verification of either the checksum or size of a key.
3048
- *
3049
- * @extends Error
3050
- */
3051
- class KeyVerificationError extends Error {
3052
- locator;
3053
- field;
3054
- expected;
3055
- actual;
3056
- /**
3057
- * Creates a new KeyVerificationError instance (error.name is "ChecksumMismatchError").
3058
- *
3059
- * @param {string} locator - The key locator where the mismatch occurred.
3060
- * @param {"checksum" | "size"} field - The field that failed verification (either "checksum" or "size").
3061
- * @param {string} expected - The expected value of the field.
3062
- * @param {string} actual - The actual value encountered.
3063
- */
3064
- constructor(locator, field, expected, actual) {
3065
- super(`Key verification ${locator} ${field} mismatch: expected ${expected}, got ${actual}`);
3066
- this.locator = locator;
3067
- this.field = field;
3068
- this.expected = expected;
3069
- this.actual = actual;
3070
- this.name = "ChecksumMismatchError";
3071
- Object.setPrototypeOf(this, KeyVerificationError.prototype);
3072
- }
3073
- }
3074
- /**
3075
- * Computes the SHA-256 checksum of a given set of bytes.
3076
- *
3077
- * @param {Uint8Array} bytes - The bytes to compute the checksum of.
3078
- */
3079
- async function sha256Hex(bytes) {
3080
- const hash = await crypto.subtle.digest("SHA-256", bytes);
3081
- return Array.from(new Uint8Array(hash))
3082
- .map((b) => b.toString(16).padStart(2, "0"))
3083
- .join("");
3084
- }
3085
-
3086
- /**
3087
- * In-memory implementation of KeyVerifier that stores and verifies key fingerprints.
3088
- * Provides functionality to compute and verify cryptographic checksums of keys, storing them
3089
- * in memory for subsequent verification. This implementation is primarily used for testing
3090
- * and development purposes where persistence is not required.
3091
- *
3092
- * Key features:
3093
- * - Computes SHA-256 checksums and sizes for key bytes.
3094
- * - Stores key fingerprints in memory using string locators.
3095
- * - Verifies key bytes against stored or provided fingerprints.
3096
- *
3097
- * @implements {KeyVerifier}
3098
- */
3099
- class MemKeyVerifier {
3100
- keyStore = {};
3101
- /**
3102
- * Computes and optionally stores key metadata. If a keyFingerprint is provided, this function will verify the computed checksum against it before storing it.
3103
- *
3104
- * @param {KeyMetadata} keyMetadata - Object containing key bytes and optional verification data.
3105
- * @throws {KeyVerificationError} When provided keyFingerprint doesn't match computed values.
3106
- * @returns {Promise<KeyFingerprint>} Computed key metadata.
3107
- */
3108
- async computeKeyMetadata(keyMetadata) {
3109
- // Compute the metadata from the key bytes
3110
- const computedFingerprint = {
3111
- checksum: await sha256Hex(keyMetadata.keyBytes),
3112
- size: keyMetadata.keyBytes.length
3113
- };
3114
- // If a KeyFingerprint is provided, verify it matches computed values.
3115
- if (keyMetadata.fingerprint) {
3116
- if (keyMetadata.fingerprint.size !== computedFingerprint.size) {
3117
- throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "size", String(keyMetadata.fingerprint.size), String(computedFingerprint.size));
3118
- }
3119
- if (keyMetadata.fingerprint.checksum !== computedFingerprint.checksum) {
3120
- throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "checksum", keyMetadata.fingerprint.checksum, computedFingerprint.checksum);
3121
- }
3122
- }
3123
- // If locator is provided, store only the fingerprint
3124
- if (keyMetadata.locator) {
3125
- this.keyStore[keyMetadata.locator] = computedFingerprint;
3126
- }
3127
- return computedFingerprint;
3128
- }
3129
- /**
3130
- * Verifies key bytes against stored or provided metadata. Follows a priority verification scheme:
3131
- * 1. If KeyFingerprint is provided in the metadata, this method verifies against that first.
3132
- * 2. If a locator is provided, attempts to verify against stored fingerprint.
3133
- * 3. If neither is available, throws an error.
3134
- *
3135
- * @param {KeyMetadata} keyMetadata - Object containing the key bytes and optional verification metadata.
3136
- * @throws {Error} When neither fingerprint nor valid locator is provided for verification.
3137
- * @throws {KeyVerificationError} When size or checksum verification fails.
3138
- * @returns {Promise<void>} Promise that resolves when verification succeeds.
3139
- */
3140
- async verifyKeyBytes(keyMetadata) {
3141
- if (!keyMetadata.keyBytes) {
3142
- throw new Error("Key bytes must be provided for verification.");
3143
- }
3144
- // Compute the fingerprint for the provided bytes.
3145
- const computedFingerprint = await this.computeKeyMetadata({
3146
- keyBytes: keyMetadata.keyBytes
3147
- });
3148
- // Determine which fingerprint to verify against.
3149
- let fingerprintToVerify;
3150
- if (keyMetadata.fingerprint) {
3151
- // If a key fingerprint is provided, use it.
3152
- fingerprintToVerify = keyMetadata.fingerprint;
3153
- }
3154
- else if (keyMetadata.locator) {
3155
- // Otherwise try to get stored fingerprint by locator.
3156
- fingerprintToVerify = this.keyStore[keyMetadata.locator];
3157
- }
3158
- if (!fingerprintToVerify) {
3159
- throw new Error("Either fingerprint or a valid locator must be provided for verification.");
3160
- }
3161
- // Verify the key size.
3162
- if (fingerprintToVerify.size !== computedFingerprint.size) {
3163
- throw new KeyVerificationError(keyMetadata.locator || "", "size", String(fingerprintToVerify.size), String(computedFingerprint.size));
3164
- }
3165
- // Verify the key checksum.
3166
- if (fingerprintToVerify.checksum !== computedFingerprint.checksum) {
3167
- throw new KeyVerificationError(keyMetadata.locator || "", "checksum", fingerprintToVerify.checksum, computedFingerprint.checksum);
3168
- }
3169
- }
3170
- }
3171
-
3172
- /**
3173
- * Error thrown when a key locator is invalid for filesystem use.
3174
- * Used to prevent path traversal and other filesystem injection when deriving paths from locators.
3175
- *
3176
- * @extends Error
3177
- */
3178
- class InvalidLocatorError extends Error {
3179
- locator;
3180
- reason;
3181
- /**
3182
- * @param message - Human-readable description of the validation failure.
3183
- * @param locator - The invalid locator string that failed validation.
3184
- * @param reason - Machine-readable reason code for the failure.
3185
- */
3186
- constructor(message, locator, reason) {
3187
- super(message);
3188
- this.locator = locator;
3189
- this.reason = reason;
3190
- this.name = "InvalidLocatorError";
3191
- Object.setPrototypeOf(this, InvalidLocatorError.prototype);
3472
+ const text = await response.text();
3473
+ return mainnet_js.VerifyingKey.fromString(text);
3474
+ }
3475
+ catch (e) {
3476
+ /// If that fails, try to fetch the verifying key from the network as bytes
3477
+ try {
3478
+ return (mainnet_js.VerifyingKey.fromBytes(await this.fetchBytes(verifierUri)));
3479
+ }
3480
+ catch (inner) {
3481
+ throw new Error("Invalid verifying key. Error: " + inner.message);
3482
+ }
3483
+ }
3484
+ }
3485
+ }
3486
+ unBondPublicKeys() {
3487
+ return this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.unbond_public);
3192
3488
  }
3193
3489
  }
3194
3490
 
@@ -3945,12 +4241,12 @@ class NetworkRecordProvider {
3945
4241
  records = await this.findCreditsRecords([microcredits], searchParameters);
3946
4242
  }
3947
4243
  catch (e) {
3948
- console.log("No records found with error:", e);
4244
+ logger.log("No records found with error:", e);
3949
4245
  }
3950
4246
  if (records && records.length > 0) {
3951
4247
  return records[0];
3952
4248
  }
3953
- console.error("Record not found with error:", records);
4249
+ logger.error("Record not found with error:", records);
3954
4250
  throw new Error("Record not found");
3955
4251
  }
3956
4252
  /**
@@ -3962,12 +4258,12 @@ class NetworkRecordProvider {
3962
4258
  records = await this.findRecords(searchParameters);
3963
4259
  }
3964
4260
  catch (e) {
3965
- console.log("No records found with error:", e);
4261
+ logger.log("No records found with error:", e);
3966
4262
  }
3967
4263
  if (records && records.length > 0) {
3968
4264
  return records[0];
3969
4265
  }
3970
- console.error("Record not found with error:", records);
4266
+ logger.error("Record not found with error:", records);
3971
4267
  throw new Error("Record not found");
3972
4268
  }
3973
4269
  /**
@@ -4062,7 +4358,7 @@ class BlockHeightSearch {
4062
4358
  * const recordScanner = new RecordScanner({ url: "https://record-scanner.aleo.org" });
4063
4359
  * recordScanner.setAccount(account);
4064
4360
  * recordScanner.setApiKey("example-api-key");
4065
- * const result = await recordScanner.register(viewKey, 0);
4361
+ * const result = await recordScanner.registerEncrypted(viewKey, 0);
4066
4362
  * if (result.ok) { const uuid = result.data.uuid; }
4067
4363
  *
4068
4364
  * const filter = {
@@ -4317,40 +4613,6 @@ class RecordScanner {
4317
4613
  }
4318
4614
  throw err;
4319
4615
  }
4320
- /**
4321
- * Register the account with the record scanning service (unencrypted POST /register). Does not throw if a valid error response from the record scanner is received; returns a result object instead.
4322
- *
4323
- * @param {ViewKey} viewKey The view key to register.
4324
- * @param {number} startBlock The block height to start scanning from.
4325
- * @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure.
4326
- */
4327
- async register(viewKey, startBlock) {
4328
- try {
4329
- const request = {
4330
- view_key: viewKey.to_string(),
4331
- start: startBlock,
4332
- };
4333
- const response = await this.request(new Request(`${this.url}/register`, {
4334
- method: "POST",
4335
- headers: { "Content-Type": "application/json" },
4336
- body: JSON.stringify(request),
4337
- }));
4338
- const data = await response.json();
4339
- // If the uuid is not set, set it on the scanner.
4340
- if (!this.uuid) {
4341
- this.uuid = data.uuid;
4342
- }
4343
- // Add the view key to the local scanner's view keys if configured to do so.
4344
- if (this.cacheViewKeysOnRegister) {
4345
- this.addViewKey(viewKey);
4346
- }
4347
- return { ok: true, data };
4348
- }
4349
- catch (err) {
4350
- console.error(`Failed to register view key: ${err}`);
4351
- return this.handleRequestError(err);
4352
- }
4353
- }
4354
4616
  /**
4355
4617
  * Fetches an ephemeral public key from the record scanning service for use with registerEncrypted.
4356
4618
  * Follows the same pattern as the delegated proving service /pubkey endpoint.
@@ -4361,6 +4623,19 @@ class RecordScanner {
4361
4623
  const response = await this.request(new Request(`${this.url}/pubkey`, { method: "GET" }));
4362
4624
  return parseJSON(await response.text());
4363
4625
  }
4626
+ /**
4627
+ * Registers the account with the record scanning service using the encrypted flow.
4628
+ * Alias of {@link registerEncrypted} — preserved so existing callers using the
4629
+ * previous unencrypted `register(viewKey, startBlock)` API continue to work
4630
+ * unchanged while transparently using the encrypted endpoint.
4631
+ *
4632
+ * @param {ViewKey} viewKey The view key to register.
4633
+ * @param {number} startBlock The block height to start scanning from.
4634
+ * @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure.
4635
+ */
4636
+ async register(viewKey, startBlock) {
4637
+ return this.registerEncrypted(viewKey, startBlock);
4638
+ }
4364
4639
  /**
4365
4640
  * Registers the account with the record scanning service using the encrypted flow: 1. fetches an ephemeral public key from /pubkey - 2. encrypts the registration request (view key + start block) - 3. POSTs to /register/encrypted. Does not HTTP error on a proper error response from the record scanner; returns a result object instead.
4366
4641
  *
@@ -4592,7 +4867,7 @@ class RecordScanner {
4592
4867
  throw new Error("Record not found");
4593
4868
  }
4594
4869
  catch (error) {
4595
- console.error(`Failed to find record: ${error}`);
4870
+ logger.error(`Failed to find record: ${error}`);
4596
4871
  throw error;
4597
4872
  }
4598
4873
  }
@@ -4753,7 +5028,7 @@ class RecordScanner {
4753
5028
  return record;
4754
5029
  }
4755
5030
  catch (error) {
4756
- console.error(`Failed to find credits record: ${error}`);
5031
+ logger.error(`Failed to find credits record: ${error}`);
4757
5032
  throw error;
4758
5033
  }
4759
5034
  }
@@ -4802,7 +5077,7 @@ class RecordScanner {
4802
5077
  });
4803
5078
  }
4804
5079
  catch (error) {
4805
- console.error(`Failed to find credits records: ${error}`);
5080
+ logger.error(`Failed to find credits records: ${error}`);
4806
5081
  throw error;
4807
5082
  }
4808
5083
  }
@@ -4840,7 +5115,7 @@ class RecordScanner {
4840
5115
  return response;
4841
5116
  }
4842
5117
  catch (error) {
4843
- console.error(`Failed to make request to ${req.url}: ${error}`);
5118
+ logger.error(`Failed to make request to ${req.url}: ${error}`);
4844
5119
  throw error;
4845
5120
  }
4846
5121
  }
@@ -5197,17 +5472,19 @@ class ProgramManager {
5197
5472
  networkClient;
5198
5473
  recordProvider;
5199
5474
  inclusionKeysLoaded = false;
5475
+ _keyStore;
5200
5476
  /** Create a new instance of the ProgramManager
5201
5477
  *
5202
5478
  * @param { string | undefined } host A host uri running the official Aleo API
5203
5479
  * @param { FunctionKeyProvider | undefined } keyProvider A key provider that implements {@link FunctionKeyProvider} interface
5204
5480
  * @param { RecordProvider | undefined } recordProvider A record provider that implements {@link RecordProvider} interface
5205
5481
  */
5206
- constructor(host, keyProvider, recordProvider, networkClientOptions) {
5482
+ constructor(host, keyProvider, recordProvider, networkClientOptions, keyStore) {
5207
5483
  this.host = host ? host : "https://api.provable.com/v2";
5208
5484
  this.networkClient = new AleoNetworkClient(this.host, networkClientOptions);
5209
5485
  this.keyProvider = keyProvider ? keyProvider : new AleoKeyProvider({ transport: networkClientOptions?.transport });
5210
5486
  this.recordProvider = recordProvider;
5487
+ this._keyStore = keyStore;
5211
5488
  }
5212
5489
  /**
5213
5490
  * Pre-load the inclusion prover for offline execution. Required when the
@@ -5285,6 +5562,330 @@ class ProgramManager {
5285
5562
  setRecordProvider(recordProvider) {
5286
5563
  this.recordProvider = recordProvider;
5287
5564
  }
5565
+ /**
5566
+ * Set the key store for automatic key caching across executions.
5567
+ *
5568
+ * @param {KeyStore} keyStore
5569
+ */
5570
+ setKeyStore(keyStore) {
5571
+ this._keyStore = keyStore;
5572
+ }
5573
+ /**
5574
+ * Build a ProgramImportsBuilder from a program and its imports.
5575
+ * Fetches imports from the network if not provided, resolves transitive
5576
+ * dependencies, and optionally pre-loads cached keys from the KeyStore.
5577
+ *
5578
+ * @param loadKeys When true (default), loads cached proving/verifying keys
5579
+ * from the KeyStore into the builder. Set to false for authorization and
5580
+ * proving request paths where keys are not synthesized.
5581
+ */
5582
+ async buildProgramImports(program, imports, loadKeys, entryFunction) {
5583
+ const builder = new mainnet_js.ProgramImports();
5584
+ const programSource = typeof program === "string" ? program : program.toString();
5585
+ const programObj = mainnet_js.Program.fromString(programSource);
5586
+ const importNames = programObj.getImports();
5587
+ if (importNames.length === 0 && (!imports || Object.keys(imports).length === 0)) {
5588
+ return { builder, importEditions: new Map() };
5589
+ }
5590
+ let resolvedImports = {};
5591
+ if (imports) {
5592
+ resolvedImports = { ...imports };
5593
+ }
5594
+ if (importNames.length > 0 && !imports) {
5595
+ try {
5596
+ resolvedImports = await this.networkClient.getProgramImports(programSource);
5597
+ }
5598
+ catch (e) {
5599
+ logger.warn(`Failed to resolve program imports from network: ${e}.`);
5600
+ }
5601
+ }
5602
+ // Build a map of which functions each import actually calls,
5603
+ // so we only load keys for functions in the call chain.
5604
+ const calledFunctions = ProgramManager.callGraphToMap(programObj.getCallGraph(entryFunction));
5605
+ // Phase 1: Collect all programs via BFS, discovering transitive imports.
5606
+ // The initial getProgramImports call above already resolves the full
5607
+ // transitive closure via recursive DFS. The BFS loop here only needs
5608
+ // to handle user-provided imports whose transitive deps may not yet be
5609
+ // known. For each unknown import we issue a single getProgram() call
5610
+ // (not a full recursive getProgramImports) and fetch siblings in
5611
+ // parallel to minimize round-trips.
5612
+ const collected = new Map();
5613
+ const collectQueue = Object.entries(resolvedImports).map(([name, src]) => [name, typeof src === "string" ? src : src.toString()]);
5614
+ while (collectQueue.length > 0) {
5615
+ const [name, source] = collectQueue.shift();
5616
+ if (collected.has(name))
5617
+ continue;
5618
+ collected.set(name, source);
5619
+ // Discover transitive imports for collection (call-graph tracing
5620
+ // happens in a separate pass after topological sorting).
5621
+ try {
5622
+ const subImports = ProgramManager.getImportNames(source);
5623
+ if (subImports.length > 0) {
5624
+ // Fetch only unknown transitive imports — the source for
5625
+ // programs already in collected/resolvedImports is known, so
5626
+ // we skip them. Fetches within a BFS level run in parallel.
5627
+ const unknownImports = subImports.filter((id) => !collected.has(id) && !(id in resolvedImports));
5628
+ if (unknownImports.length > 0) {
5629
+ const fetched = await Promise.all(unknownImports.map(async (id) => {
5630
+ try {
5631
+ const src = await this.networkClient.getProgram(id);
5632
+ return [id, src];
5633
+ }
5634
+ catch {
5635
+ return null;
5636
+ }
5637
+ }));
5638
+ for (const entry of fetched) {
5639
+ if (entry && !collected.has(entry[0])) {
5640
+ collectQueue.push(entry);
5641
+ }
5642
+ }
5643
+ }
5644
+ }
5645
+ }
5646
+ catch (e) {
5647
+ logger.warn(`Failed to resolve transitive imports for ${name}: ${e}`);
5648
+ }
5649
+ }
5650
+ // Phase 2: Add programs in topological order (leaves first).
5651
+ // A simple DFS post-order ensures dependencies are added before
5652
+ // dependents, regardless of the order collected entries arrived.
5653
+ const sorted = [];
5654
+ const visited = new Set();
5655
+ const visit = (name) => {
5656
+ if (visited.has(name))
5657
+ return;
5658
+ visited.add(name);
5659
+ const source = collected.get(name);
5660
+ if (!source)
5661
+ return;
5662
+ for (const dep of ProgramManager.getImportNames(source)) {
5663
+ if (collected.has(dep))
5664
+ visit(dep);
5665
+ }
5666
+ sorted.push([name, source]);
5667
+ };
5668
+ for (const [name] of collected)
5669
+ visit(name);
5670
+ // When scoped, trace call graphs in reverse topological order
5671
+ // (dependents before leaves) so parent calls propagate before
5672
+ // children are traced. sorted is leaves-first, so reverse it.
5673
+ if (entryFunction) {
5674
+ for (let i = sorted.length - 1; i >= 0; i--) {
5675
+ const [name, source] = sorted[i];
5676
+ const functionsNeeded = calledFunctions.get(name);
5677
+ if (!functionsNeeded)
5678
+ continue;
5679
+ try {
5680
+ const importProgram = mainnet_js.Program.fromString(source);
5681
+ for (const fn of functionsNeeded) {
5682
+ const reachable = ProgramManager.callGraphToMap(importProgram.getCallGraph(fn));
5683
+ for (const [p, fns] of reachable) {
5684
+ if (!calledFunctions.has(p)) {
5685
+ calledFunctions.set(p, new Set(fns));
5686
+ }
5687
+ else {
5688
+ for (const f of fns)
5689
+ calledFunctions.get(p).add(f);
5690
+ }
5691
+ }
5692
+ }
5693
+ }
5694
+ catch (e) {
5695
+ logger.warn(`Failed to trace call graph for ${name}: ${e}`);
5696
+ }
5697
+ }
5698
+ }
5699
+ // Resolve editions for all imports in parallel (each import may have
5700
+ // a different edition than the top-level program).
5701
+ const importEditions = new Map();
5702
+ await Promise.all(sorted.map(async ([name]) => {
5703
+ importEditions.set(name, await this.resolveEditionAndAmendment(name));
5704
+ }));
5705
+ for (const [name, source] of sorted) {
5706
+ if (builder.contains(name))
5707
+ continue;
5708
+ const { edition: importEdition, amendment: importAmendment } = importEditions.get(name);
5709
+ try {
5710
+ builder.addProgram(name, source, importEdition);
5711
+ }
5712
+ catch (e) {
5713
+ logger.warn(`Failed to add import ${name} to builder: ${e}`);
5714
+ continue;
5715
+ }
5716
+ if (loadKeys) {
5717
+ const calledFns = calledFunctions.get(name);
5718
+ await this.loadKeysFromStore(builder, name, calledFns ? [...calledFns] : [], importEdition, importAmendment);
5719
+ }
5720
+ }
5721
+ return { builder, importEditions };
5722
+ }
5723
+ /**
5724
+ * Extract `import program_name.aleo;` names from program source via regex.
5725
+ * Avoids a WASM round-trip compared to Program.fromString + getImports.
5726
+ */
5727
+ static getImportNames(programSource) {
5728
+ const result = [];
5729
+ const importPattern = /^\s*import\s+([a-zA-Z_][a-zA-Z0-9_]*\.aleo)\s*;/gm;
5730
+ let match;
5731
+ while ((match = importPattern.exec(programSource)) !== null) {
5732
+ result.push(match[1]);
5733
+ }
5734
+ return result;
5735
+ }
5736
+ /**
5737
+ * Convert the JS object returned by Program.getCallGraph() into a
5738
+ * Map<string, Set<string>> for use in buildProgramImports.
5739
+ */
5740
+ static callGraphToMap(callGraph) {
5741
+ const result = new Map();
5742
+ for (const [programName, functions] of Object.entries(callGraph)) {
5743
+ result.set(programName, new Set(functions));
5744
+ }
5745
+ return result;
5746
+ }
5747
+ /**
5748
+ * Resolve the active KeyStore, preferring the directly-set _keyStore
5749
+ * over the KeyProvider's keyStore().
5750
+ */
5751
+ async resolveKeyStore() {
5752
+ if (this._keyStore)
5753
+ return this._keyStore;
5754
+ try {
5755
+ return await this.keyProvider?.keyStore?.();
5756
+ }
5757
+ catch {
5758
+ return undefined;
5759
+ }
5760
+ }
5761
+ /**
5762
+ * Resolve the edition and amendment count for a program from the network.
5763
+ * Returns `{ edition, amendment }` or falls back to `{ edition: 1, amendment: 0 }`.
5764
+ */
5765
+ async resolveEditionAndAmendment(programName, fallbackEdition) {
5766
+ try {
5767
+ const info = await this.networkClient.getProgramAmendmentCount(programName);
5768
+ return { edition: info.edition, amendment: info.amendment_count };
5769
+ }
5770
+ catch (e) {
5771
+ logger.warn(`Error finding edition/amendment for ${programName}. Network response: '${e.message}'. Defaulting to edition ${fallbackEdition ?? 1}, amendment 0.`);
5772
+ return { edition: fallbackEdition ?? 1, amendment: 0 };
5773
+ }
5774
+ }
5775
+ /**
5776
+ * Load cached proving/verifying keys from the KeyStore into a ProgramImportsBuilder.
5777
+ * Only loads keys for the specified functions — returns immediately if
5778
+ * functionNames is empty or undefined.
5779
+ * Resolves edition and amendment from the network for accurate key locator
5780
+ * construction when not explicitly provided.
5781
+ */
5782
+ async loadKeysFromStore(builder, programName, functionNames, edition, amendment) {
5783
+ if (!functionNames || functionNames.length === 0)
5784
+ return;
5785
+ const keyStore = await this.resolveKeyStore();
5786
+ if (!keyStore)
5787
+ return;
5788
+ // Resolve edition and amendment from the network if not fully provided.
5789
+ if (edition === undefined || amendment === undefined) {
5790
+ const resolved = await this.resolveEditionAndAmendment(programName, edition);
5791
+ edition = edition ?? resolved.edition;
5792
+ amendment = amendment ?? resolved.amendment;
5793
+ }
5794
+ for (const fnName of functionNames) {
5795
+ const pkLocator = provingKeyLocator(programName, fnName, edition, amendment);
5796
+ const vkLocator = verifyingKeyLocator(programName, fnName, edition, amendment);
5797
+ try {
5798
+ const pk = await keyStore.getProvingKey(pkLocator);
5799
+ if (pk)
5800
+ builder.addProvingKey(programName, fnName, pk);
5801
+ const vk = await keyStore.getVerifyingKey(vkLocator);
5802
+ if (vk)
5803
+ builder.addVerifyingKey(programName, fnName, vk);
5804
+ }
5805
+ catch (e) {
5806
+ logger.debug(`Failed to load keys for ${programName}/${fnName}: ${e}`);
5807
+ }
5808
+ }
5809
+ }
5810
+ /**
5811
+ * Persist newly synthesized keys from the returned ProgramImportsBuilder
5812
+ * into the KeyStore. Only writes keys that are not already in the store,
5813
+ * avoiding unnecessary writes of large proving keys.
5814
+ * Fetches each program's current edition and amendment count from the network
5815
+ * for accurate key locator construction.
5816
+ */
5817
+ async persistExtractedKeys(builder, importEditions) {
5818
+ const keyStore = await this.resolveKeyStore();
5819
+ if (!keyStore)
5820
+ return;
5821
+ const programNames = Array.from(builder.programNames());
5822
+ // Reuse editions resolved during buildProgramImports when available,
5823
+ // falling back to parallel network resolution for any missing programs.
5824
+ const editionMap = importEditions ?? new Map();
5825
+ const missing = programNames.filter((name) => !editionMap.has(name));
5826
+ if (missing.length > 0) {
5827
+ await Promise.all(missing.map(async (name) => {
5828
+ editionMap.set(name, await this.resolveEditionAndAmendment(name));
5829
+ }));
5830
+ }
5831
+ for (const programName of programNames) {
5832
+ const { edition, amendment } = editionMap.get(programName);
5833
+ let fns;
5834
+ try {
5835
+ fns = Array.from(builder.functionKeysAvailable(programName));
5836
+ }
5837
+ catch (e) {
5838
+ logger.debug(`Failed to query keys for ${programName}: ${e}`);
5839
+ continue;
5840
+ }
5841
+ for (const fnName of fns) {
5842
+ try {
5843
+ const pkLocator = provingKeyLocator(programName, fnName, edition, amendment);
5844
+ const vkLocator = verifyingKeyLocator(programName, fnName, edition, amendment);
5845
+ // Skip keys already in the store.
5846
+ if (await keyStore.has(pkLocator) && await keyStore.has(vkLocator)) {
5847
+ continue;
5848
+ }
5849
+ const pk = builder.getProvingKey(programName, fnName);
5850
+ const vk = builder.getVerifyingKey(programName, fnName);
5851
+ if (pk && vk) {
5852
+ await keyStore.setKeys(pkLocator, vkLocator, [pk, vk]);
5853
+ }
5854
+ }
5855
+ catch (e) {
5856
+ logger.debug(`Failed to persist key for ${programName}/${fnName}: ${e}`);
5857
+ }
5858
+ }
5859
+ }
5860
+ }
5861
+ /**
5862
+ * Resolve top-level function keys, checking the KeyStore first and
5863
+ * falling back to the KeyProvider.
5864
+ */
5865
+ async resolveTopLevelKeys(programName, functionName, keySearchParams, edition, amendment) {
5866
+ const keyStore = await this.resolveKeyStore();
5867
+ if (keyStore) {
5868
+ try {
5869
+ const pkLocator = provingKeyLocator(programName, functionName, edition, amendment);
5870
+ const vkLocator = verifyingKeyLocator(programName, functionName, edition, amendment);
5871
+ if (await keyStore.has(pkLocator) && await keyStore.has(vkLocator)) {
5872
+ const pk = await keyStore.getProvingKey(pkLocator);
5873
+ const vk = await keyStore.getVerifyingKey(vkLocator);
5874
+ if (pk && vk)
5875
+ return [pk, vk];
5876
+ }
5877
+ }
5878
+ catch {
5879
+ // Fall through to KeyProvider
5880
+ }
5881
+ }
5882
+ try {
5883
+ return (await this.keyProvider.functionKeys(keySearchParams));
5884
+ }
5885
+ catch {
5886
+ return undefined;
5887
+ }
5888
+ }
5288
5889
  /**
5289
5890
  * Set a header in the `AleoNetworkClient`s header map
5290
5891
  *
@@ -5338,7 +5939,7 @@ class ProgramManager {
5338
5939
  return;
5339
5940
  }
5340
5941
  catch {
5341
- console.log("Setting the inclusion prover requires either a key provider to be configured for the ProgramManager OR to pass the inclusion prover directly");
5942
+ logger.log("Setting the inclusion prover requires either a key provider to be configured for the ProgramManager OR to pass the inclusion prover directly");
5342
5943
  }
5343
5944
  }
5344
5945
  /**
@@ -5413,7 +6014,7 @@ class ProgramManager {
5413
6014
  }
5414
6015
  catch (e) {
5415
6016
  // Program does not exist on the network, deployment can proceed
5416
- console.log(`Program ${programObject.id()} does not exist on the network, deploying...`);
6017
+ logger.log(`Program ${programObject.id()} does not exist on the network, deploying...`);
5417
6018
  }
5418
6019
  if (typeof programSource === "string") {
5419
6020
  throw Error(`Program ${programObject.id()} already exists on the network, please rename your program`);
@@ -5437,7 +6038,7 @@ class ProgramManager {
5437
6038
  let fee = priorityFee;
5438
6039
  // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
5439
6040
  if (!feeRecord) {
5440
- console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
6041
+ logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
5441
6042
  const programString = programObject.toString();
5442
6043
  const imports = await this.networkClient.getProgramImports(programString);
5443
6044
  const baseFee = Number(mainnet_js.ProgramManager.estimateDeploymentFee(programString, imports));
@@ -5528,7 +6129,7 @@ class ProgramManager {
5528
6129
  }
5529
6130
  catch (e) {
5530
6131
  // Program does not exist on the network, deployment can proceed
5531
- console.log(`Program ${programObject.id()} does not exist on the network...`);
6132
+ logger.log(`Program ${programObject.id()} does not exist on the network...`);
5532
6133
  }
5533
6134
  }
5534
6135
  catch (e) {
@@ -5549,7 +6150,7 @@ class ProgramManager {
5549
6150
  let fee = priorityFee;
5550
6151
  // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
5551
6152
  if (!feeRecord) {
5552
- console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
6153
+ logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
5553
6154
  const programString = programObject.toString();
5554
6155
  const imports = await this.networkClient.getProgramImports(programString);
5555
6156
  const baseFee = Number(mainnet_js.ProgramManager.estimateDeploymentFee(programString, imports));
@@ -5719,15 +6320,9 @@ class ProgramManager {
5719
6320
  if (programName === undefined) {
5720
6321
  programName = programObject.id();
5721
6322
  }
5722
- if (edition == undefined) {
5723
- try {
5724
- edition = await this.networkClient.getLatestProgramEdition(programName);
5725
- }
5726
- catch (e) {
5727
- console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
5728
- edition = 0;
5729
- }
5730
- }
6323
+ const resolved = await this.resolveEditionAndAmendment(programName, edition);
6324
+ edition = edition ?? resolved.edition;
6325
+ const { amendment } = resolved;
5731
6326
  // Get the private key from the account if it is not provided in the parameters
5732
6327
  let executionPrivateKey = privateKey;
5733
6328
  if (typeof privateKey === "undefined" &&
@@ -5748,23 +6343,19 @@ class ProgramManager {
5748
6343
  logAndThrow(`Error finding fee keys. Key finder response: '${e.message}'. Please ensure your key provider is configured correctly.`);
5749
6344
  }
5750
6345
  const [feeProvingKey, feeVerifyingKey] = feeKeys;
5751
- // If the function proving and verifying keys are not provided, attempt to find them using the key provider
6346
+ // Build the ProgramImportsBuilder for import resolution and key caching.
6347
+ const { builder: programImportsBuilder, importEditions } = await this.buildProgramImports(program, imports, true, functionName);
6348
+ // Include the top-level program's already-resolved edition so
6349
+ // persistExtractedKeys doesn't make a redundant network call for it.
6350
+ importEditions.set(programName, { edition: edition, amendment });
6351
+ // If the function proving and verifying keys are not provided, attempt to find them
5752
6352
  if (!provingKey || !verifyingKey) {
5753
- try {
5754
- [provingKey, verifyingKey] = (await this.keyProvider.functionKeys(keySearchParams));
5755
- }
5756
- catch (e) {
5757
- console.log(`Function keys not found. Key finder response: '${e}'. The function keys will be synthesized`);
5758
- }
5759
- }
5760
- // Resolve the program imports if they exist
5761
- const numberOfImports = programObject.getImports().length;
5762
- if (numberOfImports > 0 && !imports) {
5763
- try {
5764
- imports = (await this.networkClient.getProgramImports(programName));
6353
+ const keys = await this.resolveTopLevelKeys(programName, functionName, keySearchParams, edition, amendment);
6354
+ if (keys) {
6355
+ [provingKey, verifyingKey] = keys;
5765
6356
  }
5766
- catch (e) {
5767
- logAndThrow(`Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`);
6357
+ else {
6358
+ logger.log("Function keys not found in KeyStore or KeyProvider. The function keys will be synthesized");
5768
6359
  }
5769
6360
  }
5770
6361
  // Get the fee record from the account if it is not provided in the parameters
@@ -5789,17 +6380,23 @@ class ProgramManager {
5789
6380
  }
5790
6381
  // Auto-convert bare string inputs to field elements where the function expects field type.
5791
6382
  const preparedInputs = this.prepareInputs(program, functionName, inputs);
5792
- // Build the query: user-provided OfflineQuery for truly offline execution,
5793
- // CallbackQuery when a custom transport is configured, or undefined to
5794
- // let WASM use its internal SnapshotQuery with the default fetch.
5795
6383
  const query = offlineQuery
5796
6384
  ? mainnet_js.QueryOption.offlineQuery(offlineQuery)
5797
6385
  : this.networkClient.hasCustomTransport
5798
6386
  ? mainnet_js.QueryOption.callbackQuery(this.buildCallbackQuery())
5799
6387
  : undefined;
5800
6388
  await this.ensureInclusionKeys(!!offlineQuery);
5801
- // Build an execution transaction
5802
- return await mainnet_js.ProgramManager.buildExecutionTransaction(executionPrivateKey, program, functionName, preparedInputs, priorityFee, feeRecord, this.host, imports, provingKey, verifyingKey, feeProvingKey, feeVerifyingKey, query, edition);
6389
+ const result = await mainnet_js.ProgramManager.buildExecutionTransaction(executionPrivateKey, program, functionName, preparedInputs, priorityFee, feeRecord, this.host, undefined, // imports (legacy Object path — builder handles resolution)
6390
+ provingKey, verifyingKey, feeProvingKey, feeVerifyingKey, query, edition, programImportsBuilder?.clone());
6391
+ // The clone passed to WASM shares state with the original via
6392
+ // Rc<RefCell<>> — synthesized keys are already visible.
6393
+ try {
6394
+ await this.persistExtractedKeys(programImportsBuilder, importEditions);
6395
+ }
6396
+ catch (e) {
6397
+ logger.debug(`Failed to persist extracted keys: ${e}`);
6398
+ }
6399
+ return result;
5803
6400
  }
5804
6401
  /**
5805
6402
  * Builds an execution transaction for submission to the Aleo network from an Authorization and Fee Authorization.
@@ -5877,6 +6474,10 @@ class ProgramManager {
5877
6474
  const feeAuthorization = options.feeAuthorization;
5878
6475
  const keySearchParams = options.keySearchParams;
5879
6476
  const offlineQuery = options.offlineQuery;
6477
+ let edition = options.edition;
6478
+ const resolved = await this.resolveEditionAndAmendment(programName, edition);
6479
+ edition = edition ?? resolved.edition;
6480
+ const { amendment } = resolved;
5880
6481
  let provingKey = options.provingKey;
5881
6482
  let verifyingKey = options.verifyingKey;
5882
6483
  let program = options.program;
@@ -5905,24 +6506,17 @@ class ProgramManager {
5905
6506
  logAndThrow(`Error finding fee keys. Key finder response: '${e.message}'. Please ensure your key provider is configured correctly.`);
5906
6507
  }
5907
6508
  const [feeProvingKey, feeVerifyingKey] = feeKeys;
5908
- // If the function proving and verifying keys are not provided, attempt to find them using the key provider.
6509
+ // Build the ProgramImportsBuilder for import resolution and key caching.
6510
+ const { builder: programImportsBuilder, importEditions } = await this.buildProgramImports(program, imports, true, authorization.functionName());
6511
+ importEditions.set(programName, { edition: edition, amendment });
6512
+ // If the function proving and verifying keys are not provided, attempt to find them.
5909
6513
  if (!provingKey || !verifyingKey) {
5910
- try {
5911
- [provingKey, verifyingKey] = (await this.keyProvider.functionKeys(keySearchParams));
5912
- }
5913
- catch (e) {
5914
- console.log(`Function keys not found. Key finder response: '${e}'. The function keys will be synthesized`);
5915
- }
5916
- }
5917
- // Resolve the program imports if they exist.
5918
- console.log("Resolving program imports");
5919
- const numberOfImports = mainnet_js.Program.fromString(program).getImports().length;
5920
- if (numberOfImports > 0 && !imports) {
5921
- try {
5922
- imports = (await this.networkClient.getProgramImports(programName));
6514
+ const keys = await this.resolveTopLevelKeys(programName, authorization.functionName(), keySearchParams, edition, amendment);
6515
+ if (keys) {
6516
+ [provingKey, verifyingKey] = keys;
5923
6517
  }
5924
- catch (e) {
5925
- logAndThrow(`Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`);
6518
+ else {
6519
+ logger.log("Function keys not found in KeyStore or KeyProvider. The function keys will be synthesized");
5926
6520
  }
5927
6521
  }
5928
6522
  const query = offlineQuery
@@ -5931,9 +6525,18 @@ class ProgramManager {
5931
6525
  ? mainnet_js.QueryOption.callbackQuery(this.buildCallbackQuery())
5932
6526
  : undefined;
5933
6527
  await this.ensureInclusionKeys(!!offlineQuery);
5934
- // Build an execution transaction from the authorization.
5935
- console.log("Executing authorizations");
5936
- return await mainnet_js.ProgramManager.executeAuthorization(authorization, feeAuthorization, program, provingKey, verifyingKey, feeProvingKey, feeVerifyingKey, imports, this.host, query);
6528
+ // Build an execution transaction from the authorization. The
6529
+ // ProgramImportsBuilder is consumed by value (ownership transfers to WASM)
6530
+ // and returned enriched with any keys synthesized during execution.
6531
+ const result = await mainnet_js.ProgramManager.executeAuthorization(authorization, feeAuthorization, program, provingKey, verifyingKey, feeProvingKey, feeVerifyingKey, undefined, // imports (legacy Object path — builder handles resolution)
6532
+ this.host, query, programImportsBuilder?.clone(), edition);
6533
+ try {
6534
+ await this.persistExtractedKeys(programImportsBuilder, importEditions);
6535
+ }
6536
+ catch (e) {
6537
+ logger.debug(`Failed to persist extracted keys: ${e}`);
6538
+ }
6539
+ return result;
5937
6540
  }
5938
6541
  /**
5939
6542
  * Builds a SnarkVM `Authorization` for a specific function.
@@ -5996,29 +6599,17 @@ class ProgramManager {
5996
6599
  if (typeof executionPrivateKey === "undefined") {
5997
6600
  throw "No private key provided and no private key set in the ProgramManager";
5998
6601
  }
5999
- if (edition == undefined) {
6000
- try {
6001
- edition = await this.networkClient.getLatestProgramEdition(programName);
6002
- }
6003
- catch (e) {
6004
- console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
6005
- edition = 0;
6006
- }
6007
- }
6008
- // Resolve the program imports if they exist.
6009
- const numberOfImports = mainnet_js.Program.fromString(program).getImports().length;
6010
- if (numberOfImports > 0 && !imports) {
6011
- try {
6012
- imports = (await this.networkClient.getProgramImports(programName));
6013
- }
6014
- catch (e) {
6015
- logAndThrow(`Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`);
6016
- }
6017
- }
6602
+ const resolved = await this.resolveEditionAndAmendment(programName, edition);
6603
+ edition = edition ?? resolved.edition;
6018
6604
  // Auto-convert bare string inputs to field elements where the function expects field type.
6019
6605
  const preparedInputs = this.prepareInputs(program, functionName, inputs);
6606
+ // Build ProgramImportsBuilder for import resolution (no key loading —
6607
+ // authorizations don't synthesize keys).
6608
+ const { builder } = await this.buildProgramImports(program, imports, false, functionName);
6609
+ const hasImports = !builder.isEmpty();
6020
6610
  // Build and return an `Authorization` for the desired function.
6021
- return await mainnet_js.ProgramManager.authorize(executionPrivateKey, program, functionName, preparedInputs, imports, edition);
6611
+ const authorization = await mainnet_js.ProgramManager.authorize(executionPrivateKey, program, functionName, preparedInputs, hasImports ? undefined : imports, edition, hasImports ? builder?.clone() : undefined);
6612
+ return authorization;
6022
6613
  }
6023
6614
  /**
6024
6615
  * Builds a SnarkVM `Authorization` for a specific function without building a circuit first. This should be used when fast authorization generation is needed and the invoker is confident inputs are coorect.
@@ -6081,29 +6672,17 @@ class ProgramManager {
6081
6672
  if (typeof executionPrivateKey === "undefined") {
6082
6673
  throw "No private key provided and no private key set in the ProgramManager";
6083
6674
  }
6084
- // Resolve the program imports if they exist.
6085
- const numberOfImports = mainnet_js.Program.fromString(program).getImports().length;
6086
- if (numberOfImports > 0 && !imports) {
6087
- try {
6088
- imports = (await this.networkClient.getProgramImports(programName));
6089
- }
6090
- catch (e) {
6091
- logAndThrow(`Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`);
6092
- }
6093
- }
6094
- if (edition == undefined) {
6095
- try {
6096
- edition = await this.networkClient.getLatestProgramEdition(programName);
6097
- }
6098
- catch (e) {
6099
- console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
6100
- edition = 0;
6101
- }
6102
- }
6675
+ const resolved = await this.resolveEditionAndAmendment(programName, edition);
6676
+ edition = edition ?? resolved.edition;
6103
6677
  // Auto-convert bare string inputs to field elements where the function expects field type.
6104
6678
  const preparedInputs = this.prepareInputs(program, functionName, inputs);
6679
+ // Build ProgramImportsBuilder for import resolution (no key loading —
6680
+ // authorizations don't synthesize keys).
6681
+ const { builder } = await this.buildProgramImports(program, imports, false, functionName);
6682
+ const hasImports = !builder.isEmpty();
6105
6683
  // Build and return an `Authorization` for the desired function.
6106
- return await mainnet_js.ProgramManager.buildAuthorizationUnchecked(executionPrivateKey, program, functionName, preparedInputs, imports, edition);
6684
+ const authorization = await mainnet_js.ProgramManager.buildAuthorizationUnchecked(executionPrivateKey, program, functionName, preparedInputs, hasImports ? undefined : imports, edition, hasImports ? builder?.clone() : undefined);
6685
+ return authorization;
6107
6686
  }
6108
6687
  /**
6109
6688
  * Builds a `ProvingRequest` for submission to a prover for execution. If building a proving request with an ExecutionRequest, a private key must be explicitly provided.
@@ -6166,15 +6745,8 @@ class ProgramManager {
6166
6745
  if (programName === undefined) {
6167
6746
  programName = mainnet_js.Program.fromString(program).id();
6168
6747
  }
6169
- if (edition == undefined) {
6170
- try {
6171
- edition = await this.networkClient.getLatestProgramEdition(programName);
6172
- }
6173
- catch (e) {
6174
- console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
6175
- edition = 0;
6176
- }
6177
- }
6748
+ const resolved = await this.resolveEditionAndAmendment(programName, edition);
6749
+ edition = edition ?? resolved.edition;
6178
6750
  // Get the private key from the account if it is not provided in the parameters.
6179
6751
  let executionPrivateKey = privateKey;
6180
6752
  if (typeof privateKey === "undefined" &&
@@ -6212,8 +6784,30 @@ class ProgramManager {
6212
6784
  catch (e) {
6213
6785
  logAndThrow(`Error finding fee record. Record finder response: '${e.message}'. Please ensure you're connected to a valid Aleo network and a record with enough balance exists.`);
6214
6786
  }
6787
+ // Build ProgramImportsBuilder for import resolution (no key loading —
6788
+ // proving requests don't synthesize keys).
6789
+ // Note: imports is kept for the Rust-side fee estimation fallback
6790
+ // (estimate_fee_for_authorization uses the legacy imports Object to avoid
6791
+ // a RefCell double-borrow — see proving_request.rs).
6792
+ const { builder } = await this.buildProgramImports(program, imports, false, functionName);
6793
+ const hasImports = !builder.isEmpty();
6794
+ // Normalize imports to the full merged set from the builder so the
6795
+ // Rust fee estimator sees all transitive imports, not just the
6796
+ // caller's partial set. Use programNames + getProgram to avoid
6797
+ // serializing key bytes (toObject would serialize all proving/
6798
+ // verifying keys, which can be tens of MB).
6799
+ if (hasImports) {
6800
+ const merged = {};
6801
+ for (const name of Array.from(builder.programNames())) {
6802
+ const src = builder.getProgram(name);
6803
+ if (src)
6804
+ merged[name] = src;
6805
+ }
6806
+ imports = merged;
6807
+ }
6215
6808
  if (options.executionRequest instanceof mainnet_js.ExecutionRequest) {
6216
- return await mainnet_js.ProgramManager.buildProvingRequestFromExecutionRequest(options.executionRequest, program, unchecked, broadcast, edition, imports, executionPrivateKey);
6809
+ return await mainnet_js.ProgramManager.buildProvingRequestFromExecutionRequest(options.executionRequest, program, unchecked, broadcast, edition, imports, // kept for fee estimation in Rust
6810
+ executionPrivateKey, hasImports ? builder?.clone() : undefined);
6217
6811
  }
6218
6812
  else {
6219
6813
  // Ensure the private key exists.
@@ -6227,7 +6821,8 @@ class ProgramManager {
6227
6821
  // Auto-convert bare string inputs to field elements where the function expects field type.
6228
6822
  const preparedInputs = this.prepareInputs(program, functionName, inputs);
6229
6823
  // Build and return the `ProvingRequest`.
6230
- return await mainnet_js.ProgramManager.buildProvingRequest(executionPrivateKey, program, functionName, preparedInputs, baseFee, priorityFee, feeRecord, imports, broadcast, unchecked, edition, useFeeMaster);
6824
+ return await mainnet_js.ProgramManager.buildProvingRequest(executionPrivateKey, program, functionName, preparedInputs, baseFee, priorityFee, feeRecord, imports, // kept for fee estimation in Rust
6825
+ broadcast, unchecked, edition, useFeeMaster, hasImports ? builder?.clone() : undefined);
6231
6826
  }
6232
6827
  }
6233
6828
  /**
@@ -6400,6 +6995,10 @@ class ProgramManager {
6400
6995
  * assert(result === ["10u32"]);
6401
6996
  */
6402
6997
  async run(program, function_name, inputs, proveExecution, imports, keySearchParams, provingKey, verifyingKey, privateKey, offlineQuery, edition) {
6998
+ const programName = mainnet_js.Program.fromString(program).id();
6999
+ const resolved = await this.resolveEditionAndAmendment(programName, edition);
7000
+ edition = edition ?? resolved.edition;
7001
+ const { amendment } = resolved;
6403
7002
  // Get the private key from the account if it is not provided in the parameters
6404
7003
  let executionPrivateKey = privateKey;
6405
7004
  if (typeof privateKey === "undefined" &&
@@ -6409,13 +7008,16 @@ class ProgramManager {
6409
7008
  if (typeof executionPrivateKey === "undefined") {
6410
7009
  throw "No private key provided and no private key set in the ProgramManager";
6411
7010
  }
6412
- // If the function proving and verifying keys are not provided, attempt to find them using the key provider
7011
+ // Build the ProgramImportsBuilder for import resolution and key caching.
7012
+ const { builder, importEditions } = await this.buildProgramImports(program, imports, true, function_name);
7013
+ importEditions.set(programName, { edition: edition, amendment });
6413
7014
  if (!provingKey || !verifyingKey) {
6414
- try {
6415
- [provingKey, verifyingKey] = (await this.keyProvider.functionKeys(keySearchParams));
7015
+ const keys = await this.resolveTopLevelKeys(programName, function_name, keySearchParams, edition, amendment);
7016
+ if (keys) {
7017
+ [provingKey, verifyingKey] = keys;
6416
7018
  }
6417
- catch (e) {
6418
- console.log(`Function keys not found. Key finder response: '${e}'. The function keys will be synthesized`);
7019
+ else {
7020
+ logger.log("Function keys not found in KeyStore or KeyProvider. The function keys will be synthesized");
6419
7021
  }
6420
7022
  }
6421
7023
  // Auto-convert bare string inputs to field elements where the function expects field type.
@@ -6426,11 +7028,17 @@ class ProgramManager {
6426
7028
  ? mainnet_js.QueryOption.callbackQuery(this.buildCallbackQuery())
6427
7029
  : undefined;
6428
7030
  await this.ensureInclusionKeys(!!offlineQuery);
6429
- // Run the program offline and return the result
6430
- console.log("Running program offline");
6431
- console.log("Proving key: ", provingKey);
6432
- console.log("Verifying key: ", verifyingKey);
6433
- return mainnet_js.ProgramManager.executeFunctionOffline(executionPrivateKey, program, function_name, preparedInputs, proveExecution, false, imports, provingKey, verifyingKey, this.host, query, edition);
7031
+ const result = await mainnet_js.ProgramManager.executeFunctionOffline(executionPrivateKey, program, function_name, preparedInputs, proveExecution, false, undefined, // imports (legacy Object path — builder handles resolution)
7032
+ provingKey, verifyingKey, this.host, query, edition, builder?.clone());
7033
+ // The clone passed to WASM shares state with the original via
7034
+ // Rc<RefCell<>> synthesized keys are already visible.
7035
+ try {
7036
+ await this.persistExtractedKeys(builder, importEditions);
7037
+ }
7038
+ catch (e) {
7039
+ logger.debug(`Failed to persist extracted keys: ${e}`);
7040
+ }
7041
+ return result;
6434
7042
  }
6435
7043
  /**
6436
7044
  * Join two credits records into a single credits record
@@ -7419,7 +8027,7 @@ class ProgramManager {
7419
8027
  return mainnet_js.verifyFunctionExecution(execution, verifyingKey, program, function_id, imports, importedVerifyingKeys, blockHeight);
7420
8028
  }
7421
8029
  catch (e) {
7422
- console.warn(`The execution was not found in the response, cannot verify the execution: ${e}`);
8030
+ logger.warn(`The execution was not found in the response, cannot verify the execution: ${e}`);
7423
8031
  return false;
7424
8032
  }
7425
8033
  }
@@ -7512,12 +8120,8 @@ class ProgramManager {
7512
8120
  throw new Error("Authorization must be provided if estimating fee for Authorization.");
7513
8121
  }
7514
8122
  const programSource = program ? program.toString() : await this.networkClient.getProgram(programName, edition);
7515
- const programImports = imports ? imports : await this.networkClient.getProgramImports(programSource);
7516
- console.log(JSON.stringify(programImports));
7517
- if (Object.keys(programImports)) {
7518
- return mainnet_js.ProgramManager.estimateFeeForAuthorization(authorization, programSource, programImports, edition);
7519
- }
7520
- return mainnet_js.ProgramManager.estimateFeeForAuthorization(authorization, programSource, imports, edition);
8123
+ const programImports = imports ?? await this.networkClient.getProgramImports(programSource);
8124
+ return mainnet_js.ProgramManager.estimateFeeForAuthorization(authorization, programSource, programImports, edition);
7521
8125
  }
7522
8126
  /**
7523
8127
  * Estimate the execution fee for an Aleo function.
@@ -7552,10 +8156,7 @@ class ProgramManager {
7552
8156
  }
7553
8157
  const programSource = program ? program.toString() : await this.networkClient.getProgram(programName, edition);
7554
8158
  const programImports = imports ? imports : await this.networkClient.getProgramImports(programSource);
7555
- if (Object.keys(programImports)) {
7556
- return mainnet_js.ProgramManager.estimateExecutionFee(programSource, functionName, programImports, edition);
7557
- }
7558
- return mainnet_js.ProgramManager.estimateExecutionFee(programSource, functionName, imports, edition);
8159
+ return mainnet_js.ProgramManager.estimateExecutionFee(programSource, functionName, programImports, edition);
7559
8160
  }
7560
8161
  // Internal utility function for getting a credits.aleo record
7561
8162
  async getCreditsRecord(amount, nonces, record, params) {
@@ -7663,15 +8264,8 @@ class ProgramManager {
7663
8264
  if (programName === undefined) {
7664
8265
  programName = programObject.id();
7665
8266
  }
7666
- if (edition == undefined) {
7667
- try {
7668
- edition = await this.networkClient.getLatestProgramEdition(programName);
7669
- }
7670
- catch (e) {
7671
- console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
7672
- edition = 0;
7673
- }
7674
- }
8267
+ const resolved = await this.resolveEditionAndAmendment(programName, edition);
8268
+ edition = edition ?? resolved.edition;
7675
8269
  // Get the private key from the account if it is not provided in the parameters.
7676
8270
  let executionPrivateKey = privateKey;
7677
8271
  if (typeof privateKey === "undefined" &&
@@ -7687,7 +8281,7 @@ class ProgramManager {
7687
8281
  let fee = priorityFee;
7688
8282
  // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
7689
8283
  if (!feeRecord) {
7690
- console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
8284
+ logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
7691
8285
  const programString = programObject.toString();
7692
8286
  const imports = await this.networkClient.getProgramImports(programString);
7693
8287
  const baseFee = Number(mainnet_js.ProgramManager.estimateDeploymentFee(programString, imports));
@@ -7776,7 +8370,7 @@ class ProgramManager {
7776
8370
  }
7777
8371
  catch (e) {
7778
8372
  // Program does not exist on the network, deployment can proceed
7779
- console.log(`Program ${programObject.id()} does not exist on the network, deploying...`);
8373
+ logger.log(`Program ${programObject.id()} does not exist on the network, deploying...`);
7780
8374
  }
7781
8375
  if (typeof programSource === "string") {
7782
8376
  throw Error(`Program ${programObject.id()} already exists on the network, please rename your program`);
@@ -7800,7 +8394,7 @@ class ProgramManager {
7800
8394
  let fee = priorityFee;
7801
8395
  // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
7802
8396
  if (!feeRecord) {
7803
- console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
8397
+ logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
7804
8398
  const programString = programObject.toString();
7805
8399
  const imports = await this.networkClient.getProgramImports(programString);
7806
8400
  const baseFee = Number(mainnet_js.ProgramManager.estimateDeploymentFee(programString, imports));
@@ -7901,7 +8495,7 @@ class ProgramManager {
7901
8495
  let fee = priorityFee;
7902
8496
  // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
7903
8497
  if (!feeRecord) {
7904
- console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
8498
+ logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
7905
8499
  const programString = programObject.toString();
7906
8500
  const imports = await this.networkClient.getProgramImports(programString);
7907
8501
  const baseFee = Number(mainnet_js.ProgramManager.estimateDeploymentFee(programString, imports));
@@ -8275,7 +8869,7 @@ function serializeRequestSignInputToBytes(input) {
8275
8869
 
8276
8870
  // @TODO: This function is no longer needed, remove it.
8277
8871
  async function initializeWasm() {
8278
- console.warn("initializeWasm is deprecated, you no longer need to use it");
8872
+ logger.warn("initializeWasm is deprecated, you no longer need to use it");
8279
8873
  }
8280
8874
 
8281
8875
  Object.defineProperty(exports, "Address", {
@@ -8406,6 +9000,10 @@ Object.defineProperty(exports, "Program", {
8406
9000
  enumerable: true,
8407
9001
  get: function () { return mainnet_js.Program; }
8408
9002
  });
9003
+ Object.defineProperty(exports, "ProgramImportsBuilder", {
9004
+ enumerable: true,
9005
+ get: function () { return mainnet_js.ProgramImports; }
9006
+ });
8409
9007
  Object.defineProperty(exports, "ProgramManagerBase", {
8410
9008
  enumerable: true,
8411
9009
  get: function () { return mainnet_js.ProgramManager; }
@@ -8422,6 +9020,10 @@ Object.defineProperty(exports, "ProvingRequest", {
8422
9020
  enumerable: true,
8423
9021
  get: function () { return mainnet_js.ProvingRequest; }
8424
9022
  });
9023
+ Object.defineProperty(exports, "QueryOption", {
9024
+ enumerable: true,
9025
+ get: function () { return mainnet_js.QueryOption; }
9026
+ });
8425
9027
  Object.defineProperty(exports, "RecordCiphertext", {
8426
9028
  enumerable: true,
8427
9029
  get: function () { return mainnet_js.RecordCiphertext; }
@@ -8486,6 +9088,10 @@ Object.defineProperty(exports, "initThreadPool", {
8486
9088
  enumerable: true,
8487
9089
  get: function () { return mainnet_js.initThreadPool; }
8488
9090
  });
9091
+ Object.defineProperty(exports, "setWasmLogLevel", {
9092
+ enumerable: true,
9093
+ get: function () { return mainnet_js.setWasmLogLevel; }
9094
+ });
8489
9095
  Object.defineProperty(exports, "snarkVerify", {
8490
9096
  enumerable: true,
8491
9097
  get: function () { return mainnet_js.snarkVerify; }
@@ -8510,6 +9116,7 @@ exports.BlockHeightSearch = BlockHeightSearch;
8510
9116
  exports.CREDITS_PROGRAM_KEYS = CREDITS_PROGRAM_KEYS;
8511
9117
  exports.ChecksumMismatchError = KeyVerificationError;
8512
9118
  exports.DecryptionNotEnabledError = DecryptionNotEnabledError;
9119
+ exports.IndexedDBKeyStore = IndexedDBKeyStore;
8513
9120
  exports.InvalidLocatorError = InvalidLocatorError;
8514
9121
  exports.KEY_STORE = KEY_STORE;
8515
9122
  exports.KeyVerificationError = KeyVerificationError;
@@ -8538,6 +9145,7 @@ exports.encryptAuthorization = encryptAuthorization;
8538
9145
  exports.encryptProvingRequest = encryptProvingRequest;
8539
9146
  exports.encryptRegistrationRequest = encryptRegistrationRequest;
8540
9147
  exports.encryptViewKey = encryptViewKey;
9148
+ exports.getLogLevel = getLogLevel;
8541
9149
  exports.initializeWasm = initializeWasm;
8542
9150
  exports.inputsToFields = inputsToFields;
8543
9151
  exports.isInputIdStrategy = isInputIdStrategy;
@@ -8548,6 +9156,7 @@ exports.isViewKeyStrategy = isViewKeyStrategy;
8548
9156
  exports.logAndThrow = logAndThrow;
8549
9157
  exports.programChecksum = programChecksum;
8550
9158
  exports.provingKeyLocator = provingKeyLocator;
9159
+ exports.setLogLevel = setLogLevel;
8551
9160
  exports.sha256Hex = sha256Hex;
8552
9161
  exports.toAddress = toAddress;
8553
9162
  exports.toField = toField;