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