@zama-fhe/relayer-sdk 0.3.0-8 → 0.4.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/node.cjs CHANGED
@@ -3,8 +3,6 @@
3
3
  var TFHEPkg = require('node-tfhe');
4
4
  var TKMSPkg = require('node-tkms');
5
5
  var ethers = require('ethers');
6
- var createHash = require('keccak');
7
- var fetchRetry = require('fetch-retry');
8
6
 
9
7
  function _interopNamespaceDefault(e) {
10
8
  var n = Object.create(null);
@@ -29,62 +27,10 @@ var TKMSPkg__namespace = /*#__PURE__*/_interopNamespaceDefault(TKMSPkg);
29
27
  const SERIALIZED_SIZE_LIMIT_CIPHERTEXT = BigInt(1024 * 1024 * 512);
30
28
  const SERIALIZED_SIZE_LIMIT_PK = BigInt(1024 * 1024 * 512);
31
29
  const SERIALIZED_SIZE_LIMIT_CRS = BigInt(1024 * 1024 * 512);
32
- const cleanURL = (url) => {
33
- if (!url)
34
- return '';
35
- return url.endsWith('/') ? url.slice(0, -1) : url;
36
- };
37
- const numberToHex = (num) => {
38
- let hex = num.toString(16);
39
- return hex.length % 2 ? '0' + hex : hex;
40
- };
41
- const fromHexString = (hexString) => {
42
- const arr = hexString.replace(/^(0x)/, '').match(/.{1,2}/g);
43
- if (!arr)
44
- return new Uint8Array();
45
- return Uint8Array.from(arr.map((byte) => parseInt(byte, 16)));
46
- };
47
- function toHexString(bytes, with0x = false) {
48
- return `${with0x ? '0x' : ''}${bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')}`;
49
- }
50
- const bytesToBigInt = function (byteArray) {
51
- if (!byteArray || byteArray?.length === 0) {
52
- return BigInt(0);
53
- }
54
- const hex = Array.from(byteArray)
55
- .map((b) => b.toString(16).padStart(2, '0')) // byte to hex
56
- .join('');
57
- return BigInt(`0x${hex}`);
58
- };
59
- function ensure0x(s) {
60
- return !s.startsWith('0x') ? `0x${s}` : s;
61
- }
62
30
 
63
- function setAuth(init, auth) {
64
- if (auth) {
65
- switch (auth.__type) {
66
- case 'BearerToken':
67
- init.headers['Authorization'] =
68
- `Bearer ${auth.token}`;
69
- break;
70
- case 'ApiKeyHeader':
71
- init.headers[auth.header || 'x-api-key'] =
72
- auth.value;
73
- break;
74
- case 'ApiKeyCookie':
75
- if (typeof window !== 'undefined') {
76
- document.cookie = `${auth.cookie || 'x-api-key'}=${auth.value}; path=/; SameSite=Lax; Secure; HttpOnly;`;
77
- init.credentials = 'include';
78
- }
79
- else {
80
- let cookie = `${auth.cookie || 'x-api-key'}=${auth.value};`;
81
- init.headers['Cookie'] = cookie;
82
- }
83
- break;
84
- }
85
- }
86
- return init;
87
- }
31
+ // This file is auto-generated
32
+ const version = '0.4.0-alpha.1';
33
+ const sdkName = '@zama-fhe/relayer-sdk';
88
34
 
89
35
  function getErrorCause(e) {
90
36
  if (e instanceof Error && typeof e.cause === 'object' && e.cause !== null) {
@@ -139,18 +85,27 @@ async function throwRelayerResponseError(operation, response) {
139
85
  }
140
86
  }
141
87
  }
88
+ let responseJson;
89
+ try {
90
+ responseJson = await response.json();
91
+ }
92
+ catch {
93
+ responseJson = '';
94
+ }
142
95
  const cause = {
143
96
  code: 'RELAYER_FETCH_ERROR',
144
97
  operation,
145
98
  status: response.status,
146
99
  statusText: response.statusText,
147
100
  url: response.url,
101
+ response,
102
+ responseJson,
148
103
  };
149
104
  throw new Error(message, {
150
105
  cause,
151
106
  });
152
107
  }
