@provablehq/sdk 0.9.14 → 0.9.15-enc

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 (31) hide show
  1. package/dist/mainnet/account.d.ts +15 -0
  2. package/dist/mainnet/browser.d.ts +5 -2
  3. package/dist/mainnet/browser.js +429 -30
  4. package/dist/mainnet/browser.js.map +1 -1
  5. package/dist/mainnet/models/cryptoBoxPubkey.d.ts +4 -0
  6. package/dist/mainnet/models/encryptedProvingRequest.d.ts +4 -0
  7. package/dist/mainnet/models/provingResponse.d.ts +48 -2
  8. package/dist/mainnet/models/record-scanner/encryptedRegistrationRequest.d.ts +8 -0
  9. package/dist/mainnet/models/record-scanner/registrationResult.d.ts +26 -0
  10. package/dist/mainnet/network-client.d.ts +48 -2
  11. package/dist/mainnet/node-polyfill.js +2 -49
  12. package/dist/mainnet/node-polyfill.js.map +1 -1
  13. package/dist/mainnet/node.js +2 -2
  14. package/dist/mainnet/record-scanner.d.ts +53 -5
  15. package/dist/mainnet/security.d.ts +38 -0
  16. package/dist/testnet/account.d.ts +15 -0
  17. package/dist/testnet/browser.d.ts +5 -2
  18. package/dist/testnet/browser.js +429 -30
  19. package/dist/testnet/browser.js.map +1 -1
  20. package/dist/testnet/models/cryptoBoxPubkey.d.ts +4 -0
  21. package/dist/testnet/models/encryptedProvingRequest.d.ts +4 -0
  22. package/dist/testnet/models/provingResponse.d.ts +48 -2
  23. package/dist/testnet/models/record-scanner/encryptedRegistrationRequest.d.ts +8 -0
  24. package/dist/testnet/models/record-scanner/registrationResult.d.ts +26 -0
  25. package/dist/testnet/network-client.d.ts +48 -2
  26. package/dist/testnet/node-polyfill.js +2 -49
  27. package/dist/testnet/node-polyfill.js.map +1 -1
  28. package/dist/testnet/node.js +2 -2
  29. package/dist/testnet/record-scanner.d.ts +53 -5
  30. package/dist/testnet/security.d.ts +38 -0
  31. package/package.json +4 -4