153
- function throwRelayerJSONError(operation, error) {
108
+ function throwRelayerJSONError(operation, error, response) {
154
109
  let message;
155
110
  switch (operation) {
156
111
  case 'PUBLIC_DECRYPT': {
@@ -170,6 +125,7 @@ function throwRelayerJSONError(operation, error) {
170
125
  code: 'RELAYER_NO_JSON_ERROR',
171
126
  operation,
172
127
  error,
128
+ response,
173
129
  };
174
130
  throw new Error(message, {
175
131
  cause,
@@ -256,7 +212,40 @@ function throwRelayerUnknownError(operation, error, message) {
256
212
  });
257
213
  }
258
214
 
259
- function assertIsRelayerFetchResponseJson(json) {
215
+ /**
216
+ * Set the authentication method for the request. The default is no authentication.
217
+ * It supports:
218
+ * - Bearer Token
219
+ * - Custom header
220
+ * - Custom cookie
221
+ */
222
+ function setAuth(init, auth) {
223
+ if (auth) {
224
+ switch (auth.__type) {
225
+ case 'BearerToken':
226
+ init.headers['Authorization'] =
227
+ `Bearer ${auth.token}`;
228
+ break;
229
+ case 'ApiKeyHeader':
230
+ init.headers[auth.header || 'x-api-key'] =
231
+ auth.value;
232
+ break;
233
+ case 'ApiKeyCookie':
234
+ if (typeof window !== 'undefined') {
235
+ document.cookie = `${auth.cookie || 'x-api-key'}=${auth.value}; path=/; SameSite=Lax; Secure; HttpOnly;`;
236
+ init.credentials = 'include';
237
+ }
238
+ else {
239
+ let cookie = `${auth.cookie || 'x-api-key'}=${auth.value};`;
240
+ init.headers['Cookie'] = cookie;
241
+ }
242
+ break;
243
+ }
244
+ }
245
+ return init;
246
+ }
247
+
248
+ function assertIsRelayerV1FetchResponseJson(json) {
260
249
  if (!json || typeof json !== 'object') {
261
250
  throw new Error('Unexpected response JSON.');
262
251
  }
@@ -266,11 +255,13 @@ function assertIsRelayerFetchResponseJson(json) {
266
255
  throw new Error("Unexpected response JSON format: missing 'response' property.");
267
256
  }
268
257
  }
269
- async function fetchRelayerJsonRpcPost(relayerOperation, url, payload, options) {
258
+ async function fetchRelayerV1Post(relayerOperation, url, payload, options) {
270
259
  const init = setAuth({
271
260
  method: 'POST',
272
261
  headers: {
273
262
  'Content-Type': 'application/json',
263
+ 'ZAMA-SDK-VERSION': `${version}`,
264
+ 'ZAMA-SDK-NAME': `${sdkName}`,
274
265
  },
275
266
  body: JSON.stringify(payload),
276
267
  }, options?.auth);
@@ -290,10 +281,10 @@ async function fetchRelayerJsonRpcPost(relayerOperation, url, payload, options)
290
281
  parsed = await response.json();
291
282
  }
292
283
  catch (e) {
293
- throwRelayerJSONError(relayerOperation, e);
284
+ throwRelayerJSONError(relayerOperation, e, response);
294
285
  }
295
286
  try {
296
- assertIsRelayerFetchResponseJson(parsed);
287
+ assertIsRelayerV1FetchResponseJson(parsed);
297
288
  json = parsed;
298
289
  }
299
290
  catch (e) {
@@ -301,11 +292,18 @@ async function fetchRelayerJsonRpcPost(relayerOperation, url, payload, options)
301
292
  }
302
293
  return json;
303
294
  }
304
- async function fetchRelayerGet(relayerOperation, url) {
295
+ async function fetchRelayerV1Get(relayerOperation, url) {
296
+ const init = {
297
+ method: 'GET',
298
+ headers: {
299
+ 'ZAMA-SDK-VERSION': `${version}`,
300
+ 'ZAMA-SDK-NAME': `${sdkName}`,
301
+ },
302
+ };
305
303
  let response;
306
304
  let json;
307
305
  try {
308
- response = await fetch(url);
306
+ response = await fetch(url, init);
309
307
  }
310
308
  catch (e) {
311
309
  throwRelayerUnknownError(relayerOperation, e);
@@ -318,10 +316,10 @@ async function fetchRelayerGet(relayerOperation, url) {
318
316
  parsed = await response.json();
319
317
  }
320
318
  catch (e) {
321
- throwRelayerJSONError(relayerOperation, e);
319
+ throwRelayerJSONError(relayerOperation, e, response);
322
320
  }
323
321
  try {
324
- assertIsRelayerFetchResponseJson(parsed);
322
+ assertIsRelayerV1FetchResponseJson(parsed);
325
323
  json = parsed;
326
324
  }
327
325
  catch (e) {
@@ -330,49 +328,13 @@ async function fetchRelayerGet(relayerOperation, url) {
330
328
  return json;
331
329
  }
332
330
 
333
- // export type RelayerKeysItem = {
334
- // data_id: string;
335
- // param_choice: number;
336
- // urls: string[];
337
- // signatures: string[];
338
- // };
339
- // export type RelayerKey = {
340
- // data_id: string;
341
- // param_choice: number;
342
- // signatures: string[];
343
- // urls: string[];
344
- // };
345
- // export type RelayerKeys = {
346
- // response: {
347
- // fhe_key_info: {
348
- // fhe_public_key: RelayerKey;
349
- // fhe_server_key: RelayerKey;
350
- // }[];
351
- // verf_public_key: {
352
- // key_id: string;
353
- // server_id: number;
354
- // verf_public_key_address: string;
355
- // verf_public_key_url: string;
356
- // }[];
357
- // crs: {
358
- // [key: string]: RelayerKeysItem;
359
- // };
360
- // };
361
- // status: string;
362
- // };
363
331
  const keyurlCache = {};
364
- const getKeysFromRelayer = async (url, publicKeyId) => {
365
- if (keyurlCache[url]) {
366
- return keyurlCache[url];
332
+ const getKeysFromRelayer = async (versionUrl, publicKeyId) => {
333
+ if (keyurlCache[versionUrl]) {
334
+ return keyurlCache[versionUrl];
367
335
  }
368
- const data = await fetchRelayerGet('KEY_URL', `${url}/v1/keyurl`);
336
+ const data = await fetchRelayerV1Get('KEY_URL', `${versionUrl}/keyurl`);
369
337
  try {
370
- // const response = await fetch(`${url}/v1/keyurl`);
371
- // if (!response.ok) {
372
- // await throwRelayerResponseError("KEY_URL", response);
373
- // }
374
- //const data: RelayerKeys = await response.json();
375
- //if (data) {
376
338
  let pubKeyUrl;
377
339
  // If no publicKeyId is provided, use the first one
378
340
  // Warning: if there are multiple keys available, the first one will most likely never be the
@@ -444,11 +406,8 @@ const getKeysFromRelayer = async (url, publicKeyId) => {
444
406
  },
445
407
  },
446
408
  };
447
- keyurlCache[url] = result;
409
+ keyurlCache[versionUrl] = result;
448
410
  return result;
449
- // } else {
450
- // throw new Error('No public key available');
451
- // }
452
411
  }
453
412
  catch (e) {
454
413
  throw new Error('Impossible to fetch public key: wrong relayer url.', {
@@ -457,6 +416,430 @@ const getKeysFromRelayer = async (url, publicKeyId) => {
457
416
  }
458
417
  };
459
418
 
419
+ class RelayerErrorBase extends Error {
420
+ name = 'RelayerErrorBase';
421
+ _details;
422
+ _docsPath;
423
+ _docsUrl;
424
+ _version;
425
+ static VERSION = version;
426
+ static DEFAULT_DOCS_BASE_URL = 'https//docs.zama.org';
427
+ static FULL_VERSION = `@zama-fhe/relayer-sdk@${RelayerErrorBase.VERSION}`;
428
+ constructor(params) {
429
+ let details;
430
+ let docsPath;
431
+ if (params.cause instanceof RelayerErrorBase) {
432
+ docsPath = params.docsPath || params.cause.docsPath;
433
+ details = params.details || params.cause.details;
434
+ }
435
+ else {
436
+ docsPath = params.docsPath;
437
+ details = params.details || params.cause?.message;
438
+ }
439
+ const docsUrl = docsPath
440
+ ? `${params.docsBaseUrl ?? RelayerErrorBase.DEFAULT_DOCS_BASE_URL}${docsPath}${params.docsSlug ? `#${params.docsSlug}` : ''}`
441
+ : undefined;
442
+ const message = [
443
+ params.message || 'An error occurred.',
444
+ '',
445
+ ...(params.metaMessages ? [...params.metaMessages, ''] : []),
446
+ ...(docsUrl ? [`Docs: ${docsUrl}`] : []),
447
+ ...(details ? [`Details: ${details}`] : []),
448
+ `Version: ${RelayerErrorBase.FULL_VERSION}`,
449
+ ].join('\n');
450
+ super(message, params.cause ? { cause: params.cause } : undefined);
451
+ // This line is critical. If removed 'instanceof' will always fail
452
+ // Restore prototype chain (required when extending Error in TypeScript)
453
+ Object.setPrototypeOf(this, new.target.prototype);
454
+ this._details = details;
455
+ this._docsPath = docsPath;
456
+ this._docsUrl = docsUrl;
457
+ this._version = RelayerErrorBase.VERSION;
458
+ this.name = params.name ?? this.name;
459
+ }
460
+ get docsPath() {
461
+ return this._docsPath;
462
+ }
463
+ get docsUrl() {
464
+ return this._docsUrl;
465
+ }
466
+ get details() {
467
+ return this._details;
468
+ }
469
+ get version() {
470
+ return this._version;
471
+ }
472
+ }
473
+
474
+ class InvalidPropertyError extends RelayerErrorBase {
475
+ _objName;
476
+ _property;
477
+ _expectedType;
478
+ _index;
479
+ _value;
480
+ _type;
481
+ _expectedValue;
482
+ constructor({ objName, property, index, type, value, expectedValue, expectedType, }) {
483
+ let missing = type === 'undefined' && expectedValue !== undefined;
484
+ let varname;
485
+ if (!property || property === '') {
486
+ varname = index !== undefined ? `${objName}[${index}]` : `${objName}`;
487
+ }
488
+ else {
489
+ varname =
490
+ index !== undefined
491
+ ? `${objName}.${property}[${index}]`
492
+ : `${objName}.${property}`;
493
+ }
494
+ let message = missing
495
+ ? `InvalidPropertyError: Missing '${varname}'`
496
+ : `InvalidPropertyError: ${varname}`;
497
+ if (type === expectedType) {
498
+ if (value !== undefined) {
499
+ message += ` unexpected value ${value}`;
500
+ }
501
+ }
502
+ else {
503
+ if (missing) {
504
+ if (Array.isArray(expectedValue)) {
505
+ expectedValue = expectedValue.join('|');
506
+ }
507
+ message += `, expected '${varname}: ${expectedValue}'.`;
508
+ }
509
+ else if (expectedType !== 'unknown' && type !== 'unknown') {
510
+ message += ` not a ${expectedType}`;
511
+ if (type) {
512
+ message += `, type is ${type}`;
513
+ }
514
+ }
515
+ }
516
+ super({
517
+ message,
518
+ name: 'InvalidPropertyError',
519
+ });
520
+ this._objName = objName;
521
+ this._property = property;
522
+ this._value = value;
523
+ this._type = type;
524
+ this._expectedValue = expectedValue;
525
+ this._expectedType = expectedType;
526
+ this._index = index;
527
+ }
528
+ static missingProperty({ objName, property, expectedType, expectedValue, }) {
529
+ return new InvalidPropertyError({
530
+ objName,
531
+ property,
532
+ expectedType,
533
+ expectedValue,
534
+ type: 'undefined',
535
+ });
536
+ }
537
+ static invalidFormat({ objName, property, }) {
538
+ return new InvalidPropertyError({
539
+ objName,
540
+ property,
541
+ expectedType: 'unknown',
542
+ });
543
+ }
544
+ static invalidObject({ objName, expectedType, type, }) {
545
+ return new InvalidPropertyError({
546
+ objName,
547
+ property: '',
548
+ expectedType,
549
+ type,
550
+ });
551
+ }
552
+ }
553
+
554
+ /**
555
+ * Type guard that checks if a property exists on an object and is non-null/non-undefined.
556
+ *
557
+ * @template K - The property key type (string literal)
558
+ * @param o - The value to check (can be any type)
559
+ * @param property - The property name to check for
560
+ * @returns True if `o` is an object with the specified property that is not null or undefined
561
+ *
562
+ * @example
563
+ * ```typescript
564
+ * const data: unknown = { name: "Alice", age: 30 };
565
+ * if (isNonNullableRecordProperty(data, 'name')) {
566
+ * console.log(data.name); // OK
567
+ * }
568
+ * ```
569
+ */
570
+ function isNonNullableRecordProperty(o, property) {
571
+ if (!o ||
572
+ typeof o !== 'object' ||
573
+ !(property in o) ||
574
+ o[property] === undefined ||
575
+ o[property] === null) {
576
+ return false;
577
+ }
578
+ return true;
579
+ }
580
+ /**
581
+ * Assertion function that validates a property exists on an object and is non-null/non-undefined.
582
+ * Throws an `InvalidPropertyError` if validation fails.
583
+ *
584
+ * @template K - The property key type (string literal)
585
+ * @param o - The value to validate (can be any type)
586
+ * @param property - The property name to check for
587
+ * @param objName - The name of the object being validated (used in error messages)
588
+ * @throws {InvalidPropertyError} When the property is missing, null, or undefined
589
+ * @throws {never} No other errors are thrown
590
+ *
591
+ * @example
592
+ * ```typescript
593
+ * function processUser(data: unknown) {
594
+ * assertNonNullableRecordProperty(data, 'userId', 'user');
595
+ * console.log(data.userId);
596
+ * }
597
+ * ```
598
+ */
599
+ function assertNonNullableRecordProperty(o, property, objName) {
600
+ if (!isNonNullableRecordProperty(o, property)) {
601
+ throw new InvalidPropertyError({
602
+ objName,
603
+ property,
604
+ expectedType: 'non-nullable',
605
+ type: typeofProperty(o, property),
606
+ });
607
+ }
608
+ }
609
+ /**
610
+ * Type guard that checks if a property exists on an object and is an array.
611
+ *
612
+ * @template K - The property key type (string literal)
613
+ * @param o - The value to check (can be any type)
614
+ * @param property - The property name to check for
615
+ * @returns True if `o` is an object with the specified property that is a non-null array
616
+ *
617
+ * @example
618
+ * ```typescript
619
+ * const data: unknown = { items: [1, 2, 3], count: 42 };
620
+ * if (isRecordArrayProperty(data, 'items')) {
621
+ * console.log(data.items.length); // OK
622
+ * data.items.forEach(item => console.log(item)); // OK
623
+ * }
624
+ * ```
625
+ */
626
+ function isRecordArrayProperty(o, property) {
627
+ if (!isNonNullableRecordProperty(o, property)) {
628
+ return false;
629
+ }
630
+ return Array.isArray(o[property]);
631
+ }
632
+ /**
633
+ * Assertion function that validates a property exists on an object and is an array.
634
+ * Throws an `InvalidPropertyError` if validation fails.
635
+ *
636
+ * @template K - The property key type (string literal)
637
+ * @param o - The value to validate (can be any type)
638
+ * @param property - The property name to check for
639
+ * @param objName - The name of the object being validated (used in error messages)
640
+ * @throws {InvalidPropertyError} When the property is missing, null, or not an array
641
+ * @throws {never} No other errors are thrown
642
+ *
643
+ * @example
644
+ * ```typescript
645
+ * function processResults(data: unknown) {
646
+ * assertRecordArrayProperty(data, 'results', 'response');
647
+ * console.log(`Found ${data.results.length} results`);
648
+ * data.results.forEach(result => processResult(result));
649
+ * }
650
+ * ```
651
+ */
652
+ function assertRecordArrayProperty(o, property, objName) {
653
+ if (!isRecordArrayProperty(o, property)) {
654
+ throw new InvalidPropertyError({
655
+ objName,
656
+ property,
657
+ expectedType: 'Array',
658
+ type: typeofProperty(o, property),
659
+ });
660
+ }
661
+ }
662
+ function isRecordBooleanProperty(o, property) {
663
+ if (!isNonNullableRecordProperty(o, property)) {
664
+ return false;
665
+ }
666
+ return typeof o[property] === 'boolean';
667
+ }
668
+ function assertRecordBooleanProperty(o, property, objName, expectedValue) {
669
+ if (!isRecordBooleanProperty(o, property))
670
+ throw new InvalidPropertyError({
671
+ objName,
672
+ property,
673
+ expectedType: 'boolean',
674
+ type: typeofProperty(o, property),
675
+ });
676
+ if (expectedValue !== undefined) {
677
+ if (o[property] !== expectedValue) {
678
+ throw new InvalidPropertyError({
679
+ objName,
680
+ property,
681
+ expectedType: 'boolean',
682
+ expectedValue: String(expectedValue),
683
+ type: typeof o[property],
684
+ value: String(o[property]),
685
+ });
686
+ }
687
+ }
688
+ }
689
+ function typeofProperty(o, property) {
690
+ if (isNonNullableRecordProperty(o, property)) {
691
+ return typeof o[property];
692
+ }
693
+ return 'undefined';
694
+ }
695
+
696
+ class InternalError extends RelayerErrorBase {
697
+ constructor(params) {
698
+ super({
699
+ ...params,
700
+ name: 'InternalError',
701
+ message: params.message ?? 'internal error',
702
+ });
703
+ }
704
+ }
705
+ function assertRelayer(condition) {
706
+ if (!condition) {
707
+ throw new InternalError({ message: 'Assertion failed' });
708
+ }
709
+ }
710
+
711
+ function removeSuffix(s, suffix) {
712
+ if (!s) {
713
+ return '';
714
+ }
715
+ if (suffix.length === 0) {
716
+ return s;
717
+ }
718
+ return s.endsWith(suffix) ? s.slice(0, -suffix.length) : s;
719
+ }
720
+ function is0x(s) {
721
+ return typeof s === 'string' && s.startsWith('0x');
722
+ }
723
+ function isNo0x(s) {
724
+ return typeof s === 'string' && !s.startsWith('0x');
725
+ }
726
+ function ensure0x(s) {
727
+ return !s.startsWith('0x') ? `0x${s}` : s;
728
+ }
729
+ function remove0x(s) {
730
+ return s.startsWith('0x') ? s.substring(2) : s;
731
+ }
732
+ /**
733
+ * Type guard that checks if a property exists on an object and is a string.
734
+ *
735
+ * @template K - The property key type (string literal)
736
+ * @param o - The value to check (can be any type)
737
+ * @param property - The property name to check for
738
+ * @returns True if `o` is an object with the specified property that is a non-null string
739
+ *
740
+ * @example
741
+ * ```typescript
742
+ * const data: unknown = { status: "active", count: 42 };
743
+ * if (isRecordStringProperty(data, 'status')) {
744
+ * console.log(data.status.toUpperCase()); // OK
745
+ * }
746
+ * ```
747
+ */
748
+ function isRecordStringProperty(o, property) {
749
+ if (!isNonNullableRecordProperty(o, property)) {
750
+ return false;
751
+ }
752
+ return typeof o[property] === 'string';
753
+ }
754
+ /**
755
+ * Assertion function that validates a property exists on an object, is a string,
756
+ * and optionally matches specific expected value(s).
757
+ * Throws an `InvalidPropertyError` if validation fails.
758
+ *
759
+ * @template K - The property key type (string literal)
760
+ * @param o - The value to validate (can be any type)
761
+ * @param property - The property name to check for
762
+ * @param objName - The name of the object being validated (used in error messages)
763
+ * @param expectedValue - Optional specific string value or array of allowed values to match against
764
+ * @throws {InvalidPropertyError} When the property is missing, not a string, or doesn't match expectedValue
765
+ * @throws {never} No other errors are thrown
766
+ *
767
+ * @example
768
+ * ```typescript
769
+ * // Check property is a string (any value)
770
+ * assertRecordStringProperty(data, 'name', 'user');
771
+ *
772
+ * // Check property equals a specific value
773
+ * assertRecordStringProperty(data, 'status', 'response', 'active');
774
+ *
775
+ * // Check property is one of multiple allowed values
776
+ * assertRecordStringProperty(data, 'status', 'response', ['queued', 'processing', 'completed']);
777
+ * ```
778
+ */
779
+ function assertRecordStringProperty(o, property, objName, expectedValue) {
780
+ if (!isRecordStringProperty(o, property)) {
781
+ throw new InvalidPropertyError({
782
+ objName,
783
+ property,
784
+ expectedType: 'string',
785
+ expectedValue,
786
+ type: typeofProperty(o, property),
787
+ });
788
+ }
789
+ if (expectedValue !== undefined) {
790
+ if (Array.isArray(expectedValue)) {
791
+ // Check if value matches any of the allowed values
792
+ for (let i = 0; i < expectedValue.length; ++i) {
793
+ if (o[property] === expectedValue[i]) {
794
+ return;
795
+ }
796
+ }
797
+ throw new InvalidPropertyError({
798
+ objName,
799
+ property,
800
+ expectedType: 'string',
801
+ expectedValue,
802
+ type: typeof o[property], // === "string"
803
+ value: o[property],
804
+ });
805
+ }
806
+ else {
807
+ if (o[property] !== expectedValue) {
808
+ throw new InvalidPropertyError({
809
+ objName,
810
+ property,
811
+ expectedType: 'string',
812
+ expectedValue,
813
+ type: typeof o[property], // === "string"
814
+ value: o[property],
815
+ });
816
+ }
817
+ }
818
+ }
819
+ }
820
+ function assertRecordStringArrayProperty(o, property, objName) {
821
+ assertRecordArrayProperty(o, property, objName);
822
+ const arr = o[property];
823
+ for (let i = 0; i < arr.length; ++i) {
824
+ if (typeof arr[i] !== 'string') {
825
+ throw new InvalidPropertyError({
826
+ objName,
827
+ property: `${property}[${i}]`,
828
+ expectedType: 'string',
829
+ type: typeof arr[i],
830
+ });
831
+ }
832
+ }
833
+ }
834
+ function safeJSONstringify(o) {
835
+ try {
836
+ return JSON.stringify(o, (_, v) => typeof v === 'bigint' ? v.toString() : v);
837
+ }
838
+ catch {
839
+ return '';
840
+ }
841
+ }
842
+
460
843
  const abiKmsVerifier = [
461
844
  'function getKmsSigners() view returns (address[])',
462
845
  'function getThreshold() view returns (uint256)',
@@ -465,12 +848,12 @@ const abiInputVerifier = [
465
848
  'function getCoprocessorSigners() view returns (address[])',
466
849
  'function getThreshold() view returns (uint256)',
467
850
  ];
468
- const getProvider = (config) => {
469
- if (typeof config.network === 'string') {
470
- return new ethers.JsonRpcProvider(config.network);
851
+ const getProvider = (network) => {
852
+ if (typeof network === 'string') {
853
+ return new ethers.JsonRpcProvider(network);
471
854
  }
472
- else if (config.network) {
473
- return new ethers.BrowserProvider(config.network);
855
+ else if (network) {
856
+ return new ethers.BrowserProvider(network);
474
857
  }
475
858
  throw new Error('You must provide a network URL or a EIP1193 object (eg: window.ethereum)');
476
859
  };
@@ -487,8 +870,8 @@ const getChainId = async (provider, config) => {
487
870
  }
488
871
  };
489
872
  const getTfheCompactPublicKey = async (config) => {
490
- if (config.relayerUrl && !config.publicKey) {
491
- const inputs = await getKeysFromRelayer(cleanURL(config.relayerUrl));
873
+ if (config.relayerVersionUrl && !config.publicKey) {
874
+ const inputs = await getKeysFromRelayer(removeSuffix(config.relayerVersionUrl, '/'));
492
875
  return { publicKey: inputs.publicKey, publicKeyId: inputs.publicKeyId };
493
876
  }
494
877
  else if (config.publicKey && config.publicKey.data && config.publicKey.id) {
@@ -510,8 +893,8 @@ const getTfheCompactPublicKey = async (config) => {
510
893
  }
511
894
  };
512
895
  const getPublicParams = async (config) => {
513
- if (config.relayerUrl && !config.publicParams) {
514
- const inputs = await getKeysFromRelayer(cleanURL(config.relayerUrl));
896
+ if (config.relayerVersionUrl && !config.publicParams) {
897
+ const inputs = await getKeysFromRelayer(removeSuffix(config.relayerVersionUrl, '/'));
515
898
  return inputs.publicParams;
516
899
  }
517
900
  else if (config.publicParams && config.publicParams['2048']) {
@@ -534,27 +917,28 @@ const getPublicParams = async (config) => {
534
917
  throw new Error('You must provide a valid CRS with its CRS ID.');
535
918
  }
536
919
  };
537
- const getKMSSigners = async (provider, config) => {
538
- const kmsContract = new ethers.Contract(config.kmsContractAddress, abiKmsVerifier, provider);
920
+ const getKMSSigners = async (provider, kmsContractAddress) => {
921
+ const kmsContract = new ethers.Contract(kmsContractAddress, abiKmsVerifier, provider);
539
922
  const signers = await kmsContract.getKmsSigners();
540
923
  return signers;
541
924
  };
542
- const getKMSSignersThreshold = async (provider, config) => {
543
- const kmsContract = new ethers.Contract(config.kmsContractAddress, abiKmsVerifier, provider);
925
+ const getKMSSignersThreshold = async (provider, kmsContractAddress) => {
926
+ const kmsContract = new ethers.Contract(kmsContractAddress, abiKmsVerifier, provider);
544
927
  const threshold = await kmsContract.getThreshold();
545
928
  return Number(threshold); // threshold is always supposed to fit in a number
546
929
  };
547
- const getCoprocessorSigners = async (provider, config) => {
548
- const inputContract = new ethers.Contract(config.inputVerifierContractAddress, abiInputVerifier, provider);
930
+ const getCoprocessorSigners = async (provider, inputVerifierContractAddress) => {
931
+ const inputContract = new ethers.Contract(inputVerifierContractAddress, abiInputVerifier, provider);
549
932
  const signers = await inputContract.getCoprocessorSigners();
550
933
  return signers;
551
934
  };
552
- const getCoprocessorSignersThreshold = async (provider, config) => {
553
- const inputContract = new ethers.Contract(config.inputVerifierContractAddress, abiInputVerifier, provider);
935
+ const getCoprocessorSignersThreshold = async (provider, inputVerifierContractAddress) => {
936
+ const inputContract = new ethers.Contract(inputVerifierContractAddress, abiInputVerifier, provider);
554
937
  const threshold = await inputContract.getThreshold();
555
938
  return Number(threshold); // threshold is always supposed to fit in a number
556
939
  };
557
940
 
941
+ // This file contains common utilities for both user and public decryption requests
558
942
  const NumEncryptedBits = {
559
943
  0: 2, // ebool
560
944
  2: 8, // euint8
@@ -565,17 +949,6 @@ const NumEncryptedBits = {
565
949
  7: 160, // eaddress
566
950
  8: 256, // euint256
567
951
  };
568
- function getHandleType(handle) {
569
- if (handle.length !== 66) {
570
- throw new Error(`Handle ${handle} is not of valid length`);
571
- }
572
- const hexPair = handle.slice(-4, -2).toLowerCase();
573
- const typeDiscriminant = parseInt(hexPair, 16);
574
- if (!(typeDiscriminant in NumEncryptedBits)) {
575
- throw new Error(`Handle ${handle} is not of valid type`);
576
- }
577
- return typeDiscriminant;
578
- }
579
952
  function checkEncryptedBits(handles) {
580
953
  let total = 0;
581
954
  for (const handle of handles) {
@@ -597,63 +970,401 @@ function checkEncryptedBits(handles) {
597
970
  return total;
598
971
  }
599
972
 
600
- // Add type checking
601
- const getAddress$1 = (value) => ethers.getAddress(value);
602
- const aclABI$1 = [
603
- 'function persistAllowed(bytes32 handle, address account) view returns (bool)',
604
- ];
605
- const MAX_USER_DECRYPT_CONTRACT_ADDRESSES = 10;
606
- const MAX_USER_DECRYPT_DURATION_DAYS = BigInt(365);
607
- function formatAccordingToType(clearValueAsBigInt, type) {
608
- if (type === 0) {
609
- // ebool
610
- return clearValueAsBigInt === BigInt(1);
973
+ class InvalidTypeError extends RelayerErrorBase {
974
+ _objName;
975
+ _type;
976
+ _expectedType;
977
+ _expectedCustomType;
978
+ constructor({ objName, type, expectedType, expectedCustomType, }) {
979
+ super({
980
+ message: `InvalidTypeError ${objName} ${expectedType} ${type}`,
981
+ name: 'InvalidTypeError',
982
+ });
983
+ this._objName = objName;
984
+ this._type = type;
985
+ this._expectedType = expectedType;
986
+ this._expectedCustomType = expectedCustomType;
611
987
  }
612
- else if (type === 7) {
613
- // eaddress
614
- return getAddress$1('0x' + clearValueAsBigInt.toString(16).padStart(40, '0'));
988
+ get objName() {
989
+ return this._objName;
615
990
  }
616
- else if (type > 8 || type == 1) {
617
- // type == 1 : euint4 (not supported)
618
- throw new Error(`Unsupported handle type ${type}`);
991
+ get type() {
992
+ return this._type;
993
+ }
994
+ get expectedType() {
995
+ return this._expectedType;
996
+ }
997
+ get expectedCustomType() {
998
+ return this._expectedCustomType;
619
999
  }
620
- // euintXXX
621
- return clearValueAsBigInt;
622
1000
  }
623
- function buildUserDecryptResults(handles, listBigIntDecryptions) {
624
- let typesList = [];
625
- for (const handle of handles) {
626
- const hexPair = handle.slice(-4, -2).toLowerCase();
627
- const typeDiscriminant = parseInt(hexPair, 16);
628
- typesList.push(typeDiscriminant);
1001
+
1002
+ function isBytes(value, bytewidth) {
1003
+ if (!value) {
1004
+ return false;
629
1005
  }
630
- const results = {};
631
- handles.forEach((handle, idx) => (results[handle] = formatAccordingToType(listBigIntDecryptions[idx], typesList[idx])));
632
- return results;
1006
+ if (!(value instanceof Uint8Array)) {
1007
+ return false;
1008
+ }
1009
+ return value.length === bytewidth ;
633
1010
  }
634
- function checkDeadlineValidity(startTimestamp, durationDays) {
635
- if (durationDays === BigInt(0)) {
636
- throw Error('durationDays is null');
1011
+ function isBytesHex(value, bytewidth) {
1012
+ if (!is0x(value)) {
1013
+ return false;
637
1014
  }
638
- if (durationDays > MAX_USER_DECRYPT_DURATION_DAYS) {
639
- throw Error(`durationDays is above max duration of ${MAX_USER_DECRYPT_DURATION_DAYS}`);
1015
+ if (bytewidth !== undefined && value.length !== 2 * bytewidth + 2) {
1016
+ return false;
640
1017
  }
641
- const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
642
- if (startTimestamp > currentTimestamp) {
643
- throw Error('startTimestamp is set in the future');
1018
+ if ((value.length - 2) % 2 !== 0) {
1019
+ return false;
644
1020
  }
645
- const durationInSeconds = durationDays * BigInt(86400);
646
- if (startTimestamp + durationInSeconds < currentTimestamp) {
647
- throw Error('User decrypt request has expired');
1021
+ const hexRegex = /^0x[a-fA-F0-9]*$/;
1022
+ if (!hexRegex.test(value)) {
1023
+ return false;
648
1024
  }
1025
+ return true;
649
1026
  }
650
- const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider, instanceOptions) => async (_handles, privateKey, publicKey, signature, contractAddresses, userAddress, startTimestamp, durationDays, options) => {
651
- const extraData = '0x00';
652
- let pubKey;
653
- let privKey;
1027
+ function isBytesHexNo0x(value, bytewidth) {
1028
+ if (!isNo0x(value)) {
1029
+ return false;
1030
+ }
1031
+ if ((value.length - 2) % 2 !== 0) {
1032
+ return false;
1033
+ }
1034
+ const hexRegex = /^[a-fA-F0-9]*$/;
1035
+ if (!hexRegex.test(value)) {
1036
+ return false;
1037
+ }
1038
+ return true;
1039
+ }
1040
+ function isBytes32Hex(value) {
1041
+ return isBytesHex(value, 32);
1042
+ }
1043
+ function isBytes65Hex(value) {
1044
+ return isBytesHex(value, 65);
1045
+ }
1046
+ function isBytes32(value) {
1047
+ return isBytes(value, 32);
1048
+ }
1049
+ function assertIsBytes32(value) {
1050
+ if (!isBytes32(value)) {
1051
+ throw new InvalidTypeError({
1052
+ type: typeof value,
1053
+ expectedType: 'Bytes32',
1054
+ });
1055
+ }
1056
+ }
1057
+ /**
1058
+ * Type guard that checks if a property exists on an object and is a valid hex bytes string.
1059
+ * A valid BytesHex string starts with "0x" followed by an even number of hexadecimal characters.
1060
+ *
1061
+ * @template K - The property key type (string literal)
1062
+ * @param o - The value to check (can be any type)
1063
+ * @param property - The property name to check for
1064
+ * @returns True if `o` is an object with the specified property that is a valid BytesHex string
1065
+ *
1066
+ * @example
1067
+ * ```typescript
1068
+ * const data: unknown = { hash: "0x1234abcd", value: 42 };
1069
+ * if (isRecordBytesHexProperty(data, 'hash')) {
1070
+ * console.log(data.hash); // "0x1234abcd"
1071
+ * }
1072
+ * ```
1073
+ */
1074
+ function isRecordBytesHexProperty(o, property) {
1075
+ if (!isNonNullableRecordProperty(o, property)) {
1076
+ return false;
1077
+ }
1078
+ return isBytesHex(o[property]);
1079
+ }
1080
+ /**
1081
+ * Assertion function that validates a property exists on an object and is a valid hex bytes string.
1082
+ * A valid BytesHex string must start with "0x" followed by an even number of hexadecimal characters.
1083
+ * Throws an `InvalidPropertyError` if validation fails.
1084
+ *
1085
+ * @template K - The property key type (string literal)
1086
+ * @param o - The value to validate (can be any type)
1087
+ * @param property - The property name to check for
1088
+ * @param objName - The name of the object being validated (used in error messages)
1089
+ * @throws {InvalidPropertyError} When the property is missing, not a string, or not valid BytesHex format
1090
+ * @throws {never} No other errors are thrown
1091
+ *
1092
+ * @example
1093
+ * ```typescript
1094
+ * function processTransaction(data: unknown) {
1095
+ * assertRecordBytesHexProperty(data, 'txHash', 'transaction');
1096
+ * console.log(data.txHash); // e.g., "0x1234..."
1097
+ * }
1098
+ * ```
1099
+ */
1100
+ function assertRecordBytesHexProperty(o, property, objName) {
1101
+ if (!isRecordBytesHexProperty(o, property)) {
1102
+ throw new InvalidPropertyError({
1103
+ objName,
1104
+ property,
1105
+ expectedType: 'BytesHex',
1106
+ type: typeofProperty(o, property),
1107
+ });
1108
+ }
1109
+ }
1110
+ function isRecordBytesHexNo0xProperty(o, property) {
1111
+ if (!isNonNullableRecordProperty(o, property)) {
1112
+ return false;
1113
+ }
1114
+ return isBytesHexNo0x(o[property]);
1115
+ }
1116
+ function assertRecordBytesHexNo0xProperty(o, property, objName) {
1117
+ if (!isRecordBytesHexNo0xProperty(o, property)) {
1118
+ throw new InvalidPropertyError({
1119
+ objName,
1120
+ property,
1121
+ expectedType: 'BytesHexNo0x',
1122
+ type: typeofProperty(o, property),
1123
+ });
1124
+ }
1125
+ }
1126
+ function assertRecordBytes32HexArrayProperty(o, property, objName) {
1127
+ assertRecordArrayProperty(o, property, objName);
1128
+ const arr = o[property];
1129
+ for (let i = 0; i < arr.length; ++i) {
1130
+ if (!isBytes32Hex(arr[i])) {
1131
+ throw new InvalidPropertyError({
1132
+ objName,
1133
+ property: `${property}[${i}]`,
1134
+ expectedType: 'Bytes32Hex',
1135
+ type: typeof arr[i],
1136
+ });
1137
+ }
1138
+ }
1139
+ }
1140
+ function assertRecordBytes65HexArrayProperty(o, property, objName) {
1141
+ assertRecordArrayProperty(o, property, objName);
1142
+ const arr = o[property];
1143
+ for (let i = 0; i < arr.length; ++i) {
1144
+ if (!isBytes65Hex(arr[i])) {
1145
+ throw new InvalidPropertyError({
1146
+ objName,
1147
+ property: `${property}[${i}]`,
1148
+ expectedType: 'Bytes65Hex',
1149
+ type: typeof arr[i],
1150
+ });
1151
+ }
1152
+ }
1153
+ }
1154
+ /**
1155
+ * Assertion function that validates a property exists on an object, is an array,
1156
+ * and every element is a valid hex bytes string (with "0x" prefix).
1157
+ * Throws an `InvalidPropertyError` if validation fails.
1158
+ *
1159
+ * @template K - The property key type (string literal)
1160
+ * @param o - The value to validate (can be any type)
1161
+ * @param property - The property name to check for
1162
+ * @param objName - The name of the object being validated (used in error messages)
1163
+ * @throws {InvalidPropertyError} When the property is missing, not an array, or any element is not valid BytesHex
1164
+ * @throws {never} No other errors are thrown
1165
+ *
1166
+ * @example
1167
+ * ```typescript
1168
+ * function processHashes(data: unknown) {
1169
+ * assertRecordBytesHexArrayProperty(data, 'txHashes', 'transaction');
1170
+ * data.txHashes.forEach(hash => {
1171
+ * console.log(hash); // e.g., "0x1234abcd..."
1172
+ * });
1173
+ * }
1174
+ * ```
1175
+ */
1176
+ function assertRecordBytesHexArrayProperty(o, property, objName) {
1177
+ assertRecordArrayProperty(o, property, objName);
1178
+ const arr = o[property];
1179
+ for (let i = 0; i < arr.length; ++i) {
1180
+ if (!isBytesHex(arr[i])) {
1181
+ throw new InvalidPropertyError({
1182
+ objName,
1183
+ property: `${property}[${i}]`,
1184
+ expectedType: 'BytesHex',
1185
+ type: typeof arr[i],
1186
+ });
1187
+ }
1188
+ }
1189
+ }
1190
+ /**
1191
+ * Assertion function that validates a property exists on an object, is an array,
1192
+ * and every element is a valid hex bytes string (without "0x" prefix).
1193
+ * Throws an `InvalidPropertyError` if validation fails.
1194
+ *
1195
+ * @template K - The property key type (string literal)
1196
+ * @param o - The value to validate (can be any type)
1197
+ * @param property - The property name to check for
1198
+ * @param objName - The name of the object being validated (used in error messages)
1199
+ * @throws {InvalidPropertyError} When the property is missing, not an array, or any element is not valid BytesHexNo0x
1200
+ * @throws {never} No other errors are thrown
1201
+ *
1202
+ * @example
1203
+ * ```typescript
1204
+ * function processSignatures(data: unknown) {
1205
+ * assertRecordBytesHexNo0xArrayProperty(data, 'signatures', 'response');
1206
+ * data.signatures.forEach(sig => {
1207
+ * console.log(sig); // e.g., "1234abcd..." (no 0x prefix)
1208
+ * });
1209
+ * }
1210
+ * ```
1211
+ */
1212
+ function assertRecordBytesHexNo0xArrayProperty(o, property, objName) {
1213
+ assertRecordArrayProperty(o, property, objName);
1214
+ const arr = o[property];
1215
+ for (let i = 0; i < arr.length; ++i) {
1216
+ if (!isBytesHexNo0x(arr[i])) {
1217
+ throw new InvalidPropertyError({
1218
+ objName,
1219
+ property: `${property}[${i}]`,
1220
+ expectedType: 'BytesHexNo0x',
1221
+ type: typeof arr[i],
1222
+ });
1223
+ }
1224
+ }
1225
+ }
1226
+ function isRecordUint8ArrayProperty(o, property) {
1227
+ if (!isNonNullableRecordProperty(o, property)) {
1228
+ return false;
1229
+ }
1230
+ return o[property] instanceof Uint8Array;
1231
+ }
1232
+ function assertUint8ArrayProperty(o, property, objName) {
1233
+ if (!isRecordUint8ArrayProperty(o, property)) {
1234
+ throw new InvalidPropertyError({
1235
+ objName,
1236
+ property,
1237
+ expectedType: 'Uint8Array',
1238
+ type: typeofProperty(o, property),
1239
+ });
1240
+ }
1241
+ }
1242
+ /**
1243
+ * Convert a Uint8Array to a hex string (without 0x prefix).
1244
+ */
1245
+ function bytesToHexNo0x(bytes) {
1246
+ if (!bytes || bytes?.length === 0) {
1247
+ return '';
1248
+ }
1249
+ let hex = '';
1250
+ for (let i = 0; i < bytes.length; i++) {
1251
+ hex += bytes[i].toString(16).padStart(2, '0');
1252
+ }
1253
+ return hex;
1254
+ }
1255
+ /**
1256
+ * Convert a Uint8Array to a 0x prefixed hex string
1257
+ */
1258
+ function bytesToHex(bytes) {
1259
+ return `0x${bytesToHexNo0x(bytes)}`;
1260
+ }
1261
+ /**
1262
+ * Convert a hex string prefixed by 0x or not to a Uint8Array
1263
+ */
1264
+ function hexToBytes(hexString) {
1265
+ const arr = hexString.replace(/^(0x)/, '').match(/.{1,2}/g);
1266
+ if (!arr)
1267
+ return new Uint8Array();
1268
+ return Uint8Array.from(arr.map((byte) => parseInt(byte, 16)));
1269
+ }
1270
+ /**
1271
+ * Convert a Uint8Array to a bigint
1272
+ */
1273
+ function bytesToBigInt(byteArray) {
1274
+ if (!byteArray || byteArray.length === 0) {
1275
+ return BigInt(0);
1276
+ }
1277
+ let result = BigInt(0);
1278
+ for (let i = 0; i < byteArray.length; i++) {
1279
+ result = (result << BigInt(8)) | BigInt(byteArray[i]);
1280
+ }
1281
+ return result;
1282
+ }
1283
+ function toHexString(bytes, with0x = false) {
1284
+ return `${with0x ? '0x' : ''}${bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')}`;
1285
+ }
1286
+ async function fetchBytes(url) {
1287
+ const response = await fetch(url);
1288
+ if (!response.ok) {
1289
+ throw new Error(`HTTP error! status: ${response.status} on ${response.url}`);
1290
+ }
1291
+ // Warning : bytes is not widely supported yet!
1292
+ const bytes = typeof response.bytes === 'function'
1293
+ ? await response.bytes()
1294
+ : new Uint8Array(await response.arrayBuffer());
1295
+ return bytes;
1296
+ }
1297
+ function concatBytes(...arrays) {
1298
+ let totalLength = 0;
1299
+ for (const arr of arrays) {
1300
+ totalLength += arr.length;
1301
+ }
1302
+ const result = new Uint8Array(totalLength);
1303
+ let offset = 0;
1304
+ for (const arr of arrays) {
1305
+ result.set(arr, offset);
1306
+ offset += arr.length;
1307
+ }
1308
+ return result;
1309
+ }
1310
+
1311
+ // Add type checking
1312
+ const getAddress$1 = (value) => ethers.getAddress(value);
1313
+ const aclABI$1 = [
1314
+ 'function persistAllowed(bytes32 handle, address account) view returns (bool)',
1315
+ ];
1316
+ const MAX_USER_DECRYPT_CONTRACT_ADDRESSES = 10;
1317
+ const MAX_USER_DECRYPT_DURATION_DAYS = BigInt(365);
1318
+ function formatAccordingToType(clearValueAsBigInt, type) {
1319
+ if (type === 0) {
1320
+ // ebool
1321
+ return clearValueAsBigInt === BigInt(1);
1322
+ }
1323
+ else if (type === 7) {
1324
+ // eaddress
1325
+ return getAddress$1('0x' + clearValueAsBigInt.toString(16).padStart(40, '0'));
1326
+ }
1327
+ else if (type > 8 || type == 1) {
1328
+ // type == 1 : euint4 (not supported)
1329
+ throw new Error(`Unsupported handle type ${type}`);
1330
+ }
1331
+ // euintXXX
1332
+ return clearValueAsBigInt;
1333
+ }
1334
+ function buildUserDecryptResults(handles, listBigIntDecryptions) {
1335
+ let typesList = [];
1336
+ for (const handle of handles) {
1337
+ const hexPair = handle.slice(-4, -2).toLowerCase();
1338
+ const typeDiscriminant = parseInt(hexPair, 16);
1339
+ typesList.push(typeDiscriminant);
1340
+ }
1341
+ const results = {};
1342
+ handles.forEach((handle, idx) => (results[handle] = formatAccordingToType(listBigIntDecryptions[idx], typesList[idx])));
1343
+ return results;
1344
+ }
1345
+ function checkDeadlineValidity(startTimestamp, durationDays) {
1346
+ if (durationDays === BigInt(0)) {
1347
+ throw Error('durationDays is null');
1348
+ }
1349
+ if (durationDays > MAX_USER_DECRYPT_DURATION_DAYS) {
1350
+ throw Error(`durationDays is above max duration of ${MAX_USER_DECRYPT_DURATION_DAYS}`);
1351
+ }
1352
+ const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
1353
+ if (startTimestamp > currentTimestamp) {
1354
+ throw Error('startTimestamp is set in the future');
1355
+ }
1356
+ const durationInSeconds = durationDays * BigInt(86400);
1357
+ if (startTimestamp + durationInSeconds < currentTimestamp) {
1358
+ throw Error('User decrypt request has expired');
1359
+ }
1360
+ }
1361
+ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContractAddress, aclContractAddress, relayerProvider, provider, defaultOptions) => async (_handles, privateKey, publicKey, signature, contractAddresses, userAddress, startTimestamp, durationDays, options) => {
1362
+ const extraData = '0x00';
1363
+ let pubKey;
1364
+ let privKey;
654
1365
  try {
655
- pubKey = TKMS.u8vec_to_ml_kem_pke_pk(fromHexString(publicKey));
656
- privKey = TKMS.u8vec_to_ml_kem_pke_sk(fromHexString(privateKey));
1366
+ pubKey = TKMS.u8vec_to_ml_kem_pke_pk(hexToBytes(publicKey));
1367
+ privKey = TKMS.u8vec_to_ml_kem_pke_sk(hexToBytes(privateKey));
657
1368
  }
658
1369
  catch (e) {
659
1370
  throw new Error('Invalid public or private key', { cause: e });
@@ -661,16 +1372,16 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
661
1372
  // Casting handles if string
662
1373
  const signatureSanitized = signature.replace(/^(0x)/, '');
663
1374
  const publicKeySanitized = publicKey.replace(/^(0x)/, '');
664
- const handles = _handles.map((h) => ({
1375
+ const handleContractPairs = _handles.map((h) => ({
665
1376
  handle: typeof h.handle === 'string'
666
- ? toHexString(fromHexString(h.handle), true)
1377
+ ? toHexString(hexToBytes(h.handle), true)
667
1378
  : toHexString(h.handle, true),
668
1379
  contractAddress: getAddress$1(h.contractAddress),
669
1380
  }));
670
- checkEncryptedBits(handles.map((h) => h.handle));
1381
+ checkEncryptedBits(handleContractPairs.map((h) => h.handle));
671
1382
  checkDeadlineValidity(BigInt(startTimestamp), BigInt(durationDays));
672
- const acl = new ethers.ethers.Contract(aclContractAddress, aclABI$1, provider);
673
- const verifications = handles.map(async ({ handle, contractAddress }) => {
1383
+ const acl = new ethers.Contract(aclContractAddress, aclABI$1, provider);
1384
+ const verifications = handleContractPairs.map(async ({ handle, contractAddress }) => {
674
1385
  const userAllowed = await acl.persistAllowed(handle, userAddress);
675
1386
  const contractAllowed = await acl.persistAllowed(handle, contractAddress);
676
1387
  if (!userAllowed) {
@@ -694,7 +1405,7 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
694
1405
  throw e;
695
1406
  });
696
1407
  const payloadForRequest = {
697
- handleContractPairs: handles,
1408
+ handleContractPairs,
698
1409
  requestValidity: {
699
1410
  startTimestamp: startTimestamp.toString(), // Convert to string
700
1411
  durationDays: durationDays.toString(), // Convert to string
@@ -706,7 +1417,10 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
706
1417
  publicKey: publicKeySanitized,
707
1418
  extraData,
708
1419
  };
709
- const json = await fetchRelayerJsonRpcPost('USER_DECRYPT', `${relayerUrl}/v1/user-decrypt`, payloadForRequest, instanceOptions ?? options);
1420
+ const json = await relayerProvider.fetchPostUserDecrypt(payloadForRequest, {
1421
+ ...defaultOptions,
1422
+ ...options,
1423
+ });
710
1424
  // assume the KMS Signers have the correct order
711
1425
  let indexedKmsSigners = kmsSigners.map((signer, index) => {
712
1426
  return TKMS.new_server_id_addr(index + 1, signer);
@@ -728,12 +1442,13 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
728
1442
  signature: signatureSanitized,
729
1443
  client_address: userAddress,
730
1444
  enc_key: publicKeySanitized,
731
- ciphertext_handles: handles.map((h) => h.handle.replace(/^0x/, '')),
1445
+ ciphertext_handles: handleContractPairs.map((h) => h.handle.replace(/^0x/, '')),
732
1446
  eip712_verifying_contract: verifyingContractAddress,
733
1447
  };
734
- const decryption = TKMS.process_user_decryption_resp_from_js(client, payloadForVerification, eip712Domain, json.response, pubKey, privKey, true);
1448
+ const decryption = TKMS.process_user_decryption_resp_from_js(client, payloadForVerification, eip712Domain, json, //json.response,
1449
+ pubKey, privKey, true);
735
1450
  const listBigIntDecryptions = decryption.map((d) => bytesToBigInt(d.bytes));
736
- const results = buildUserDecryptResults(handles.map((h) => h.handle), listBigIntDecryptions);
1451
+ const results = buildUserDecryptResults(handleContractPairs.map((h) => h.handle), listBigIntDecryptions);
737
1452
  return results;
738
1453
  }
739
1454
  catch (e) {
@@ -858,10 +1573,10 @@ const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKe
858
1573
  };
859
1574
  const closestPP = getClosestPP();
860
1575
  const pp = publicParams[closestPP].publicParams;
861
- const buffContract = fromHexString(contractAddress);
862
- const buffUser = fromHexString(userAddress);
863
- const buffAcl = fromHexString(aclContractAddress);
864
- const buffChainId = fromHexString(chainId.toString(16).padStart(64, '0'));
1576
+ const buffContract = hexToBytes(contractAddress);
1577
+ const buffUser = hexToBytes(userAddress);
1578
+ const buffAcl = hexToBytes(aclContractAddress);
1579
+ const buffChainId = hexToBytes(chainId.toString(16).padStart(64, '0'));
865
1580
  const auxData = new Uint8Array(buffContract.length + buffUser.length + buffAcl.length + 32);
866
1581
  auxData.set(buffContract, 0);
867
1582
  auxData.set(buffUser, 20);
@@ -874,184 +1589,500 @@ const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKe
874
1589
  };
875
1590
  };
876
1591
 
877
- /**
878
- * **FHE Type Mapping for Input Builders**
879
- * * Maps the **number of encrypted bits** used by a FHEVM primary type
880
- * to its corresponding **FheTypeId**. This constant is primarily used by
881
- * `EncryptedInput` and `RelayerEncryptedInput` builders to determine the correct
882
- * input type and calculate the total required bit-length.
883
- *
884
- * **Structure: \{ Encrypted Bit Length: FheTypeId \}**
885
- *
886
- * | Bits | FheTypeId | FHE Type Name | Note |
887
- * | :--- | :-------- | :------------ | :--- |
888
- * | 2 | 0 | `ebool` | The boolean type. |
889
- * | (N/A)| 1 | `euint4` | **Deprecated** and omitted from this map. |
890
- * | 8 | 2 | `euint8` | |
891
- * | 16 | 3 | `euint16` | |
892
- * | 32 | 4 | `euint32` | |
893
- * | 64 | 5 | `euint64` | |
894
- * | 128 | 6 | `euint128` | |
895
- * | 160 | 7 | `eaddress` | Used for encrypted Ethereum addresses. |
896
- * | 256 | 8 | `euint256` | The maximum supported integer size. |
897
- */
898
- const ENCRYPTION_TYPES = {
899
- 2: 0, // ebool (FheTypeId=0) is using 2 encrypted bits
900
- // euint4 (FheTypeId=1) is deprecated
901
- 8: 2, // euint8 (FheTypeId=2) is using 8 encrypted bits
902
- 16: 3, // euint16 (FheTypeId=3) is using 16 encrypted bits
903
- 32: 4, // euint32 (FheTypeId=4) is using 32 encrypted bits
904
- 64: 5, // euint64 (FheTypeId=5) is using 64 encrypted bits
905
- 128: 6, // euint128 (FheTypeId=128) is using 128 encrypted bits
906
- 160: 7, // eaddress (FheTypeId=7) is using 160 encrypted bits
907
- 256: 8, // euint256 (FheTypeId=8) is using 256 encrypted bits
908
- };
909
-
910
1592
  const MAX_UINT64 = BigInt('18446744073709551615'); // 2^64 - 1
911
- const RAW_CT_HASH_DOMAIN_SEPARATOR = 'ZK-w_rct';
912
- const HANDLE_HASH_DOMAIN_SEPARATOR = 'ZK-w_hdl';
913
- const computeHandles = (ciphertextWithZKProof, bitwidths, aclContractAddress, chainId, ciphertextVersion) => {
914
- // Should be identical to:
915
- // https://github.com/zama-ai/fhevm-backend/blob/bae00d1b0feafb63286e94acdc58dc88d9c481bf/fhevm-engine/zkproof-worker/src/verifier.rs#L301
916
- const blob_hash = createHash('keccak256')
917
- .update(Buffer.from(RAW_CT_HASH_DOMAIN_SEPARATOR))
918
- .update(Buffer.from(ciphertextWithZKProof))
919
- .digest();
920
- const aclContractAddress20Bytes = Buffer.from(fromHexString(aclContractAddress));
921
- const hex = chainId.toString(16).padStart(64, '0'); // 64 hex chars = 32 bytes
922
- const chainId32Bytes = Buffer.from(hex, 'hex');
923
- const handles = bitwidths.map((bitwidth, encryptionIndex) => {
924
- const encryptionType = ENCRYPTION_TYPES[bitwidth];
925
- const encryptionIndex1Byte = Buffer.from([encryptionIndex]);
926
- const handleHash = createHash('keccak256')
927
- .update(Buffer.from(HANDLE_HASH_DOMAIN_SEPARATOR))
928
- .update(blob_hash)
929
- .update(encryptionIndex1Byte)
930
- .update(aclContractAddress20Bytes)
931
- .update(chainId32Bytes)
932
- .digest();
933
- const dataInput = new Uint8Array(32);
934
- dataInput.set(handleHash, 0);
935
- // Check if chainId exceeds 8 bytes
936
- if (BigInt(chainId) > MAX_UINT64) {
937
- throw new Error('ChainId exceeds maximum allowed value (8 bytes)'); // fhevm assumes chainID is only taking up to 8 bytes
938
- }
939
- const chainId8Bytes = fromHexString(hex).slice(24, 32);
940
- dataInput[21] = encryptionIndex;
941
- dataInput.set(chainId8Bytes, 22);
942
- dataInput[30] = encryptionType;
943
- dataInput[31] = ciphertextVersion;
944
- return dataInput;
945
- });
946
- return handles;
947
- };
948
-
949
- // Add type checking
950
- const getAddress = (value) => ethers.getAddress(value);
951
- const currentCiphertextVersion = () => {
952
- return 0;
953
- };
954
- function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold) {
955
- const addressMap = new Map();
956
- recoveredAddresses.forEach((address, index) => {
957
- if (addressMap.has(address)) {
958
- const duplicateValue = address;
959
- throw new Error(`Duplicate coprocessor signer address found: ${duplicateValue} appears multiple times in recovered addresses`);
960
- }
961
- addressMap.set(address, index);
962
- });
963
- for (const address of recoveredAddresses) {
964
- if (!coprocessorSigners.includes(address)) {
965
- throw new Error(`Invalid address found: ${address} is not in the list of coprocessor signers`);
1593
+ BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
1594
+ const MAX_UINT32 = 0xffffffff;
1595
+ const MAX_UINT8 = 0xff;
1596
+ function numberToHex(num) {
1597
+ let hex = num.toString(16);
1598
+ return hex.length % 2 ? '0' + hex : hex;
1599
+ }
1600
+ function isUintNumber(value) {
1601
+ if (typeof value === 'number') {
1602
+ if (value < 0) {
1603
+ return false;
966
1604
  }
1605
+ return Number.isInteger(value);
967
1606
  }
968
- return recoveredAddresses.length >= threshold;
1607
+ return false;
969
1608
  }
970
- function isFhevmRelayerInputProofResponse(json) {
971
- const response = json.response;
972
- if (typeof response !== 'object' || response === null) {
1609
+ function isUintBigInt(value) {
1610
+ if (typeof value === 'bigint') {
1611
+ return value >= 0;
1612
+ }
1613
+ return false;
1614
+ }
1615
+ function isUint(value) {
1616
+ if (isUintNumber(value)) {
1617
+ return true;
1618
+ }
1619
+ else if (isUintBigInt(value)) {
1620
+ return true;
1621
+ }
1622
+ return false;
1623
+ }
1624
+ function isUint8(value) {
1625
+ if (!isUint(value)) {
973
1626
  return false;
974
1627
  }
975
- if (!('handles' in response && Array.isArray(response.handles))) {
1628
+ return value <= MAX_UINT8;
1629
+ }
1630
+ function isUint32(value) {
1631
+ if (!isUint(value)) {
976
1632
  return false;
977
1633
  }
978
- if (!('signatures' in response && Array.isArray(response.signatures))) {
1634
+ return value <= MAX_UINT32;
1635
+ }
1636
+ function isUint64(value) {
1637
+ if (!isUint(value)) {
979
1638
  return false;
980
1639
  }
981
- return (response.signatures.every((s) => typeof s === 'string') &&
982
- response.handles.every((h) => typeof h === 'string'));
1640
+ return value <= MAX_UINT64;
983
1641
  }
984
- const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerUrl, tfheCompactPublicKey, publicParams, coprocessorSigners, thresholdCoprocessorSigners, instanceOptions) => (contractAddress, userAddress) => {
985
- if (!ethers.isAddress(contractAddress)) {
986
- throw new Error('Contract address is not a valid address.');
1642
+ function uint32ToBytes32(uint32) {
1643
+ if (!isUint32(uint32)) {
1644
+ throw new InvalidTypeError({ expectedType: 'Uint32' });
987
1645
  }
988
- if (!ethers.isAddress(userAddress)) {
989
- throw new Error('User address is not a valid address.');
1646
+ const buffer = new ArrayBuffer(32);
1647
+ const view = new DataView(buffer);
1648
+ view.setUint32(28, Number(uint32), false);
1649
+ return new Uint8Array(buffer);
1650
+ }
1651
+ function assertIsUint8(value) {
1652
+ if (!isUint8(value)) {
1653
+ throw new InvalidTypeError({
1654
+ type: typeof value,
1655
+ expectedType: 'Uint8',
1656
+ });
990
1657
  }
991
- const input = createEncryptedInput({
992
- aclContractAddress,
993
- chainId,
994
- tfheCompactPublicKey,
995
- publicParams,
996
- contractAddress,
997
- userAddress,
998
- });
999
- return {
1000
- _input: input,
1001
- addBool(value) {
1002
- input.addBool(value);
1003
- return this;
1004
- },
1005
- add8(value) {
1006
- input.add8(value);
1007
- return this;
1008
- },
1009
- add16(value) {
1010
- input.add16(value);
1011
- return this;
1012
- },
1013
- add32(value) {
1014
- input.add32(value);
1015
- return this;
1016
- },
1017
- add64(value) {
1018
- input.add64(value);
1019
- return this;
1020
- },
1021
- add128(value) {
1022
- input.add128(value);
1023
- return this;
1024
- },
1025
- add256(value) {
1026
- input.add256(value);
1027
- return this;
1028
- },
1029
- addAddress(value) {
1030
- input.addAddress(value);
1031
- return this;
1032
- },
1033
- getBits() {
1034
- return input.getBits();
1035
- },
1036
- encrypt: async (options) => {
1037
- const extraData = '0x00';
1038
- const bits = input.getBits();
1039
- const ciphertext = input.encrypt();
1040
- const payload = {
1041
- contractAddress: getAddress(contractAddress),
1042
- userAddress: getAddress(userAddress),
1043
- ciphertextWithInputVerification: toHexString(ciphertext),
1044
- contractChainId: ('0x' + chainId.toString(16)),
1045
- extraData,
1046
- };
1047
- const json = await fetchRelayerJsonRpcPost('INPUT_PROOF', `${relayerUrl}/v1/input-proof`, payload, options ?? instanceOptions);
1048
- if (!isFhevmRelayerInputProofResponse(json)) {
1658
+ }
1659
+ function assertIsUint64(value) {
1660
+ if (!isUint64(value)) {
1661
+ throw new InvalidTypeError({
1662
+ type: typeof value,
1663
+ expectedType: 'Uint64',
1664
+ });
1665
+ }
1666
+ }
1667
+ function isRecordUintProperty(o, property) {
1668
+ if (!isNonNullableRecordProperty(o, property)) {
1669
+ return false;
1670
+ }
1671
+ return isUint(o[property]);
1672
+ }
1673
+ function assertRecordUintProperty(o, property, objName) {
1674
+ if (!isRecordUintProperty(o, property)) {
1675
+ throw new InvalidPropertyError({
1676
+ objName,
1677
+ property,
1678
+ type: typeofProperty(o, property),
1679
+ expectedType: 'Uint',
1680
+ });
1681
+ }
1682
+ }
1683
+
1684
+ class ChecksummedAddressError extends RelayerErrorBase {
1685
+ constructor({ address }) {
1686
+ super({
1687
+ message: `Checksummed address "${address}" is invalid.`,
1688
+ name: 'ChecksummedAddressError',
1689
+ });
1690
+ }
1691
+ }
1692
+
1693
+ function isChecksummedAddress(value) {
1694
+ if (typeof value !== 'string') {
1695
+ return false;
1696
+ }
1697
+ if (!value.startsWith('0x')) {
1698
+ return false;
1699
+ }
1700
+ if (value.length !== 42) {
1701
+ return false;
1702
+ }
1703
+ try {
1704
+ const a = ethers.getAddress(value);
1705
+ return a === value;
1706
+ }
1707
+ catch (e) {
1708
+ return false;
1709
+ }
1710
+ }
1711
+ function assertIsChecksummedAddress(value) {
1712
+ if (!isChecksummedAddress(value)) {
1713
+ throw new ChecksummedAddressError({ address: String(value) });
1714
+ }
1715
+ }
1716
+ function isAddress(value) {
1717
+ if (typeof value !== 'string') {
1718
+ return false;
1719
+ }
1720
+ if (!value.startsWith('0x')) {
1721
+ return false;
1722
+ }
1723
+ if (value.length !== 42) {
1724
+ return false;
1725
+ }
1726
+ if (!ethers.isAddress(value)) {
1727
+ return false;
1728
+ }
1729
+ return true;
1730
+ }
1731
+ function checksummedAddressToBytes20(address) {
1732
+ if (!isAddress(address)) {
1733
+ throw new InvalidTypeError({ expectedType: 'ChecksummedAddress' });
1734
+ }
1735
+ const hex = remove0x(address);
1736
+ const bytes = new Uint8Array(20);
1737
+ for (let i = 0; i < 20; i++) {
1738
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
1739
+ }
1740
+ return bytes;
1741
+ }
1742
+
1743
+ class FhevmHandleError extends RelayerErrorBase {
1744
+ constructor({ handle, message }) {
1745
+ super({
1746
+ message: message ?? `FHEVM Handle "${handle}" is invalid.`,
1747
+ name: 'FhevmHandleError',
1748
+ });
1749
+ }
1750
+ }
1751
+
1752
+ ////////////////////////////////////////////////////////////////////////////////
1753
+ // FhevmHandle
1754
+ ////////////////////////////////////////////////////////////////////////////////
1755
+ class FhevmHandle {
1756
+ _hash21;
1757
+ _chainId;
1758
+ _fheTypeId;
1759
+ _version;
1760
+ _computed;
1761
+ _index;
1762
+ static RAW_CT_HASH_DOMAIN_SEPARATOR = 'ZK-w_rct';
1763
+ static HANDLE_HASH_DOMAIN_SEPARATOR = 'ZK-w_hdl';
1764
+ static FheTypeIdToEncryptionBitwidths = {
1765
+ 0: 2,
1766
+ 2: 8,
1767
+ 3: 16,
1768
+ 4: 32,
1769
+ 5: 64,
1770
+ 6: 128,
1771
+ 7: 160,
1772
+ 8: 256,
1773
+ };
1774
+ static FheTypeEncryptionBitwidthsToId = {
1775
+ 2: 0,
1776
+ 8: 2,
1777
+ 16: 3,
1778
+ 32: 4,
1779
+ 64: 5,
1780
+ 128: 6,
1781
+ 160: 7,
1782
+ 256: 8,
1783
+ };
1784
+ static FheTypeIdToSolidityPrimitiveType = {
1785
+ 0: 'bool',
1786
+ 2: 'uint256',
1787
+ 3: 'uint256',
1788
+ 4: 'uint256',
1789
+ 5: 'uint256',
1790
+ 6: 'uint256',
1791
+ 7: 'address',
1792
+ 8: 'uint256',
1793
+ };
1794
+ static {
1795
+ Object.freeze(FhevmHandle.FheTypeIdToEncryptionBitwidths);
1796
+ Object.freeze(FhevmHandle.FheTypeEncryptionBitwidthsToId);
1797
+ }
1798
+ constructor(hash21, chainId, fheTypeId, version, computed, index) {
1799
+ if (BigInt(chainId) > MAX_UINT64) {
1800
+ // fhevm assumes chainID is only taking up to 8 bytes
1801
+ throw new Error('ChainId exceeds maximum allowed value (8 bytes)');
1802
+ }
1803
+ this._hash21 = hash21;
1804
+ this._chainId = chainId;
1805
+ this._fheTypeId = fheTypeId;
1806
+ this._version = version;
1807
+ this._computed = computed;
1808
+ if (index !== undefined) {
1809
+ this._index = index;
1810
+ }
1811
+ }
1812
+ get hash21() {
1813
+ return this._hash21;
1814
+ }
1815
+ get chainId() {
1816
+ return this._chainId;
1817
+ }
1818
+ get fheTypeId() {
1819
+ return this._fheTypeId;
1820
+ }
1821
+ get version() {
1822
+ return this._version;
1823
+ }
1824
+ get computed() {
1825
+ return this._computed;
1826
+ }
1827
+ get index() {
1828
+ return this._index;
1829
+ }
1830
+ static fromZKProof(params) {
1831
+ assertIsChecksummedAddress(params.aclAddress);
1832
+ assertIsUint64(params.chainId);
1833
+ assertIsUint8(params.ciphertextVersion);
1834
+ let fheTypeIds;
1835
+ if (params.fheTypeIds !== undefined) {
1836
+ fheTypeIds = params.fheTypeIds;
1837
+ }
1838
+ else if (params.fheTypeEncryptionBitwidths !== undefined) {
1839
+ fheTypeIds = params.fheTypeEncryptionBitwidths.map((w) => FhevmHandle.FheTypeEncryptionBitwidthsToId[w]);
1840
+ }
1841
+ else {
1842
+ throw new InternalError({
1843
+ message: 'createInputHandles requires either fheTypeIds or fheTypeEncryptionBitwidths',
1844
+ });
1845
+ }
1846
+ assertIsUint8(fheTypeIds.length);
1847
+ let ciphertextWithZKProof;
1848
+ if (typeof params.ciphertextWithZKProof === 'string') {
1849
+ ciphertextWithZKProof = hexToBytes(params.ciphertextWithZKProof);
1850
+ }
1851
+ else if (params.ciphertextWithZKProof instanceof Uint8Array) {
1852
+ ciphertextWithZKProof = params.ciphertextWithZKProof;
1853
+ }
1854
+ else {
1855
+ throw new InternalError({
1856
+ message: 'Invalid ciphertextWithZKProof argument',
1857
+ });
1858
+ }
1859
+ if (ciphertextWithZKProof.length === 0) {
1860
+ throw new InternalError({
1861
+ message: 'Invalid ciphertextWithZKProof argument',
1862
+ });
1863
+ }
1864
+ const encoder = new TextEncoder();
1865
+ const domainSepBytes = encoder.encode(FhevmHandle.RAW_CT_HASH_DOMAIN_SEPARATOR);
1866
+ const blobHashBytes32Hex = ethers.keccak256(concatBytes(domainSepBytes, ciphertextWithZKProof));
1867
+ const handles = [];
1868
+ for (let i = 0; i < fheTypeIds.length; ++i) {
1869
+ const hash21 = FhevmHandle._computeInputHash21(hexToBytes(blobHashBytes32Hex), params.aclAddress, params.chainId, i);
1870
+ handles.push(new FhevmHandle(hash21, params.chainId, fheTypeIds[i], params.ciphertextVersion, false, i));
1871
+ }
1872
+ return handles;
1873
+ }
1874
+ /**
1875
+ * blobHashBytes32 = keccak256(ciphertextWithZKProof)
1876
+ */
1877
+ static _computeInputHash21(blobHashBytes32, aclAddress, chainId, index) {
1878
+ /*
1879
+ https://github.com/zama-ai/fhevm/blob/8ffbd5906ab3d57af178e049930e3fc065c9d4b3/coprocessor/fhevm-engine/zkproof-worker/src/verifier.rs#L431C7-L431C8
1880
+
1881
+ handle_hash = Bytes("ZK-w_hdl") + blobHash 32 Bytes + index 1 Byte + aclAddress 20 Bytes + chainId 32 bytes
1882
+ ===========================================================================================================
1883
+
1884
+ const HANDLE_HASH_DOMAIN_SEPARATOR: [u8; 8] = *b"ZK-w_hdl";
1885
+
1886
+ let mut handle_hash = Keccak256::new();
1887
+ handle_hash.update(HANDLE_HASH_DOMAIN_SEPARATOR);
1888
+ handle_hash.update(blob_hash);
1889
+ handle_hash.update([ct_idx as u8]);
1890
+ handle_hash.update(
1891
+ Address::from_str(&aux_data.acl_contract_address)
1892
+ .expect("valid acl_contract_address")
1893
+ .into_array(),
1894
+ );
1895
+ handle_hash.update(chain_id_bytes);
1896
+ let mut handle = handle_hash.finalize().to_vec();
1897
+ assert_eq!(handle.len(), 32);
1898
+
1899
+ */
1900
+ assertIsBytes32(blobHashBytes32);
1901
+ assertIsChecksummedAddress(aclAddress);
1902
+ assertIsUint8(index);
1903
+ assertIsUint64(chainId);
1904
+ const encryptionIndexByte1 = new Uint8Array([index]);
1905
+ const aclContractAddressBytes20 = checksummedAddressToBytes20(aclAddress);
1906
+ const chainIdBytes32 = uint32ToBytes32(chainId);
1907
+ const encoder = new TextEncoder();
1908
+ const domainSepBytes = encoder.encode(FhevmHandle.HANDLE_HASH_DOMAIN_SEPARATOR);
1909
+ return ethers.keccak256(concatBytes(domainSepBytes, blobHashBytes32, encryptionIndexByte1, aclContractAddressBytes20, chainIdBytes32));
1910
+ }
1911
+ toBytes32() {
1912
+ assertRelayer((this._index === undefined && this._computed) ||
1913
+ (this._index !== undefined && this._index < 255 && !this._computed));
1914
+ const chainId32Bytes = uint32ToBytes32(this._chainId);
1915
+ const chainId8Bytes = chainId32Bytes.subarray(24, 32);
1916
+ const handleHash = hexToBytes(this._hash21);
1917
+ const handleBytes32AsBytes = new Uint8Array(32);
1918
+ handleBytes32AsBytes.set(handleHash, 0);
1919
+ handleBytes32AsBytes[21] = this._index === undefined ? 255 : this._index;
1920
+ handleBytes32AsBytes.set(chainId8Bytes, 22);
1921
+ handleBytes32AsBytes[30] = this._fheTypeId;
1922
+ handleBytes32AsBytes[31] = this._version;
1923
+ return handleBytes32AsBytes;
1924
+ }
1925
+ toBytes32Hex() {
1926
+ return bytesToHex(this.toBytes32());
1927
+ }
1928
+ static checkHandleHex(handle) {
1929
+ if (!isBytes32Hex(handle)) {
1930
+ throw new FhevmHandleError({ handle });
1931
+ }
1932
+ }
1933
+ static isFheTypeId(value) {
1934
+ switch (value) {
1935
+ case 0:
1936
+ // 1: euint4 is deprecated
1937
+ case 2:
1938
+ case 3:
1939
+ case 4:
1940
+ case 5:
1941
+ case 6:
1942
+ case 7:
1943
+ case 8:
1944
+ return true;
1945
+ default:
1946
+ return false;
1947
+ }
1948
+ }
1949
+ static getFheTypeId(handle) {
1950
+ if (!isBytes32Hex(handle)) {
1951
+ throw new FhevmHandleError({ handle });
1952
+ }
1953
+ const hexPair = handle.slice(-4, -2).toLowerCase();
1954
+ const typeDiscriminant = parseInt(hexPair, 16);
1955
+ if (!FhevmHandle.isFheTypeId(typeDiscriminant)) {
1956
+ throw new FhevmHandleError({
1957
+ handle,
1958
+ message: `FHEVM Handle "${handle}" is invalid. Unknown FheType: ${typeDiscriminant}`,
1959
+ });
1960
+ }
1961
+ return typeDiscriminant;
1962
+ }
1963
+ }
1964
+
1965
+ // Add type checking
1966
+ const getAddress = (value) => ethers.getAddress(value);
1967
+ const currentCiphertextVersion = () => {
1968
+ return 0;
1969
+ };
1970
+ function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold) {
1971
+ const addressMap = new Map();
1972
+ recoveredAddresses.forEach((address, index) => {
1973
+ if (addressMap.has(address)) {
1974
+ const duplicateValue = address;
1975
+ throw new Error(`Duplicate coprocessor signer address found: ${duplicateValue} appears multiple times in recovered addresses`);
1976
+ }
1977
+ addressMap.set(address, index);
1978
+ });
1979
+ for (const address of recoveredAddresses) {
1980
+ if (!coprocessorSigners.includes(address)) {
1981
+ throw new Error(`Invalid address found: ${address} is not in the list of coprocessor signers`);
1982
+ }
1983
+ }
1984
+ return recoveredAddresses.length >= threshold;
1985
+ }
1986
+ function isFhevmRelayerInputProofResponse(json) {
1987
+ const response = json;
1988
+ // const response = json.response as unknown;
1989
+ if (typeof response !== 'object' || response === null) {
1990
+ return false;
1991
+ }
1992
+ if (!('handles' in response && Array.isArray(response.handles))) {
1993
+ return false;
1994
+ }
1995
+ if (!('signatures' in response && Array.isArray(response.signatures))) {
1996
+ return false;
1997
+ }
1998
+ return (response.signatures.every((s) => typeof s === 'string') &&
1999
+ response.handles.every((h) => typeof h === 'string'));
2000
+ }
2001
+ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerProvider, tfheCompactPublicKey, publicParams, coprocessorSigners, thresholdCoprocessorSigners, defaultOptions) => (contractAddress, userAddress) => {
2002
+ if (!ethers.isAddress(contractAddress)) {
2003
+ throw new Error('Contract address is not a valid address.');
2004
+ }
2005
+ if (!ethers.isAddress(userAddress)) {
2006
+ throw new Error('User address is not a valid address.');
2007
+ }
2008
+ const input = createEncryptedInput({
2009
+ aclContractAddress,
2010
+ chainId,
2011
+ tfheCompactPublicKey,
2012
+ publicParams,
2013
+ contractAddress,
2014
+ userAddress,
2015
+ });
2016
+ return {
2017
+ _input: input,
2018
+ addBool(value) {
2019
+ input.addBool(value);
2020
+ return this;
2021
+ },
2022
+ add8(value) {
2023
+ input.add8(value);
2024
+ return this;
2025
+ },
2026
+ add16(value) {
2027
+ input.add16(value);
2028
+ return this;
2029
+ },
2030
+ add32(value) {
2031
+ input.add32(value);
2032
+ return this;
2033
+ },
2034
+ add64(value) {
2035
+ input.add64(value);
2036
+ return this;
2037
+ },
2038
+ add128(value) {
2039
+ input.add128(value);
2040
+ return this;
2041
+ },
2042
+ add256(value) {
2043
+ input.add256(value);
2044
+ return this;
2045
+ },
2046
+ addAddress(value) {
2047
+ input.addAddress(value);
2048
+ return this;
2049
+ },
2050
+ getBits() {
2051
+ return input.getBits();
2052
+ },
2053
+ getCiphertextWithInputVerification() {
2054
+ return input.encrypt();
2055
+ },
2056
+ encrypt: async (options) => {
2057
+ const extraData = '0x00';
2058
+ const bits = input.getBits();
2059
+ const ciphertext = input.encrypt();
2060
+ const payload = {
2061
+ contractAddress: getAddress(contractAddress),
2062
+ userAddress: getAddress(userAddress),
2063
+ ciphertextWithInputVerification: toHexString(ciphertext),
2064
+ contractChainId: ('0x' + chainId.toString(16)),
2065
+ extraData,
2066
+ };
2067
+ const json = await relayerProvider.fetchPostInputProof(payload, {
2068
+ ...defaultOptions,
2069
+ ...options,
2070
+ });
2071
+ if (!isFhevmRelayerInputProofResponse(json)) {
1049
2072
  throwRelayerInternalError('INPUT_PROOF', json);
1050
2073
  }
1051
- const handles = computeHandles(ciphertext, bits, aclContractAddress, chainId, currentCiphertextVersion());
2074
+ const fhevmHandles = FhevmHandle.fromZKProof({
2075
+ ciphertextWithZKProof: ciphertext,
2076
+ chainId,
2077
+ aclAddress: aclContractAddress,
2078
+ ciphertextVersion: currentCiphertextVersion(),
2079
+ fheTypeEncryptionBitwidths: bits,
2080
+ });
2081
+ const handles = fhevmHandles.map((h) => h.toBytes32());
2082
+ const result = json;
1052
2083
  // Note that the hex strings returned by the relayer do have have the 0x prefix
1053
- if (json.response.handles && json.response.handles.length > 0) {
1054
- const responseHandles = json.response.handles.map(fromHexString);
2084
+ if (result.handles && result.handles.length > 0) {
2085
+ const responseHandles = result.handles.map(hexToBytes);
1055
2086
  if (handles.length != responseHandles.length) {
1056
2087
  throw new Error(`Incorrect Handles list sizes: (expected) ${handles.length} != ${responseHandles.length} (received)`);
1057
2088
  }
@@ -1065,7 +2096,7 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
1065
2096
  }
1066
2097
  }
1067
2098
  }
1068
- const signatures = json.response.signatures;
2099
+ const signatures = result.signatures;
1069
2100
  // verify signatures for inputs:
1070
2101
  const domain = {
1071
2102
  name: 'InputVerification',
@@ -1108,12 +2139,27 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
1108
2139
  inputProof += extraData.slice(2);
1109
2140
  return {
1110
2141
  handles,
1111
- inputProof: fromHexString(inputProof),
2142
+ inputProof: hexToBytes(inputProof),
1112
2143
  };
1113
2144
  },
1114
2145
  };
1115
2146
  };
1116
2147
 
2148
+ function ensureError(e) {
2149
+ if (e instanceof Error) {
2150
+ return e;
2151
+ }
2152
+ const message = e.message ?? 'Non-Error value caught in exception handler';
2153
+ const name = e.name ?? 'ErrorWrapper';
2154
+ const cause = e.cause ?? e;
2155
+ const err = new Error(message, { cause });
2156
+ err.name = name;
2157
+ return err;
2158
+ }
2159
+ function assertNever(value, message) {
2160
+ throw new InternalError({ message });
2161
+ }
2162
+
1117
2163
  const aclABI = [
1118
2164
  'function isAllowedForDecryption(bytes32 handle) view returns (bool)',
1119
2165
  ];
@@ -1139,7 +2185,7 @@ function abiEncodeClearValues(clearValues) {
1139
2185
  const abiValues = [];
1140
2186
  for (let i = 0; i < handlesBytes32Hex.length; ++i) {
1141
2187
  const handle = handlesBytes32Hex[i];
1142
- const handleType = getHandleType(handle);
2188
+ const handleType = FhevmHandle.getFheTypeId(handle);
1143
2189
  let clearTextValue = clearValues[handle];
1144
2190
  if (typeof clearTextValue === 'boolean') {
1145
2191
  clearTextValue = clearTextValue ? '0x01' : '0x00';
@@ -1176,7 +2222,7 @@ function abiEncodeClearValues(clearValues) {
1176
2222
  break;
1177
2223
  }
1178
2224
  default: {
1179
- throw new Error(`Unsupported Fhevm primitive type id: ${handleType}`);
2225
+ assertNever(handleType, `Unsupported Fhevm primitive type id: ${handleType}`);
1180
2226
  }
1181
2227
  }
1182
2228
  }
@@ -1201,21 +2247,11 @@ function buildDecryptionProof(kmsSignatures, extraData) {
1201
2247
  ]);
1202
2248
  return decryptionProof;
1203
2249
  }
1204
- const CiphertextType = {
1205
- 0: 'bool',
1206
- 2: 'uint256',
1207
- 3: 'uint256',
1208
- 4: 'uint256',
1209
- 5: 'uint256',
1210
- 6: 'uint256',
1211
- 7: 'address',
1212
- 8: 'uint256',
1213
- };
1214
2250
  function deserializeClearValues(handles, decryptedResult) {
1215
2251
  let typesList = [];
1216
2252
  for (const handle of handles) {
1217
- const hexPair = handle.slice(-4, -2).toLowerCase();
1218
- const typeDiscriminant = parseInt(hexPair, 16);
2253
+ const typeDiscriminant = FhevmHandle.getFheTypeId(handle);
2254
+ assertRelayer(FhevmHandle.isFheTypeId(typeDiscriminant));
1219
2255
  typesList.push(typeDiscriminant);
1220
2256
  }
1221
2257
  const restoredEncoded = '0x' +
@@ -1223,7 +2259,7 @@ function deserializeClearValues(handles, decryptedResult) {
1223
2259
  decryptedResult.slice(2) +
1224
2260
  '00'.repeat(32); // dummy empty bytes[] length (ignored)
1225
2261
  const abiTypes = typesList.map((t) => {
1226
- const abiType = CiphertextType[t]; // all types are valid because this was supposedly checked already inside the `checkEncryptedBits` function
2262
+ const abiType = FhevmHandle.FheTypeIdToSolidityPrimitiveType[t]; // all types are valid because this was supposedly checked already inside the `checkEncryptedBits` function
1227
2263
  return abiType;
1228
2264
  });
1229
2265
  const coder = new ethers.AbiCoder();
@@ -1234,14 +2270,14 @@ function deserializeClearValues(handles, decryptedResult) {
1234
2270
  handles.forEach((handle, idx) => (results[handle] = rawValues[idx]));
1235
2271
  return results;
1236
2272
  }
1237
- const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider, instanceOptions) => async (_handles, options) => {
2273
+ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress, relayerProvider, provider, defaultOptions) => async (_handles, options) => {
1238
2274
  const extraData = '0x00';
1239
2275
  const acl = new ethers.ethers.Contract(aclContractAddress, aclABI, provider);
1240
2276
  let handles;
1241
2277
  try {
1242
2278
  handles = await Promise.all(_handles.map(async (_handle) => {
1243
2279
  const handle = typeof _handle === 'string'
1244
- ? toHexString(fromHexString(_handle), true)
2280
+ ? toHexString(hexToBytes(_handle), true)
1245
2281
  : toHexString(_handle, true);
1246
2282
  const isAllowedForDecryption = await acl.isAllowedForDecryption(handle);
1247
2283
  if (!isAllowedForDecryption) {
@@ -1259,7 +2295,10 @@ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, veri
1259
2295
  ciphertextHandles: handles,
1260
2296
  extraData,
1261
2297
  };
1262
- const json = await fetchRelayerJsonRpcPost('PUBLIC_DECRYPT', `${relayerUrl}/v1/public-decrypt`, payloadForRequest, options ?? instanceOptions);
2298
+ const json = await relayerProvider.fetchPostPublicDecrypt(payloadForRequest, {
2299
+ ...defaultOptions,
2300
+ ...options,
2301
+ });
1263
2302
  // verify signatures on decryption:
1264
2303
  const domain = {
1265
2304
  name: 'Decryption',
@@ -1274,10 +2313,11 @@ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, veri
1274
2313
  { name: 'extraData', type: 'bytes' },
1275
2314
  ],
1276
2315
  };
1277
- const result = json.response[0];
1278
- const decryptedResult = ensure0x(result.decrypted_value);
2316
+ //const result = json.response[0];
2317
+ const result = json;
2318
+ const decryptedResult = ensure0x(result.decryptedValue);
1279
2319
  const kmsSignatures = result.signatures.map(ensure0x);
1280
- // TODO result.extra_data (RelayerPublicDecryptJsonResponse)
2320
+ // TODO result.extraData (RelayerPublicDecryptJsonResponse)
1281
2321
  const signedExtraData = '0x';
1282
2322
  const recoveredAddresses = kmsSignatures.map((kmsSignature) => {
1283
2323
  const recoveredAddress = ethers.ethers.verifyTypedData(domain, types, { ctHandles: handles, decryptedResult, extraData: signedExtraData }, kmsSignature);
@@ -1401,89 +2441,2645 @@ const generateKeypair = () => {
1401
2441
  };
1402
2442
  };
1403
2443
 
1404
- global.fetch = fetchRetry(global.fetch, { retries: 5, retryDelay: 500 });
1405
- const MainnetConfig = {
1406
- aclContractAddress: '0xcA2E8f1F656CD25C01F05d0b243Ab1ecd4a8ffb6',
1407
- kmsContractAddress: '0x77627828a55156b04Ac0DC0eb30467f1a552BB03',
1408
- inputVerifierContractAddress: '0xCe0FC2e05CFff1B719EFF7169f7D80Af770c8EA2',
1409
- verifyingContractAddressDecryption: '0x0f6024a97684f7d90ddb0fAAD79cB15F2C888D24',
1410
- verifyingContractAddressInputVerification: '0xcB1bB072f38bdAF0F328CdEf1Fc6eDa1DF029287',
1411
- chainId: 1,
1412
- gatewayChainId: 261131,
1413
- network: 'https://ethereum-rpc.publicnode.com',
1414
- relayerUrl: 'https://relayer.mainnet.zama.org',
1415
- };
1416
- Object.freeze(MainnetConfig);
1417
- const SepoliaConfig = {
1418
- // ACL_CONTRACT_ADDRESS (FHEVM Host chain)
1419
- aclContractAddress: '0xf0Ffdc93b7E186bC2f8CB3dAA75D86d1930A433D',
1420
- // KMS_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
1421
- kmsContractAddress: '0xbE0E383937d564D7FF0BC3b46c51f0bF8d5C311A',
1422
- // INPUT_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
1423
- inputVerifierContractAddress: '0xBBC1fFCdc7C316aAAd72E807D9b0272BE8F84DA0',
1424
- // DECRYPTION_ADDRESS (Gateway chain)
1425
- verifyingContractAddressDecryption: '0x5D8BD78e2ea6bbE41f26dFe9fdaEAa349e077478',
1426
- // INPUT_VERIFICATION_ADDRESS (Gateway chain)
1427
- verifyingContractAddressInputVerification: '0x483b9dE06E4E4C7D35CCf5837A1668487406D955',
1428
- // FHEVM Host chain id
1429
- chainId: 11155111,
1430
- // Gateway chain id
1431
- gatewayChainId: 10901,
1432
- // Optional RPC provider to host chain
1433
- network: 'https://ethereum-sepolia-rpc.publicnode.com',
1434
- // Relayer URL
1435
- relayerUrl: 'https://relayer.testnet.zama.org',
1436
- };
1437
- const createInstance = async (config) => {
1438
- const { verifyingContractAddressDecryption, verifyingContractAddressInputVerification, publicKey, kmsContractAddress, aclContractAddress, gatewayChainId, auth, } = config;
1439
- if (!kmsContractAddress || !ethers.isAddress(kmsContractAddress)) {
1440
- throw new Error('KMS contract address is not valid or empty');
2444
+ class AbstractRelayerProvider {
2445
+ _relayerUrl;
2446
+ constructor(relayerUrl) {
2447
+ this._relayerUrl = relayerUrl;
1441
2448
  }
1442
- if (!verifyingContractAddressDecryption ||
1443
- !ethers.isAddress(verifyingContractAddressDecryption)) {
1444
- throw new Error('Verifying contract for Decryption address is not valid or empty');
2449
+ get url() {
2450
+ return this._relayerUrl;
1445
2451
  }
1446
- if (!verifyingContractAddressInputVerification ||
1447
- !ethers.isAddress(verifyingContractAddressInputVerification)) {
1448
- throw new Error('Verifying contract for InputVerification address is not valid or empty');
2452
+ get keyUrl() {
2453
+ return `${this.url}/keyurl`;
1449
2454
  }
1450
- if (!aclContractAddress || !ethers.isAddress(aclContractAddress)) {
1451
- throw new Error('ACL contract address is not valid or empty');
2455
+ get inputProof() {
2456
+ return `${this.url}/input-proof`;
1452
2457
  }
1453
- if (publicKey && !(publicKey.data instanceof Uint8Array))
1454
- throw new Error('publicKey must be a Uint8Array');
1455
- const provider = getProvider(config);
1456
- if (!provider) {
1457
- throw new Error('No network has been provided!');
2458
+ get publicDecrypt() {
2459
+ return `${this.url}/public-decrypt`;
1458
2460
  }
1459
- const chainId = await getChainId(provider, config);
1460
- const publicKeyData = await getTfheCompactPublicKey(config);
1461
- const publicParamsData = await getPublicParams(config);
1462
- const kmsSigners = await getKMSSigners(provider, config);
1463
- const thresholdKMSSigners = await getKMSSignersThreshold(provider, config);
1464
- const coprocessorSigners = await getCoprocessorSigners(provider, config);
1465
- const thresholdCoprocessorSigners = await getCoprocessorSignersThreshold(provider, config);
1466
- return {
1467
- createEncryptedInput: createRelayerEncryptedInput(aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, cleanURL(config.relayerUrl), publicKeyData.publicKey, publicParamsData, coprocessorSigners, thresholdCoprocessorSigners, auth && { auth }),
1468
- generateKeypair,
1469
- createEIP712: createEIP712(verifyingContractAddressDecryption, chainId),
1470
- publicDecrypt: publicDecryptRequest(kmsSigners, thresholdKMSSigners, gatewayChainId, verifyingContractAddressDecryption, aclContractAddress, cleanURL(config.relayerUrl), provider, auth && { auth }),
1471
- userDecrypt: userDecryptRequest(kmsSigners, gatewayChainId, chainId, verifyingContractAddressDecryption, aclContractAddress, cleanURL(config.relayerUrl), provider, auth && { auth }),
1472
- getPublicKey: () => publicKeyData.publicKey
1473
- ? {
1474
- publicKey: publicKeyData.publicKey.safe_serialize(SERIALIZED_SIZE_LIMIT_PK),
1475
- publicKeyId: publicKeyData.publicKeyId,
1476
- }
1477
- : null,
1478
- getPublicParams: (bits) => {
1479
- if (publicParamsData[bits]) {
1480
- return {
1481
- publicParams: publicParamsData[bits].publicParams.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS),
1482
- publicParamsId: publicParamsData[bits].publicParamsId,
1483
- };
1484
- }
1485
- return null;
1486
- },
2461
+ get userDecrypt() {
2462
+ return `${this.url}/user-decrypt`;
2463
+ }
2464
+ }
2465
+ function assertIsRelayerInputProofResult(value, name) {
2466
+ assertRecordBytes32HexArrayProperty(value, 'handles', name);
2467
+ assertRecordBytes65HexArrayProperty(value, 'signatures', name);
2468
+ }
2469
+ function assertIsRelayerPublicDecryptResult(value, name) {
2470
+ assertRecordBytesHexNo0xArrayProperty(value, 'signatures', name);
2471
+ assertRecordStringProperty(value, 'decryptedValue', name);
2472
+ assertRecordBytesHexProperty(value, 'extraData', name);
2473
+ }
2474
+ function assertIsRelayerUserDecryptResult(value, name) {
2475
+ if (!Array.isArray(value)) {
2476
+ throw InvalidPropertyError.invalidObject({
2477
+ objName: name,
2478
+ expectedType: 'Array',
2479
+ type: typeof value,
2480
+ });
2481
+ }
2482
+ for (let i = 0; i < value.length; ++i) {
2483
+ // Missing extraData
2484
+ assertRecordBytesHexNo0xProperty(value[i], 'payload', `${name}[i]`);
2485
+ assertRecordBytesHexNo0xProperty(value[i], 'signature', `${name}[i]`);
2486
+ }
2487
+ }
2488
+
2489
+ class RelayerV1Provider extends AbstractRelayerProvider {
2490
+ constructor(relayerUrl) {
2491
+ super(relayerUrl);
2492
+ }
2493
+ get version() {
2494
+ return 1;
2495
+ }
2496
+ async fetchGetKeyUrl() {
2497
+ const response = await fetchRelayerV1Get('KEY_URL', this.keyUrl);
2498
+ return response;
2499
+ }
2500
+ async fetchPostInputProof(payload, options) {
2501
+ /*
2502
+ Expected v1 format:
2503
+ ===================
2504
+ {
2505
+ "response": {
2506
+ "handles": [
2507
+ "0xb0b1af7734450c2b7d944571af7e5b438cc62a2a26000000000000aa36a70400"
2508
+ ],
2509
+ "signatures": [
2510
+ "0x70dcb78534f05c4448d3441b4704d3ff4a8478af56a3464497533c2e3c476d77165b09028847f0c3ed4b342b1e8b4252a93b521a3d8d07b724bcff740383e1361b"
2511
+ ]
2512
+ }
2513
+ }
2514
+ */
2515
+ const json = await fetchRelayerV1Post('INPUT_PROOF', this.inputProof, payload, options);
2516
+ assertIsRelayerInputProofResult(json.response, 'fetchPostInputProof()');
2517
+ return json.response;
2518
+ }
2519
+ async fetchPostPublicDecrypt(payload, options) {
2520
+ const json = await fetchRelayerV1Post('PUBLIC_DECRYPT', this.publicDecrypt, payload, options);
2521
+ const response = json.response[0];
2522
+ const result = {
2523
+ signatures: response.signatures,
2524
+ decryptedValue: response.decrypted_value,
2525
+ extraData: '0x',
2526
+ };
2527
+ assertIsRelayerPublicDecryptResult(result, 'fetchPostPublicDecrypt()');
2528
+ return result;
2529
+ }
2530
+ async fetchPostUserDecrypt(payload, options) {
2531
+ const json = await fetchRelayerV1Post('USER_DECRYPT', this.userDecrypt, payload, options);
2532
+ assertIsRelayerUserDecryptResult(json.response, 'RelayerUserDecryptResult()');
2533
+ return json.response;
2534
+ }
2535
+ }
2536
+
2537
+ class RelayerV2ProviderError extends RelayerErrorBase {
2538
+ _operation;
2539
+ constructor(params) {
2540
+ super(params);
2541
+ this._operation = params.operation;
2542
+ }
2543
+ get operation() {
2544
+ return this._operation;
2545
+ }
2546
+ }
2547
+ class RelayerV2GetKeyUrlError extends RelayerV2ProviderError {
2548
+ constructor({ cause }) {
2549
+ super({
2550
+ message: `Invalid relayer response.`,
2551
+ name: 'RelayerV2GetKeyUrlError',
2552
+ operation: 'KEY_URL',
2553
+ cause,
2554
+ });
2555
+ }
2556
+ }
2557
+ class RelayerV2GetKeyUrlInvalidResponseError extends RelayerV2GetKeyUrlError {
2558
+ constructor({ cause }) {
2559
+ super({ cause });
2560
+ }
2561
+ }
2562
+
2563
+ function assertIsRelayerV2ApiError400NoDetails(value, name) {
2564
+ assertRecordStringProperty(value, 'label', name);
2565
+ if (!(value.label === 'malformed_json' ||
2566
+ value.label === 'request_error' ||
2567
+ value.label === 'not_ready_for_decryption')) {
2568
+ throw new InvalidPropertyError({
2569
+ objName: name,
2570
+ property: 'label',
2571
+ expectedType: 'string',
2572
+ expectedValue: [
2573
+ 'malformed_json',
2574
+ 'request_error',
2575
+ 'not_ready_for_decryption',
2576
+ ],
2577
+ type: typeof value.label, // === "string"
2578
+ value: value.label,
2579
+ });
2580
+ }
2581
+ assertRecordStringProperty(value, 'message', name);
2582
+ }
2583
+
2584
+ /*
2585
+ type RelayerV2ApiError400WithDetails = {
2586
+ label: 'missing_fields' | 'validation_failed';
2587
+ message: string;
2588
+ details: Array<RelayerV2ErrorDetail>;
2589
+ };
2590
+ type RelayerV2ErrorDetail = {
2591
+ field: string;
2592
+ issue: string;
2593
+ }
2594
+ */
2595
+ function assertIsRelayerV2ApiError400WithDetails(value, name) {
2596
+ assertRecordStringProperty(value, 'label', name);
2597
+ if (!(value.label === 'missing_fields' || value.label === 'validation_failed')) {
2598
+ throw new InvalidPropertyError({
2599
+ objName: name,
2600
+ property: 'label',
2601
+ expectedType: 'string',
2602
+ expectedValue: ['missing_fields', 'validation_failed'],
2603
+ type: typeof value.label,
2604
+ value: value.label,
2605
+ });
2606
+ }
2607
+ assertRecordStringProperty(value, 'message', name);
2608
+ assertRecordArrayProperty(value, 'details', name);
2609
+ const arr = value.details;
2610
+ for (let i = 0; i < arr.length; ++i) {
2611
+ const detail = arr[i];
2612
+ assertRecordStringProperty(detail, 'field', `${name}.details[${i}]`);
2613
+ assertRecordStringProperty(detail, 'issue', `${name}.details[${i}]`);
2614
+ }
2615
+ }
2616
+
2617
+ /*
2618
+ export type RelayerV2ApiError429 = {
2619
+ label: 'rate_limited';
2620
+ message: string;
2621
+ };
2622
+ */
2623
+ function assertIsRelayerV2ApiError429(value, name) {
2624
+ assertRecordStringProperty(value, 'label', name, 'rate_limited');
2625
+ assertRecordStringProperty(value, 'message', name);
2626
+ }
2627
+
2628
+ /*
2629
+ export type RelayerV2ApiError500 = {
2630
+ label: 'internal_server_error';
2631
+ message: string;
2632
+ };
2633
+ */
2634
+ function assertIsRelayerV2ApiError500(value, name) {
2635
+ assertRecordStringProperty(value, 'label', name, 'internal_server_error');
2636
+ assertRecordStringProperty(value, 'message', name);
2637
+ }
2638
+
2639
+ /*
2640
+ export type RelayerV2ApiError404 = {
2641
+ label: 'not_found';
2642
+ message: string;
2643
+ };
2644
+ */
2645
+ function assertIsRelayerV2ApiError404(value, name) {
2646
+ assertRecordStringProperty(value, 'label', name, 'not_found');
2647
+ assertRecordStringProperty(value, 'message', name);
2648
+ }
2649
+
2650
+ /*
2651
+ export type RelayerV2ApiError503 = {
2652
+ label: "protocol_paused" | "gateway_not_reachable";
2653
+ message: string;
2654
+ };
2655
+ */
2656
+ function assertIsRelayerV2ApiError503(value, name) {
2657
+ assertRecordStringProperty(value, 'label', name, [
2658
+ 'protocol_paused',
2659
+ 'gateway_not_reachable',
2660
+ ]);
2661
+ assertRecordStringProperty(value, 'message', name);
2662
+ }
2663
+
2664
+ /*
2665
+ export type RelayerV2ApiError504 = {
2666
+ label: 'readiness_check_timedout' | 'response_timedout';
2667
+ message: string;
2668
+ };
2669
+ */
2670
+ function assertIsRelayerV2ApiError504(value, name) {
2671
+ assertRecordStringProperty(value, 'label', name, [
2672
+ 'readiness_check_timedout',
2673
+ 'response_timedout',
2674
+ ]);
2675
+ assertRecordStringProperty(value, 'message', name);
2676
+ }
2677
+
2678
+ function assertIsRelayerV2ResponseFailed(value, name) {
2679
+ assertRecordStringProperty(value, 'status', name, 'failed');
2680
+ assertNonNullableRecordProperty(value, 'error', name);
2681
+ assertIsRelayerV2ApiError(value.error, `${name}.error`);
2682
+ }
2683
+ function assertIsRelayerV2ApiError(value, name) {
2684
+ assertRecordStringProperty(value, 'label', name);
2685
+ // 400
2686
+ if (value.label === 'malformed_json' ||
2687
+ value.label === 'request_error' ||
2688
+ value.label === 'not_ready_for_decryption') {
2689
+ assertIsRelayerV2ApiError400NoDetails(value, name);
2690
+ }
2691
+ // 400 (with details)
2692
+ else if (value.label === 'missing_fields' ||
2693
+ value.label === 'validation_failed') {
2694
+ assertIsRelayerV2ApiError400WithDetails(value, name);
2695
+ }
2696
+ // 404
2697
+ else if (value.label === 'not_found') {
2698
+ assertIsRelayerV2ApiError404(value, name);
2699
+ }
2700
+ // 429
2701
+ else if (value.label === 'rate_limited') {
2702
+ assertIsRelayerV2ApiError429(value, name);
2703
+ }
2704
+ // 500
2705
+ else if (value.label === 'internal_server_error') {
2706
+ assertIsRelayerV2ApiError500(value, name);
2707
+ }
2708
+ // 503
2709
+ else if (value.label === 'protocol_paused' ||
2710
+ value.label === 'gateway_not_reachable') {
2711
+ assertIsRelayerV2ApiError503(value, name);
2712
+ }
2713
+ // 504
2714
+ else if (value.label === 'readiness_check_timedout' ||
2715
+ value.label === 'response_timedout') {
2716
+ assertIsRelayerV2ApiError504(value, name);
2717
+ }
2718
+ // Unsupported
2719
+ else {
2720
+ throw new InvalidPropertyError({
2721
+ objName: name,
2722
+ property: 'label',
2723
+ expectedType: 'string',
2724
+ expectedValue: [
2725
+ 'malformed_json',
2726
+ 'request_error',
2727
+ 'not_ready_for_decryption',
2728
+ 'missing_fields',
2729
+ 'validation_failed',
2730
+ 'rate_limited',
2731
+ 'internal_server_error',
2732
+ 'protocol_paused',
2733
+ 'gateway_not_reachable',
2734
+ 'readiness_check_timedout',
2735
+ 'response_timedout',
2736
+ ],
2737
+ type: typeof value.label,
2738
+ value: value.label,
2739
+ });
2740
+ }
2741
+ }
2742
+ ////////////////////////////////////////////////////////////////////////////////
2743
+ // 400
2744
+ ////////////////////////////////////////////////////////////////////////////////
2745
+ function assertIsRelayerV2ResponseFailedWithError400(value, name) {
2746
+ assertIsRelayerV2ResponseFailed(value, name);
2747
+ if (value.error.label === 'malformed_json' ||
2748
+ value.error.label === 'request_error' ||
2749
+ value.error.label === 'not_ready_for_decryption') {
2750
+ assertIsRelayerV2ApiError400NoDetails(value.error, `${name}.error`);
2751
+ }
2752
+ else if (value.error.label === 'missing_fields' ||
2753
+ value.error.label === 'validation_failed') {
2754
+ assertIsRelayerV2ApiError400WithDetails(value.error, `${name}.error`);
2755
+ }
2756
+ else {
2757
+ throw new InvalidPropertyError({
2758
+ objName: `${name}.error`,
2759
+ property: 'label',
2760
+ expectedType: 'string',
2761
+ expectedValue: [
2762
+ 'malformed_json',
2763
+ 'request_error',
2764
+ 'not_ready_for_decryption',
2765
+ 'missing_fields',
2766
+ 'validation_failed',
2767
+ ],
2768
+ type: typeof value.error.label,
2769
+ value: value.error.label,
2770
+ });
2771
+ }
2772
+ }
2773
+ ////////////////////////////////////////////////////////////////////////////////
2774
+ // 404
2775
+ ////////////////////////////////////////////////////////////////////////////////
2776
+ function assertIsRelayerV2ResponseFailedWithError404(value, name) {
2777
+ assertIsRelayerV2ResponseFailed(value, name);
2778
+ assertIsRelayerV2ApiError404(value.error, `${name}.error`);
2779
+ }
2780
+ ////////////////////////////////////////////////////////////////////////////////
2781
+ // 429
2782
+ ////////////////////////////////////////////////////////////////////////////////
2783
+ function assertIsRelayerV2ResponseFailedWithError429(value, name) {
2784
+ assertIsRelayerV2ResponseFailed(value, name);
2785
+ assertIsRelayerV2ApiError429(value.error, `${name}.error`);
2786
+ }
2787
+ ////////////////////////////////////////////////////////////////////////////////
2788
+ // 500
2789
+ ////////////////////////////////////////////////////////////////////////////////
2790
+ function assertIsRelayerV2ResponseFailedWithError500(value, name) {
2791
+ assertIsRelayerV2ResponseFailed(value, name);
2792
+ assertIsRelayerV2ApiError500(value.error, `${name}.error`);
2793
+ }
2794
+ ////////////////////////////////////////////////////////////////////////////////
2795
+ // 503
2796
+ ////////////////////////////////////////////////////////////////////////////////
2797
+ function assertIsRelayerV2ResponseFailedWithError503(value, name) {
2798
+ assertIsRelayerV2ResponseFailed(value, name);
2799
+ assertIsRelayerV2ApiError503(value.error, `${name}.error`);
2800
+ }
2801
+ ////////////////////////////////////////////////////////////////////////////////
2802
+ // 504
2803
+ ////////////////////////////////////////////////////////////////////////////////
2804
+ function assertIsRelayerV2ResponseFailedWithError504(value, name) {
2805
+ assertIsRelayerV2ResponseFailed(value, name);
2806
+ assertIsRelayerV2ApiError504(value.error, `${name}.error`);
2807
+ }
2808
+
2809
+ class RelayerV2FetchErrorBase extends RelayerErrorBase {
2810
+ _fetchMethod;
2811
+ _url;
2812
+ _jobId;
2813
+ _operation;
2814
+ _retryCount;
2815
+ _elapsed;
2816
+ _state;
2817
+ constructor(params) {
2818
+ super({
2819
+ ...params,
2820
+ name: params.name ?? 'RelayerV2FetchErrorBase',
2821
+ });
2822
+ this._fetchMethod = params.fetchMethod;
2823
+ this._url = params.url;
2824
+ this._operation = params.operation;
2825
+ this._elapsed = params.elapsed;
2826
+ this._retryCount = params.retryCount;
2827
+ this._state = params.state;
2828
+ this._jobId = params.jobId;
2829
+ }
2830
+ get url() {
2831
+ return this._url;
2832
+ }
2833
+ get operation() {
2834
+ return this._operation;
2835
+ }
2836
+ get fetchMethod() {
2837
+ return this._fetchMethod;
2838
+ }
2839
+ get jobId() {
2840
+ return this._jobId;
2841
+ }
2842
+ get retryCount() {
2843
+ return this._retryCount;
2844
+ }
2845
+ get elapsed() {
2846
+ return this._elapsed;
2847
+ }
2848
+ get state() {
2849
+ return this._state;
2850
+ }
2851
+ get isAbort() {
2852
+ // AbortError is not an instance of Error!
2853
+ return this.cause ? this.cause.name === 'AbortError' : false;
2854
+ }
2855
+ }
2856
+
2857
+ class RelayerV2ResponseErrorBase extends RelayerV2FetchErrorBase {
2858
+ _status;
2859
+ constructor(params) {
2860
+ super({
2861
+ ...params,
2862
+ name: params.name ?? 'RelayerV2ResponseErrorBase',
2863
+ });
2864
+ this._status = params.status;
2865
+ }
2866
+ get status() {
2867
+ return this._status;
2868
+ }
2869
+ }
2870
+
2871
+ class RelayerV2ResponseInvalidBodyError extends RelayerV2ResponseErrorBase {
2872
+ constructor(params) {
2873
+ super({
2874
+ ...params,
2875
+ cause: ensureError(params.cause),
2876
+ name: 'RelayerV2ResponseInvalidBodyError',
2877
+ message: `fetchMethod: ${params.fetchMethod} status:${params.status} url:${params.url} operation:${params.operation}`,
2878
+ });
2879
+ }
2880
+ }
2881
+
2882
+ class RelayerV2ResponseStatusError extends RelayerV2ResponseErrorBase {
2883
+ constructor(params) {
2884
+ super({
2885
+ ...params,
2886
+ name: 'RelayerV2ResponseStatusError',
2887
+ message: `fetchMethod: ${params.fetchMethod} status:${params.status} url:${params.url} operation:${params.operation}`,
2888
+ });
2889
+ }
2890
+ }
2891
+
2892
+ function assertIsRelayerV2ResultInputProof(value, name) {
2893
+ assertRecordBooleanProperty(value, 'accepted', name);
2894
+ if (value.accepted) {
2895
+ assertIsRelayerV2ResultInputProofAccepted(value, name);
2896
+ }
2897
+ else {
2898
+ assertIsRelayerV2ResultInputProofRejected(value, name);
2899
+ }
2900
+ }
2901
+ /*
2902
+ type RelayerV2ResultInputProofAccepted = {
2903
+ accepted: true;
2904
+ extra_data: BytesHex;
2905
+ handles: Bytes32Hex[];
2906
+ signatures: BytesHex[];
2907
+ }
2908
+ */
2909
+ function assertIsRelayerV2ResultInputProofAccepted(value, name) {
2910
+ assertRecordBooleanProperty(value, 'accepted', name, true);
2911
+ assertRecordBytes32HexArrayProperty(value, 'handles', name);
2912
+ assertRecordBytesHexArrayProperty(value, 'signatures', name);
2913
+ assertRecordBytesHexProperty(value, 'extraData', name);
2914
+ }
2915
+ /*
2916
+ type RelayerV2ResultInputProofRejected = {
2917
+ accepted: false;
2918
+ extra_data: BytesHex;
2919
+ }
2920
+ */
2921
+ function assertIsRelayerV2ResultInputProofRejected(value, name) {
2922
+ assertRecordBooleanProperty(value, 'accepted', name, false);
2923
+ assertRecordBytesHexProperty(value, 'extraData', name);
2924
+ }
2925
+
2926
+ function assertIsRelayerV2GetResponseInputProofSucceeded(value, name) {
2927
+ assertNonNullableRecordProperty(value, 'result', name);
2928
+ assertRecordStringProperty(value, 'status', name, 'succeeded');
2929
+ assertRecordStringProperty(value, 'requestId', name);
2930
+ assertIsRelayerV2ResultInputProof(value.result, `${name}.result`);
2931
+ }
2932
+
2933
+ function assertIsRelayerV2ResultPublicDecrypt(value, name) {
2934
+ assertRecordBytesHexNo0xArrayProperty(value, 'signatures', name);
2935
+ assertRecordBytesHexNo0xProperty(value, 'decryptedValue', name);
2936
+ assertRecordBytesHexProperty(value, 'extraData', name);
2937
+ }
2938
+
2939
+ function assertIsRelayerV2GetResponsePublicDecryptSucceeded(value, name) {
2940
+ assertNonNullableRecordProperty(value, 'result', name);
2941
+ assertRecordStringProperty(value, 'status', name, 'succeeded');
2942
+ assertRecordStringProperty(value, 'requestId', name);
2943
+ assertIsRelayerV2ResultPublicDecrypt(value.result, `${name}.result`);
2944
+ }
2945
+
2946
+ /**
2947
+ * Assertion function that validates a value is a valid `RelayerV2ResultUserDecrypt` object.
2948
+ * Validates the structure returned from the relayer for user decryption operations.
2949
+ * Throws an `InvalidPropertyError` if validation fails.
2950
+ *
2951
+ * @param value - The value to validate (can be any type)
2952
+ * @param name - The name of the value being validated (used in error messages)
2953
+ * @throws {InvalidPropertyError} When any required property is missing or has an invalid format
2954
+ * @throws {never} No other errors are thrown
2955
+ */
2956
+ function assertIsRelayerV2ResultUserDecrypt(value, name) {
2957
+ assertRecordArrayProperty(value, 'result', name);
2958
+ for (let i = 0; i < value.result.length; ++i) {
2959
+ // Missing extraData
2960
+ assertRecordBytesHexNo0xProperty(value.result[i], 'payload', `${name}.result[${i}]`);
2961
+ assertRecordBytesHexNo0xProperty(value.result[i], 'signature', `${name}.result[${i}]`);
2962
+ }
2963
+ }
2964
+
2965
+ function assertIsRelayerV2GetResponseUserDecryptSucceeded(value, name) {
2966
+ assertNonNullableRecordProperty(value, 'result', name);
2967
+ assertRecordStringProperty(value, 'status', name, 'succeeded');
2968
+ assertRecordStringProperty(value, 'requestId', name);
2969
+ assertIsRelayerV2ResultUserDecrypt(value.result, `${name}.result`);
2970
+ }
2971
+
2972
+ class RelayerV2RequestErrorBase extends RelayerErrorBase {
2973
+ _url;
2974
+ _operation;
2975
+ _jobId;
2976
+ constructor(params) {
2977
+ super({ ...params, name: params.name ?? 'RelayerV2RequestErrorBase' });
2978
+ this._url = params.url;
2979
+ this._operation = params.operation;
2980
+ this._jobId = params.jobId;
2981
+ }
2982
+ get url() {
2983
+ return this._url;
2984
+ }
2985
+ get jobId() {
2986
+ return this._jobId;
2987
+ }
2988
+ get operation() {
2989
+ return this._operation;
2990
+ }
2991
+ }
2992
+
2993
+ class RelayerV2RequestInternalError extends RelayerV2RequestErrorBase {
2994
+ _status;
2995
+ _state;
2996
+ constructor(params) {
2997
+ super({
2998
+ ...params,
2999
+ name: 'RelayerV2RequestInternalError',
3000
+ message: params.message ?? 'internal error',
3001
+ });
3002
+ this._status = params.status;
3003
+ this._state = params.state;
3004
+ }
3005
+ get status() {
3006
+ return this._status;
3007
+ }
3008
+ get state() {
3009
+ return this._state;
3010
+ }
3011
+ }
3012
+
3013
+ class RelayerV2ResponseApiError extends RelayerV2ResponseErrorBase {
3014
+ constructor(params) {
3015
+ super({
3016
+ ...params,
3017
+ name: 'RelayerV2ResponseApiError',
3018
+ message: params.relayerApiError.message,
3019
+ });
3020
+ }
3021
+ }
3022
+
3023
+ class RelayerV2FetchError extends RelayerV2FetchErrorBase {
3024
+ constructor(params) {
3025
+ super({
3026
+ ...params,
3027
+ name: 'RelayerV2FetchError',
3028
+ message: `Fetch ${params.fetchMethod} error`,
3029
+ cause: ensureError(params.cause),
3030
+ });
3031
+ }
3032
+ }
3033
+
3034
+ class RelayerV2ResponseInputProofRejectedError extends RelayerV2ResponseErrorBase {
3035
+ _result;
3036
+ constructor(params) {
3037
+ super({
3038
+ ...params,
3039
+ name: 'RelayerV2ResponseInputProofRejectedError',
3040
+ message: `InputProof rejected`,
3041
+ });
3042
+ this._result = params.result;
3043
+ }
3044
+ get result() {
3045
+ return this._result;
3046
+ }
3047
+ }
3048
+
3049
+ class RelayerV2StateError extends RelayerErrorBase {
3050
+ _state;
3051
+ constructor(params) {
3052
+ super({
3053
+ ...params,
3054
+ name: 'RelayerV2StateError',
3055
+ });
3056
+ this._state = { ...params.state };
3057
+ Object.freeze(this._state);
3058
+ }
3059
+ get state() {
3060
+ return this._state;
3061
+ }
3062
+ }
3063
+
3064
+ class RelayerV2MaxRetryError extends RelayerV2FetchErrorBase {
3065
+ constructor(params) {
3066
+ super({
3067
+ ...params,
3068
+ name: 'RelayerV2MaxRetryError',
3069
+ message: 'max retry error',
3070
+ });
3071
+ }
3072
+ }
3073
+
3074
+ function assertIsRelayerV2PostResultQueued(value, name) {
3075
+ assertRecordStringProperty(value, 'jobId', name);
3076
+ }
3077
+
3078
+ /*
3079
+ {
3080
+ status: 'queued';
3081
+ requestId: string;
3082
+ result: {
3083
+ jobId: string;
3084
+ };
3085
+ }
3086
+ */
3087
+ function assertIsRelayerV2PostResponseQueued(value, name) {
3088
+ assertRecordStringProperty(value, 'status', name, 'queued');
3089
+ assertRecordStringProperty(value, 'requestId', name);
3090
+ assertNonNullableRecordProperty(value, 'result', name);
3091
+ assertIsRelayerV2PostResultQueued(value.result, `${name}.result`);
3092
+ }
3093
+ /*
3094
+ {
3095
+ status: 'queued';
3096
+ requestId: string;
3097
+ }
3098
+ */
3099
+ function assertIsRelayerV2GetResponseQueued(value, name) {
3100
+ assertRecordStringProperty(value, 'status', name, 'queued');
3101
+ assertRecordStringProperty(value, 'requestId', name);
3102
+ }
3103
+
3104
+ class RelayerV2AsyncRequest {
3105
+ _jobId;
3106
+ _jobIdTimestamp;
3107
+ _state;
3108
+ _relayerOperation;
3109
+ _publicAPINoReentrancy;
3110
+ _internalAbortController;
3111
+ _internalAbortSignal;
3112
+ _externalAbortSignal;
3113
+ _terminateReason;
3114
+ _terminateError;
3115
+ _retryCount;
3116
+ _retryAfterTimeoutID;
3117
+ _url;
3118
+ _payload;
3119
+ _fhevmInstanceOptions;
3120
+ _retryAfterTimeoutPromiseFuncReject;
3121
+ _onProgress;
3122
+ _requestMaxDurationInSecs;
3123
+ _requestStartTimestamp;
3124
+ _requestGlobalTimeoutID;
3125
+ _throwErrorIfNoRetryAfter;
3126
+ static DEFAULT_RETRY_AFTER_SECS = 2;
3127
+ static DEFAULT_GLOBAL_REQUEST_TIMEOUT_SECS = 60 * 60;
3128
+ static MAX_GET_RETRY = 100;
3129
+ static MAX_POST_RETRY = 100;
3130
+ constructor(params) {
3131
+ if (params.relayerOperation !== 'INPUT_PROOF' &&
3132
+ params.relayerOperation !== 'PUBLIC_DECRYPT' &&
3133
+ params.relayerOperation !== 'USER_DECRYPT') {
3134
+ throw new InvalidPropertyError({
3135
+ objName: 'RelayerV2AsyncRequestParams',
3136
+ property: 'relayerOperation',
3137
+ expectedType: 'string',
3138
+ value: params.relayerOperation,
3139
+ expectedValue: 'INPUT_PROOF | PUBLIC_DECRYPT | USER_DECRYPT',
3140
+ });
3141
+ }
3142
+ this._relayerOperation = params.relayerOperation;
3143
+ this._internalAbortController = new AbortController();
3144
+ this._internalAbortSignal = this._internalAbortController.signal;
3145
+ this._internalAbortSignal.addEventListener('abort', this._handleInternalSignalAbort);
3146
+ this._externalAbortSignal = params.signal;
3147
+ if (this._externalAbortSignal) {
3148
+ this._externalAbortSignal.addEventListener('abort', this._handleExternalSignalAbort);
3149
+ }
3150
+ this._url = params.url;
3151
+ this._payload = params.payload;
3152
+ this._fhevmInstanceOptions = params.instanceOptions;
3153
+ this._onProgress = params.onProgress;
3154
+ this._state = {
3155
+ aborted: false,
3156
+ canceled: false,
3157
+ failed: false,
3158
+ fetching: false,
3159
+ running: false,
3160
+ succeeded: false,
3161
+ terminated: false,
3162
+ };
3163
+ this._retryCount = 0;
3164
+ this._retryAfterTimeoutID = undefined;
3165
+ this._requestGlobalTimeoutID = undefined;
3166
+ this._terminateReason = undefined;
3167
+ this._publicAPINoReentrancy = false;
3168
+ this._throwErrorIfNoRetryAfter = params.throwErrorIfNoRetryAfter ?? false;
3169
+ this._requestMaxDurationInSecs =
3170
+ params.timeoutInSeconds ??
3171
+ RelayerV2AsyncRequest.DEFAULT_GLOBAL_REQUEST_TIMEOUT_SECS;
3172
+ }
3173
+ //////////////////////////////////////////////////////////////////////////////
3174
+ // Public API: run
3175
+ //////////////////////////////////////////////////////////////////////////////
3176
+ async run() {
3177
+ if (this._publicAPINoReentrancy) {
3178
+ throw new RelayerV2StateError({
3179
+ message: `Relayer.run() failed. Call not permitted.`,
3180
+ state: { ...this._state },
3181
+ });
3182
+ }
3183
+ if (this._state.terminated) {
3184
+ throw new RelayerV2StateError({
3185
+ message: `Relayer.run() failed. Request already terminated.`,
3186
+ state: { ...this._state },
3187
+ });
3188
+ }
3189
+ if (this._state.canceled) {
3190
+ throw new RelayerV2StateError({
3191
+ message: `Relayer.run() failed. Request already canceled.`,
3192
+ state: { ...this._state },
3193
+ });
3194
+ }
3195
+ if (this._state.succeeded) {
3196
+ throw new RelayerV2StateError({
3197
+ message: `Relayer.run() failed. Request already succeeded.`,
3198
+ state: { ...this._state },
3199
+ });
3200
+ }
3201
+ if (this._state.failed) {
3202
+ throw new RelayerV2StateError({
3203
+ message: `Relayer.run() failed. Request already failed.`,
3204
+ state: { ...this._state },
3205
+ });
3206
+ }
3207
+ if (this._state.aborted) {
3208
+ throw new RelayerV2StateError({
3209
+ message: `Relayer.run() failed. Request already aborted.`,
3210
+ state: { ...this._state },
3211
+ });
3212
+ }
3213
+ if (this._externalAbortSignal?.aborted === true) {
3214
+ throw new RelayerV2StateError({
3215
+ message: `Relayer.run() failed. External AbortSignal already aborted (reason:${this._externalAbortSignal?.reason}).`,
3216
+ state: { ...this._state },
3217
+ });
3218
+ }
3219
+ if (this._internalAbortSignal?.aborted === true) {
3220
+ throw new RelayerV2StateError({
3221
+ message: `Relayer.run() failed. Internal AbortSignal already aborted (reason:${this._internalAbortSignal?.reason}).`,
3222
+ state: { ...this._state },
3223
+ });
3224
+ }
3225
+ if (this._state.running) {
3226
+ throw new RelayerV2StateError({
3227
+ message: `Relayer.run() failed. Request already running.`,
3228
+ state: { ...this._state },
3229
+ });
3230
+ }
3231
+ this._state.running = true;
3232
+ this._requestStartTimestamp = Date.now();
3233
+ this._setGlobalRequestTimeout(this._requestMaxDurationInSecs * 1000);
3234
+ try {
3235
+ const json = await this._runPostLoop();
3236
+ this._state.succeeded = true;
3237
+ this._terminate('succeeded');
3238
+ return json;
3239
+ }
3240
+ catch (e) {
3241
+ this._state.failed = true;
3242
+ if (e.name === 'AbortError') {
3243
+ this._assert(this._state.aborted, 'this._state.aborted');
3244
+ this._assert(this._state.terminated, 'this._state.terminated');
3245
+ }
3246
+ // Ignored if already terminated. For example, if abort has been previously called.
3247
+ this._terminate('failed', e);
3248
+ throw e;
3249
+ }
3250
+ }
3251
+ //////////////////////////////////////////////////////////////////////////////
3252
+ // Public API: cancel
3253
+ //////////////////////////////////////////////////////////////////////////////
3254
+ _canContinue() {
3255
+ return !(this._state.canceled ||
3256
+ this._state.terminated ||
3257
+ this._state.succeeded ||
3258
+ this._state.aborted);
3259
+ }
3260
+ cancel() {
3261
+ if (this._publicAPINoReentrancy) {
3262
+ throw new RelayerV2StateError({
3263
+ message: `Relayer.cancel() failed. Call not permitted.`,
3264
+ state: { ...this._state },
3265
+ });
3266
+ }
3267
+ if (!this._canContinue()) {
3268
+ this._trace('cancel', '!this._canContinue()');
3269
+ return;
3270
+ }
3271
+ this._state.canceled = true;
3272
+ this._internalAbortController?.abort('cancel');
3273
+ // Debug
3274
+ this._assert(this._state.aborted, 'this._state.aborted');
3275
+ this._assert(this._state.terminated, 'this._state.terminated');
3276
+ }
3277
+ //////////////////////////////////////////////////////////////////////////////
3278
+ // Public API: getters
3279
+ //////////////////////////////////////////////////////////////////////////////
3280
+ get state() {
3281
+ return { ...this._state };
3282
+ }
3283
+ get canceled() {
3284
+ return this._state.canceled;
3285
+ }
3286
+ get terminated() {
3287
+ return this._state.terminated;
3288
+ }
3289
+ get terminateReason() {
3290
+ return this._terminateReason;
3291
+ }
3292
+ get terminateError() {
3293
+ return this._terminateError;
3294
+ }
3295
+ get running() {
3296
+ return this._state.running;
3297
+ }
3298
+ get fetching() {
3299
+ return this._state.fetching;
3300
+ }
3301
+ get failed() {
3302
+ return this._state.failed;
3303
+ }
3304
+ get succeeded() {
3305
+ return this._state.succeeded;
3306
+ }
3307
+ get startTimeMs() {
3308
+ return this._requestStartTimestamp;
3309
+ }
3310
+ get elapsedTimeMs() {
3311
+ if (this._requestStartTimestamp === undefined) {
3312
+ return undefined;
3313
+ }
3314
+ return Date.now() - this._requestStartTimestamp;
3315
+ }
3316
+ get retryCount() {
3317
+ return this._retryCount;
3318
+ }
3319
+ //////////////////////////////////////////////////////////////////////////////
3320
+ // Post Loop
3321
+ //////////////////////////////////////////////////////////////////////////////
3322
+ // POST : 202 | 400 | 429 | 500 | 503
3323
+ async _runPostLoop() {
3324
+ // No infinite loop!
3325
+ let i = 0;
3326
+ while (i < RelayerV2AsyncRequest.MAX_POST_RETRY) {
3327
+ ++i;
3328
+ this._assertCanContinueAfterAwait();
3329
+ // At this stage: `terminated` is guaranteed to be `false`.
3330
+ // However, the `fetch` call can potentially throw an `AbortError`. In this case
3331
+ // in the error catch the `terminated` flag will be `true`! But, that's ok because the
3332
+ // next part of the function will never be executed (thrown error).
3333
+ const elapsed = this._jobId ? Date.now() - this._jobIdTimestamp : 0;
3334
+ const response = await this._fetchPost(elapsed);
3335
+ // At this stage: `terminated` is guaranteed to be `false`.
3336
+ const responseStatus = response.status;
3337
+ switch (responseStatus) {
3338
+ // RelayerV2ResponseQueued
3339
+ case 202: {
3340
+ // response.json() errors:
3341
+ // 1. if body is already read (call json() 2 times)
3342
+ // - TypeError: Body is unusable: Body has already been read
3343
+ // 2. if body is invalid JSON
3344
+ // - SyntaxError: Unexpected end of JSON input
3345
+ // - SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2) at JSON.parse (<anonymous>)
3346
+ const bodyJson = await this._getResponseJson(response);
3347
+ try {
3348
+ assertIsRelayerV2PostResponseQueued(bodyJson, 'body');
3349
+ }
3350
+ catch (cause) {
3351
+ this._throwResponseInvalidBodyError({
3352
+ fetchMethod: 'POST',
3353
+ status: responseStatus,
3354
+ cause: cause,
3355
+ elapsed,
3356
+ bodyJson: safeJSONstringify(bodyJson),
3357
+ });
3358
+ }
3359
+ let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
3360
+ if (retry_after_sec < 1)
3361
+ retry_after_sec = 1;
3362
+ // Debug: will throw an assert failed error if jobId has already been set
3363
+ this._setJobIdOnce(bodyJson.result.jobId);
3364
+ // Async onProgress callback
3365
+ this._postAsyncOnProgressCallback({
3366
+ type: 'queued',
3367
+ url: this._url,
3368
+ method: 'POST',
3369
+ status: responseStatus,
3370
+ requestId: bodyJson.requestId,
3371
+ jobId: this.jobId,
3372
+ operation: this._relayerOperation,
3373
+ retryCount: this._retryCount,
3374
+ retryAfter: retry_after_sec,
3375
+ elapsed,
3376
+ });
3377
+ // Wait if needed (minimum 1s)
3378
+ await this._setRetryAfterTimeout(retry_after_sec * 1000);
3379
+ const json = await this._runGetLoop();
3380
+ return json;
3381
+ }
3382
+ // RelayerV2ResponseFailed
3383
+ // RelayerV2ApiError400
3384
+ // RelayerV2ApiError400WithDetails
3385
+ case 400: {
3386
+ const bodyJson = await this._getResponseJson(response);
3387
+ try {
3388
+ assertIsRelayerV2ResponseFailedWithError400(bodyJson, 'body');
3389
+ }
3390
+ catch (cause) {
3391
+ this._throwResponseInvalidBodyError({
3392
+ fetchMethod: 'POST',
3393
+ status: responseStatus,
3394
+ cause: cause,
3395
+ elapsed,
3396
+ bodyJson: safeJSONstringify(bodyJson),
3397
+ });
3398
+ }
3399
+ this._throwRelayerV2ResponseApiError({
3400
+ fetchMethod: 'POST',
3401
+ status: responseStatus,
3402
+ relayerApiError: bodyJson.error,
3403
+ elapsed,
3404
+ });
3405
+ }
3406
+ // RelayerV2ResponseFailed
3407
+ // RelayerV2ApiError429
3408
+ case 429: {
3409
+ // Retry
3410
+ // Rate Limit error (Cloudflare/Kong/Relayer), reason in message
3411
+ const bodyJson = await this._getResponseJson(response);
3412
+ try {
3413
+ assertIsRelayerV2ResponseFailedWithError429(bodyJson, 'body');
3414
+ }
3415
+ catch (cause) {
3416
+ this._throwResponseInvalidBodyError({
3417
+ fetchMethod: 'POST',
3418
+ status: responseStatus,
3419
+ cause: cause,
3420
+ elapsed,
3421
+ bodyJson: safeJSONstringify(bodyJson),
3422
+ });
3423
+ }
3424
+ let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
3425
+ if (retry_after_sec < 1)
3426
+ retry_after_sec = 1;
3427
+ // Async onProgress callback
3428
+ this._postAsyncOnProgressCallback({
3429
+ type: 'ratelimited',
3430
+ url: this._url,
3431
+ method: 'POST',
3432
+ status: responseStatus,
3433
+ retryAfter: retry_after_sec,
3434
+ retryCount: this._retryCount,
3435
+ elapsed,
3436
+ message: bodyJson.error.message,
3437
+ });
3438
+ // Wait if needed (minimum 1s)
3439
+ await this._setRetryAfterTimeout(retry_after_sec * 1000);
3440
+ continue;
3441
+ }
3442
+ // RelayerV2ResponseFailed
3443
+ // RelayerV2ApiError500
3444
+ case 500: {
3445
+ // Abort
3446
+ // Relayer internal error
3447
+ const bodyJson = await this._getResponseJson(response);
3448
+ try {
3449
+ assertIsRelayerV2ResponseFailedWithError500(bodyJson, 'body');
3450
+ }
3451
+ catch (cause) {
3452
+ this._throwResponseInvalidBodyError({
3453
+ fetchMethod: 'POST',
3454
+ status: responseStatus,
3455
+ cause: cause,
3456
+ elapsed,
3457
+ bodyJson: safeJSONstringify(bodyJson),
3458
+ });
3459
+ }
3460
+ this._throwRelayerV2ResponseApiError({
3461
+ fetchMethod: 'POST',
3462
+ status: responseStatus,
3463
+ relayerApiError: bodyJson.error,
3464
+ elapsed,
3465
+ });
3466
+ }
3467
+ // RelayerV2ResponseFailed
3468
+ // RelayerV2ApiError503
3469
+ case 503: {
3470
+ // Abort
3471
+ // Possible Reasons: Gateway has some internal error (unknown)
3472
+ const bodyJson = await this._getResponseJson(response);
3473
+ try {
3474
+ assertIsRelayerV2ResponseFailedWithError503(bodyJson, 'body');
3475
+ }
3476
+ catch (cause) {
3477
+ this._throwResponseInvalidBodyError({
3478
+ fetchMethod: 'POST',
3479
+ status: responseStatus,
3480
+ cause: cause,
3481
+ elapsed,
3482
+ bodyJson: safeJSONstringify(bodyJson),
3483
+ });
3484
+ }
3485
+ this._throwRelayerV2ResponseApiError({
3486
+ fetchMethod: 'POST',
3487
+ status: responseStatus,
3488
+ relayerApiError: bodyJson.error,
3489
+ elapsed,
3490
+ });
3491
+ }
3492
+ default: {
3493
+ // Use TS compiler + `never` to guarantee the switch integrity
3494
+ const throwUnsupportedStatus = (unsupportedStatus) => {
3495
+ throw new RelayerV2ResponseStatusError({
3496
+ fetchMethod: 'POST',
3497
+ status: unsupportedStatus,
3498
+ url: this._url,
3499
+ operation: this._relayerOperation,
3500
+ elapsed,
3501
+ retryCount: this._retryCount,
3502
+ state: { ...this._state },
3503
+ });
3504
+ };
3505
+ throwUnsupportedStatus(responseStatus);
3506
+ }
3507
+ }
3508
+ }
3509
+ // Max retry error
3510
+ this._throwMaxRetryError({ fetchMethod: 'POST' });
3511
+ }
3512
+ //////////////////////////////////////////////////////////////////////////////
3513
+ // Get Loop
3514
+ //////////////////////////////////////////////////////////////////////////////
3515
+ // GET: 200 | 202 | 404 | 500 | 503 | 504
3516
+ // GET is not rate-limited, therefore there is not 429 error
3517
+ async _runGetLoop() {
3518
+ this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
3519
+ this._assert(this._jobIdTimestamp !== undefined, 'this._jobIdTimestamp !== undefined');
3520
+ let i = 0;
3521
+ while (i < RelayerV2AsyncRequest.MAX_GET_RETRY) {
3522
+ ++i;
3523
+ this._assertCanContinueAfterAwait();
3524
+ const elapsed = Date.now() - this._jobIdTimestamp;
3525
+ const response = await this._fetchGet(elapsed);
3526
+ // At this stage: `terminated` is guaranteed to be `false`.
3527
+ const responseStatus = response.status;
3528
+ switch (responseStatus) {
3529
+ // RelayerV2GetResponseSucceeded
3530
+ case 200: {
3531
+ const bodyJson = await this._getResponseJson(response);
3532
+ try {
3533
+ if (this._relayerOperation === 'INPUT_PROOF') {
3534
+ assertIsRelayerV2GetResponseInputProofSucceeded(bodyJson, 'body');
3535
+ // Async onProgress callback
3536
+ this._postAsyncOnProgressCallback({
3537
+ type: 'succeeded',
3538
+ url: this._url,
3539
+ method: 'GET',
3540
+ status: responseStatus,
3541
+ jobId: this.jobId,
3542
+ requestId: bodyJson.requestId,
3543
+ operation: this._relayerOperation,
3544
+ retryCount: this._retryCount,
3545
+ elapsed,
3546
+ result: bodyJson.result,
3547
+ });
3548
+ if (!bodyJson.result.accepted) {
3549
+ const e = new RelayerV2ResponseInputProofRejectedError({
3550
+ url: this._url,
3551
+ fetchMethod: 'GET',
3552
+ jobId: this.jobId,
3553
+ operation: this._relayerOperation,
3554
+ retryCount: this._retryCount,
3555
+ status: responseStatus,
3556
+ state: { ...this._state },
3557
+ elapsed,
3558
+ result: bodyJson.result,
3559
+ });
3560
+ throw e;
3561
+ }
3562
+ }
3563
+ else if (this._relayerOperation === 'PUBLIC_DECRYPT') {
3564
+ assertIsRelayerV2GetResponsePublicDecryptSucceeded(bodyJson, 'body');
3565
+ // Async onProgress callback
3566
+ this._postAsyncOnProgressCallback({
3567
+ type: 'succeeded',
3568
+ url: this._url,
3569
+ method: 'GET',
3570
+ status: responseStatus,
3571
+ jobId: this.jobId,
3572
+ requestId: bodyJson.requestId,
3573
+ operation: this._relayerOperation,
3574
+ retryCount: this._retryCount,
3575
+ elapsed,
3576
+ result: bodyJson.result,
3577
+ });
3578
+ }
3579
+ else if (this._relayerOperation === 'USER_DECRYPT') {
3580
+ assertIsRelayerV2GetResponseUserDecryptSucceeded(bodyJson, 'body');
3581
+ // Async onProgress callback
3582
+ this._postAsyncOnProgressCallback({
3583
+ type: 'succeeded',
3584
+ url: this._url,
3585
+ method: 'GET',
3586
+ status: responseStatus,
3587
+ jobId: this.jobId,
3588
+ requestId: bodyJson.requestId,
3589
+ operation: this._relayerOperation,
3590
+ retryCount: this._retryCount,
3591
+ elapsed,
3592
+ result: bodyJson.result,
3593
+ });
3594
+ }
3595
+ }
3596
+ catch (cause) {
3597
+ // Special case for InputProof rejected
3598
+ if (cause instanceof RelayerV2ResponseInputProofRejectedError) {
3599
+ throw cause;
3600
+ }
3601
+ this._throwResponseInvalidBodyError({
3602
+ fetchMethod: 'GET',
3603
+ status: responseStatus,
3604
+ elapsed,
3605
+ cause: cause,
3606
+ bodyJson: safeJSONstringify(bodyJson),
3607
+ });
3608
+ }
3609
+ // RelayerV2ResultPublicDecrypt
3610
+ // RelayerV2ResultUserDecrypt
3611
+ // RelayerV2ResultInputProof;
3612
+ return bodyJson.result;
3613
+ }
3614
+ // RelayerV2ResponseQueued
3615
+ case 202: {
3616
+ const bodyJson = await this._getResponseJson(response);
3617
+ try {
3618
+ assertIsRelayerV2GetResponseQueued(bodyJson, 'body');
3619
+ }
3620
+ catch (cause) {
3621
+ this._throwResponseInvalidBodyError({
3622
+ fetchMethod: 'GET',
3623
+ status: responseStatus,
3624
+ elapsed,
3625
+ cause: cause,
3626
+ bodyJson: safeJSONstringify(bodyJson),
3627
+ });
3628
+ }
3629
+ let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
3630
+ if (retry_after_sec < 1)
3631
+ retry_after_sec = 1;
3632
+ // Async onProgress callback
3633
+ this._postAsyncOnProgressCallback({
3634
+ type: 'queued',
3635
+ url: this._url,
3636
+ method: 'GET',
3637
+ status: responseStatus,
3638
+ requestId: bodyJson.requestId,
3639
+ operation: this._relayerOperation,
3640
+ jobId: this.jobId,
3641
+ retryAfter: retry_after_sec,
3642
+ retryCount: this._retryCount,
3643
+ elapsed,
3644
+ });
3645
+ // Wait if needed (minimum 1s)
3646
+ await this._setRetryAfterTimeout(retry_after_sec * 1000);
3647
+ continue;
3648
+ }
3649
+ case 400: {
3650
+ // Abort
3651
+ // Wrong jobId, incorrect format or unknown value etc.
3652
+ const bodyJson = await this._getResponseJson(response);
3653
+ try {
3654
+ assertIsRelayerV2ResponseFailedWithError400(bodyJson, 'body');
3655
+ }
3656
+ catch (cause) {
3657
+ this._throwResponseInvalidBodyError({
3658
+ fetchMethod: 'GET',
3659
+ status: responseStatus,
3660
+ elapsed,
3661
+ cause: cause,
3662
+ bodyJson: safeJSONstringify(bodyJson),
3663
+ });
3664
+ }
3665
+ this._throwRelayerV2ResponseApiError({
3666
+ fetchMethod: 'GET',
3667
+ status: responseStatus,
3668
+ relayerApiError: bodyJson.error,
3669
+ elapsed,
3670
+ });
3671
+ }
3672
+ case 404: {
3673
+ // Abort
3674
+ // Wrong jobId, incorrect format or unknown value etc.
3675
+ const bodyJson = await this._getResponseJson(response);
3676
+ try {
3677
+ assertIsRelayerV2ResponseFailedWithError404(bodyJson, 'body');
3678
+ }
3679
+ catch (cause) {
3680
+ this._throwResponseInvalidBodyError({
3681
+ fetchMethod: 'GET',
3682
+ status: responseStatus,
3683
+ elapsed,
3684
+ cause: cause,
3685
+ bodyJson: safeJSONstringify(bodyJson),
3686
+ });
3687
+ }
3688
+ this._throwRelayerV2ResponseApiError({
3689
+ fetchMethod: 'GET',
3690
+ status: responseStatus,
3691
+ relayerApiError: bodyJson.error,
3692
+ elapsed,
3693
+ });
3694
+ }
3695
+ // RelayerV2ResponseFailed
3696
+ // RelayerV2ApiError500
3697
+ case 500: {
3698
+ // Abort
3699
+ // Relayer internal error
3700
+ const bodyJson = await this._getResponseJson(response);
3701
+ try {
3702
+ assertIsRelayerV2ResponseFailedWithError500(bodyJson, 'body');
3703
+ }
3704
+ catch (cause) {
3705
+ this._throwResponseInvalidBodyError({
3706
+ fetchMethod: 'GET',
3707
+ status: responseStatus,
3708
+ elapsed,
3709
+ cause: cause,
3710
+ bodyJson: safeJSONstringify(bodyJson),
3711
+ });
3712
+ }
3713
+ this._throwRelayerV2ResponseApiError({
3714
+ fetchMethod: 'GET',
3715
+ status: responseStatus,
3716
+ relayerApiError: bodyJson.error,
3717
+ elapsed,
3718
+ });
3719
+ }
3720
+ // RelayerV2ResponseFailed
3721
+ // RelayerV2ApiError503
3722
+ case 503: {
3723
+ // Abort
3724
+ // Possible Reasons: Gateway has some internal error (unknown)
3725
+ const bodyJson = await this._getResponseJson(response);
3726
+ try {
3727
+ assertIsRelayerV2ResponseFailedWithError503(bodyJson, 'body');
3728
+ }
3729
+ catch (cause) {
3730
+ this._throwResponseInvalidBodyError({
3731
+ fetchMethod: 'GET',
3732
+ status: responseStatus,
3733
+ elapsed,
3734
+ cause: cause,
3735
+ bodyJson: safeJSONstringify(bodyJson),
3736
+ });
3737
+ }
3738
+ this._throwRelayerV2ResponseApiError({
3739
+ fetchMethod: 'GET',
3740
+ status: responseStatus,
3741
+ relayerApiError: bodyJson.error,
3742
+ elapsed,
3743
+ });
3744
+ }
3745
+ // RelayerV2ResponseFailed
3746
+ // RelayerV2ApiError504
3747
+ case 504: {
3748
+ // Abort
3749
+ // Possible Reasons: Gateway has not responded in time (gateway timeout)
3750
+ const bodyJson = await this._getResponseJson(response);
3751
+ try {
3752
+ assertIsRelayerV2ResponseFailedWithError504(bodyJson, 'body');
3753
+ }
3754
+ catch (cause) {
3755
+ this._throwResponseInvalidBodyError({
3756
+ fetchMethod: 'GET',
3757
+ status: responseStatus,
3758
+ elapsed,
3759
+ cause: cause,
3760
+ bodyJson: safeJSONstringify(bodyJson),
3761
+ });
3762
+ }
3763
+ this._throwRelayerV2ResponseApiError({
3764
+ fetchMethod: 'GET',
3765
+ status: responseStatus,
3766
+ relayerApiError: bodyJson.error,
3767
+ elapsed,
3768
+ });
3769
+ }
3770
+ default: {
3771
+ // Use TS compiler + `never` to guarantee the switch integrity
3772
+ const throwUnsupportedStatus = (unsupportedStatus) => {
3773
+ throw new RelayerV2ResponseStatusError({
3774
+ fetchMethod: 'GET',
3775
+ status: unsupportedStatus,
3776
+ url: this._url,
3777
+ jobId: this.jobId,
3778
+ operation: this._relayerOperation,
3779
+ elapsed,
3780
+ retryCount: this._retryCount,
3781
+ state: { ...this._state },
3782
+ });
3783
+ };
3784
+ throwUnsupportedStatus(responseStatus);
3785
+ }
3786
+ }
3787
+ }
3788
+ // Max retry error
3789
+ this._throwMaxRetryError({ fetchMethod: 'GET' });
3790
+ }
3791
+ //////////////////////////////////////////////////////////////////////////////
3792
+ async _getResponseJson(response) {
3793
+ const bodyJson = await response.json();
3794
+ this._assertCanContinueAfterAwait();
3795
+ return bodyJson;
3796
+ }
3797
+ //////////////////////////////////////////////////////////////////////////////
3798
+ _getRetryAfterHeaderValueInSecs(response) {
3799
+ if (!response.headers.has('Retry-After')) {
3800
+ if (this._throwErrorIfNoRetryAfter) {
3801
+ throw new Error(`Missing 'Retry-After' header key`);
3802
+ }
3803
+ return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_SECS;
3804
+ }
3805
+ try {
3806
+ const n = Number.parseInt(response.headers.get('Retry-After'));
3807
+ if (isUint(n)) {
3808
+ return n;
3809
+ }
3810
+ }
3811
+ catch {
3812
+ //
3813
+ }
3814
+ if (this._throwErrorIfNoRetryAfter) {
3815
+ throw new Error(`Invalid 'Retry-After' header key`);
3816
+ }
3817
+ return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_SECS;
3818
+ }
3819
+ //////////////////////////////////////////////////////////////////////////////
3820
+ // JobId
3821
+ //////////////////////////////////////////////////////////////////////////////
3822
+ /**
3823
+ * Sets the unique job identifier for this request.
3824
+ *
3825
+ * This function enforces a strict initialization constraint: the jobId must be
3826
+ * set exactly once during the entire lifecycle of the state machine instance.
3827
+ *
3828
+ * This immutability ensures that all subsequent operations, logging, and state
3829
+ * transitions are consistently associated with the correct external request.
3830
+ *
3831
+ * @param jobId - The unique identifier associated with the asynchronous job request.
3832
+ * @private
3833
+ * @throws {RelayerV2RequestInternalError} Thrown if jobId is undefined or if the jobId has already been set.
3834
+ */
3835
+ _setJobIdOnce(jobId) {
3836
+ this._assert(jobId !== undefined, 'jobId !== undefined');
3837
+ this._assert(this._jobId === undefined, 'this._jobId === undefined');
3838
+ this._jobId = jobId;
3839
+ this._jobIdTimestamp = Date.now();
3840
+ }
3841
+ get jobId() {
3842
+ this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
3843
+ return this._jobId;
3844
+ }
3845
+ //////////////////////////////////////////////////////////////////////////////
3846
+ // Fetch functions
3847
+ //////////////////////////////////////////////////////////////////////////////
3848
+ async _fetchPost(elapsed) {
3849
+ // Debug state-check guards:
3850
+ // - the jobId is guaranteed to be undefined.
3851
+ // - `terminated` is guaranteed to be `false`
3852
+ // - `fetching` is guaranteed to be `false`
3853
+ this._assert(this._jobId === undefined, 'this._jobId === undefined');
3854
+ this._assert(!this._state.terminated, '!this._state.terminated');
3855
+ this._assert(!this._state.fetching, '!this._state.fetching');
3856
+ this._trace('_fetchPost', 'enter');
3857
+ const init = setAuth({
3858
+ method: 'POST',
3859
+ headers: {
3860
+ 'Content-Type': 'application/json',
3861
+ 'ZAMA-SDK-VERSION': `${version}`,
3862
+ 'ZAMA-SDK-NAME': `${sdkName}`,
3863
+ },
3864
+ body: JSON.stringify(this._payload),
3865
+ ...(this._internalAbortSignal
3866
+ ? { signal: this._internalAbortSignal }
3867
+ : {}),
3868
+ }, this._fhevmInstanceOptions?.auth);
3869
+ this._state.fetching = true;
3870
+ let response;
3871
+ try {
3872
+ response = await fetch(this._url, init);
3873
+ }
3874
+ catch (cause) {
3875
+ this._state.fetching = false;
3876
+ // Warning: `terminated` can be `true` here!
3877
+ // (ex: if `controller.abort()` has been called from the outside while still executing `fetch`)
3878
+ this._trace('_fetchPost', 'catch(e) + throw e: ' + cause);
3879
+ // Keep the standard 'AbortError'
3880
+ if (cause.name === 'AbortError') {
3881
+ throw cause;
3882
+ }
3883
+ else {
3884
+ this._throwFetchError({
3885
+ fetchMethod: 'POST',
3886
+ cause,
3887
+ elapsed,
3888
+ });
3889
+ }
3890
+ }
3891
+ this._state.fetching = false;
3892
+ // Debug state-check guards:
3893
+ // - the jobId is guaranteed to be undefined.
3894
+ // - `terminated` is guaranteed to be `false`
3895
+ this._assert(this._jobId === undefined, 'this._jobId === undefined');
3896
+ this._assert(!this._state.terminated, '!this._state.terminated');
3897
+ // Debug
3898
+ this._assertCanContinueAfterAwait();
3899
+ this._trace('_fetchPost', 'return response Ok');
3900
+ return response;
3901
+ }
3902
+ //////////////////////////////////////////////////////////////////////////////
3903
+ async _fetchGet(elapsed) {
3904
+ // Debug state-check guards:
3905
+ // - the jobId is guaranteed to be set.
3906
+ // - `terminated` is guaranteed to be `false`
3907
+ // - `fetching` is guaranteed to be `false`
3908
+ this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
3909
+ this._assert(!this._state.terminated, '!this._state.terminated');
3910
+ this._assert(!this._state.fetching, '!this._state.fetching');
3911
+ this._trace('_fetchGet', `jobId=${this.jobId}`);
3912
+ const init = {
3913
+ method: 'GET',
3914
+ headers: {
3915
+ 'ZAMA-SDK-VERSION': `${version}`,
3916
+ 'ZAMA-SDK-NAME': `${sdkName}`,
3917
+ },
3918
+ ...(this._internalAbortSignal
3919
+ ? { signal: this._internalAbortSignal }
3920
+ : {}),
3921
+ };
3922
+ this._state.fetching = true;
3923
+ let response;
3924
+ try {
3925
+ response = await fetch(`${this._url}/${this.jobId}`, init);
3926
+ }
3927
+ catch (cause) {
3928
+ this._state.fetching = false;
3929
+ // Warning: `terminated` can be `true` here!
3930
+ // (ex: if `controller.abort()` has been called from the outside while still executing `fetch`)
3931
+ this._trace('_fetchGet', `jobId=${this.jobId}, catch(e) + throw e: ${cause}`);
3932
+ // Keep the standard 'AbortError'
3933
+ if (cause.name === 'AbortError') {
3934
+ throw cause;
3935
+ }
3936
+ else {
3937
+ this._throwFetchError({
3938
+ fetchMethod: 'GET',
3939
+ cause,
3940
+ elapsed,
3941
+ });
3942
+ }
3943
+ }
3944
+ this._state.fetching = false;
3945
+ // Debug state-check guards:
3946
+ // - the jobId is guaranteed to be set.
3947
+ // - `terminated` is guaranteed to be `false`
3948
+ this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
3949
+ this._assert(!this._state.terminated, '!this._state.terminated');
3950
+ // Debug
3951
+ this._assertCanContinueAfterAwait();
3952
+ this._trace('_fetchGet', `jobId=${this.jobId}, return response Ok, status=${response.status}`);
3953
+ return response;
3954
+ }
3955
+ //////////////////////////////////////////////////////////////////////////////
3956
+ // AbortSignal
3957
+ //////////////////////////////////////////////////////////////////////////////
3958
+ // Warning: Use arrow function only!
3959
+ _handleExternalSignalAbort = (ev) => {
3960
+ const signal = ev.currentTarget;
3961
+ // TESTING: the following sequences must be extensively tested:
3962
+ // ============================================================
3963
+ //
3964
+ // Each steps could potentially be called synchronously one after the other
3965
+ // or asynchronously: step 2 is called from the next microtick
3966
+ //
3967
+ // 1. externalSignal.abort();
3968
+ // 2. request.cancel();
3969
+ //
3970
+ // 1. externalSignal.abort();
3971
+ // 2. externalSignal.abort();
3972
+ //
3973
+ // 1. request.cancel();
3974
+ // 2. externalSignal.abort();
3975
+ // Debug state-check guards:
3976
+ this._assert(this instanceof RelayerV2AsyncRequest, `this instanceof RelayerV2AsyncRequest`);
3977
+ this._assert(signal === this._externalAbortSignal, 'signal === this._externalAbortSignal');
3978
+ this._assert(!this._state.terminated, `!this._state.terminated`);
3979
+ this._assert(!this._state.aborted, '!this._state.aborted');
3980
+ this._assert(!this._state.canceled, '!this._state.canceled');
3981
+ this.cancel();
3982
+ };
3983
+ // Warning: Use arrow function only!
3984
+ _handleInternalSignalAbort = (ev) => {
3985
+ const signal = ev.currentTarget;
3986
+ // Debug state-check guards:
3987
+ this._assert(this instanceof RelayerV2AsyncRequest, `this instanceof RelayerV2AsyncRequest`);
3988
+ this._assert(signal === this._internalAbortSignal, 'signal === this._internalAbortSignal');
3989
+ this._assert(!this._state.terminated, `!this._state.terminated`);
3990
+ this._assert(!this._state.aborted, '!this._state.aborted');
3991
+ this._state.aborted = true;
3992
+ if (signal.reason !== 'cancel') {
3993
+ this._assert(!this._state.canceled, '!this._state.canceled');
3994
+ }
3995
+ this._terminate('abort');
3996
+ };
3997
+ //////////////////////////////////////////////////////////////////////////////
3998
+ // Terminate
3999
+ //////////////////////////////////////////////////////////////////////////////
4000
+ /**
4001
+ * Can be called multiple times
4002
+ */
4003
+ _terminate(reason, error) {
4004
+ // Warning: this._state.fetching can be true
4005
+ // ex: call cancel while fetch is running
4006
+ if (this._state.terminated) {
4007
+ this._trace(`_terminate`, `reason=${reason}. Already terminated with reason='${this._terminateReason}'. IGNORE`);
4008
+ this._assert(this._terminateReason !== undefined, 'this._terminateReason !== undefined');
4009
+ this._assert(this._internalAbortSignal === undefined, 'this._signal === undefined');
4010
+ this._assert(this._requestGlobalTimeoutID === undefined, 'this._requestGlobalTimeoutID === undefined');
4011
+ this._assert(this._retryAfterTimeoutID === undefined, 'this._retryAfterTimeoutID === undefined');
4012
+ this._assert(this._retryAfterTimeoutPromiseFuncReject === undefined, 'this._retryAfterTimeoutPromiseFuncReject === undefined');
4013
+ return;
4014
+ }
4015
+ this._trace('_terminate', `reason=${reason}`);
4016
+ this._terminateReason = reason;
4017
+ this._terminateError = error;
4018
+ this._state.terminated = true;
4019
+ this._tryClearRetryAfterTimeout();
4020
+ this._tryClearGlobalRequestTimeout();
4021
+ const is = this._internalAbortSignal;
4022
+ const es = this._externalAbortSignal;
4023
+ this._externalAbortSignal = undefined;
4024
+ this._internalAbortSignal = undefined;
4025
+ this._internalAbortController = undefined;
4026
+ if (es) {
4027
+ es.removeEventListener('abort', this._handleExternalSignalAbort);
4028
+ }
4029
+ if (is) {
4030
+ is.removeEventListener('abort', this._handleInternalSignalAbort);
4031
+ }
4032
+ this._trace('_terminate', `reason=${reason} completed.`);
4033
+ }
4034
+ //////////////////////////////////////////////////////////////////////////////
4035
+ // Retry-After timeout
4036
+ //////////////////////////////////////////////////////////////////////////////
4037
+ async _setRetryAfterTimeout(delayMs) {
4038
+ // Debug
4039
+ this._assert(!this._state.terminated, '!this._state.terminated');
4040
+ this._assert(this._retryAfterTimeoutID === undefined, 'this._retryAfterTimeoutID === undefined');
4041
+ this._assert(delayMs >= 1000, 'delayMs >= 1000');
4042
+ this._trace('_setRetryAfterTimeout', `delayMs=${delayMs}`);
4043
+ if (this._retryAfterTimeoutID !== undefined) {
4044
+ return Promise.reject(new Error(`retry-after already running.`));
4045
+ }
4046
+ const p = new Promise((resolve, reject) => {
4047
+ this._retryAfterTimeoutPromiseFuncReject = reject;
4048
+ const callback = () => {
4049
+ this._retryAfterTimeoutID = undefined;
4050
+ this._retryAfterTimeoutPromiseFuncReject = undefined;
4051
+ resolve();
4052
+ };
4053
+ this._retryCount++;
4054
+ this._retryAfterTimeoutID = setTimeout(callback, delayMs);
4055
+ });
4056
+ this._assert(this._retryAfterTimeoutID !== undefined, 'this._retryAfterTimeoutID !== undefined');
4057
+ this._assert(this._retryAfterTimeoutPromiseFuncReject !== undefined, 'this._retryAfterTimeoutPromiseFuncReject !== undefined');
4058
+ return p;
4059
+ }
4060
+ //////////////////////////////////////////////////////////////////////////////
4061
+ _tryClearRetryAfterTimeout() {
4062
+ if (this._retryAfterTimeoutID === undefined) {
4063
+ // Debug
4064
+ this._assert(this._retryAfterTimeoutPromiseFuncReject === undefined, 'this._retryAfterTimeoutPromiseFuncReject === undefined');
4065
+ return;
4066
+ }
4067
+ const reject = this._retryAfterTimeoutPromiseFuncReject;
4068
+ const tid = this._retryAfterTimeoutID;
4069
+ this._retryAfterTimeoutID = undefined;
4070
+ this._retryAfterTimeoutPromiseFuncReject = undefined;
4071
+ clearTimeout(tid);
4072
+ reject(new Error('_tryClearRetryAfterTimeout'));
4073
+ }
4074
+ //////////////////////////////////////////////////////////////////////////////
4075
+ // Global Request Timeout
4076
+ //////////////////////////////////////////////////////////////////////////////
4077
+ _setGlobalRequestTimeout(delayMs) {
4078
+ // Debug
4079
+ this._assert(this._requestGlobalTimeoutID === undefined, 'this._requestGlobalTimeoutID === undefined');
4080
+ const callback = () => {
4081
+ this._requestGlobalTimeoutID = undefined;
4082
+ this._handleGlobalRequestTimeout();
4083
+ };
4084
+ this._requestGlobalTimeoutID = setTimeout(callback, delayMs);
4085
+ }
4086
+ _handleGlobalRequestTimeout() {
4087
+ this._terminate('timeout');
4088
+ }
4089
+ _tryClearGlobalRequestTimeout() {
4090
+ if (this._requestGlobalTimeoutID === undefined) {
4091
+ return;
4092
+ }
4093
+ const tid = this._requestGlobalTimeoutID;
4094
+ this._requestGlobalTimeoutID = undefined;
4095
+ clearTimeout(tid);
4096
+ }
4097
+ //////////////////////////////////////////////////////////////////////////////
4098
+ // Progress
4099
+ //////////////////////////////////////////////////////////////////////////////
4100
+ _postAsyncOnProgressCallback(args) {
4101
+ const onProgressFunc = this._onProgress;
4102
+ if (onProgressFunc) {
4103
+ // setTimeout(() => {
4104
+ // onProgressFunc(args);
4105
+ // }, 0);
4106
+ // onProgressFunc() will execute asynchronously in the next cycle of
4107
+ // the JavaScript event loop (the microtask queue).
4108
+ // Promise.resolve().then(() => {
4109
+ // onProgressFunc(args);
4110
+ // });
4111
+ queueMicrotask(() => {
4112
+ onProgressFunc(args);
4113
+ });
4114
+ }
4115
+ }
4116
+ //////////////////////////////////////////////////////////////////////////////
4117
+ // Errors
4118
+ //////////////////////////////////////////////////////////////////////////////
4119
+ _throwRelayerV2ResponseApiError(params) {
4120
+ // Clone
4121
+ const clonedRelayerApiError = JSON.parse(JSON.stringify(params.relayerApiError));
4122
+ // Async onProgress callback
4123
+ this._postAsyncOnProgressCallback({
4124
+ type: 'failed',
4125
+ url: this._url,
4126
+ method: params.fetchMethod,
4127
+ status: params.status,
4128
+ ...(this._jobId ? { jobId: this._jobId } : {}),
4129
+ operation: this._relayerOperation,
4130
+ retryCount: this._retryCount,
4131
+ elapsed: params.elapsed,
4132
+ relayerApiError: clonedRelayerApiError,
4133
+ });
4134
+ throw new RelayerV2ResponseApiError({
4135
+ url: this._url,
4136
+ fetchMethod: params.fetchMethod,
4137
+ status: params.status,
4138
+ jobId: this._jobId,
4139
+ operation: this._relayerOperation,
4140
+ retryCount: this._retryCount,
4141
+ elapsed: params.elapsed,
4142
+ relayerApiError: params.relayerApiError,
4143
+ state: { ...this._state },
4144
+ });
4145
+ }
4146
+ _assert(condition, message) {
4147
+ if (!condition) {
4148
+ this._throwInternalError(`Assertion failed: ${message}`);
4149
+ }
4150
+ }
4151
+ _throwInternalError(message) {
4152
+ throw new RelayerV2RequestInternalError({
4153
+ operation: this._relayerOperation,
4154
+ url: this._url,
4155
+ message,
4156
+ state: JSON.stringify(this._state),
4157
+ jobId: this._jobId, // internal value
4158
+ });
4159
+ }
4160
+ _throwMaxRetryError(params) {
4161
+ const elapsed = this._jobIdTimestamp
4162
+ ? Date.now() - this._jobIdTimestamp
4163
+ : 0;
4164
+ throw new RelayerV2MaxRetryError({
4165
+ operation: this._relayerOperation,
4166
+ url: this._url,
4167
+ state: { ...this._state },
4168
+ retryCount: this._retryCount,
4169
+ jobId: this._jobId, // internal value
4170
+ fetchMethod: params.fetchMethod,
4171
+ elapsed,
4172
+ });
4173
+ }
4174
+ _throwResponseInvalidBodyError(params) {
4175
+ throw new RelayerV2ResponseInvalidBodyError({
4176
+ ...params,
4177
+ url: this._url,
4178
+ jobId: this._jobId,
4179
+ operation: this._relayerOperation,
4180
+ state: { ...this._state },
4181
+ retryCount: this._retryCount,
4182
+ });
4183
+ }
4184
+ _throwFetchError(params) {
4185
+ throw new RelayerV2FetchError({
4186
+ ...params,
4187
+ url: this._url,
4188
+ jobId: this._jobId,
4189
+ operation: this._relayerOperation,
4190
+ state: { ...this._state },
4191
+ retryCount: this._retryCount,
4192
+ });
4193
+ }
4194
+ /**
4195
+ * Assert Continuation Guard
4196
+ *
4197
+ * This internal method implements a state-check guard to ensure the state machine
4198
+ * can safely proceed after an asynchronous operation has completed.
4199
+ *
4200
+ * In a state machine with asynchronous calls (e.g., fetch, timer delays), the system's
4201
+ * state (e.g., this._state) might change externally during the 'await' pause
4202
+ * (e.g., due to a timeout, an external abort signal, or a concurrent state transition).
4203
+ *
4204
+ * If the internal check (this._canContinue()) returns false, it means the current
4205
+ * operation is no longer valid, and execution must stop immediately to prevent state corruption.
4206
+ * This pattern is essential for reliable asynchronous state machines.
4207
+ *
4208
+ * @throws {RelayerV2RequestInternalError} Thrown if the state check fails (i.e., this._canContinue() is false).
4209
+ * The error includes relevant state information (like current state and jobId)
4210
+ * to aid in debugging the exact point of the integrity failure.
4211
+ */
4212
+ _assertCanContinueAfterAwait() {
4213
+ if (!this._canContinue()) {
4214
+ this._throwInternalError('cannot continue.');
4215
+ }
4216
+ }
4217
+ //////////////////////////////////////////////////////////////////////////////
4218
+ // Trace
4219
+ //////////////////////////////////////////////////////////////////////////////
4220
+ _trace(functionName, message) {
4221
+ console.log(`[RelayerV2AsyncRequest]:${functionName}: ${message}`);
4222
+ }
4223
+ }
4224
+
4225
+ function assertIsRelayerV2GetResponseKeyUrl(value, name) {
4226
+ assertNonNullableRecordProperty(value, 'response', name);
4227
+ // crs
4228
+ assertNonNullableRecordProperty(value.response, 'crs', `${name}.response`);
4229
+ const crs = value.response.crs;
4230
+ const keys = Object.keys(crs);
4231
+ for (let i = 0; i < keys.length; ++i) {
4232
+ // RelayerV2KeyData
4233
+ assertIsRelayerV2KeyData(crs[keys[i]], `${name}.response.crs.${keys[i]}`);
4234
+ }
4235
+ // fhe_key_info
4236
+ assertRecordArrayProperty(value.response, 'fheKeyInfo', `${name}.response`);
4237
+ const fheKeyInfo = value.response.fheKeyInfo;
4238
+ for (let i = 0; i < fheKeyInfo.length; ++i) {
4239
+ const ki = fheKeyInfo[i];
4240
+ const kiName = `${name}.response.fheKeyInfo[${i}]`;
4241
+ assertNonNullableRecordProperty(ki, 'fhePublicKey', kiName);
4242
+ assertIsRelayerV2KeyData(ki.fhePublicKey, `${kiName}.fhePublicKey`);
4243
+ }
4244
+ }
4245
+ function assertIsRelayerV2KeyData(value, name) {
4246
+ assertRecordStringProperty(value, 'dataId', name);
4247
+ assertRecordStringArrayProperty(value, 'urls', name);
4248
+ }
4249
+ function toRelayerV1KeyUrlResponse(response) {
4250
+ const fheKeyInfoV1 = response.response.fheKeyInfo.map((v2Info) => ({
4251
+ fhe_public_key: {
4252
+ data_id: v2Info.fhePublicKey.dataId,
4253
+ urls: v2Info.fhePublicKey.urls,
4254
+ },
4255
+ }));
4256
+ const crsV1 = {};
4257
+ for (const [key, v2Data] of Object.entries(response.response.crs)) {
4258
+ crsV1[key] = {
4259
+ data_id: v2Data.dataId,
4260
+ urls: v2Data.urls,
4261
+ };
4262
+ }
4263
+ return {
4264
+ response: {
4265
+ fhe_key_info: fheKeyInfoV1,
4266
+ crs: crsV1,
4267
+ },
4268
+ };
4269
+ }
4270
+
4271
+ class RelayerV2Provider extends AbstractRelayerProvider {
4272
+ constructor(relayerUrl) {
4273
+ super(relayerUrl);
4274
+ }
4275
+ get version() {
4276
+ return 2;
4277
+ }
4278
+ async fetchGetKeyUrlV2() {
4279
+ const response = await fetchRelayerV1Get('KEY_URL', this.keyUrl);
4280
+ // Relayer error
4281
+ try {
4282
+ assertIsRelayerV2GetResponseKeyUrl(response, 'fetchGetKeyUrl()');
4283
+ }
4284
+ catch (e) {
4285
+ throw new RelayerV2GetKeyUrlInvalidResponseError({
4286
+ cause: ensureError(e),
4287
+ });
4288
+ }
4289
+ return response;
4290
+ }
4291
+ async fetchGetKeyUrl() {
4292
+ const response = await this.fetchGetKeyUrlV2();
4293
+ return toRelayerV1KeyUrlResponse(response);
4294
+ }
4295
+ async fetchPostInputProof(payload, instanceOptions, fetchOptions) {
4296
+ const request = new RelayerV2AsyncRequest({
4297
+ relayerOperation: 'INPUT_PROOF',
4298
+ url: this.inputProof,
4299
+ payload,
4300
+ instanceOptions,
4301
+ ...fetchOptions,
4302
+ });
4303
+ const result = (await request.run());
4304
+ assertIsRelayerInputProofResult(result, 'fetchPostInputProof()');
4305
+ return result;
4306
+ }
4307
+ async fetchPostPublicDecrypt(payload, instanceOptions, fetchOptions) {
4308
+ const request = new RelayerV2AsyncRequest({
4309
+ relayerOperation: 'PUBLIC_DECRYPT',
4310
+ url: this.publicDecrypt,
4311
+ payload,
4312
+ instanceOptions,
4313
+ ...fetchOptions,
4314
+ });
4315
+ const result = await request.run();
4316
+ assertIsRelayerPublicDecryptResult(result, 'fetchPostPublicDecrypt()');
4317
+ return result;
4318
+ }
4319
+ async fetchPostUserDecrypt(payload, instanceOptions, fetchOptions) {
4320
+ const request = new RelayerV2AsyncRequest({
4321
+ relayerOperation: 'USER_DECRYPT',
4322
+ url: this.userDecrypt,
4323
+ payload,
4324
+ instanceOptions,
4325
+ ...fetchOptions,
4326
+ });
4327
+ const result = (await request.run());
4328
+ assertIsRelayerUserDecryptResult(result.result, 'fetchPostUserDecrypt()');
4329
+ return result.result;
4330
+ }
4331
+ }
4332
+
4333
+ class AbstractRelayerFhevm {
4334
+ }
4335
+
4336
+ class TFHECrs {
4337
+ _id;
4338
+ _compactPkeCrs;
4339
+ _bits;
4340
+ _srcUrl;
4341
+ constructor(params) {
4342
+ this._id = params.id;
4343
+ this._compactPkeCrs = params.compactPkeCrs;
4344
+ this._bits = params.bits;
4345
+ this._srcUrl = params.srcUrl;
4346
+ }
4347
+ /*
4348
+ {
4349
+ id: string,
4350
+ data: Uint8Array,
4351
+ bits: number
4352
+ srcUrl?: string
4353
+ }
4354
+ */
4355
+ static isKeyBytesType(value) {
4356
+ try {
4357
+ TFHECrs.assertKeyBytesType(value, '');
4358
+ return true;
4359
+ }
4360
+ catch {
4361
+ return false;
4362
+ }
4363
+ }
4364
+ /*
4365
+ {
4366
+ id: string,
4367
+ bits: number
4368
+ srcUrl: string
4369
+ }
4370
+ */
4371
+ static isKeyUrlType(value) {
4372
+ try {
4373
+ TFHECrs.assertKeyUrlType(value, '');
4374
+ return true;
4375
+ }
4376
+ catch {
4377
+ return false;
4378
+ }
4379
+ }
4380
+ /*
4381
+ {
4382
+ id: string,
4383
+ data: Uint8Array,
4384
+ bits: number
4385
+ srcUrl?: string
4386
+ }
4387
+ */
4388
+ static assertKeyBytesType(value, name) {
4389
+ assertRecordStringProperty(value, 'id', name);
4390
+ assertUint8ArrayProperty(value, 'data', name);
4391
+ assertRecordUintProperty(value, 'bits', name);
4392
+ if (isNonNullableRecordProperty(value, 'srcUrl')) {
4393
+ assertRecordStringProperty(value, 'srcUrl', name);
4394
+ }
4395
+ }
4396
+ /*
4397
+ {
4398
+ id: string,
4399
+ bits: number
4400
+ srcUrl: string
4401
+ }
4402
+ */
4403
+ static assertKeyUrlType(value, name) {
4404
+ assertRecordStringProperty(value, 'id', name);
4405
+ assertRecordUintProperty(value, 'bits', name);
4406
+ assertRecordStringProperty(value, 'srcUrl', name);
4407
+ }
4408
+ /*
4409
+ {
4410
+ 2048: {
4411
+ publicParamsId: string,
4412
+ publicParams: Uint8Array
4413
+ }
4414
+ }
4415
+ */
4416
+ static assertIsPublicParams2048BytesType(value, name) {
4417
+ assertNonNullableRecordProperty(value, '2048', name);
4418
+ assertRecordStringProperty(value['2048'], 'publicParamsId', `${name}.2048`);
4419
+ assertUint8ArrayProperty(value['2048'], 'publicParams', `${name}.2048`);
4420
+ }
4421
+ /*
4422
+ {
4423
+ 2048: {
4424
+ publicParamsId: string,
4425
+ publicParams: Uint8Array
4426
+ }
4427
+ }
4428
+ */
4429
+ static isPublicParams2048BytesType(value) {
4430
+ try {
4431
+ TFHECrs.assertIsPublicParams2048BytesType(value, '');
4432
+ return true;
4433
+ }
4434
+ catch {
4435
+ return false;
4436
+ }
4437
+ }
4438
+ static async fromBytesOrUrl(params) {
4439
+ if (TFHECrs.isKeyBytesType(params)) {
4440
+ return TFHECrs._fromBytes(params);
4441
+ }
4442
+ else if (TFHECrs.isPublicParams2048BytesType(params)) {
4443
+ return TFHECrs._fromPublicParamsBytes(params);
4444
+ }
4445
+ else if (TFHECrs.isKeyUrlType(params)) {
4446
+ return TFHECrs._fromUrl(params);
4447
+ }
4448
+ else {
4449
+ throw new Error('Invalid public key (deserialization failed)');
4450
+ }
4451
+ }
4452
+ static fromBytes(params) {
4453
+ try {
4454
+ TFHECrs.assertKeyBytesType(params, 'arg');
4455
+ return TFHECrs._fromBytes(params);
4456
+ }
4457
+ catch (e) {
4458
+ throw new Error('Invalid public key (deserialization failed)', {
4459
+ cause: e,
4460
+ });
4461
+ }
4462
+ }
4463
+ static _fromBytes(params) {
4464
+ const _params = {
4465
+ compactPkeCrs: TFHE.CompactPkeCrs.safe_deserialize(params.data, SERIALIZED_SIZE_LIMIT_CRS),
4466
+ id: params.id,
4467
+ bits: params.bits,
4468
+ srcUrl: params.srcUrl,
4469
+ };
4470
+ return new TFHECrs(_params);
4471
+ }
4472
+ static fromPublicParamsBytes(params) {
4473
+ try {
4474
+ TFHECrs.assertIsPublicParams2048BytesType(params, 'arg');
4475
+ return TFHECrs._fromPublicParamsBytes(params);
4476
+ }
4477
+ catch (e) {
4478
+ throw new Error('Invalid public key (deserialization failed)', {
4479
+ cause: e,
4480
+ });
4481
+ }
4482
+ }
4483
+ static _fromPublicParamsBytes(params) {
4484
+ return TFHECrs._fromBytes({
4485
+ bits: 2048,
4486
+ data: params['2048'].publicParams,
4487
+ id: params['2048'].publicParamsId,
4488
+ });
4489
+ }
4490
+ static async fromUrl(params) {
4491
+ try {
4492
+ TFHECrs.assertKeyUrlType(params, 'arg');
4493
+ return TFHECrs._fromUrl(params);
4494
+ }
4495
+ catch (e) {
4496
+ throw new Error('Impossible to fetch public key: wrong relayer url.', {
4497
+ cause: e,
4498
+ });
4499
+ }
4500
+ }
4501
+ static async _fromUrl(params) {
4502
+ TFHECrs.assertKeyUrlType(params, 'arg');
4503
+ const compactPkeCrsBytes = await fetchBytes(params.srcUrl);
4504
+ return TFHECrs.fromBytes({
4505
+ data: compactPkeCrsBytes,
4506
+ id: params.id,
4507
+ bits: params.bits,
4508
+ srcUrl: params.srcUrl,
4509
+ });
4510
+ }
4511
+ /*
4512
+ {
4513
+ id: string,
4514
+ bits: number,
4515
+ data: Uint8Array,
4516
+ srcUrl?: string
4517
+ }
4518
+ */
4519
+ toBytes() {
4520
+ return {
4521
+ data: this._compactPkeCrs.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS),
4522
+ id: this._id,
4523
+ bits: this._bits,
4524
+ ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
4525
+ };
4526
+ }
4527
+ /*
4528
+ {
4529
+ 2048: {
4530
+ publicParamsId: string,
4531
+ publicParams: TFHE.CompactPkeCrs
4532
+ }
4533
+ }
4534
+ */
4535
+ toPublicParamsWasm() {
4536
+ if (this._bits !== 2048) {
4537
+ throw new Error(`Unsupported PublicParams bits format ${this._bits}`);
4538
+ }
4539
+ const pp = {
4540
+ 2048: {
4541
+ publicParams: this._compactPkeCrs,
4542
+ publicParamsId: this._id,
4543
+ },
4544
+ };
4545
+ return pp;
4546
+ }
4547
+ /*
4548
+ {
4549
+ 2048: {
4550
+ publicParamsId: string,
4551
+ publicParams: Uint8Array
4552
+ }
4553
+ }
4554
+ */
4555
+ toPublicParamsBytes() {
4556
+ if (this._bits !== 2048) {
4557
+ throw new Error(`Unsupported PublicParams bits format ${this._bits}`);
4558
+ }
4559
+ const pp = {
4560
+ 2048: {
4561
+ publicParams: this.toBytes().data,
4562
+ publicParamsId: this._id,
4563
+ },
4564
+ };
4565
+ return pp;
4566
+ }
4567
+ }
4568
+
4569
+ class TFHEPublicKey {
4570
+ _id;
4571
+ _tfheCompactPublicKey;
4572
+ _srcUrl;
4573
+ constructor(params) {
4574
+ this._id = params.id;
4575
+ this._tfheCompactPublicKey = params.tfheCompactPublicKey;
4576
+ this._srcUrl = params.srcUrl;
4577
+ }
4578
+ /*
4579
+ {
4580
+ id: string,
4581
+ data: Uint8Array,
4582
+ srcUrl?: string
4583
+ }
4584
+ */
4585
+ static isKeyBytesType(value) {
4586
+ try {
4587
+ TFHEPublicKey.assertKeyBytesType(value, '');
4588
+ return true;
4589
+ }
4590
+ catch {
4591
+ return false;
4592
+ }
4593
+ }
4594
+ /*
4595
+ {
4596
+ id: string,
4597
+ srcUrl: string
4598
+ }
4599
+ */
4600
+ static isKeyUrlType(value) {
4601
+ try {
4602
+ TFHEPublicKey.assertKeyUrlType(value, '');
4603
+ return true;
4604
+ }
4605
+ catch {
4606
+ return false;
4607
+ }
4608
+ }
4609
+ /*
4610
+ {
4611
+ id: string,
4612
+ data: Uint8Array,
4613
+ srcUrl?: string
4614
+ }
4615
+ */
4616
+ static assertKeyBytesType(value, name) {
4617
+ assertRecordStringProperty(value, 'id', name);
4618
+ assertUint8ArrayProperty(value, 'data', name);
4619
+ if (isNonNullableRecordProperty(value, 'srcUrl')) {
4620
+ assertRecordStringProperty(value, 'srcUrl', name);
4621
+ }
4622
+ }
4623
+ /*
4624
+ {
4625
+ id: string,
4626
+ srcUrl: string
4627
+ }
4628
+ */
4629
+ static assertKeyUrlType(value, name) {
4630
+ assertRecordStringProperty(value, 'id', name);
4631
+ assertRecordStringProperty(value, 'srcUrl', name);
4632
+ }
4633
+ static async fromBytesOrUrl(params) {
4634
+ if (TFHEPublicKey.isKeyBytesType(params)) {
4635
+ return TFHEPublicKey._fromBytes(params);
4636
+ }
4637
+ else if (TFHEPublicKey.isKeyUrlType(params)) {
4638
+ return TFHEPublicKey._fromUrl(params);
4639
+ }
4640
+ else {
4641
+ throw new Error('Invalid public key (deserialization failed)');
4642
+ }
4643
+ }
4644
+ /*
4645
+ {
4646
+ id: string,
4647
+ data: Uint8Array,
4648
+ srcUrl?: string
4649
+ }
4650
+ */
4651
+ static fromBytes(params) {
4652
+ try {
4653
+ TFHEPublicKey.assertKeyBytesType(params, 'arg');
4654
+ return TFHEPublicKey._fromBytes(params);
4655
+ }
4656
+ catch (e) {
4657
+ throw new Error('Invalid public key (deserialization failed)', {
4658
+ cause: e,
4659
+ });
4660
+ }
4661
+ }
4662
+ /*
4663
+ {
4664
+ id: string,
4665
+ data: Uint8Array,
4666
+ srcUrl?: string
4667
+ }
4668
+ */
4669
+ static _fromBytes(params) {
4670
+ const _params = {
4671
+ tfheCompactPublicKey: TFHE.TfheCompactPublicKey.safe_deserialize(params.data, SERIALIZED_SIZE_LIMIT_PK),
4672
+ id: params.id,
4673
+ srcUrl: params.srcUrl,
4674
+ };
4675
+ return new TFHEPublicKey(_params);
4676
+ }
4677
+ /*
4678
+ {
4679
+ id: string,
4680
+ srcUrl: string
4681
+ }
4682
+ */
4683
+ static async fromUrl(params) {
4684
+ try {
4685
+ TFHEPublicKey.assertKeyUrlType(params, 'arg');
4686
+ return TFHEPublicKey._fromUrl(params);
4687
+ }
4688
+ catch (e) {
4689
+ throw new Error('Impossible to fetch public key: wrong relayer url.', {
4690
+ cause: e,
4691
+ });
4692
+ }
4693
+ }
4694
+ /*
4695
+ {
4696
+ id: string,
4697
+ srcUrl: string
4698
+ }
4699
+ */
4700
+ static async _fromUrl(params) {
4701
+ const tfheCompactPublicKeyBytes = await fetchBytes(params.srcUrl);
4702
+ return TFHEPublicKey.fromBytes({
4703
+ data: tfheCompactPublicKeyBytes,
4704
+ id: params.id,
4705
+ srcUrl: params.srcUrl,
4706
+ });
4707
+ }
4708
+ /*
4709
+ {
4710
+ id: string,
4711
+ data: Uint8Array,
4712
+ srcUrl?: string
4713
+ }
4714
+ */
4715
+ toBytes() {
4716
+ return {
4717
+ data: this._tfheCompactPublicKey.safe_serialize(SERIALIZED_SIZE_LIMIT_PK),
4718
+ id: this._id,
4719
+ ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
4720
+ };
4721
+ }
4722
+ /*
4723
+ {
4724
+ publicKey: TFHE.TfheCompactPublicKey
4725
+ publicKeyId: string
4726
+ }
4727
+ */
4728
+ toPublicKeyWasm() {
4729
+ return {
4730
+ publicKey: this._tfheCompactPublicKey,
4731
+ publicKeyId: this._id,
4732
+ };
4733
+ }
4734
+ /*
4735
+ {
4736
+ publicKey: Uint8Array
4737
+ publicKeyId: string
4738
+ }
4739
+ */
4740
+ toPublicKeyBytes() {
4741
+ return {
4742
+ publicKey: this.toBytes().data,
4743
+ publicKeyId: this._id,
4744
+ };
4745
+ }
4746
+ }
4747
+
4748
+ //const __KEY_URL_CACHE__: Record<string, RelayerV2PublicKey> = {};
4749
+ class RelayerV2PublicKey {
4750
+ _crs2048;
4751
+ _publicKey;
4752
+ constructor(params) {
4753
+ this._publicKey = params.publicKey;
4754
+ this._crs2048 = params.crs2048;
4755
+ }
4756
+ static tryFromBytes(value) {
4757
+ if (!isNonNullableRecordProperty(value, 'publicParams')) {
4758
+ return null;
4759
+ }
4760
+ if (!TFHECrs.isPublicParams2048BytesType(value.publicParams)) {
4761
+ return null;
4762
+ }
4763
+ if (!isNonNullableRecordProperty(value, 'publicKey')) {
4764
+ return null;
4765
+ }
4766
+ if (!TFHEPublicKey.isKeyBytesType(value.publicKey)) {
4767
+ return null;
4768
+ }
4769
+ const publicKey = value.publicKey;
4770
+ const publicParams = value.publicParams;
4771
+ return RelayerV2PublicKey.fromBytes({
4772
+ publicKey,
4773
+ publicParams,
4774
+ });
4775
+ }
4776
+ static fromBytes(params) {
4777
+ TFHECrs.assertIsPublicParams2048BytesType(params.publicParams, 'arg.publicParams');
4778
+ const publicKey = TFHEPublicKey.fromBytes(params.publicKey);
4779
+ const crs2048 = TFHECrs.fromBytes({
4780
+ id: params.publicParams[2048].publicParamsId,
4781
+ data: params.publicParams[2048].publicParams,
4782
+ bits: 2048,
4783
+ });
4784
+ return new RelayerV2PublicKey({ publicKey, crs2048 });
4785
+ }
4786
+ static async fromRelayerResponse(response) {
4787
+ try {
4788
+ assertIsRelayerV2GetResponseKeyUrl(response, 'RelayerV2GetResponseKeyUrl');
4789
+ const pub_key_0 = response.response.fheKeyInfo[0].fhePublicKey;
4790
+ const tfheCompactPublicKeyId = pub_key_0.dataId;
4791
+ const tfheCompactPublicKeyUrl = pub_key_0.urls[0];
4792
+ const crs_2048 = response.response.crs['2048'];
4793
+ const compactPkeCrs2048Id = crs_2048.dataId;
4794
+ const compactPkeCrs2048Url = crs_2048.urls[0];
4795
+ const publicKey = await TFHEPublicKey.fromUrl({
4796
+ id: tfheCompactPublicKeyId,
4797
+ srcUrl: tfheCompactPublicKeyUrl,
4798
+ });
4799
+ const crs = await TFHECrs.fromUrl({
4800
+ id: compactPkeCrs2048Id,
4801
+ bits: 2048,
4802
+ srcUrl: compactPkeCrs2048Url,
4803
+ });
4804
+ return new RelayerV2PublicKey({ publicKey, crs2048: crs });
4805
+ }
4806
+ catch (e) {
4807
+ throw new Error('Impossible to fetch public key: wrong relayer url.', {
4808
+ cause: e,
4809
+ });
4810
+ }
4811
+ }
4812
+ getTFHEPublicKey() {
4813
+ return this._publicKey;
4814
+ }
4815
+ getTFHECrs() {
4816
+ return this._crs2048;
4817
+ }
4818
+ toBytes() {
4819
+ return {
4820
+ publicKey: this._publicKey.toBytes(),
4821
+ publicParams: this._crs2048.toPublicParamsBytes(),
4822
+ };
4823
+ }
4824
+ }
4825
+
4826
+ class RelayerV2Fhevm extends AbstractRelayerFhevm {
4827
+ _relayerProvider;
4828
+ _relayerPublicKey;
4829
+ constructor(params) {
4830
+ super();
4831
+ this._relayerProvider = params.relayerProvider;
4832
+ this._relayerPublicKey = params.relayerPublicKey;
4833
+ }
4834
+ get version() {
4835
+ return 2;
4836
+ }
4837
+ get relayerVersionUrl() {
4838
+ return this.relayerProvider.url;
4839
+ }
4840
+ static async fromConfig(config) {
4841
+ const relayerProvider = new RelayerV2Provider(config.relayerVersionUrl);
4842
+ let relayerPublicKey = RelayerV2PublicKey.tryFromBytes(config);
4843
+ if (!relayerPublicKey) {
4844
+ const response = await relayerProvider.fetchGetKeyUrlV2();
4845
+ relayerPublicKey = await RelayerV2PublicKey.fromRelayerResponse(response);
4846
+ }
4847
+ return new RelayerV2Fhevm({
4848
+ relayerProvider,
4849
+ relayerPublicKey,
4850
+ });
4851
+ }
4852
+ get relayerProvider() {
4853
+ return this._relayerProvider;
4854
+ }
4855
+ getPublicKeyBytes() {
4856
+ return this._relayerPublicKey.getTFHEPublicKey().toPublicKeyBytes();
4857
+ }
4858
+ getPublicKeyWasm() {
4859
+ return this._relayerPublicKey.getTFHEPublicKey().toPublicKeyWasm();
4860
+ }
4861
+ getPublicParamsBytes(bits) {
4862
+ if (bits !== 2048) {
4863
+ throw new Error(`Unsupported PublicParams bits format ${bits}`);
4864
+ }
4865
+ return this._relayerPublicKey.getTFHECrs().toPublicParamsBytes()['2048'];
4866
+ }
4867
+ getPublicParamsWasm(bits) {
4868
+ if (bits !== 2048) {
4869
+ throw new Error(`Unsupported PublicParams bits format ${bits}`);
4870
+ }
4871
+ return this._relayerPublicKey.getTFHECrs().toPublicParamsWasm()['2048'];
4872
+ }
4873
+ }
4874
+
4875
+ class RelayerV1Fhevm extends AbstractRelayerFhevm {
4876
+ _relayerProvider;
4877
+ _publicKeyData;
4878
+ _publicParamsData;
4879
+ constructor(params) {
4880
+ super();
4881
+ this._relayerProvider = params.relayerProvider;
4882
+ this._publicKeyData = params.publicKeyData;
4883
+ this._publicParamsData = params.publicParamsData;
4884
+ }
4885
+ get version() {
4886
+ return 1;
4887
+ }
4888
+ get relayerVersionUrl() {
4889
+ return this.relayerProvider.url;
4890
+ }
4891
+ static async fromConfig(config) {
4892
+ const relayerProvider = new RelayerV1Provider(config.relayerVersionUrl);
4893
+ const publicKeyData = await getTfheCompactPublicKey(config);
4894
+ const publicParamsData = await getPublicParams(config);
4895
+ return new RelayerV1Fhevm({
4896
+ relayerProvider,
4897
+ publicKeyData,
4898
+ publicParamsData,
4899
+ });
4900
+ }
4901
+ get relayerProvider() {
4902
+ return this._relayerProvider;
4903
+ }
4904
+ getPublicKeyBytes() {
4905
+ return {
4906
+ publicKey: this._publicKeyData.publicKey.safe_serialize(SERIALIZED_SIZE_LIMIT_PK),
4907
+ publicKeyId: this._publicKeyData.publicKeyId,
4908
+ };
4909
+ }
4910
+ getPublicKeyWasm() {
4911
+ return {
4912
+ publicKey: this._publicKeyData.publicKey,
4913
+ publicKeyId: this._publicKeyData.publicKeyId,
4914
+ };
4915
+ }
4916
+ getPublicParamsBytes(bits) {
4917
+ if (bits !== 2048) {
4918
+ throw new Error(`Unsupported PublicParams bits format ${bits}`);
4919
+ }
4920
+ return {
4921
+ publicParams: this._publicParamsData['2048'].publicParams.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS),
4922
+ publicParamsId: this._publicParamsData['2048'].publicParamsId,
4923
+ };
4924
+ }
4925
+ getPublicParamsWasm(bits) {
4926
+ if (bits !== 2048) {
4927
+ throw new Error(`Unsupported PublicParams bits format ${bits}`);
4928
+ }
4929
+ return {
4930
+ publicParams: this._publicParamsData['2048'].publicParams,
4931
+ publicParamsId: this._publicParamsData['2048'].publicParamsId,
4932
+ };
4933
+ }
4934
+ }
4935
+
4936
+ async function createRelayerFhevm(config) {
4937
+ const resolved = _resolveRelayerUrl(config.relayerUrl, config.defaultRelayerVersion);
4938
+ if (!resolved) {
4939
+ throw new Error(`Invalid relayerUrl: ${config.relayerUrl}`);
4940
+ }
4941
+ if (resolved.version === 2) {
4942
+ return RelayerV2Fhevm.fromConfig({
4943
+ relayerVersionUrl: resolved.url,
4944
+ publicKey: config.publicKey,
4945
+ publicParams: config.publicParams,
4946
+ });
4947
+ }
4948
+ return RelayerV1Fhevm.fromConfig({
4949
+ relayerVersionUrl: resolved.url,
4950
+ publicKey: config.publicKey,
4951
+ publicParams: config.publicParams,
4952
+ });
4953
+ }
4954
+ function _resolveRelayerUrl(value, defaultVersion) {
4955
+ if (!value || typeof value !== 'string') {
4956
+ return null;
4957
+ }
4958
+ const urlNoSlash = removeSuffix(value, '/');
4959
+ if (!URL.canParse(urlNoSlash)) {
4960
+ return null;
4961
+ }
4962
+ if (urlNoSlash.endsWith('/v1')) {
4963
+ return {
4964
+ url: value,
4965
+ version: 1,
4966
+ };
4967
+ }
4968
+ if (urlNoSlash.endsWith('/v2')) {
4969
+ return {
4970
+ url: value,
4971
+ version: 2,
4972
+ };
4973
+ }
4974
+ if (typeof defaultVersion !== 'number') {
4975
+ throw new Error(`relayerUrl cannot be resolved. (value=${value})`);
4976
+ }
4977
+ return {
4978
+ url: `${urlNoSlash}/v${defaultVersion}`,
4979
+ version: defaultVersion,
4980
+ };
4981
+ }
4982
+
4983
+ ////////////////////////////////////////////////////////////////////////////////
4984
+ // MainnetConfig
4985
+ ////////////////////////////////////////////////////////////////////////////////
4986
+ const MainnetConfig = {
4987
+ aclContractAddress: '0xcA2E8f1F656CD25C01F05d0b243Ab1ecd4a8ffb6',
4988
+ kmsContractAddress: '0x77627828a55156b04Ac0DC0eb30467f1a552BB03',
4989
+ inputVerifierContractAddress: '0xCe0FC2e05CFff1B719EFF7169f7D80Af770c8EA2',
4990
+ verifyingContractAddressDecryption: '0x0f6024a97684f7d90ddb0fAAD79cB15F2C888D24',
4991
+ verifyingContractAddressInputVerification: '0xcB1bB072f38bdAF0F328CdEf1Fc6eDa1DF029287',
4992
+ chainId: 1,
4993
+ gatewayChainId: 261131,
4994
+ network: 'https://ethereum-rpc.publicnode.com',
4995
+ relayerUrl: 'https://relayer.mainnet.zama.org',
4996
+ };
4997
+ Object.freeze(MainnetConfig);
4998
+ ////////////////////////////////////////////////////////////////////////////////
4999
+ // SepoliaConfig
5000
+ ////////////////////////////////////////////////////////////////////////////////
5001
+ const SepoliaConfig = {
5002
+ aclContractAddress: '0xf0Ffdc93b7E186bC2f8CB3dAA75D86d1930A433D',
5003
+ kmsContractAddress: '0xbE0E383937d564D7FF0BC3b46c51f0bF8d5C311A',
5004
+ inputVerifierContractAddress: '0xBBC1fFCdc7C316aAAd72E807D9b0272BE8F84DA0',
5005
+ verifyingContractAddressDecryption: '0x5D8BD78e2ea6bbE41f26dFe9fdaEAa349e077478',
5006
+ verifyingContractAddressInputVerification: '0x483b9dE06E4E4C7D35CCf5837A1668487406D955',
5007
+ chainId: 11155111,
5008
+ gatewayChainId: 10901,
5009
+ network: 'https://ethereum-sepolia-rpc.publicnode.com',
5010
+ relayerUrl: 'https://relayer.testnet.zama.org',
5011
+ };
5012
+ Object.freeze(SepoliaConfig);
5013
+ ////////////////////////////////////////////////////////////////////////////////
5014
+ // createInstance
5015
+ ////////////////////////////////////////////////////////////////////////////////
5016
+ const createInstance = async (config) => {
5017
+ const { verifyingContractAddressDecryption, verifyingContractAddressInputVerification, publicKey, inputVerifierContractAddress, kmsContractAddress, aclContractAddress, gatewayChainId, auth, } = config;
5018
+ if (!isChecksummedAddress(aclContractAddress)) {
5019
+ throw new Error('ACL contract address is not valid or empty');
5020
+ }
5021
+ if (!isChecksummedAddress(inputVerifierContractAddress)) {
5022
+ throw new Error('InputVerifier contract address is not valid or empty');
5023
+ }
5024
+ if (!isChecksummedAddress(kmsContractAddress)) {
5025
+ throw new Error('KMS contract address is not valid or empty');
5026
+ }
5027
+ if (!isChecksummedAddress(verifyingContractAddressDecryption)) {
5028
+ throw new Error('Verifying contract for Decryption address is not valid or empty');
5029
+ }
5030
+ if (!isChecksummedAddress(verifyingContractAddressInputVerification)) {
5031
+ throw new Error('Verifying contract for InputVerification address is not valid or empty');
5032
+ }
5033
+ if (publicKey && !(publicKey.data instanceof Uint8Array)) {
5034
+ throw new Error('publicKey must be a Uint8Array');
5035
+ }
5036
+ // TODO change argument
5037
+ // provider is never undefined | null here!
5038
+ const provider = getProvider(config.network);
5039
+ const relayerUrl = config.relayerUrl ?? SepoliaConfig.relayerUrl;
5040
+ const relayerFhevm = await createRelayerFhevm({
5041
+ relayerUrl,
5042
+ publicKey: config.publicKey,
5043
+ publicParams: config.publicParams,
5044
+ defaultRelayerVersion: 1,
5045
+ });
5046
+ const chainId = await getChainId(provider, config);
5047
+ const kmsSigners = await getKMSSigners(provider, kmsContractAddress);
5048
+ const thresholdKMSSigners = await getKMSSignersThreshold(provider, kmsContractAddress);
5049
+ const coprocessorSigners = await getCoprocessorSigners(provider, inputVerifierContractAddress);
5050
+ const thresholdCoprocessorSigners = await getCoprocessorSignersThreshold(provider, inputVerifierContractAddress);
5051
+ return {
5052
+ createEncryptedInput: createRelayerEncryptedInput(aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerFhevm.relayerProvider, relayerFhevm.getPublicKeyWasm().publicKey, { 2048: relayerFhevm.getPublicParamsWasm(2048) }, coprocessorSigners, thresholdCoprocessorSigners, auth && { auth }),
5053
+ generateKeypair,
5054
+ createEIP712: createEIP712(verifyingContractAddressDecryption, chainId),
5055
+ publicDecrypt: publicDecryptRequest(kmsSigners, thresholdKMSSigners, gatewayChainId, verifyingContractAddressDecryption, aclContractAddress,
5056
+ //cleanURL(config.relayerUrl),
5057
+ relayerFhevm.relayerProvider, provider, auth && { auth }),
5058
+ userDecrypt: userDecryptRequest(kmsSigners, gatewayChainId, chainId, verifyingContractAddressDecryption, aclContractAddress,
5059
+ //cleanURL(config.relayerUrl),
5060
+ relayerFhevm.relayerProvider, provider, auth && { auth }),
5061
+ getPublicKey: () => relayerFhevm.getPublicKeyBytes(),
5062
+ getPublicParams: (bits) => relayerFhevm.getPublicParamsBytes(bits),
5063
+ // getPublicKey: () =>
5064
+ // publicKeyData.publicKey
5065
+ // ? {
5066
+ // publicKey: publicKeyData.publicKey.safe_serialize(
5067
+ // SERIALIZED_SIZE_LIMIT_PK,
5068
+ // ),
5069
+ // publicKeyId: publicKeyData.publicKeyId,
5070
+ // }
5071
+ // : null,
5072
+ // getPublicParams: (bits: keyof PublicParams) => {
5073
+ // if (publicParamsData[bits]) {
5074
+ // return {
5075
+ // publicParams: publicParamsData[bits]!.publicParams.safe_serialize(
5076
+ // SERIALIZED_SIZE_LIMIT_CRS,
5077
+ // ),
5078
+ // publicParamsId: publicParamsData[bits]!.publicParamsId,
5079
+ // };
5080
+ // }
5081
+ // return null;
5082
+ // },
1487
5083
  };
1488
5084
  };
1489
5085
 
@@ -1507,7 +5103,6 @@ const createTfhePublicKey = () => {
1507
5103
  global.TFHE = TFHEPkg__namespace;
1508
5104
  global.TKMS = TKMSPkg__namespace;
1509
5105
 
1510
- exports.ENCRYPTION_TYPES = ENCRYPTION_TYPES;
1511
5106
  exports.MainnetConfig = MainnetConfig;
1512
5107
  exports.SepoliaConfig = SepoliaConfig;
1513
5108
  exports.createEIP712 = createEIP712;