@@ -52,6 +52,21 @@ export declare class Account {
52
52
  * const account = Account.fromCiphertext(process.env.ciphertext, process.env.password);
53
53
  */
54
54
  static fromCiphertext(ciphertext: PrivateKeyCiphertext | string, password: string): Account;
55
+ /**
56
+ * Validates whether the given input is a valid Aleo address.
57
+ * @param {string | Uint8Array} address The address to validate, either as a string or bytes
58
+ * @returns {boolean} True if the address is valid, false otherwise
59
+ *
60
+ * @example
61
+ * import { Account } from "@provablehq/sdk/testnet.js";
62
+ *
63
+ * const isValid = Account.isValidAddress("aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px");
64
+ * console.log(isValid); // true
65
+ *
66
+ * const isInvalid = Account.isValidAddress("invalid_address");
67
+ * console.log(isInvalid); // false
68
+ */
69
+ static isValidAddress(address: string | Uint8Array): boolean;
55
70
  /**
56
71
  * Creates a PrivateKey from the provided parameters.
57
72
  * @param {AccountParam} params The parameters containing either a private key string or a seed
@@ -3,8 +3,10 @@ import { Account } from "./account.js";
3
3
  import { AleoNetworkClient, ProgramImports } from "./network-client.js";
4
4
  import { BlockJSON, Header, Metadata } from "./models/blockJSON.js";
5
5
  import { ConfirmedTransactionJSON } from "./models/confirmed_transaction.js";
6
+ import { CryptoBoxPubKey } from "./models/cryptoBoxPubkey.js";
6
7
  import { DeploymentJSON, VerifyingKeys } from "./models/deployment/deploymentJSON.js";
7
8
  import { DeploymentObject } from "./models/deployment/deploymentObject.js";
9
+ import { EncryptedProvingRequest } from "./models/encryptedProvingRequest.js";
8
10
  import { EncryptedRecord } from "./models/record-provider/encryptedRecord.js";
9
11
  import { ExecutionJSON, FeeExecutionJSON } from "./models/execution/executionJSON.js";
10
12
  import { ExecutionObject, FeeExecutionObject } from "./models/execution/executionObject.js";
@@ -24,7 +26,7 @@ import { PlaintextLiteral } from "./models/plaintext/literal.js";
24
26
  import { PlaintextObject } from "./models/plaintext/plaintext.js";
25
27
  import { PlaintextStruct } from "./models/plaintext/struct.js";
26
28
  import { ProvingRequestJSON } from "./models/provingRequest.js";
27
- import { ProvingResponse } from "./models/provingResponse.js";
29
+ import { ProvingResponse, BroadcastResponse, BroadcastResult, ProvingResult, ProvingFailure, ProvingSuccess, ProveApiErrorBody, ProvingRequestError, isProvingResponse, isProveApiErrorBody } from "./models/provingResponse.js";
28
30
  import { RatificationJSON } from "./models/ratification.js";
29
31
  import { RecordsFilter } from "./models/record-scanner/recordsFilter.js";
30
32
  import { RecordsResponseFilter } from "./models/record-scanner/recordsResponseFilter.js";
@@ -45,4 +47,5 @@ export { logAndThrow } from "./utils.js";
45
47
  export { Address, Authorization, Boolean, BHP256, BHP512, BHP768, BHP1024, Ciphertext, ComputeKey, Execution as FunctionExecution, ExecutionRequest, ExecutionResponse, EncryptionToolkit, Field, GraphKey, Group, I8, I16, I32, I64, I128, OfflineQuery, Pedersen64, Pedersen128, Plaintext, Poseidon2, Poseidon4, Poseidon8, PrivateKey, PrivateKeyCiphertext, Program, ProgramManager as ProgramManagerBase, ProvingKey, ProvingRequest, RecordCiphertext, RecordPlaintext, Signature, Scalar, Transaction, Transition, U8, U16, U32, U64, U128, VerifyingKey, ViewKey, initThreadPool, getOrInitConsensusVersionTestHeights, verifyFunctionExecution, } from "./wasm.js";
46
48
  export { initializeWasm };
47
49
  export { Key, CREDITS_PROGRAM_KEYS, KEY_STORE, PRIVATE_TRANSFER, PRIVATE_TO_PUBLIC_TRANSFER, PRIVATE_TRANSFER_TYPES, PUBLIC_TRANSFER, PUBLIC_TRANSFER_AS_SIGNER, PUBLIC_TO_PRIVATE_TRANSFER, RECORD_DOMAIN, VALID_TRANSFER_TYPES, } from "./constants.js";
48
- export { Account, AleoKeyProvider, AleoKeyProviderParams, AleoKeyProviderInitParams, AleoNetworkClient, BlockJSON, BlockHeightSearch, CachedKeyPair, ConfirmedTransactionJSON, DeploymentJSON, DeploymentObject, EncryptedRecord, ExecutionJSON, ExecutionObject, FeeExecutionJSON, FeeExecutionObject, FinalizeJSON, FunctionInput, FunctionObject, FunctionKeyPair, FunctionKeyProvider, Header, ImportedPrograms, ImportedVerifyingKeys, InputJSON, InputObject, KeySearchParams, Metadata, NetworkRecordProvider, OfflineKeyProvider, OfflineSearchParams, OutputJSON, OutputObject, OwnedFilter, OwnedRecord, OwnerJSON, PartialSolutionJSON, PlaintextArray, PlaintextLiteral, PlaintextObject, PlaintextStruct, ProgramImports, ProvingRequestJSON, ProvingResponse, RatificationJSON, RecordsFilter, RecordsResponseFilter, RecordProvider, RecordScanner, RecordSearchParams, SealanceMerkleTree, SolutionJSON, SolutionsJSON, TransactionJSON, TransactionObject, TransitionJSON, TransitionObject, VerifyingKeys, };
50
+ export { Account, AleoKeyProvider, AleoKeyProviderParams, AleoKeyProviderInitParams, AleoNetworkClient, BlockJSON, BlockHeightSearch, BroadcastResponse, BroadcastResult, CachedKeyPair, ConfirmedTransactionJSON, CryptoBoxPubKey, DeploymentJSON, DeploymentObject, EncryptedProvingRequest, EncryptedRecord, ExecutionJSON, ExecutionObject, FeeExecutionJSON, FeeExecutionObject, FinalizeJSON, FunctionInput, FunctionObject, FunctionKeyPair, FunctionKeyProvider, Header, isProvingResponse, isProveApiErrorBody, ImportedPrograms, ImportedVerifyingKeys, InputJSON, InputObject, KeySearchParams, Metadata, NetworkRecordProvider, OfflineKeyProvider, OfflineSearchParams, OutputJSON, OutputObject, OwnedFilter, OwnedRecord, OwnerJSON, PartialSolutionJSON, PlaintextArray, PlaintextLiteral, PlaintextObject, PlaintextStruct, ProgramImports, ProveApiErrorBody, ProvingFailure, ProvingRequestError, ProvingRequestJSON, ProvingResult, ProvingSuccess, ProvingResponse, RatificationJSON, RecordsFilter, RecordsResponseFilter, RecordProvider, RecordScanner, RecordSearchParams, SealanceMerkleTree, SolutionJSON, SolutionsJSON, TransactionJSON, TransactionObject, TransitionJSON, TransitionObject, VerifyingKeys, };
51
+ export { encryptAuthorization, encryptProvingRequest, encryptViewKey, encryptRegistrationRequest } from "./security.js";
@@ -1,6 +1,7 @@
1
1
  import 'core-js/proposals/json-parse-with-source.js';
2
2
  import { ViewKey, ComputeKey, Address, PrivateKeyCiphertext, PrivateKey, RecordCiphertext, EncryptionToolkit, Group, Metadata, VerifyingKey, Program, Plaintext, Transaction, ProvingRequest, ProvingKey, RecordPlaintext, Field, Poseidon4, ProgramManager as ProgramManager$1, verifyFunctionExecution } from '@provablehq/wasm/mainnet.js';
3
3
  export { Address, Authorization, BHP1024, BHP256, BHP512, BHP768, Boolean, Ciphertext, ComputeKey, EncryptionToolkit, ExecutionRequest, ExecutionResponse, Field, Execution as FunctionExecution, GraphKey, Group, I128, I16, I32, I64, I8, OfflineQuery, Pedersen128, Pedersen64, Plaintext, Poseidon2, Poseidon4, Poseidon8, PrivateKey, PrivateKeyCiphertext, Program, ProgramManager as ProgramManagerBase, ProvingKey, ProvingRequest, RecordCiphertext, RecordPlaintext, Scalar, Signature, Transaction, Transition, U128, U16, U32, U64, U8, VerifyingKey, ViewKey, getOrInitConsensusVersionTestHeights, initThreadPool, verifyFunctionExecution } from '@provablehq/wasm/mainnet.js';
4
+ import sodium from 'libsodium-wrappers';
4
5
  import { bech32m } from '@scure/base';
5
6
 
6
7
  /**
@@ -72,6 +73,23 @@ class Account {
72
73
  throw new Error("Wrong password or invalid ciphertext");
73
74
  }
74
75
  }
76
+ /**
77
+ * Validates whether the given input is a valid Aleo address.
78
+ * @param {string | Uint8Array} address The address to validate, either as a string or bytes
79
+ * @returns {boolean} True if the address is valid, false otherwise
80
+ *
81
+ * @example
82
+ * import { Account } from "@provablehq/sdk/testnet.js";
83
+ *
84
+ * const isValid = Account.isValidAddress("aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px");
85
+ * console.log(isValid); // true
86
+ *
87
+ * const isInvalid = Account.isValidAddress("invalid_address");
88
+ * console.log(isInvalid); // false
89
+ */
90
+ static isValidAddress(address) {
91
+ return Address.isValid(address);
92
+ }
75
93
  /**
76
94
  * Creates a PrivateKey from the provided parameters.
77
95
  * @param {AccountParam} params The parameters containing either a private key string or a seed
@@ -466,6 +484,91 @@ async function retryWithBackoff(fn, { maxAttempts = 5, baseDelay = 100, jitter,
466
484
  throw new Error("retryWithBackoff: unreachable");
467
485
  }
468
486
 
487
+ /** Type guard: value is a ProvingResponse. */
488
+ function isProvingResponse(value) {
489
+ return (typeof value === "object" &&
490
+ value !== null &&
491
+ "transaction" in value &&
492
+ "broadcast_result" in value &&
493
+ typeof value.broadcast_result === "object");
494
+ }
495
+ /** Type guard: value is a ProveApiErrorBody. */
496
+ function isProveApiErrorBody(value) {
497
+ return (typeof value === "object" &&
498
+ value !== null &&
499
+ "message" in value);
500
+ }
501
+
502
+ await sodium.ready;
503
+ /**
504
+ * Encrypt an authorization with a libsodium cryptobox public key.
505
+ *
506
+ * @param {string} publicKey The cryptobox X25519 public key to encrypt with (encoded in RFC 4648 standard Base64).
507
+ * @param {Authorization} authorization the authorization to encrypt.
508
+ *
509
+ * @returns {string} the encrypted authorization in RFC 4648 standard Base64.
510
+ */
511
+ function encryptAuthorization(publicKey, authorization) {
512
+ // Ready the cryptobox lib.
513
+ return encryptMessage(publicKey, authorization.toBytesLe());
514
+ }
515
+ /**
516
+ * Encrypt a ProvingRequest with a libsodium cryptobox public key.
517
+ *
518
+ * @param {string} publicKey The cryptobox X25519 public key to encrypt with (encoded in RFC 4648 standard Base64).
519
+ * @param {Authorization} provingRequest the ProvingRequest to encrypt.
520
+ *
521
+ * @returns {string} the encrypted ProvingRequest in RFC 4648 standard Base64.
522
+ */
523
+ function encryptProvingRequest(publicKey, provingRequest) {
524
+ return encryptMessage(publicKey, provingRequest.toBytesLe());
525
+ }
526
+ /**
527
+ * Encrypt a view key with a libsodium cryptobox public key.
528
+ *
529
+ * @param {string} publicKey The cryptobox X25519 public key to encrypt with (encoded in RFC 4648 standard Base64).
530
+ * @param {ViewKey} viewKey the view key to encrypt.
531
+ *
532
+ * @returns {string} the encrypted view key in RFC 4648 standard Base64.
533
+ */
534
+ function encryptViewKey(publicKey, viewKey) {
535
+ return encryptMessage(publicKey, viewKey.toBytesLe());
536
+ }
537
+ /**
538
+ * Encrypt a record scanner registration request.
539
+ *
540
+ * @param {string} publicKey The cryptobox X25519 public key to encrypt with (encoded in RFC 4648 standard Base64).
541
+ * @param {ViewKey} viewKey the view key to encrypt.
542
+ * @param {number} start the start height of the registration request.
543
+ *
544
+ * @returns {string} the encrypted view key in RFC 4648 standard Base64.
545
+ */
546
+ function encryptRegistrationRequest(publicKey, viewKey, start) {
547
+ // Turn the view key into a Uint8Array.
548
+ const vk_bytes = viewKey.toBytesLe();
549
+ // Create a new array to hold the original bytes and the 4-byte start height.
550
+ const bytes = new Uint8Array(vk_bytes.length + 4);
551
+ // Copy existing bytes.
552
+ bytes.set(vk_bytes, 0);
553
+ // Write the 4-byte number in LE format at the end of the array.
554
+ const view = new DataView(bytes.buffer);
555
+ view.setUint32(vk_bytes.length, start, true);
556
+ // Encrypt the encoded bytes.
557
+ return encryptMessage(publicKey, bytes);
558
+ }
559
+ /**
560
+ * Encrypt arbitrary bytes with a libsodium cryptobox public key.
561
+ *
562
+ * @param {string} publicKey The cryptobox X25519 public key to encrypt with (encoded in RFC 4648 standard Base64).
563
+ * @param {Uint8Array} message the bytes to encrypt.
564
+ *
565
+ * @returns {string} the encrypted bytes in RFC 4648 standard Base64.
566
+ */
567
+ function encryptMessage(publicKey, message) {
568
+ const publicKeyBytes = sodium.from_base64(publicKey, sodium.base64_variants.ORIGINAL);
569
+ return sodium.to_base64(sodium.crypto_box_seal(message, publicKeyBytes), sodium.base64_variants.ORIGINAL);
570
+ }
571
+
469
572
  const KEY_STORE = Metadata.baseUrl();
470
573
  function convert(metadata) {
471
574
  // This looks up the method name in VerifyingKey
@@ -591,18 +694,37 @@ class AleoNetworkClient {
591
694
  apiKey;
592
695
  consumerId;
593
696
  jwtData;
697
+ proverUri;
698
+ recordScannerUri;
594
699
  constructor(host, options) {
595
700
  this.host = host + "/mainnet";
596
701
  this.network = "mainnet";
597
702
  this.ctx = {};
598
703
  this.verboseErrors = true;
599
- if (options && options.headers) {
600
- this.headers = options.headers;
704
+ if (options) {
705
+ if (options.headers) {
706
+ this.headers = options.headers;
707
+ }
708
+ else {
709
+ this.headers = {
710
+ // This is replaced by the actual version by a Rollup plugin
711
+ "X-Aleo-SDK-Version": "0.9.15-enc",
712
+ "X-Aleo-environment": environment(),
713
+ };
714
+ }
715
+ // If a prover uri was specified, set the prover uri.
716
+ if (options.proverUri) {
717
+ this.proverUri = options.proverUri + "/mainnet";
718
+ }
719
+ // If a record scanner uri was specified, set the record scanner uri.
720
+ if (options.recordScannerUri) {
721
+ this.recordScannerUri = options.recordScannerUri + "/mainnet";
722
+ }
601
723
  }
602
724
  else {
603
725
  this.headers = {
604
726
  // This is replaced by the actual version by a Rollup plugin
605
- "X-Aleo-SDK-Version": "0.9.14",
727
+ "X-Aleo-SDK-Version": "0.9.15-enc",
606
728
  "X-Aleo-environment": environment(),
607
729
  };
608
730
  }
@@ -647,6 +769,40 @@ class AleoNetworkClient {
647
769
  setHost(host) {
648
770
  this.host = host + "/mainnet";
649
771
  }
772
+ /**
773
+ * Set a new uri for a remote prover.
774
+ *
775
+ * @param {string} proverUri The uri of the remote prover.
776
+ *
777
+ * @example
778
+ * import { AleoNetworkClient } from "@provablehq/sdk/mainnet.js";
779
+ *
780
+ * // Create a networkClient that connects to the provable explorer api.
781
+ * const networkClient = new AleoNetworkClient("http://api.explorer.provable.com/v1", undefined);
782
+ *
783
+ * // Set the prover uri.
784
+ * networkClient.setProverUri("https://prover.provable.prove");
785
+ */
786
+ setProverUri(proverUri) {
787
+ this.proverUri = proverUri + "/mainnet";
788
+ }
789
+ /**
790
+ * Set a new uri for a remote record scanner.
791
+ *
792
+ * @param {string} recordScannerUri The uri of the remote record scanner.
793
+ *
794
+ * @example
795
+ * import { AleoNetworkClient } from "@provablehq/sdk/mainnet.js";
796
+ *
797
+ * // Create a networkClient that connects to the provable explorer api.
798
+ * const networkClient = new AleoNetworkClient("http://api.explorer.provable.com/v1", undefined);
799
+ *
800
+ * // Set the record scanner uri.
801
+ * networkClient.setRecordScannerUri("https://scanner.provable.scan");
802
+ */
803
+ setRecordScannerUri(recordScannerUri) {
804
+ this.recordScannerUri = recordScannerUri + "/mainnet";
805
+ }
650
806
  /**
651
807
  * Set verbose errors to true or false for the `AleoNetworkClient`. When set to true, if `submitTransaction` fails, the failure responses will report descriptive information as to why the transaction failed.
652
808
  *
@@ -1972,33 +2128,87 @@ class AleoNetworkClient {
1972
2128
  };
1973
2129
  }
1974
2130
  /**
1975
- * Submit a `ProvingRequest` to a remote proving service for delegated proving. If the broadcast flag of the `ProvingRequest` is set to `true` the remote service will attempt to broadcast the result `Transaction` on behalf of the requestor.
2131
+ * Parses a /prove or /prove/encrypted response. Returns a result object (never throws for 200/400/500/503).
2132
+ */
2133
+ async handleProvingResponse(response) {
2134
+ // Get the proving response text.
2135
+ const text = await response.text();
2136
+ let body;
2137
+ // Parse the body.
2138
+ try {
2139
+ body = parseJSON(text);
2140
+ }
2141
+ catch {
2142
+ body = {};
2143
+ }
2144
+ // If the status is 200, attempt to parse the Proving Request along its expected structure.
2145
+ if (response.status === 200) {
2146
+ if (isProvingResponse(body)) {
2147
+ return { ok: true, data: body };
2148
+ }
2149
+ return {
2150
+ ok: false,
2151
+ status: response.status,
2152
+ error: { message: "Invalid response from proving service" },
2153
+ };
2154
+ }
2155
+ // If the response is non 200, return the information back to the caller so it can be handled.
2156
+ if (response.status === 400 || response.status === 500 || response.status === 503) {
2157
+ const error = isProveApiErrorBody(body)
2158
+ ? body
2159
+ : { message: text || `${response.status} error` };
2160
+ return { ok: false, status: response.status, error };
2161
+ }
2162
+ return {
2163
+ ok: false,
2164
+ status: response.status,
2165
+ error: { message: text || `${response.status} error` },
2166
+ };
2167
+ }
2168
+ /**
2169
+ * Submit a `ProvingRequest` to a remote proving service for delegated proving. If the broadcast flag of the `ProvingRequest` is set to `true` the remote service will attempt to broadcast the result `Transaction` on behalf of the requestor. Throws on HTTP 400, 500, 503 (and retries on 500/503). Callers should {@link submitProvingRequestSafe} to handle proving request failures without throwing.
1976
2170
  *
1977
2171
  * @param {DelegatedProvingParams} options - The optional parameters required to submit a proving request.
1978
2172
  * @returns {Promise<ProvingResponse>} The ProvingResponse containing the transaction result and the result of the broadcast if the `broadcast` flag was set to `true`.
1979
2173
  */
1980
2174
  async submitProvingRequest(options) {
1981
- const proverUri = options.url ?? this.host;
2175
+ const result = await this.submitProvingRequestSafe(options);
2176
+ if (result.ok) {
2177
+ return result.data;
2178
+ }
2179
+ const err = new Error(result.error.message);
2180
+ err.status = result.status;
2181
+ throw err;
2182
+ }
2183
+ /**
2184
+ * Submit a proving request and return a result object instead of throwing. This method is usable when callers want to handle HTTP status (400, 500, 503) yourself. Retries on 500/503 and returns on 200 or 400.
2185
+ *
2186
+ * @param {DelegatedProvingParams} options - The optional parameters required to submit a proving request.
2187
+ * @returns {Promise<ProvingResult>} `{ ok: true, data }` on success (200), or `{ ok: false, status, error }` on 400/500/503. Check `result.ok` and then either `result.data` or `result.status` / `result.error.message`.
2188
+ */
2189
+ async submitProvingRequestSafe(options) {
2190
+ // Attempt to get the Prover URI first from the options, then from any configured globally, or third try the main configured host.
2191
+ const proverUri = (options.url ?? this.proverUri) ?? this.host;
1982
2192
  const provingRequestString = options.provingRequest instanceof ProvingRequest
1983
2193
  ? options.provingRequest.toString()
1984
2194
  : options.provingRequest;
2195
+ // Try to get JWT data to access the Provable API.
1985
2196
  const apiKey = options.apiKey ?? this.apiKey;
1986
2197
  const consumerId = options.consumerId ?? this.consumerId;
1987
2198
  let jwtData = options.jwtData ?? this.jwtData;
1988
- // Check if JWT is expired or missing
1989
- const bufferTime = FIVE_MINUTES; // 5 minutes buffer
1990
- const isExpired = jwtData && Date.now() >= jwtData.expiration - bufferTime;
2199
+ // Check to see if the JWT needs refreshing.
2200
+ const isExpired = jwtData && Date.now() >= jwtData.expiration - FIVE_MINUTES;
1991
2201
  if (!jwtData || isExpired) {
1992
2202
  if (options.apiKey && options.consumerId) {
1993
2203
  jwtData = await this.refreshJwt(apiKey, consumerId);
1994
- // Update both the class and the options with the new JWT
1995
2204
  this.jwtData = jwtData;
1996
2205
  options.jwtData = jwtData;
1997
2206
  }
1998
2207
  else {
1999
- throw new Error('JWT or both apiKey and consumerId are required');
2208
+ console.warn('JWT or both apiKey and consumerId are required when using the Provable API');
2000
2209
  }
2001
2210
  }
2211
+ // Create the necessary headers to hit the provable api.
2002
2212
  const headers = {
2003
2213
  ...this.headers,
2004
2214
  "X-ALEO-METHOD": "submitProvingRequest",
@@ -2007,17 +2217,67 @@ class AleoNetworkClient {
2007
2217
  if (jwtData?.jwt) {
2008
2218
  headers["Authorization"] = jwtData.jwt;
2009
2219
  }
2010
- try {
2011
- const response = await retryWithBackoff(() => post(`${proverUri}`, {
2220
+ // Encapsulate the requests in a locally scoped function that can be run with a retry closure.
2221
+ const runRequest = async () => {
2222
+ // If DPS privacy is set, call invoke the encrypted flow.
2223
+ if (options.dpsPrivacy) {
2224
+ // Get an ephemeral public key from a DPS service.
2225
+ const pubKeyResponse = await get(proverUri + "/pubkey", {
2226
+ headers,
2227
+ });
2228
+ // Encrypt the provingRequest.
2229
+ const pubkey = parseJSON(await pubKeyResponse.text());
2230
+ const ciphertext = encryptProvingRequest(pubkey.public_key, ProvingRequest.fromString(provingRequestString));
2231
+ // Form the expected query a DPS service expects (including the key_id).
2232
+ const payload = {
2233
+ key_id: pubkey.key_id,
2234
+ ciphertext: ciphertext,
2235
+ };
2236
+ const res = await fetch(`${proverUri}/prove/encrypted`, {
2237
+ method: "POST",
2238
+ body: JSON.stringify(payload),
2239
+ headers,
2240
+ });
2241
+ // Properly handle the proving response.
2242
+ return this.handleProvingResponse(res);
2243
+ }
2244
+ // If encrypted usage is not specified use the unencrypted endpoint.
2245
+ const proveEndpoint = proverUri.endsWith("/prove")
2246
+ ? proverUri
2247
+ : proverUri + "/prove";
2248
+ const res = await fetch(proveEndpoint, {
2249
+ method: "POST",
2012
2250
  body: provingRequestString,
2013
- headers
2014
- }));
2015
- const responseText = await response.text();
2016
- return parseJSON(responseText);
2251
+ headers,
2252
+ });
2253
+ // Properly handle the proving response.
2254
+ return this.handleProvingResponse(res);
2255
+ };
2256
+ try {
2257
+ // Run the request with retries.
2258
+ return await retryWithBackoff(async () => {
2259
+ // Run the encrypted or non-encrypted flow as specified by the flags.
2260
+ const result = await runRequest();
2261
+ if (result.ok) {
2262
+ return result;
2263
+ }
2264
+ // If 500s are hit responses are returned, attempt retries.
2265
+ if (result.status === 500 || result.status === 503) {
2266
+ const err = new Error(result.error.message);
2267
+ err.status = result.status;
2268
+ throw err;
2269
+ }
2270
+ return result;
2271
+ });
2017
2272
  }
2018
- catch (error) {
2019
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
2020
- throw new Error(`Failed to submit proving request: ${errorMessage}`);
2273
+ catch (err) {
2274
+ // If an error is returned, provide usable information to the caller.
2275
+ const e = err;
2276
+ return {
2277
+ ok: false,
2278
+ status: e.status ?? 500,
2279
+ error: { message: e.message },
2280
+ };
2021
2281
  }
2022
2282
  }
2023
2283
  /**
@@ -3333,6 +3593,20 @@ class BlockHeightSearch {
3333
3593
  }
3334
3594
  }
3335
3595
 
3596
+ /**
3597
+ * Error thrown when a record scanner request fails (e.g. /register, /register/encrypted).
3598
+ * Includes HTTP status so callers can handle 422 vs 500 etc.
3599
+ */
3600
+ class RecordScannerRequestError extends Error {
3601
+ status;
3602
+ constructor(message, status) {
3603
+ super(message);
3604
+ this.name = "RecordScannerRequestError";
3605
+ this.status = status;
3606
+ Object.setPrototypeOf(this, RecordScannerRequestError.prototype);
3607
+ }
3608
+ }
3609
+
3336
3610
  /**
3337
3611
  * RecordScanner is a RecordProvider implementation that uses the record scanner service to find records.
3338
3612
  *
@@ -3342,7 +3616,8 @@ class BlockHeightSearch {
3342
3616
  * const recordScanner = new RecordScanner({ url: "https://record-scanner.aleo.org" });
3343
3617
  * recordScanner.setAccount(account);
3344
3618
  * recordScanner.setApiKey("your-api-key");
3345
- * const uuid = await recordScanner.register(0);
3619
+ * const result = await recordScanner.register(viewKey, 0);
3620
+ * if (result.ok) { const uuid = result.data.uuid; }
3346
3621
  *
3347
3622
  * const filter = {
3348
3623
  * uuid,
@@ -3378,9 +3653,21 @@ class RecordScanner {
3378
3653
  url;
3379
3654
  apiKey;
3380
3655
  uuid;
3656
+ consumerId;
3657
+ jwtData;
3381
3658
  constructor(options) {
3382
- this.url = options.url;
3659
+ // Set the network by detecting which version of the SDK is being used.
3660
+ const network = "/mainnet";
3661
+ // If the user has configured a network in their uri, throw
3662
+ if (options.url.endsWith("/mainnet") || options.url.endsWith("/testnet")) {
3663
+ throw new Error("The record scanning url should not include the specific network, this is automatically configured by the Provable SDK.");
3664
+ }
3665
+ // Configure the url to use the network the SDK is using.
3666
+ this.url = options.url + network;
3667
+ // Configure authentication options/
3383
3668
  this.apiKey = typeof options.apiKey === "string" ? { header: "X-Provable-API-Key", value: options.apiKey } : options.apiKey;
3669
+ this.consumerId = options.consumerId;
3670
+ this.jwtData = options.jwtData;
3384
3671
  }
3385
3672
  /**
3386
3673
  * Set the API key to use for the record scanner.
@@ -3390,6 +3677,59 @@ class RecordScanner {
3390
3677
  async setApiKey(apiKey) {
3391
3678
  this.apiKey = typeof apiKey === "string" ? { header: "X-Provable-API-Key", value: apiKey } : apiKey;
3392
3679
  }
3680
+ /**
3681
+ * Set the consumer ID used for JWT refresh when using authenticated record scanner (e.g. Provable API).
3682
+ */
3683
+ async setConsumerId(consumerId) {
3684
+ this.consumerId = consumerId;
3685
+ }
3686
+ /**
3687
+ * Set JWT data for authentication. Optional; when not set, JWT can be refreshed from apiKey + consumerId if provided.
3688
+ */
3689
+ async setJwtData(jwtData) {
3690
+ this.jwtData = jwtData;
3691
+ }
3692
+ /**
3693
+ * Refreshes the JWT by making a POST request to /jwts/{consumer_id}. Used when authentication is required.
3694
+ */
3695
+ async refreshJwt(apiKey, consumerId) {
3696
+ const response = await post(`https://api.provable.com/jwts/${consumerId}`, {
3697
+ headers: {
3698
+ "X-Provable-API-Key": apiKey,
3699
+ },
3700
+ });
3701
+ const authHeader = response.headers.get("authorization");
3702
+ if (!authHeader) {
3703
+ throw new Error("No authorization header in JWT refresh response");
3704
+ }
3705
+ const body = await response.json();
3706
+ return {
3707
+ jwt: authHeader,
3708
+ expiration: body.exp * 1000, // Convert to milliseconds
3709
+ };
3710
+ }
3711
+ /**
3712
+ * Returns auth headers (e.g. Authorization with JWT). Refreshes JWT if expired and apiKey + consumerId are set. Empty when auth is not configured.
3713
+ */
3714
+ async getAuthHeaders() {
3715
+ let jwtData = this.jwtData;
3716
+ const isExpired = jwtData && Date.now() >= jwtData.expiration - FIVE_MINUTES;
3717
+ if (!jwtData || isExpired) {
3718
+ const apiKey = this.apiKey?.value;
3719
+ if (apiKey && this.consumerId) {
3720
+ jwtData = await this.refreshJwt(apiKey, this.consumerId);
3721
+ this.jwtData = jwtData;
3722
+ }
3723
+ else if (jwtData?.jwt) {
3724
+ // Use existing JWT even if expired when we can't refresh
3725
+ return { Authorization: jwtData.jwt };
3726
+ }
3727
+ else {
3728
+ return {};
3729
+ }
3730
+ }
3731
+ return jwtData?.jwt ? { Authorization: jwtData.jwt } : {};
3732
+ }
3393
3733
  /**
3394
3734
  * Set the UUID to use for the record scanner.
3395
3735
  *
@@ -3399,14 +3739,15 @@ class RecordScanner {
3399
3739
  this.uuid = uuidOrViewKey instanceof ViewKey ? this.computeUUID(uuidOrViewKey) : uuidOrViewKey;
3400
3740
  }
3401
3741
  /**
3402
- * Register the account with the record scanner service.
3742
+ * Register the account with the record scanner service (unencrypted POST /register). Does not throw if a valid error response from the record scanner is received; returns a result object instead.
3403
3743
  *
3744
+ * @param {ViewKey} viewKey The view key to register.
3404
3745
  * @param {number} startBlock The block height to start scanning from.
3405
- * @returns {Promise<RegistrationResponse>} The response from the record scanner service.
3746
+ * @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure.
3406
3747
  */
3407
3748
  async register(viewKey, startBlock) {
3408
3749
  try {
3409
- let request = {
3750
+ const request = {
3410
3751
  view_key: viewKey.to_string(),
3411
3752
  start: startBlock,
3412
3753
  };
@@ -3417,11 +3758,63 @@ class RecordScanner {
3417
3758
  }));
3418
3759
  const data = await response.json();
3419
3760
  this.uuid = data.uuid;
3420
- return data;
3761
+ return { ok: true, data };
3421
3762
  }
3422
- catch (error) {
3423
- console.error(`Failed to register view key: ${error}`);
3424
- throw error;
3763
+ catch (err) {
3764
+ if (err instanceof RecordScannerRequestError) {
3765
+ return {
3766
+ ok: false,
3767
+ status: err.status,
3768
+ error: { message: err.message },
3769
+ };
3770
+ }
3771
+ console.error(`Failed to register view key: ${err}`);
3772
+ throw err;
3773
+ }
3774
+ }
3775
+ /**
3776
+ * Fetches an ephemeral public key from the record scanner service for use with registerEncrypted.
3777
+ * Follows the same pattern as the delegated proving service /pubkey endpoint.
3778
+ *
3779
+ * @returns {Promise<CryptoBoxPubKey>} The service's ephemeral public key and key_id.
3780
+ */
3781
+ async getPubkey() {
3782
+ const response = await this.request(new Request(`${this.url}/pubkey`, { method: "GET" }));
3783
+ return parseJSON(await response.text());
3784
+ }
3785
+ /**
3786
+ * Registers the account with the record scanner 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.
3787
+ *
3788
+ * @param {ViewKey} viewKey The view key to register.
3789
+ * @param {number} startBlock The block height to start scanning from.
3790
+ * @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure.
3791
+ */
3792
+ async registerEncrypted(viewKey, startBlock) {
3793
+ try {
3794
+ const pubkey = await this.getPubkey();
3795
+ const ciphertext = encryptRegistrationRequest(pubkey.public_key, viewKey, startBlock);
3796
+ const payload = {
3797
+ key_id: pubkey.key_id,
3798
+ ciphertext,
3799
+ };
3800
+ const response = await this.request(new Request(`${this.url}/register/encrypted`, {
3801
+ method: "POST",
3802
+ headers: { "Content-Type": "application/json" },
3803
+ body: JSON.stringify(payload),
3804
+ }));
3805
+ const data = await response.json();
3806
+ this.uuid = data.uuid;
3807
+ return { ok: true, data };
3808
+ }
3809
+ catch (err) {
3810
+ if (err instanceof RecordScannerRequestError) {
3811
+ return {
3812
+ ok: false,
3813
+ status: err.status,
3814
+ error: { message: err.message },
3815
+ };
3816
+ }
3817
+ throw err;
3425
3818
  }
3426
3819
  }
3427
3820
  /**
@@ -3614,18 +4007,24 @@ class RecordScanner {
3614
4007
  }
3615
4008
  /**
3616
4009
  * Wrapper function to make a request to the record scanner service and handle any errors.
4010
+ * Optionally adds JWT Authorization header when consumerId/jwtData (or apiKey+consumerId) are configured.
3617
4011
  *
3618
4012
  * @param {Request} req The request to make.
3619
4013
  * @returns {Promise<Response>} The response.
3620
4014
  */
3621
4015
  async request(req) {
3622
4016
  try {
4017
+ const authHeaders = await this.getAuthHeaders();
4018
+ for (const [key, value] of Object.entries(authHeaders)) {
4019
+ req.headers.set(key, value);
4020
+ }
3623
4021
  if (this.apiKey) {
3624
4022
  req.headers.set(this.apiKey.header, this.apiKey.value);
3625
4023
  }
3626
4024
  const response = await fetch(req);
3627
4025
  if (!response.ok) {
3628
- throw new Error(await response.text() ?? `Request to ${req.url} failed with status ${response.status}`);
4026
+ const text = await response.text();
4027
+ throw new RecordScannerRequestError(text || `Request to ${req.url} failed with status ${response.status}`, response.status);
3629
4028
  }
3630
4029
  return response;
3631
4030
  }
@@ -6623,5 +7022,5 @@ async function initializeWasm() {
6623
7022
  console.warn("initializeWasm is deprecated, you no longer need to use it");
6624
7023
  }
6625
7024
 
6626
- export { Account, AleoKeyProvider, AleoKeyProviderParams, AleoNetworkClient, BlockHeightSearch, CREDITS_PROGRAM_KEYS, KEY_STORE, NetworkRecordProvider, OfflineKeyProvider, OfflineSearchParams, PRIVATE_TO_PUBLIC_TRANSFER, PRIVATE_TRANSFER, PRIVATE_TRANSFER_TYPES, PUBLIC_TO_PRIVATE_TRANSFER, PUBLIC_TRANSFER, PUBLIC_TRANSFER_AS_SIGNER, ProgramManager, RECORD_DOMAIN, RecordScanner, SealanceMerkleTree, VALID_TRANSFER_TYPES, initializeWasm, logAndThrow };
7025
+ export { Account, AleoKeyProvider, AleoKeyProviderParams, AleoNetworkClient, BlockHeightSearch, CREDITS_PROGRAM_KEYS, KEY_STORE, NetworkRecordProvider, OfflineKeyProvider, OfflineSearchParams, PRIVATE_TO_PUBLIC_TRANSFER, PRIVATE_TRANSFER, PRIVATE_TRANSFER_TYPES, PUBLIC_TO_PRIVATE_TRANSFER, PUBLIC_TRANSFER, PUBLIC_TRANSFER_AS_SIGNER, ProgramManager, RECORD_DOMAIN, RecordScanner, SealanceMerkleTree, VALID_TRANSFER_TYPES, encryptAuthorization, encryptProvingRequest, encryptRegistrationRequest, encryptViewKey, initializeWasm, isProveApiErrorBody, isProvingResponse, logAndThrow };
6627
7026
  //# sourceMappingURL=browser.js.map