@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/web.js CHANGED
@@ -1,6 +1,4 @@
1
- import { JsonRpcProvider, BrowserProvider, Contract, getAddress as getAddress$2, ethers, isAddress, AbiCoder } from 'ethers';
2
- import createHash from 'keccak';
3
- import fetchRetry from 'fetch-retry';
1
+ import { JsonRpcProvider, BrowserProvider, Contract, getAddress as getAddress$2, isAddress as isAddress$1, keccak256, ethers, AbiCoder } from 'ethers';
4
2
  import { threads } from 'wasm-feature-detect';
5
3
 
6
4
  var global$1 = (typeof global !== "undefined" ? global :
@@ -16255,62 +16253,10 @@ async function __wbg_init(module_or_path) {
16255
16253
  const SERIALIZED_SIZE_LIMIT_CIPHERTEXT = BigInt(1024 * 1024 * 512);
16256
16254
  const SERIALIZED_SIZE_LIMIT_PK = BigInt(1024 * 1024 * 512);
16257
16255
  const SERIALIZED_SIZE_LIMIT_CRS = BigInt(1024 * 1024 * 512);
16258
- const cleanURL = (url) => {
16259
- if (!url)
16260
- return '';
16261
- return url.endsWith('/') ? url.slice(0, -1) : url;
16262
- };
16263
- const numberToHex = (num) => {
16264
- let hex = num.toString(16);
16265
- return hex.length % 2 ? '0' + hex : hex;
16266
- };
16267
- const fromHexString = (hexString) => {
16268
- const arr = hexString.replace(/^(0x)/, '').match(/.{1,2}/g);
16269
- if (!arr)
16270
- return new Uint8Array();
16271
- return Uint8Array.from(arr.map((byte) => parseInt(byte, 16)));
16272
- };
16273
- function toHexString(bytes, with0x = false) {
16274
- return `${with0x ? '0x' : ''}${bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')}`;
16275
- }
16276
- const bytesToBigInt = function (byteArray) {
16277
- if (!byteArray || byteArray?.length === 0) {
16278
- return BigInt(0);
16279
- }
16280
- const hex = Array.from(byteArray)
16281
- .map((b) => b.toString(16).padStart(2, '0')) // byte to hex
16282
- .join('');
16283
- return BigInt(`0x${hex}`);
16284
- };
16285
- function ensure0x(s) {
16286
- return !s.startsWith('0x') ? `0x${s}` : s;
16287
- }
16288
16256
 
16289
- function setAuth(init, auth) {
16290
- if (auth) {
16291
- switch (auth.__type) {
16292
- case 'BearerToken':
16293
- init.headers['Authorization'] =
16294
- `Bearer ${auth.token}`;
16295
- break;
16296
- case 'ApiKeyHeader':
16297
- init.headers[auth.header || 'x-api-key'] =
16298
- auth.value;
16299
- break;
16300
- case 'ApiKeyCookie':
16301
- if (typeof window !== 'undefined') {
16302
- document.cookie = `${auth.cookie || 'x-api-key'}=${auth.value}; path=/; SameSite=Lax; Secure; HttpOnly;`;
16303
- init.credentials = 'include';
16304
- }
16305
- else {
16306
- let cookie = `${auth.cookie || 'x-api-key'}=${auth.value};`;
16307
- init.headers['Cookie'] = cookie;
16308
- }
16309
- break;
16310
- }
16311
- }
16312
- return init;
16313
- }
16257
+ // This file is auto-generated
16258
+ const version = '0.4.0-alpha.1';
16259
+ const sdkName = '@zama-fhe/relayer-sdk';
16314
16260
 
16315
16261
  function getErrorCause(e) {
16316
16262
  if (e instanceof Error && typeof e.cause === 'object' && e.cause !== null) {
@@ -16365,18 +16311,27 @@ async function throwRelayerResponseError(operation, response) {
16365
16311
  }
16366
16312
  }
16367
16313
  }
16314
+ let responseJson;
16315
+ try {
16316
+ responseJson = await response.json();
16317
+ }
16318
+ catch {
16319
+ responseJson = '';
16320
+ }
16368
16321
  const cause = {
16369
16322
  code: 'RELAYER_FETCH_ERROR',
16370
16323
  operation,
16371
16324
  status: response.status,
16372
16325
  statusText: response.statusText,
16373
16326
  url: response.url,
16327
+ response,
16328
+ responseJson,
16374
16329
  };
16375
16330
  throw new Error(message, {
16376
16331
  cause,
16377
16332
  });
16378
16333
  }
16379
- function throwRelayerJSONError(operation, error) {
16334
+ function throwRelayerJSONError(operation, error, response) {
16380
16335
  let message;
16381
16336
  switch (operation) {
16382
16337
  case 'PUBLIC_DECRYPT': {
@@ -16396,6 +16351,7 @@ function throwRelayerJSONError(operation, error) {
16396
16351
  code: 'RELAYER_NO_JSON_ERROR',
16397
16352
  operation,
16398
16353
  error,
16354
+ response,
16399
16355
  };
16400
16356
  throw new Error(message, {
16401
16357
  cause,
@@ -16482,7 +16438,40 @@ function throwRelayerUnknownError(operation, error, message) {
16482
16438
  });
16483
16439
  }
16484
16440
 
16485
- function assertIsRelayerFetchResponseJson(json) {
16441
+ /**
16442
+ * Set the authentication method for the request. The default is no authentication.
16443
+ * It supports:
16444
+ * - Bearer Token
16445
+ * - Custom header
16446
+ * - Custom cookie
16447
+ */
16448
+ function setAuth(init, auth) {
16449
+ if (auth) {
16450
+ switch (auth.__type) {
16451
+ case 'BearerToken':
16452
+ init.headers['Authorization'] =
16453
+ `Bearer ${auth.token}`;
16454
+ break;
16455
+ case 'ApiKeyHeader':
16456
+ init.headers[auth.header || 'x-api-key'] =
16457
+ auth.value;
16458
+ break;
16459
+ case 'ApiKeyCookie':
16460
+ if (typeof window !== 'undefined') {
16461
+ document.cookie = `${auth.cookie || 'x-api-key'}=${auth.value}; path=/; SameSite=Lax; Secure; HttpOnly;`;
16462
+ init.credentials = 'include';
16463
+ }
16464
+ else {
16465
+ let cookie = `${auth.cookie || 'x-api-key'}=${auth.value};`;
16466
+ init.headers['Cookie'] = cookie;
16467
+ }
16468
+ break;
16469
+ }
16470
+ }
16471
+ return init;
16472
+ }
16473
+
16474
+ function assertIsRelayerV1FetchResponseJson(json) {
16486
16475
  if (!json || typeof json !== 'object') {
16487
16476
  throw new Error('Unexpected response JSON.');
16488
16477
  }
@@ -16492,11 +16481,13 @@ function assertIsRelayerFetchResponseJson(json) {
16492
16481
  throw new Error("Unexpected response JSON format: missing 'response' property.");
16493
16482
  }
16494
16483
  }
16495
- async function fetchRelayerJsonRpcPost(relayerOperation, url, payload, options) {
16484
+ async function fetchRelayerV1Post(relayerOperation, url, payload, options) {
16496
16485
  const init = setAuth({
16497
16486
  method: 'POST',
16498
16487
  headers: {
16499
16488
  'Content-Type': 'application/json',
16489
+ 'ZAMA-SDK-VERSION': `${version}`,
16490
+ 'ZAMA-SDK-NAME': `${sdkName}`,
16500
16491
  },
16501
16492
  body: JSON.stringify(payload),
16502
16493
  }, options?.auth);
@@ -16516,10 +16507,10 @@ async function fetchRelayerJsonRpcPost(relayerOperation, url, payload, options)
16516
16507
  parsed = await response.json();
16517
16508
  }
16518
16509
  catch (e) {
16519
- throwRelayerJSONError(relayerOperation, e);
16510
+ throwRelayerJSONError(relayerOperation, e, response);
16520
16511
  }
16521
16512
  try {
16522
- assertIsRelayerFetchResponseJson(parsed);
16513
+ assertIsRelayerV1FetchResponseJson(parsed);
16523
16514
  json = parsed;
16524
16515
  }
16525
16516
  catch (e) {
@@ -16527,11 +16518,18 @@ async function fetchRelayerJsonRpcPost(relayerOperation, url, payload, options)
16527
16518
  }
16528
16519
  return json;
16529
16520
  }
16530
- async function fetchRelayerGet(relayerOperation, url) {
16521
+ async function fetchRelayerV1Get(relayerOperation, url) {
16522
+ const init = {
16523
+ method: 'GET',
16524
+ headers: {
16525
+ 'ZAMA-SDK-VERSION': `${version}`,
16526
+ 'ZAMA-SDK-NAME': `${sdkName}`,
16527
+ },
16528
+ };
16531
16529
  let response;
16532
16530
  let json;
16533
16531
  try {
16534
- response = await fetch(url);
16532
+ response = await fetch(url, init);
16535
16533
  }
16536
16534
  catch (e) {
16537
16535
  throwRelayerUnknownError(relayerOperation, e);
@@ -16544,10 +16542,10 @@ async function fetchRelayerGet(relayerOperation, url) {
16544
16542
  parsed = await response.json();
16545
16543
  }
16546
16544
  catch (e) {
16547
- throwRelayerJSONError(relayerOperation, e);
16545
+ throwRelayerJSONError(relayerOperation, e, response);
16548
16546
  }
16549
16547
  try {
16550
- assertIsRelayerFetchResponseJson(parsed);
16548
+ assertIsRelayerV1FetchResponseJson(parsed);
16551
16549
  json = parsed;
16552
16550
  }
16553
16551
  catch (e) {
@@ -16556,49 +16554,13 @@ async function fetchRelayerGet(relayerOperation, url) {
16556
16554
  return json;
16557
16555
  }
16558
16556
 
16559
- // export type RelayerKeysItem = {
16560
- // data_id: string;
16561
- // param_choice: number;
16562
- // urls: string[];
16563
- // signatures: string[];
16564
- // };
16565
- // export type RelayerKey = {
16566
- // data_id: string;
16567
- // param_choice: number;
16568
- // signatures: string[];
16569
- // urls: string[];
16570
- // };
16571
- // export type RelayerKeys = {
16572
- // response: {
16573
- // fhe_key_info: {
16574
- // fhe_public_key: RelayerKey;
16575
- // fhe_server_key: RelayerKey;
16576
- // }[];
16577
- // verf_public_key: {
16578
- // key_id: string;
16579
- // server_id: number;
16580
- // verf_public_key_address: string;
16581
- // verf_public_key_url: string;
16582
- // }[];
16583
- // crs: {
16584
- // [key: string]: RelayerKeysItem;
16585
- // };
16586
- // };
16587
- // status: string;
16588
- // };
16589
16557
  const keyurlCache = {};
16590
- const getKeysFromRelayer = async (url, publicKeyId) => {
16591
- if (keyurlCache[url]) {
16592
- return keyurlCache[url];
16558
+ const getKeysFromRelayer = async (versionUrl, publicKeyId) => {
16559
+ if (keyurlCache[versionUrl]) {
16560
+ return keyurlCache[versionUrl];
16593
16561
  }
16594
- const data = await fetchRelayerGet('KEY_URL', `${url}/v1/keyurl`);
16562
+ const data = await fetchRelayerV1Get('KEY_URL', `${versionUrl}/keyurl`);
16595
16563
  try {
16596
- // const response = await fetch(`${url}/v1/keyurl`);
16597
- // if (!response.ok) {
16598
- // await throwRelayerResponseError("KEY_URL", response);
16599
- // }
16600
- //const data: RelayerKeys = await response.json();
16601
- //if (data) {
16602
16564
  let pubKeyUrl;
16603
16565
  // If no publicKeyId is provided, use the first one
16604
16566
  // Warning: if there are multiple keys available, the first one will most likely never be the
@@ -16670,11 +16632,8 @@ const getKeysFromRelayer = async (url, publicKeyId) => {
16670
16632
  },
16671
16633
  },
16672
16634
  };
16673
- keyurlCache[url] = result;
16635
+ keyurlCache[versionUrl] = result;
16674
16636
  return result;
16675
- // } else {
16676
- // throw new Error('No public key available');
16677
- // }
16678
16637
  }
16679
16638
  catch (e) {
16680
16639
  throw new Error('Impossible to fetch public key: wrong relayer url.', {
@@ -16683,6 +16642,430 @@ const getKeysFromRelayer = async (url, publicKeyId) => {
16683
16642
  }
16684
16643
  };
16685
16644
 
16645
+ class RelayerErrorBase extends Error {
16646
+ name = 'RelayerErrorBase';
16647
+ _details;
16648
+ _docsPath;
16649
+ _docsUrl;
16650
+ _version;
16651
+ static VERSION = version;
16652
+ static DEFAULT_DOCS_BASE_URL = 'https//docs.zama.org';
16653
+ static FULL_VERSION = `@zama-fhe/relayer-sdk@${RelayerErrorBase.VERSION}`;
16654
+ constructor(params) {
16655
+ let details;
16656
+ let docsPath;
16657
+ if (params.cause instanceof RelayerErrorBase) {
16658
+ docsPath = params.docsPath || params.cause.docsPath;
16659
+ details = params.details || params.cause.details;
16660
+ }
16661
+ else {
16662
+ docsPath = params.docsPath;
16663
+ details = params.details || params.cause?.message;
16664
+ }
16665
+ const docsUrl = docsPath
16666
+ ? `${params.docsBaseUrl ?? RelayerErrorBase.DEFAULT_DOCS_BASE_URL}${docsPath}${params.docsSlug ? `#${params.docsSlug}` : ''}`
16667
+ : undefined;
16668
+ const message = [
16669
+ params.message || 'An error occurred.',
16670
+ '',
16671
+ ...(params.metaMessages ? [...params.metaMessages, ''] : []),
16672
+ ...(docsUrl ? [`Docs: ${docsUrl}`] : []),
16673
+ ...(details ? [`Details: ${details}`] : []),
16674
+ `Version: ${RelayerErrorBase.FULL_VERSION}`,
16675
+ ].join('\n');
16676
+ super(message, params.cause ? { cause: params.cause } : undefined);
16677
+ // This line is critical. If removed 'instanceof' will always fail
16678
+ // Restore prototype chain (required when extending Error in TypeScript)
16679
+ Object.setPrototypeOf(this, new.target.prototype);
16680
+ this._details = details;
16681
+ this._docsPath = docsPath;
16682
+ this._docsUrl = docsUrl;
16683
+ this._version = RelayerErrorBase.VERSION;
16684
+ this.name = params.name ?? this.name;
16685
+ }
16686
+ get docsPath() {
16687
+ return this._docsPath;
16688
+ }
16689
+ get docsUrl() {
16690
+ return this._docsUrl;
16691
+ }
16692
+ get details() {
16693
+ return this._details;
16694
+ }
16695
+ get version() {
16696
+ return this._version;
16697
+ }
16698
+ }
16699
+
16700
+ class InvalidPropertyError extends RelayerErrorBase {
16701
+ _objName;
16702
+ _property;
16703
+ _expectedType;
16704
+ _index;
16705
+ _value;
16706
+ _type;
16707
+ _expectedValue;
16708
+ constructor({ objName, property, index, type, value, expectedValue, expectedType, }) {
16709
+ let missing = type === 'undefined' && expectedValue !== undefined;
16710
+ let varname;
16711
+ if (!property || property === '') {
16712
+ varname = index !== undefined ? `${objName}[${index}]` : `${objName}`;
16713
+ }
16714
+ else {
16715
+ varname =
16716
+ index !== undefined
16717
+ ? `${objName}.${property}[${index}]`
16718
+ : `${objName}.${property}`;
16719
+ }
16720
+ let message = missing
16721
+ ? `InvalidPropertyError: Missing '${varname}'`
16722
+ : `InvalidPropertyError: ${varname}`;
16723
+ if (type === expectedType) {
16724
+ if (value !== undefined) {
16725
+ message += ` unexpected value ${value}`;
16726
+ }
16727
+ }
16728
+ else {
16729
+ if (missing) {
16730
+ if (Array.isArray(expectedValue)) {
16731
+ expectedValue = expectedValue.join('|');
16732
+ }
16733
+ message += `, expected '${varname}: ${expectedValue}'.`;
16734
+ }
16735
+ else if (expectedType !== 'unknown' && type !== 'unknown') {
16736
+ message += ` not a ${expectedType}`;
16737
+ if (type) {
16738
+ message += `, type is ${type}`;
16739
+ }
16740
+ }
16741
+ }
16742
+ super({
16743
+ message,
16744
+ name: 'InvalidPropertyError',
16745
+ });
16746
+ this._objName = objName;
16747
+ this._property = property;
16748
+ this._value = value;
16749
+ this._type = type;
16750
+ this._expectedValue = expectedValue;
16751
+ this._expectedType = expectedType;
16752
+ this._index = index;
16753
+ }
16754
+ static missingProperty({ objName, property, expectedType, expectedValue, }) {
16755
+ return new InvalidPropertyError({
16756
+ objName,
16757
+ property,
16758
+ expectedType,
16759
+ expectedValue,
16760
+ type: 'undefined',
16761
+ });
16762
+ }
16763
+ static invalidFormat({ objName, property, }) {
16764
+ return new InvalidPropertyError({
16765
+ objName,
16766
+ property,
16767
+ expectedType: 'unknown',
16768
+ });
16769
+ }
16770
+ static invalidObject({ objName, expectedType, type, }) {
16771
+ return new InvalidPropertyError({
16772
+ objName,
16773
+ property: '',
16774
+ expectedType,
16775
+ type,
16776
+ });
16777
+ }
16778
+ }
16779
+
16780
+ /**
16781
+ * Type guard that checks if a property exists on an object and is non-null/non-undefined.
16782
+ *
16783
+ * @template K - The property key type (string literal)
16784
+ * @param o - The value to check (can be any type)
16785
+ * @param property - The property name to check for
16786
+ * @returns True if `o` is an object with the specified property that is not null or undefined
16787
+ *
16788
+ * @example
16789
+ * ```typescript
16790
+ * const data: unknown = { name: "Alice", age: 30 };
16791
+ * if (isNonNullableRecordProperty(data, 'name')) {
16792
+ * console.log(data.name); // OK
16793
+ * }
16794
+ * ```
16795
+ */
16796
+ function isNonNullableRecordProperty(o, property) {
16797
+ if (!o ||
16798
+ typeof o !== 'object' ||
16799
+ !(property in o) ||
16800
+ o[property] === undefined ||
16801
+ o[property] === null) {
16802
+ return false;
16803
+ }
16804
+ return true;
16805
+ }
16806
+ /**
16807
+ * Assertion function that validates a property exists on an object and is non-null/non-undefined.
16808
+ * Throws an `InvalidPropertyError` if validation fails.
16809
+ *
16810
+ * @template K - The property key type (string literal)
16811
+ * @param o - The value to validate (can be any type)
16812
+ * @param property - The property name to check for
16813
+ * @param objName - The name of the object being validated (used in error messages)
16814
+ * @throws {InvalidPropertyError} When the property is missing, null, or undefined
16815
+ * @throws {never} No other errors are thrown
16816
+ *
16817
+ * @example
16818
+ * ```typescript
16819
+ * function processUser(data: unknown) {
16820
+ * assertNonNullableRecordProperty(data, 'userId', 'user');
16821
+ * console.log(data.userId);
16822
+ * }
16823
+ * ```
16824
+ */
16825
+ function assertNonNullableRecordProperty(o, property, objName) {
16826
+ if (!isNonNullableRecordProperty(o, property)) {
16827
+ throw new InvalidPropertyError({
16828
+ objName,
16829
+ property,
16830
+ expectedType: 'non-nullable',
16831
+ type: typeofProperty(o, property),
16832
+ });
16833
+ }
16834
+ }
16835
+ /**
16836
+ * Type guard that checks if a property exists on an object and is an array.
16837
+ *
16838
+ * @template K - The property key type (string literal)
16839
+ * @param o - The value to check (can be any type)
16840
+ * @param property - The property name to check for
16841
+ * @returns True if `o` is an object with the specified property that is a non-null array
16842
+ *
16843
+ * @example
16844
+ * ```typescript
16845
+ * const data: unknown = { items: [1, 2, 3], count: 42 };
16846
+ * if (isRecordArrayProperty(data, 'items')) {
16847
+ * console.log(data.items.length); // OK
16848
+ * data.items.forEach(item => console.log(item)); // OK
16849
+ * }
16850
+ * ```
16851
+ */
16852
+ function isRecordArrayProperty(o, property) {
16853
+ if (!isNonNullableRecordProperty(o, property)) {
16854
+ return false;
16855
+ }
16856
+ return Array.isArray(o[property]);
16857
+ }
16858
+ /**
16859
+ * Assertion function that validates a property exists on an object and is an array.
16860
+ * Throws an `InvalidPropertyError` if validation fails.
16861
+ *
16862
+ * @template K - The property key type (string literal)
16863
+ * @param o - The value to validate (can be any type)
16864
+ * @param property - The property name to check for
16865
+ * @param objName - The name of the object being validated (used in error messages)
16866
+ * @throws {InvalidPropertyError} When the property is missing, null, or not an array
16867
+ * @throws {never} No other errors are thrown
16868
+ *
16869
+ * @example
16870
+ * ```typescript
16871
+ * function processResults(data: unknown) {
16872
+ * assertRecordArrayProperty(data, 'results', 'response');
16873
+ * console.log(`Found ${data.results.length} results`);
16874
+ * data.results.forEach(result => processResult(result));
16875
+ * }
16876
+ * ```
16877
+ */
16878
+ function assertRecordArrayProperty(o, property, objName) {
16879
+ if (!isRecordArrayProperty(o, property)) {
16880
+ throw new InvalidPropertyError({
16881
+ objName,
16882
+ property,
16883
+ expectedType: 'Array',
16884
+ type: typeofProperty(o, property),
16885
+ });
16886
+ }
16887
+ }
16888
+ function isRecordBooleanProperty(o, property) {
16889
+ if (!isNonNullableRecordProperty(o, property)) {
16890
+ return false;
16891
+ }
16892
+ return typeof o[property] === 'boolean';
16893
+ }
16894
+ function assertRecordBooleanProperty(o, property, objName, expectedValue) {
16895
+ if (!isRecordBooleanProperty(o, property))
16896
+ throw new InvalidPropertyError({
16897
+ objName,
16898
+ property,
16899
+ expectedType: 'boolean',
16900
+ type: typeofProperty(o, property),
16901
+ });
16902
+ if (expectedValue !== undefined) {
16903
+ if (o[property] !== expectedValue) {
16904
+ throw new InvalidPropertyError({
16905
+ objName,
16906
+ property,
16907
+ expectedType: 'boolean',
16908
+ expectedValue: String(expectedValue),
16909
+ type: typeof o[property],
16910
+ value: String(o[property]),
16911
+ });
16912
+ }
16913
+ }
16914
+ }
16915
+ function typeofProperty(o, property) {
16916
+ if (isNonNullableRecordProperty(o, property)) {
16917
+ return typeof o[property];
16918
+ }
16919
+ return 'undefined';
16920
+ }
16921
+
16922
+ class InternalError extends RelayerErrorBase {
16923
+ constructor(params) {
16924
+ super({
16925
+ ...params,
16926
+ name: 'InternalError',
16927
+ message: params.message ?? 'internal error',
16928
+ });
16929
+ }
16930
+ }
16931
+ function assertRelayer(condition) {
16932
+ if (!condition) {
16933
+ throw new InternalError({ message: 'Assertion failed' });
16934
+ }
16935
+ }
16936
+
16937
+ function removeSuffix(s, suffix) {
16938
+ if (!s) {
16939
+ return '';
16940
+ }
16941
+ if (suffix.length === 0) {
16942
+ return s;
16943
+ }
16944
+ return s.endsWith(suffix) ? s.slice(0, -suffix.length) : s;
16945
+ }
16946
+ function is0x(s) {
16947
+ return typeof s === 'string' && s.startsWith('0x');
16948
+ }
16949
+ function isNo0x(s) {
16950
+ return typeof s === 'string' && !s.startsWith('0x');
16951
+ }
16952
+ function ensure0x(s) {
16953
+ return !s.startsWith('0x') ? `0x${s}` : s;
16954
+ }
16955
+ function remove0x(s) {
16956
+ return s.startsWith('0x') ? s.substring(2) : s;
16957
+ }
16958
+ /**
16959
+ * Type guard that checks if a property exists on an object and is a string.
16960
+ *
16961
+ * @template K - The property key type (string literal)
16962
+ * @param o - The value to check (can be any type)
16963
+ * @param property - The property name to check for
16964
+ * @returns True if `o` is an object with the specified property that is a non-null string
16965
+ *
16966
+ * @example
16967
+ * ```typescript
16968
+ * const data: unknown = { status: "active", count: 42 };
16969
+ * if (isRecordStringProperty(data, 'status')) {
16970
+ * console.log(data.status.toUpperCase()); // OK
16971
+ * }
16972
+ * ```
16973
+ */
16974
+ function isRecordStringProperty(o, property) {
16975
+ if (!isNonNullableRecordProperty(o, property)) {
16976
+ return false;
16977
+ }
16978
+ return typeof o[property] === 'string';
16979
+ }
16980
+ /**
16981
+ * Assertion function that validates a property exists on an object, is a string,
16982
+ * and optionally matches specific expected value(s).
16983
+ * Throws an `InvalidPropertyError` if validation fails.
16984
+ *
16985
+ * @template K - The property key type (string literal)
16986
+ * @param o - The value to validate (can be any type)
16987
+ * @param property - The property name to check for
16988
+ * @param objName - The name of the object being validated (used in error messages)
16989
+ * @param expectedValue - Optional specific string value or array of allowed values to match against
16990
+ * @throws {InvalidPropertyError} When the property is missing, not a string, or doesn't match expectedValue
16991
+ * @throws {never} No other errors are thrown
16992
+ *
16993
+ * @example
16994
+ * ```typescript
16995
+ * // Check property is a string (any value)
16996
+ * assertRecordStringProperty(data, 'name', 'user');
16997
+ *
16998
+ * // Check property equals a specific value
16999
+ * assertRecordStringProperty(data, 'status', 'response', 'active');
17000
+ *
17001
+ * // Check property is one of multiple allowed values
17002
+ * assertRecordStringProperty(data, 'status', 'response', ['queued', 'processing', 'completed']);
17003
+ * ```
17004
+ */
17005
+ function assertRecordStringProperty(o, property, objName, expectedValue) {
17006
+ if (!isRecordStringProperty(o, property)) {
17007
+ throw new InvalidPropertyError({
17008
+ objName,
17009
+ property,
17010
+ expectedType: 'string',
17011
+ expectedValue,
17012
+ type: typeofProperty(o, property),
17013
+ });
17014
+ }
17015
+ if (expectedValue !== undefined) {
17016
+ if (Array.isArray(expectedValue)) {
17017
+ // Check if value matches any of the allowed values
17018
+ for (let i = 0; i < expectedValue.length; ++i) {
17019
+ if (o[property] === expectedValue[i]) {
17020
+ return;
17021
+ }
17022
+ }
17023
+ throw new InvalidPropertyError({
17024
+ objName,
17025
+ property,
17026
+ expectedType: 'string',
17027
+ expectedValue,
17028
+ type: typeof o[property], // === "string"
17029
+ value: o[property],
17030
+ });
17031
+ }
17032
+ else {
17033
+ if (o[property] !== expectedValue) {
17034
+ throw new InvalidPropertyError({
17035
+ objName,
17036
+ property,
17037
+ expectedType: 'string',
17038
+ expectedValue,
17039
+ type: typeof o[property], // === "string"
17040
+ value: o[property],
17041
+ });
17042
+ }
17043
+ }
17044
+ }
17045
+ }
17046
+ function assertRecordStringArrayProperty(o, property, objName) {
17047
+ assertRecordArrayProperty(o, property, objName);
17048
+ const arr = o[property];
17049
+ for (let i = 0; i < arr.length; ++i) {
17050
+ if (typeof arr[i] !== 'string') {
17051
+ throw new InvalidPropertyError({
17052
+ objName,
17053
+ property: `${property}[${i}]`,
17054
+ expectedType: 'string',
17055
+ type: typeof arr[i],
17056
+ });
17057
+ }
17058
+ }
17059
+ }
17060
+ function safeJSONstringify(o) {
17061
+ try {
17062
+ return JSON.stringify(o, (_, v) => typeof v === 'bigint' ? v.toString() : v);
17063
+ }
17064
+ catch {
17065
+ return '';
17066
+ }
17067
+ }
17068
+
16686
17069
  const abiKmsVerifier = [
16687
17070
  'function getKmsSigners() view returns (address[])',
16688
17071
  'function getThreshold() view returns (uint256)',
@@ -16691,12 +17074,12 @@ const abiInputVerifier = [
16691
17074
  'function getCoprocessorSigners() view returns (address[])',
16692
17075
  'function getThreshold() view returns (uint256)',
16693
17076
  ];
16694
- const getProvider = (config) => {
16695
- if (typeof config.network === 'string') {
16696
- return new JsonRpcProvider(config.network);
17077
+ const getProvider = (network) => {
17078
+ if (typeof network === 'string') {
17079
+ return new JsonRpcProvider(network);
16697
17080
  }
16698
- else if (config.network) {
16699
- return new BrowserProvider(config.network);
17081
+ else if (network) {
17082
+ return new BrowserProvider(network);
16700
17083
  }
16701
17084
  throw new Error('You must provide a network URL or a EIP1193 object (eg: window.ethereum)');
16702
17085
  };
@@ -16713,8 +17096,8 @@ const getChainId = async (provider, config) => {
16713
17096
  }
16714
17097
  };
16715
17098
  const getTfheCompactPublicKey = async (config) => {
16716
- if (config.relayerUrl && !config.publicKey) {
16717
- const inputs = await getKeysFromRelayer(cleanURL(config.relayerUrl));
17099
+ if (config.relayerVersionUrl && !config.publicKey) {
17100
+ const inputs = await getKeysFromRelayer(removeSuffix(config.relayerVersionUrl, '/'));
16718
17101
  return { publicKey: inputs.publicKey, publicKeyId: inputs.publicKeyId };
16719
17102
  }
16720
17103
  else if (config.publicKey && config.publicKey.data && config.publicKey.id) {
@@ -16736,8 +17119,8 @@ const getTfheCompactPublicKey = async (config) => {
16736
17119
  }
16737
17120
  };
16738
17121
  const getPublicParams = async (config) => {
16739
- if (config.relayerUrl && !config.publicParams) {
16740
- const inputs = await getKeysFromRelayer(cleanURL(config.relayerUrl));
17122
+ if (config.relayerVersionUrl && !config.publicParams) {
17123
+ const inputs = await getKeysFromRelayer(removeSuffix(config.relayerVersionUrl, '/'));
16741
17124
  return inputs.publicParams;
16742
17125
  }
16743
17126
  else if (config.publicParams && config.publicParams['2048']) {
@@ -16760,27 +17143,28 @@ const getPublicParams = async (config) => {
16760
17143
  throw new Error('You must provide a valid CRS with its CRS ID.');
16761
17144
  }
16762
17145
  };
16763
- const getKMSSigners = async (provider, config) => {
16764
- const kmsContract = new Contract(config.kmsContractAddress, abiKmsVerifier, provider);
17146
+ const getKMSSigners = async (provider, kmsContractAddress) => {
17147
+ const kmsContract = new Contract(kmsContractAddress, abiKmsVerifier, provider);
16765
17148
  const signers = await kmsContract.getKmsSigners();
16766
17149
  return signers;
16767
17150
  };
16768
- const getKMSSignersThreshold = async (provider, config) => {
16769
- const kmsContract = new Contract(config.kmsContractAddress, abiKmsVerifier, provider);
17151
+ const getKMSSignersThreshold = async (provider, kmsContractAddress) => {
17152
+ const kmsContract = new Contract(kmsContractAddress, abiKmsVerifier, provider);
16770
17153
  const threshold = await kmsContract.getThreshold();
16771
17154
  return Number(threshold); // threshold is always supposed to fit in a number
16772
17155
  };
16773
- const getCoprocessorSigners = async (provider, config) => {
16774
- const inputContract = new Contract(config.inputVerifierContractAddress, abiInputVerifier, provider);
17156
+ const getCoprocessorSigners = async (provider, inputVerifierContractAddress) => {
17157
+ const inputContract = new Contract(inputVerifierContractAddress, abiInputVerifier, provider);
16775
17158
  const signers = await inputContract.getCoprocessorSigners();
16776
17159
  return signers;
16777
17160
  };
16778
- const getCoprocessorSignersThreshold = async (provider, config) => {
16779
- const inputContract = new Contract(config.inputVerifierContractAddress, abiInputVerifier, provider);
17161
+ const getCoprocessorSignersThreshold = async (provider, inputVerifierContractAddress) => {
17162
+ const inputContract = new Contract(inputVerifierContractAddress, abiInputVerifier, provider);
16780
17163
  const threshold = await inputContract.getThreshold();
16781
17164
  return Number(threshold); // threshold is always supposed to fit in a number
16782
17165
  };
16783
17166
 
17167
+ // This file contains common utilities for both user and public decryption requests
16784
17168
  const NumEncryptedBits = {
16785
17169
  0: 2, // ebool
16786
17170
  2: 8, // euint8
@@ -16791,17 +17175,6 @@ const NumEncryptedBits = {
16791
17175
  7: 160, // eaddress
16792
17176
  8: 256, // euint256
16793
17177
  };
16794
- function getHandleType(handle) {
16795
- if (handle.length !== 66) {
16796
- throw new Error(`Handle ${handle} is not of valid length`);
16797
- }
16798
- const hexPair = handle.slice(-4, -2).toLowerCase();
16799
- const typeDiscriminant = parseInt(hexPair, 16);
16800
- if (!(typeDiscriminant in NumEncryptedBits)) {
16801
- throw new Error(`Handle ${handle} is not of valid type`);
16802
- }
16803
- return typeDiscriminant;
16804
- }
16805
17178
  function checkEncryptedBits(handles) {
16806
17179
  let total = 0;
16807
17180
  for (const handle of handles) {
@@ -16823,63 +17196,401 @@ function checkEncryptedBits(handles) {
16823
17196
  return total;
16824
17197
  }
16825
17198
 
16826
- // Add type checking
16827
- const getAddress$1 = (value) => getAddress$2(value);
16828
- const aclABI$1 = [
16829
- 'function persistAllowed(bytes32 handle, address account) view returns (bool)',
16830
- ];
16831
- const MAX_USER_DECRYPT_CONTRACT_ADDRESSES = 10;
16832
- const MAX_USER_DECRYPT_DURATION_DAYS = BigInt(365);
16833
- function formatAccordingToType(clearValueAsBigInt, type) {
16834
- if (type === 0) {
16835
- // ebool
16836
- return clearValueAsBigInt === BigInt(1);
17199
+ class InvalidTypeError extends RelayerErrorBase {
17200
+ _objName;
17201
+ _type;
17202
+ _expectedType;
17203
+ _expectedCustomType;
17204
+ constructor({ objName, type, expectedType, expectedCustomType, }) {
17205
+ super({
17206
+ message: `InvalidTypeError ${objName} ${expectedType} ${type}`,
17207
+ name: 'InvalidTypeError',
17208
+ });
17209
+ this._objName = objName;
17210
+ this._type = type;
17211
+ this._expectedType = expectedType;
17212
+ this._expectedCustomType = expectedCustomType;
16837
17213
  }
16838
- else if (type === 7) {
16839
- // eaddress
16840
- return getAddress$1('0x' + clearValueAsBigInt.toString(16).padStart(40, '0'));
17214
+ get objName() {
17215
+ return this._objName;
16841
17216
  }
16842
- else if (type > 8 || type == 1) {
16843
- // type == 1 : euint4 (not supported)
16844
- throw new Error(`Unsupported handle type ${type}`);
17217
+ get type() {
17218
+ return this._type;
16845
17219
  }
16846
- // euintXXX
16847
- return clearValueAsBigInt;
16848
- }
16849
- function buildUserDecryptResults(handles, listBigIntDecryptions) {
16850
- let typesList = [];
16851
- for (const handle of handles) {
16852
- const hexPair = handle.slice(-4, -2).toLowerCase();
16853
- const typeDiscriminant = parseInt(hexPair, 16);
16854
- typesList.push(typeDiscriminant);
17220
+ get expectedType() {
17221
+ return this._expectedType;
17222
+ }
17223
+ get expectedCustomType() {
17224
+ return this._expectedCustomType;
16855
17225
  }
16856
- const results = {};
16857
- handles.forEach((handle, idx) => (results[handle] = formatAccordingToType(listBigIntDecryptions[idx], typesList[idx])));
16858
- return results;
16859
17226
  }
16860
- function checkDeadlineValidity(startTimestamp, durationDays) {
16861
- if (durationDays === BigInt(0)) {
16862
- throw Error('durationDays is null');
17227
+
17228
+ function isBytes(value, bytewidth) {
17229
+ if (!value) {
17230
+ return false;
16863
17231
  }
16864
- if (durationDays > MAX_USER_DECRYPT_DURATION_DAYS) {
16865
- throw Error(`durationDays is above max duration of ${MAX_USER_DECRYPT_DURATION_DAYS}`);
17232
+ if (!(value instanceof Uint8Array)) {
17233
+ return false;
16866
17234
  }
16867
- const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
16868
- if (startTimestamp > currentTimestamp) {
16869
- throw Error('startTimestamp is set in the future');
17235
+ return value.length === bytewidth ;
17236
+ }
17237
+ function isBytesHex(value, bytewidth) {
17238
+ if (!is0x(value)) {
17239
+ return false;
16870
17240
  }
16871
- const durationInSeconds = durationDays * BigInt(86400);
16872
- if (startTimestamp + durationInSeconds < currentTimestamp) {
16873
- throw Error('User decrypt request has expired');
17241
+ if (bytewidth !== undefined && value.length !== 2 * bytewidth + 2) {
17242
+ return false;
16874
17243
  }
16875
- }
16876
- const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider, instanceOptions) => async (_handles, privateKey, publicKey, signature, contractAddresses, userAddress, startTimestamp, durationDays, options) => {
17244
+ if ((value.length - 2) % 2 !== 0) {
17245
+ return false;
17246
+ }
17247
+ const hexRegex = /^0x[a-fA-F0-9]*$/;
17248
+ if (!hexRegex.test(value)) {
17249
+ return false;
17250
+ }
17251
+ return true;
17252
+ }
17253
+ function isBytesHexNo0x(value, bytewidth) {
17254
+ if (!isNo0x(value)) {
17255
+ return false;
17256
+ }
17257
+ if ((value.length - 2) % 2 !== 0) {
17258
+ return false;
17259
+ }
17260
+ const hexRegex = /^[a-fA-F0-9]*$/;
17261
+ if (!hexRegex.test(value)) {
17262
+ return false;
17263
+ }
17264
+ return true;
17265
+ }
17266
+ function isBytes32Hex(value) {
17267
+ return isBytesHex(value, 32);
17268
+ }
17269
+ function isBytes65Hex(value) {
17270
+ return isBytesHex(value, 65);
17271
+ }
17272
+ function isBytes32(value) {
17273
+ return isBytes(value, 32);
17274
+ }
17275
+ function assertIsBytes32(value) {
17276
+ if (!isBytes32(value)) {
17277
+ throw new InvalidTypeError({
17278
+ type: typeof value,
17279
+ expectedType: 'Bytes32',
17280
+ });
17281
+ }
17282
+ }
17283
+ /**
17284
+ * Type guard that checks if a property exists on an object and is a valid hex bytes string.
17285
+ * A valid BytesHex string starts with "0x" followed by an even number of hexadecimal characters.
17286
+ *
17287
+ * @template K - The property key type (string literal)
17288
+ * @param o - The value to check (can be any type)
17289
+ * @param property - The property name to check for
17290
+ * @returns True if `o` is an object with the specified property that is a valid BytesHex string
17291
+ *
17292
+ * @example
17293
+ * ```typescript
17294
+ * const data: unknown = { hash: "0x1234abcd", value: 42 };
17295
+ * if (isRecordBytesHexProperty(data, 'hash')) {
17296
+ * console.log(data.hash); // "0x1234abcd"
17297
+ * }
17298
+ * ```
17299
+ */
17300
+ function isRecordBytesHexProperty(o, property) {
17301
+ if (!isNonNullableRecordProperty(o, property)) {
17302
+ return false;
17303
+ }
17304
+ return isBytesHex(o[property]);
17305
+ }
17306
+ /**
17307
+ * Assertion function that validates a property exists on an object and is a valid hex bytes string.
17308
+ * A valid BytesHex string must start with "0x" followed by an even number of hexadecimal characters.
17309
+ * Throws an `InvalidPropertyError` if validation fails.
17310
+ *
17311
+ * @template K - The property key type (string literal)
17312
+ * @param o - The value to validate (can be any type)
17313
+ * @param property - The property name to check for
17314
+ * @param objName - The name of the object being validated (used in error messages)
17315
+ * @throws {InvalidPropertyError} When the property is missing, not a string, or not valid BytesHex format
17316
+ * @throws {never} No other errors are thrown
17317
+ *
17318
+ * @example
17319
+ * ```typescript
17320
+ * function processTransaction(data: unknown) {
17321
+ * assertRecordBytesHexProperty(data, 'txHash', 'transaction');
17322
+ * console.log(data.txHash); // e.g., "0x1234..."
17323
+ * }
17324
+ * ```
17325
+ */
17326
+ function assertRecordBytesHexProperty(o, property, objName) {
17327
+ if (!isRecordBytesHexProperty(o, property)) {
17328
+ throw new InvalidPropertyError({
17329
+ objName,
17330
+ property,
17331
+ expectedType: 'BytesHex',
17332
+ type: typeofProperty(o, property),
17333
+ });
17334
+ }
17335
+ }
17336
+ function isRecordBytesHexNo0xProperty(o, property) {
17337
+ if (!isNonNullableRecordProperty(o, property)) {
17338
+ return false;
17339
+ }
17340
+ return isBytesHexNo0x(o[property]);
17341
+ }
17342
+ function assertRecordBytesHexNo0xProperty(o, property, objName) {
17343
+ if (!isRecordBytesHexNo0xProperty(o, property)) {
17344
+ throw new InvalidPropertyError({
17345
+ objName,
17346
+ property,
17347
+ expectedType: 'BytesHexNo0x',
17348
+ type: typeofProperty(o, property),
17349
+ });
17350
+ }
17351
+ }
17352
+ function assertRecordBytes32HexArrayProperty(o, property, objName) {
17353
+ assertRecordArrayProperty(o, property, objName);
17354
+ const arr = o[property];
17355
+ for (let i = 0; i < arr.length; ++i) {
17356
+ if (!isBytes32Hex(arr[i])) {
17357
+ throw new InvalidPropertyError({
17358
+ objName,
17359
+ property: `${property}[${i}]`,
17360
+ expectedType: 'Bytes32Hex',
17361
+ type: typeof arr[i],
17362
+ });
17363
+ }
17364
+ }
17365
+ }
17366
+ function assertRecordBytes65HexArrayProperty(o, property, objName) {
17367
+ assertRecordArrayProperty(o, property, objName);
17368
+ const arr = o[property];
17369
+ for (let i = 0; i < arr.length; ++i) {
17370
+ if (!isBytes65Hex(arr[i])) {
17371
+ throw new InvalidPropertyError({
17372
+ objName,
17373
+ property: `${property}[${i}]`,
17374
+ expectedType: 'Bytes65Hex',
17375
+ type: typeof arr[i],
17376
+ });
17377
+ }
17378
+ }
17379
+ }
17380
+ /**
17381
+ * Assertion function that validates a property exists on an object, is an array,
17382
+ * and every element is a valid hex bytes string (with "0x" prefix).
17383
+ * Throws an `InvalidPropertyError` if validation fails.
17384
+ *
17385
+ * @template K - The property key type (string literal)
17386
+ * @param o - The value to validate (can be any type)
17387
+ * @param property - The property name to check for
17388
+ * @param objName - The name of the object being validated (used in error messages)
17389
+ * @throws {InvalidPropertyError} When the property is missing, not an array, or any element is not valid BytesHex
17390
+ * @throws {never} No other errors are thrown
17391
+ *
17392
+ * @example
17393
+ * ```typescript
17394
+ * function processHashes(data: unknown) {
17395
+ * assertRecordBytesHexArrayProperty(data, 'txHashes', 'transaction');
17396
+ * data.txHashes.forEach(hash => {
17397
+ * console.log(hash); // e.g., "0x1234abcd..."
17398
+ * });
17399
+ * }
17400
+ * ```
17401
+ */
17402
+ function assertRecordBytesHexArrayProperty(o, property, objName) {
17403
+ assertRecordArrayProperty(o, property, objName);
17404
+ const arr = o[property];
17405
+ for (let i = 0; i < arr.length; ++i) {
17406
+ if (!isBytesHex(arr[i])) {
17407
+ throw new InvalidPropertyError({
17408
+ objName,
17409
+ property: `${property}[${i}]`,
17410
+ expectedType: 'BytesHex',
17411
+ type: typeof arr[i],
17412
+ });
17413
+ }
17414
+ }
17415
+ }
17416
+ /**
17417
+ * Assertion function that validates a property exists on an object, is an array,
17418
+ * and every element is a valid hex bytes string (without "0x" prefix).
17419
+ * Throws an `InvalidPropertyError` if validation fails.
17420
+ *
17421
+ * @template K - The property key type (string literal)
17422
+ * @param o - The value to validate (can be any type)
17423
+ * @param property - The property name to check for
17424
+ * @param objName - The name of the object being validated (used in error messages)
17425
+ * @throws {InvalidPropertyError} When the property is missing, not an array, or any element is not valid BytesHexNo0x
17426
+ * @throws {never} No other errors are thrown
17427
+ *
17428
+ * @example
17429
+ * ```typescript
17430
+ * function processSignatures(data: unknown) {
17431
+ * assertRecordBytesHexNo0xArrayProperty(data, 'signatures', 'response');
17432
+ * data.signatures.forEach(sig => {
17433
+ * console.log(sig); // e.g., "1234abcd..." (no 0x prefix)
17434
+ * });
17435
+ * }
17436
+ * ```
17437
+ */
17438
+ function assertRecordBytesHexNo0xArrayProperty(o, property, objName) {
17439
+ assertRecordArrayProperty(o, property, objName);
17440
+ const arr = o[property];
17441
+ for (let i = 0; i < arr.length; ++i) {
17442
+ if (!isBytesHexNo0x(arr[i])) {
17443
+ throw new InvalidPropertyError({
17444
+ objName,
17445
+ property: `${property}[${i}]`,
17446
+ expectedType: 'BytesHexNo0x',
17447
+ type: typeof arr[i],
17448
+ });
17449
+ }
17450
+ }
17451
+ }
17452
+ function isRecordUint8ArrayProperty(o, property) {
17453
+ if (!isNonNullableRecordProperty(o, property)) {
17454
+ return false;
17455
+ }
17456
+ return o[property] instanceof Uint8Array;
17457
+ }
17458
+ function assertUint8ArrayProperty(o, property, objName) {
17459
+ if (!isRecordUint8ArrayProperty(o, property)) {
17460
+ throw new InvalidPropertyError({
17461
+ objName,
17462
+ property,
17463
+ expectedType: 'Uint8Array',
17464
+ type: typeofProperty(o, property),
17465
+ });
17466
+ }
17467
+ }
17468
+ /**
17469
+ * Convert a Uint8Array to a hex string (without 0x prefix).
17470
+ */
17471
+ function bytesToHexNo0x(bytes) {
17472
+ if (!bytes || bytes?.length === 0) {
17473
+ return '';
17474
+ }
17475
+ let hex = '';
17476
+ for (let i = 0; i < bytes.length; i++) {
17477
+ hex += bytes[i].toString(16).padStart(2, '0');
17478
+ }
17479
+ return hex;
17480
+ }
17481
+ /**
17482
+ * Convert a Uint8Array to a 0x prefixed hex string
17483
+ */
17484
+ function bytesToHex(bytes) {
17485
+ return `0x${bytesToHexNo0x(bytes)}`;
17486
+ }
17487
+ /**
17488
+ * Convert a hex string prefixed by 0x or not to a Uint8Array
17489
+ */
17490
+ function hexToBytes(hexString) {
17491
+ const arr = hexString.replace(/^(0x)/, '').match(/.{1,2}/g);
17492
+ if (!arr)
17493
+ return new Uint8Array();
17494
+ return Uint8Array.from(arr.map((byte) => parseInt(byte, 16)));
17495
+ }
17496
+ /**
17497
+ * Convert a Uint8Array to a bigint
17498
+ */
17499
+ function bytesToBigInt(byteArray) {
17500
+ if (!byteArray || byteArray.length === 0) {
17501
+ return BigInt(0);
17502
+ }
17503
+ let result = BigInt(0);
17504
+ for (let i = 0; i < byteArray.length; i++) {
17505
+ result = (result << BigInt(8)) | BigInt(byteArray[i]);
17506
+ }
17507
+ return result;
17508
+ }
17509
+ function toHexString(bytes, with0x = false) {
17510
+ return `${with0x ? '0x' : ''}${bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')}`;
17511
+ }
17512
+ async function fetchBytes(url) {
17513
+ const response = await fetch(url);
17514
+ if (!response.ok) {
17515
+ throw new Error(`HTTP error! status: ${response.status} on ${response.url}`);
17516
+ }
17517
+ // Warning : bytes is not widely supported yet!
17518
+ const bytes = typeof response.bytes === 'function'
17519
+ ? await response.bytes()
17520
+ : new Uint8Array(await response.arrayBuffer());
17521
+ return bytes;
17522
+ }
17523
+ function concatBytes(...arrays) {
17524
+ let totalLength = 0;
17525
+ for (const arr of arrays) {
17526
+ totalLength += arr.length;
17527
+ }
17528
+ const result = new Uint8Array(totalLength);
17529
+ let offset = 0;
17530
+ for (const arr of arrays) {
17531
+ result.set(arr, offset);
17532
+ offset += arr.length;
17533
+ }
17534
+ return result;
17535
+ }
17536
+
17537
+ // Add type checking
17538
+ const getAddress$1 = (value) => getAddress$2(value);
17539
+ const aclABI$1 = [
17540
+ 'function persistAllowed(bytes32 handle, address account) view returns (bool)',
17541
+ ];
17542
+ const MAX_USER_DECRYPT_CONTRACT_ADDRESSES = 10;
17543
+ const MAX_USER_DECRYPT_DURATION_DAYS = BigInt(365);
17544
+ function formatAccordingToType(clearValueAsBigInt, type) {
17545
+ if (type === 0) {
17546
+ // ebool
17547
+ return clearValueAsBigInt === BigInt(1);
17548
+ }
17549
+ else if (type === 7) {
17550
+ // eaddress
17551
+ return getAddress$1('0x' + clearValueAsBigInt.toString(16).padStart(40, '0'));
17552
+ }
17553
+ else if (type > 8 || type == 1) {
17554
+ // type == 1 : euint4 (not supported)
17555
+ throw new Error(`Unsupported handle type ${type}`);
17556
+ }
17557
+ // euintXXX
17558
+ return clearValueAsBigInt;
17559
+ }
17560
+ function buildUserDecryptResults(handles, listBigIntDecryptions) {
17561
+ let typesList = [];
17562
+ for (const handle of handles) {
17563
+ const hexPair = handle.slice(-4, -2).toLowerCase();
17564
+ const typeDiscriminant = parseInt(hexPair, 16);
17565
+ typesList.push(typeDiscriminant);
17566
+ }
17567
+ const results = {};
17568
+ handles.forEach((handle, idx) => (results[handle] = formatAccordingToType(listBigIntDecryptions[idx], typesList[idx])));
17569
+ return results;
17570
+ }
17571
+ function checkDeadlineValidity(startTimestamp, durationDays) {
17572
+ if (durationDays === BigInt(0)) {
17573
+ throw Error('durationDays is null');
17574
+ }
17575
+ if (durationDays > MAX_USER_DECRYPT_DURATION_DAYS) {
17576
+ throw Error(`durationDays is above max duration of ${MAX_USER_DECRYPT_DURATION_DAYS}`);
17577
+ }
17578
+ const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));
17579
+ if (startTimestamp > currentTimestamp) {
17580
+ throw Error('startTimestamp is set in the future');
17581
+ }
17582
+ const durationInSeconds = durationDays * BigInt(86400);
17583
+ if (startTimestamp + durationInSeconds < currentTimestamp) {
17584
+ throw Error('User decrypt request has expired');
17585
+ }
17586
+ }
17587
+ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContractAddress, aclContractAddress, relayerProvider, provider, defaultOptions) => async (_handles, privateKey, publicKey, signature, contractAddresses, userAddress, startTimestamp, durationDays, options) => {
16877
17588
  const extraData = '0x00';
16878
17589
  let pubKey;
16879
17590
  let privKey;
16880
17591
  try {
16881
- pubKey = TKMS.u8vec_to_ml_kem_pke_pk(fromHexString(publicKey));
16882
- privKey = TKMS.u8vec_to_ml_kem_pke_sk(fromHexString(privateKey));
17592
+ pubKey = TKMS.u8vec_to_ml_kem_pke_pk(hexToBytes(publicKey));
17593
+ privKey = TKMS.u8vec_to_ml_kem_pke_sk(hexToBytes(privateKey));
16883
17594
  }
16884
17595
  catch (e) {
16885
17596
  throw new Error('Invalid public or private key', { cause: e });
@@ -16887,16 +17598,16 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
16887
17598
  // Casting handles if string
16888
17599
  const signatureSanitized = signature.replace(/^(0x)/, '');
16889
17600
  const publicKeySanitized = publicKey.replace(/^(0x)/, '');
16890
- const handles = _handles.map((h) => ({
17601
+ const handleContractPairs = _handles.map((h) => ({
16891
17602
  handle: typeof h.handle === 'string'
16892
- ? toHexString(fromHexString(h.handle), true)
17603
+ ? toHexString(hexToBytes(h.handle), true)
16893
17604
  : toHexString(h.handle, true),
16894
17605
  contractAddress: getAddress$1(h.contractAddress),
16895
17606
  }));
16896
- checkEncryptedBits(handles.map((h) => h.handle));
17607
+ checkEncryptedBits(handleContractPairs.map((h) => h.handle));
16897
17608
  checkDeadlineValidity(BigInt(startTimestamp), BigInt(durationDays));
16898
- const acl = new ethers.Contract(aclContractAddress, aclABI$1, provider);
16899
- const verifications = handles.map(async ({ handle, contractAddress }) => {
17609
+ const acl = new Contract(aclContractAddress, aclABI$1, provider);
17610
+ const verifications = handleContractPairs.map(async ({ handle, contractAddress }) => {
16900
17611
  const userAllowed = await acl.persistAllowed(handle, userAddress);
16901
17612
  const contractAllowed = await acl.persistAllowed(handle, contractAddress);
16902
17613
  if (!userAllowed) {
@@ -16920,7 +17631,7 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
16920
17631
  throw e;
16921
17632
  });
16922
17633
  const payloadForRequest = {
16923
- handleContractPairs: handles,
17634
+ handleContractPairs,
16924
17635
  requestValidity: {
16925
17636
  startTimestamp: startTimestamp.toString(), // Convert to string
16926
17637
  durationDays: durationDays.toString(), // Convert to string
@@ -16932,7 +17643,10 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
16932
17643
  publicKey: publicKeySanitized,
16933
17644
  extraData,
16934
17645
  };
16935
- const json = await fetchRelayerJsonRpcPost('USER_DECRYPT', `${relayerUrl}/v1/user-decrypt`, payloadForRequest, instanceOptions ?? options);
17646
+ const json = await relayerProvider.fetchPostUserDecrypt(payloadForRequest, {
17647
+ ...defaultOptions,
17648
+ ...options,
17649
+ });
16936
17650
  // assume the KMS Signers have the correct order
16937
17651
  let indexedKmsSigners = kmsSigners.map((signer, index) => {
16938
17652
  return TKMS.new_server_id_addr(index + 1, signer);
@@ -16954,12 +17668,13 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
16954
17668
  signature: signatureSanitized,
16955
17669
  client_address: userAddress,
16956
17670
  enc_key: publicKeySanitized,
16957
- ciphertext_handles: handles.map((h) => h.handle.replace(/^0x/, '')),
17671
+ ciphertext_handles: handleContractPairs.map((h) => h.handle.replace(/^0x/, '')),
16958
17672
  eip712_verifying_contract: verifyingContractAddress,
16959
17673
  };
16960
- const decryption = TKMS.process_user_decryption_resp_from_js(client, payloadForVerification, eip712Domain, json.response, pubKey, privKey, true);
17674
+ const decryption = TKMS.process_user_decryption_resp_from_js(client, payloadForVerification, eip712Domain, json, //json.response,
17675
+ pubKey, privKey, true);
16961
17676
  const listBigIntDecryptions = decryption.map((d) => bytesToBigInt(d.bytes));
16962
- const results = buildUserDecryptResults(handles.map((h) => h.handle), listBigIntDecryptions);
17677
+ const results = buildUserDecryptResults(handleContractPairs.map((h) => h.handle), listBigIntDecryptions);
16963
17678
  return results;
16964
17679
  }
16965
17680
  catch (e) {
@@ -16984,10 +17699,10 @@ const checkEncryptedValue = (value, bits) => {
16984
17699
  }
16985
17700
  };
16986
17701
  const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKey, publicParams, contractAddress, userAddress, }) => {
16987
- if (!isAddress(contractAddress)) {
17702
+ if (!isAddress$1(contractAddress)) {
16988
17703
  throw new Error('Contract address is not a valid address.');
16989
17704
  }
16990
- if (!isAddress(userAddress)) {
17705
+ if (!isAddress$1(userAddress)) {
16991
17706
  throw new Error('User address is not a valid address.');
16992
17707
  }
16993
17708
  const publicKey = tfheCompactPublicKey;
@@ -17053,7 +17768,7 @@ const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKe
17053
17768
  return this;
17054
17769
  },
17055
17770
  addAddress(value) {
17056
- if (!isAddress(value)) {
17771
+ if (!isAddress$1(value)) {
17057
17772
  throw new Error('The value must be a valid address.');
17058
17773
  }
17059
17774
  checkLimit(160);
@@ -17084,10 +17799,10 @@ const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKe
17084
17799
  };
17085
17800
  const closestPP = getClosestPP();
17086
17801
  const pp = publicParams[closestPP].publicParams;
17087
- const buffContract = fromHexString(contractAddress);
17088
- const buffUser = fromHexString(userAddress);
17089
- const buffAcl = fromHexString(aclContractAddress);
17090
- const buffChainId = fromHexString(chainId.toString(16).padStart(64, '0'));
17802
+ const buffContract = hexToBytes(contractAddress);
17803
+ const buffUser = hexToBytes(userAddress);
17804
+ const buffAcl = hexToBytes(aclContractAddress);
17805
+ const buffChainId = hexToBytes(chainId.toString(16).padStart(64, '0'));
17091
17806
  const auxData = new Uint8Array(buffContract.length + buffUser.length + buffAcl.length + 32);
17092
17807
  auxData.set(buffContract, 0);
17093
17808
  auxData.set(buffUser, 20);
@@ -17100,165 +17815,470 @@ const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKe
17100
17815
  };
17101
17816
  };
17102
17817
 
17103
- /**
17104
- * **FHE Type Mapping for Input Builders**
17105
- * * Maps the **number of encrypted bits** used by a FHEVM primary type
17106
- * to its corresponding **FheTypeId**. This constant is primarily used by
17107
- * `EncryptedInput` and `RelayerEncryptedInput` builders to determine the correct
17108
- * input type and calculate the total required bit-length.
17109
- *
17110
- * **Structure: \{ Encrypted Bit Length: FheTypeId \}**
17111
- *
17112
- * | Bits | FheTypeId | FHE Type Name | Note |
17113
- * | :--- | :-------- | :------------ | :--- |
17114
- * | 2 | 0 | `ebool` | The boolean type. |
17115
- * | (N/A)| 1 | `euint4` | **Deprecated** and omitted from this map. |
17116
- * | 8 | 2 | `euint8` | |
17117
- * | 16 | 3 | `euint16` | |
17118
- * | 32 | 4 | `euint32` | |
17119
- * | 64 | 5 | `euint64` | |
17120
- * | 128 | 6 | `euint128` | |
17121
- * | 160 | 7 | `eaddress` | Used for encrypted Ethereum addresses. |
17122
- * | 256 | 8 | `euint256` | The maximum supported integer size. |
17123
- */
17124
- const ENCRYPTION_TYPES = {
17125
- 2: 0, // ebool (FheTypeId=0) is using 2 encrypted bits
17126
- // euint4 (FheTypeId=1) is deprecated
17127
- 8: 2, // euint8 (FheTypeId=2) is using 8 encrypted bits
17128
- 16: 3, // euint16 (FheTypeId=3) is using 16 encrypted bits
17129
- 32: 4, // euint32 (FheTypeId=4) is using 32 encrypted bits
17130
- 64: 5, // euint64 (FheTypeId=5) is using 64 encrypted bits
17131
- 128: 6, // euint128 (FheTypeId=128) is using 128 encrypted bits
17132
- 160: 7, // eaddress (FheTypeId=7) is using 160 encrypted bits
17133
- 256: 8, // euint256 (FheTypeId=8) is using 256 encrypted bits
17134
- };
17135
-
17136
17818
  const MAX_UINT64 = BigInt('18446744073709551615'); // 2^64 - 1
17137
- const RAW_CT_HASH_DOMAIN_SEPARATOR = 'ZK-w_rct';
17138
- const HANDLE_HASH_DOMAIN_SEPARATOR = 'ZK-w_hdl';
17139
- const computeHandles = (ciphertextWithZKProof, bitwidths, aclContractAddress, chainId, ciphertextVersion) => {
17140
- // Should be identical to:
17141
- // https://github.com/zama-ai/fhevm-backend/blob/bae00d1b0feafb63286e94acdc58dc88d9c481bf/fhevm-engine/zkproof-worker/src/verifier.rs#L301
17142
- const blob_hash = createHash('keccak256')
17143
- .update(Buffer.from(RAW_CT_HASH_DOMAIN_SEPARATOR))
17144
- .update(Buffer.from(ciphertextWithZKProof))
17145
- .digest();
17146
- const aclContractAddress20Bytes = Buffer.from(fromHexString(aclContractAddress));
17147
- const hex = chainId.toString(16).padStart(64, '0'); // 64 hex chars = 32 bytes
17148
- const chainId32Bytes = Buffer.from(hex, 'hex');
17149
- const handles = bitwidths.map((bitwidth, encryptionIndex) => {
17150
- const encryptionType = ENCRYPTION_TYPES[bitwidth];
17151
- const encryptionIndex1Byte = Buffer.from([encryptionIndex]);
17152
- const handleHash = createHash('keccak256')
17153
- .update(Buffer.from(HANDLE_HASH_DOMAIN_SEPARATOR))
17154
- .update(blob_hash)
17155
- .update(encryptionIndex1Byte)
17156
- .update(aclContractAddress20Bytes)
17157
- .update(chainId32Bytes)
17158
- .digest();
17159
- const dataInput = new Uint8Array(32);
17160
- dataInput.set(handleHash, 0);
17161
- // Check if chainId exceeds 8 bytes
17162
- if (BigInt(chainId) > MAX_UINT64) {
17163
- throw new Error('ChainId exceeds maximum allowed value (8 bytes)'); // fhevm assumes chainID is only taking up to 8 bytes
17164
- }
17165
- const chainId8Bytes = fromHexString(hex).slice(24, 32);
17166
- dataInput[21] = encryptionIndex;
17167
- dataInput.set(chainId8Bytes, 22);
17168
- dataInput[30] = encryptionType;
17169
- dataInput[31] = ciphertextVersion;
17170
- return dataInput;
17171
- });
17172
- return handles;
17173
- };
17174
-
17175
- // Add type checking
17176
- const getAddress = (value) => getAddress$2(value);
17177
- const currentCiphertextVersion = () => {
17178
- return 0;
17179
- };
17180
- function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold) {
17181
- const addressMap = new Map();
17182
- recoveredAddresses.forEach((address, index) => {
17183
- if (addressMap.has(address)) {
17184
- const duplicateValue = address;
17185
- throw new Error(`Duplicate coprocessor signer address found: ${duplicateValue} appears multiple times in recovered addresses`);
17186
- }
17187
- addressMap.set(address, index);
17188
- });
17189
- for (const address of recoveredAddresses) {
17190
- if (!coprocessorSigners.includes(address)) {
17191
- throw new Error(`Invalid address found: ${address} is not in the list of coprocessor signers`);
17819
+ BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
17820
+ const MAX_UINT32 = 0xffffffff;
17821
+ const MAX_UINT8 = 0xff;
17822
+ function numberToHex(num) {
17823
+ let hex = num.toString(16);
17824
+ return hex.length % 2 ? '0' + hex : hex;
17825
+ }
17826
+ function isUintNumber(value) {
17827
+ if (typeof value === 'number') {
17828
+ if (value < 0) {
17829
+ return false;
17192
17830
  }
17831
+ return Number.isInteger(value);
17193
17832
  }
17194
- return recoveredAddresses.length >= threshold;
17833
+ return false;
17195
17834
  }
17196
- function isFhevmRelayerInputProofResponse(json) {
17197
- const response = json.response;
17198
- if (typeof response !== 'object' || response === null) {
17835
+ function isUintBigInt(value) {
17836
+ if (typeof value === 'bigint') {
17837
+ return value >= 0;
17838
+ }
17839
+ return false;
17840
+ }
17841
+ function isUint(value) {
17842
+ if (isUintNumber(value)) {
17843
+ return true;
17844
+ }
17845
+ else if (isUintBigInt(value)) {
17846
+ return true;
17847
+ }
17848
+ return false;
17849
+ }
17850
+ function isUint8(value) {
17851
+ if (!isUint(value)) {
17199
17852
  return false;
17200
17853
  }
17201
- if (!('handles' in response && Array.isArray(response.handles))) {
17854
+ return value <= MAX_UINT8;
17855
+ }
17856
+ function isUint32(value) {
17857
+ if (!isUint(value)) {
17202
17858
  return false;
17203
17859
  }
17204
- if (!('signatures' in response && Array.isArray(response.signatures))) {
17860
+ return value <= MAX_UINT32;
17861
+ }
17862
+ function isUint64(value) {
17863
+ if (!isUint(value)) {
17205
17864
  return false;
17206
17865
  }
17207
- return (response.signatures.every((s) => typeof s === 'string') &&
17208
- response.handles.every((h) => typeof h === 'string'));
17866
+ return value <= MAX_UINT64;
17209
17867
  }
17210
- const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerUrl, tfheCompactPublicKey, publicParams, coprocessorSigners, thresholdCoprocessorSigners, instanceOptions) => (contractAddress, userAddress) => {
17211
- if (!isAddress(contractAddress)) {
17212
- throw new Error('Contract address is not a valid address.');
17868
+ function uint32ToBytes32(uint32) {
17869
+ if (!isUint32(uint32)) {
17870
+ throw new InvalidTypeError({ expectedType: 'Uint32' });
17871
+ }
17872
+ const buffer = new ArrayBuffer(32);
17873
+ const view = new DataView(buffer);
17874
+ view.setUint32(28, Number(uint32), false);
17875
+ return new Uint8Array(buffer);
17876
+ }
17877
+ function assertIsUint8(value) {
17878
+ if (!isUint8(value)) {
17879
+ throw new InvalidTypeError({
17880
+ type: typeof value,
17881
+ expectedType: 'Uint8',
17882
+ });
17213
17883
  }
17214
- if (!isAddress(userAddress)) {
17215
- throw new Error('User address is not a valid address.');
17884
+ }
17885
+ function assertIsUint64(value) {
17886
+ if (!isUint64(value)) {
17887
+ throw new InvalidTypeError({
17888
+ type: typeof value,
17889
+ expectedType: 'Uint64',
17890
+ });
17216
17891
  }
17217
- const input = createEncryptedInput({
17218
- aclContractAddress,
17219
- chainId,
17220
- tfheCompactPublicKey,
17221
- publicParams,
17222
- contractAddress,
17223
- userAddress,
17224
- });
17225
- return {
17226
- _input: input,
17227
- addBool(value) {
17228
- input.addBool(value);
17229
- return this;
17230
- },
17231
- add8(value) {
17232
- input.add8(value);
17233
- return this;
17234
- },
17235
- add16(value) {
17236
- input.add16(value);
17237
- return this;
17238
- },
17239
- add32(value) {
17240
- input.add32(value);
17241
- return this;
17242
- },
17243
- add64(value) {
17244
- input.add64(value);
17245
- return this;
17246
- },
17247
- add128(value) {
17248
- input.add128(value);
17249
- return this;
17250
- },
17251
- add256(value) {
17252
- input.add256(value);
17253
- return this;
17254
- },
17255
- addAddress(value) {
17256
- input.addAddress(value);
17257
- return this;
17258
- },
17892
+ }
17893
+ function isRecordUintProperty(o, property) {
17894
+ if (!isNonNullableRecordProperty(o, property)) {
17895
+ return false;
17896
+ }
17897
+ return isUint(o[property]);
17898
+ }
17899
+ function assertRecordUintProperty(o, property, objName) {
17900
+ if (!isRecordUintProperty(o, property)) {
17901
+ throw new InvalidPropertyError({
17902
+ objName,
17903
+ property,
17904
+ type: typeofProperty(o, property),
17905
+ expectedType: 'Uint',
17906
+ });
17907
+ }
17908
+ }
17909
+
17910
+ class ChecksummedAddressError extends RelayerErrorBase {
17911
+ constructor({ address }) {
17912
+ super({
17913
+ message: `Checksummed address "${address}" is invalid.`,
17914
+ name: 'ChecksummedAddressError',
17915
+ });
17916
+ }
17917
+ }
17918
+
17919
+ function isChecksummedAddress(value) {
17920
+ if (typeof value !== 'string') {
17921
+ return false;
17922
+ }
17923
+ if (!value.startsWith('0x')) {
17924
+ return false;
17925
+ }
17926
+ if (value.length !== 42) {
17927
+ return false;
17928
+ }
17929
+ try {
17930
+ const a = getAddress$2(value);
17931
+ return a === value;
17932
+ }
17933
+ catch (e) {
17934
+ return false;
17935
+ }
17936
+ }
17937
+ function assertIsChecksummedAddress(value) {
17938
+ if (!isChecksummedAddress(value)) {
17939
+ throw new ChecksummedAddressError({ address: String(value) });
17940
+ }
17941
+ }
17942
+ function isAddress(value) {
17943
+ if (typeof value !== 'string') {
17944
+ return false;
17945
+ }
17946
+ if (!value.startsWith('0x')) {
17947
+ return false;
17948
+ }
17949
+ if (value.length !== 42) {
17950
+ return false;
17951
+ }
17952
+ if (!isAddress$1(value)) {
17953
+ return false;
17954
+ }
17955
+ return true;
17956
+ }
17957
+ function checksummedAddressToBytes20(address) {
17958
+ if (!isAddress(address)) {
17959
+ throw new InvalidTypeError({ expectedType: 'ChecksummedAddress' });
17960
+ }
17961
+ const hex = remove0x(address);
17962
+ const bytes = new Uint8Array(20);
17963
+ for (let i = 0; i < 20; i++) {
17964
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
17965
+ }
17966
+ return bytes;
17967
+ }
17968
+
17969
+ class FhevmHandleError extends RelayerErrorBase {
17970
+ constructor({ handle, message }) {
17971
+ super({
17972
+ message: message ?? `FHEVM Handle "${handle}" is invalid.`,
17973
+ name: 'FhevmHandleError',
17974
+ });
17975
+ }
17976
+ }
17977
+
17978
+ ////////////////////////////////////////////////////////////////////////////////
17979
+ // FhevmHandle
17980
+ ////////////////////////////////////////////////////////////////////////////////
17981
+ class FhevmHandle {
17982
+ _hash21;
17983
+ _chainId;
17984
+ _fheTypeId;
17985
+ _version;
17986
+ _computed;
17987
+ _index;
17988
+ static RAW_CT_HASH_DOMAIN_SEPARATOR = 'ZK-w_rct';
17989
+ static HANDLE_HASH_DOMAIN_SEPARATOR = 'ZK-w_hdl';
17990
+ static FheTypeIdToEncryptionBitwidths = {
17991
+ 0: 2,
17992
+ 2: 8,
17993
+ 3: 16,
17994
+ 4: 32,
17995
+ 5: 64,
17996
+ 6: 128,
17997
+ 7: 160,
17998
+ 8: 256,
17999
+ };
18000
+ static FheTypeEncryptionBitwidthsToId = {
18001
+ 2: 0,
18002
+ 8: 2,
18003
+ 16: 3,
18004
+ 32: 4,
18005
+ 64: 5,
18006
+ 128: 6,
18007
+ 160: 7,
18008
+ 256: 8,
18009
+ };
18010
+ static FheTypeIdToSolidityPrimitiveType = {
18011
+ 0: 'bool',
18012
+ 2: 'uint256',
18013
+ 3: 'uint256',
18014
+ 4: 'uint256',
18015
+ 5: 'uint256',
18016
+ 6: 'uint256',
18017
+ 7: 'address',
18018
+ 8: 'uint256',
18019
+ };
18020
+ static {
18021
+ Object.freeze(FhevmHandle.FheTypeIdToEncryptionBitwidths);
18022
+ Object.freeze(FhevmHandle.FheTypeEncryptionBitwidthsToId);
18023
+ }
18024
+ constructor(hash21, chainId, fheTypeId, version, computed, index) {
18025
+ if (BigInt(chainId) > MAX_UINT64) {
18026
+ // fhevm assumes chainID is only taking up to 8 bytes
18027
+ throw new Error('ChainId exceeds maximum allowed value (8 bytes)');
18028
+ }
18029
+ this._hash21 = hash21;
18030
+ this._chainId = chainId;
18031
+ this._fheTypeId = fheTypeId;
18032
+ this._version = version;
18033
+ this._computed = computed;
18034
+ if (index !== undefined) {
18035
+ this._index = index;
18036
+ }
18037
+ }
18038
+ get hash21() {
18039
+ return this._hash21;
18040
+ }
18041
+ get chainId() {
18042
+ return this._chainId;
18043
+ }
18044
+ get fheTypeId() {
18045
+ return this._fheTypeId;
18046
+ }
18047
+ get version() {
18048
+ return this._version;
18049
+ }
18050
+ get computed() {
18051
+ return this._computed;
18052
+ }
18053
+ get index() {
18054
+ return this._index;
18055
+ }
18056
+ static fromZKProof(params) {
18057
+ assertIsChecksummedAddress(params.aclAddress);
18058
+ assertIsUint64(params.chainId);
18059
+ assertIsUint8(params.ciphertextVersion);
18060
+ let fheTypeIds;
18061
+ if (params.fheTypeIds !== undefined) {
18062
+ fheTypeIds = params.fheTypeIds;
18063
+ }
18064
+ else if (params.fheTypeEncryptionBitwidths !== undefined) {
18065
+ fheTypeIds = params.fheTypeEncryptionBitwidths.map((w) => FhevmHandle.FheTypeEncryptionBitwidthsToId[w]);
18066
+ }
18067
+ else {
18068
+ throw new InternalError({
18069
+ message: 'createInputHandles requires either fheTypeIds or fheTypeEncryptionBitwidths',
18070
+ });
18071
+ }
18072
+ assertIsUint8(fheTypeIds.length);
18073
+ let ciphertextWithZKProof;
18074
+ if (typeof params.ciphertextWithZKProof === 'string') {
18075
+ ciphertextWithZKProof = hexToBytes(params.ciphertextWithZKProof);
18076
+ }
18077
+ else if (params.ciphertextWithZKProof instanceof Uint8Array) {
18078
+ ciphertextWithZKProof = params.ciphertextWithZKProof;
18079
+ }
18080
+ else {
18081
+ throw new InternalError({
18082
+ message: 'Invalid ciphertextWithZKProof argument',
18083
+ });
18084
+ }
18085
+ if (ciphertextWithZKProof.length === 0) {
18086
+ throw new InternalError({
18087
+ message: 'Invalid ciphertextWithZKProof argument',
18088
+ });
18089
+ }
18090
+ const encoder = new TextEncoder();
18091
+ const domainSepBytes = encoder.encode(FhevmHandle.RAW_CT_HASH_DOMAIN_SEPARATOR);
18092
+ const blobHashBytes32Hex = keccak256(concatBytes(domainSepBytes, ciphertextWithZKProof));
18093
+ const handles = [];
18094
+ for (let i = 0; i < fheTypeIds.length; ++i) {
18095
+ const hash21 = FhevmHandle._computeInputHash21(hexToBytes(blobHashBytes32Hex), params.aclAddress, params.chainId, i);
18096
+ handles.push(new FhevmHandle(hash21, params.chainId, fheTypeIds[i], params.ciphertextVersion, false, i));
18097
+ }
18098
+ return handles;
18099
+ }
18100
+ /**
18101
+ * blobHashBytes32 = keccak256(ciphertextWithZKProof)
18102
+ */
18103
+ static _computeInputHash21(blobHashBytes32, aclAddress, chainId, index) {
18104
+ /*
18105
+ https://github.com/zama-ai/fhevm/blob/8ffbd5906ab3d57af178e049930e3fc065c9d4b3/coprocessor/fhevm-engine/zkproof-worker/src/verifier.rs#L431C7-L431C8
18106
+
18107
+ handle_hash = Bytes("ZK-w_hdl") + blobHash 32 Bytes + index 1 Byte + aclAddress 20 Bytes + chainId 32 bytes
18108
+ ===========================================================================================================
18109
+
18110
+ const HANDLE_HASH_DOMAIN_SEPARATOR: [u8; 8] = *b"ZK-w_hdl";
18111
+
18112
+ let mut handle_hash = Keccak256::new();
18113
+ handle_hash.update(HANDLE_HASH_DOMAIN_SEPARATOR);
18114
+ handle_hash.update(blob_hash);
18115
+ handle_hash.update([ct_idx as u8]);
18116
+ handle_hash.update(
18117
+ Address::from_str(&aux_data.acl_contract_address)
18118
+ .expect("valid acl_contract_address")
18119
+ .into_array(),
18120
+ );
18121
+ handle_hash.update(chain_id_bytes);
18122
+ let mut handle = handle_hash.finalize().to_vec();
18123
+ assert_eq!(handle.len(), 32);
18124
+
18125
+ */
18126
+ assertIsBytes32(blobHashBytes32);
18127
+ assertIsChecksummedAddress(aclAddress);
18128
+ assertIsUint8(index);
18129
+ assertIsUint64(chainId);
18130
+ const encryptionIndexByte1 = new Uint8Array([index]);
18131
+ const aclContractAddressBytes20 = checksummedAddressToBytes20(aclAddress);
18132
+ const chainIdBytes32 = uint32ToBytes32(chainId);
18133
+ const encoder = new TextEncoder();
18134
+ const domainSepBytes = encoder.encode(FhevmHandle.HANDLE_HASH_DOMAIN_SEPARATOR);
18135
+ return keccak256(concatBytes(domainSepBytes, blobHashBytes32, encryptionIndexByte1, aclContractAddressBytes20, chainIdBytes32));
18136
+ }
18137
+ toBytes32() {
18138
+ assertRelayer((this._index === undefined && this._computed) ||
18139
+ (this._index !== undefined && this._index < 255 && !this._computed));
18140
+ const chainId32Bytes = uint32ToBytes32(this._chainId);
18141
+ const chainId8Bytes = chainId32Bytes.subarray(24, 32);
18142
+ const handleHash = hexToBytes(this._hash21);
18143
+ const handleBytes32AsBytes = new Uint8Array(32);
18144
+ handleBytes32AsBytes.set(handleHash, 0);
18145
+ handleBytes32AsBytes[21] = this._index === undefined ? 255 : this._index;
18146
+ handleBytes32AsBytes.set(chainId8Bytes, 22);
18147
+ handleBytes32AsBytes[30] = this._fheTypeId;
18148
+ handleBytes32AsBytes[31] = this._version;
18149
+ return handleBytes32AsBytes;
18150
+ }
18151
+ toBytes32Hex() {
18152
+ return bytesToHex(this.toBytes32());
18153
+ }
18154
+ static checkHandleHex(handle) {
18155
+ if (!isBytes32Hex(handle)) {
18156
+ throw new FhevmHandleError({ handle });
18157
+ }
18158
+ }
18159
+ static isFheTypeId(value) {
18160
+ switch (value) {
18161
+ case 0:
18162
+ // 1: euint4 is deprecated
18163
+ case 2:
18164
+ case 3:
18165
+ case 4:
18166
+ case 5:
18167
+ case 6:
18168
+ case 7:
18169
+ case 8:
18170
+ return true;
18171
+ default:
18172
+ return false;
18173
+ }
18174
+ }
18175
+ static getFheTypeId(handle) {
18176
+ if (!isBytes32Hex(handle)) {
18177
+ throw new FhevmHandleError({ handle });
18178
+ }
18179
+ const hexPair = handle.slice(-4, -2).toLowerCase();
18180
+ const typeDiscriminant = parseInt(hexPair, 16);
18181
+ if (!FhevmHandle.isFheTypeId(typeDiscriminant)) {
18182
+ throw new FhevmHandleError({
18183
+ handle,
18184
+ message: `FHEVM Handle "${handle}" is invalid. Unknown FheType: ${typeDiscriminant}`,
18185
+ });
18186
+ }
18187
+ return typeDiscriminant;
18188
+ }
18189
+ }
18190
+
18191
+ // Add type checking
18192
+ const getAddress = (value) => getAddress$2(value);
18193
+ const currentCiphertextVersion = () => {
18194
+ return 0;
18195
+ };
18196
+ function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold) {
18197
+ const addressMap = new Map();
18198
+ recoveredAddresses.forEach((address, index) => {
18199
+ if (addressMap.has(address)) {
18200
+ const duplicateValue = address;
18201
+ throw new Error(`Duplicate coprocessor signer address found: ${duplicateValue} appears multiple times in recovered addresses`);
18202
+ }
18203
+ addressMap.set(address, index);
18204
+ });
18205
+ for (const address of recoveredAddresses) {
18206
+ if (!coprocessorSigners.includes(address)) {
18207
+ throw new Error(`Invalid address found: ${address} is not in the list of coprocessor signers`);
18208
+ }
18209
+ }
18210
+ return recoveredAddresses.length >= threshold;
18211
+ }
18212
+ function isFhevmRelayerInputProofResponse(json) {
18213
+ const response = json;
18214
+ // const response = json.response as unknown;
18215
+ if (typeof response !== 'object' || response === null) {
18216
+ return false;
18217
+ }
18218
+ if (!('handles' in response && Array.isArray(response.handles))) {
18219
+ return false;
18220
+ }
18221
+ if (!('signatures' in response && Array.isArray(response.signatures))) {
18222
+ return false;
18223
+ }
18224
+ return (response.signatures.every((s) => typeof s === 'string') &&
18225
+ response.handles.every((h) => typeof h === 'string'));
18226
+ }
18227
+ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerProvider, tfheCompactPublicKey, publicParams, coprocessorSigners, thresholdCoprocessorSigners, defaultOptions) => (contractAddress, userAddress) => {
18228
+ if (!isAddress$1(contractAddress)) {
18229
+ throw new Error('Contract address is not a valid address.');
18230
+ }
18231
+ if (!isAddress$1(userAddress)) {
18232
+ throw new Error('User address is not a valid address.');
18233
+ }
18234
+ const input = createEncryptedInput({
18235
+ aclContractAddress,
18236
+ chainId,
18237
+ tfheCompactPublicKey,
18238
+ publicParams,
18239
+ contractAddress,
18240
+ userAddress,
18241
+ });
18242
+ return {
18243
+ _input: input,
18244
+ addBool(value) {
18245
+ input.addBool(value);
18246
+ return this;
18247
+ },
18248
+ add8(value) {
18249
+ input.add8(value);
18250
+ return this;
18251
+ },
18252
+ add16(value) {
18253
+ input.add16(value);
18254
+ return this;
18255
+ },
18256
+ add32(value) {
18257
+ input.add32(value);
18258
+ return this;
18259
+ },
18260
+ add64(value) {
18261
+ input.add64(value);
18262
+ return this;
18263
+ },
18264
+ add128(value) {
18265
+ input.add128(value);
18266
+ return this;
18267
+ },
18268
+ add256(value) {
18269
+ input.add256(value);
18270
+ return this;
18271
+ },
18272
+ addAddress(value) {
18273
+ input.addAddress(value);
18274
+ return this;
18275
+ },
17259
18276
  getBits() {
17260
18277
  return input.getBits();
17261
18278
  },
18279
+ getCiphertextWithInputVerification() {
18280
+ return input.encrypt();
18281
+ },
17262
18282
  encrypt: async (options) => {
17263
18283
  const extraData = '0x00';
17264
18284
  const bits = input.getBits();
@@ -17270,14 +18290,25 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
17270
18290
  contractChainId: ('0x' + chainId.toString(16)),
17271
18291
  extraData,
17272
18292
  };
17273
- const json = await fetchRelayerJsonRpcPost('INPUT_PROOF', `${relayerUrl}/v1/input-proof`, payload, options ?? instanceOptions);
18293
+ const json = await relayerProvider.fetchPostInputProof(payload, {
18294
+ ...defaultOptions,
18295
+ ...options,
18296
+ });
17274
18297
  if (!isFhevmRelayerInputProofResponse(json)) {
17275
18298
  throwRelayerInternalError('INPUT_PROOF', json);
17276
18299
  }
17277
- const handles = computeHandles(ciphertext, bits, aclContractAddress, chainId, currentCiphertextVersion());
18300
+ const fhevmHandles = FhevmHandle.fromZKProof({
18301
+ ciphertextWithZKProof: ciphertext,
18302
+ chainId,
18303
+ aclAddress: aclContractAddress,
18304
+ ciphertextVersion: currentCiphertextVersion(),
18305
+ fheTypeEncryptionBitwidths: bits,
18306
+ });
18307
+ const handles = fhevmHandles.map((h) => h.toBytes32());
18308
+ const result = json;
17278
18309
  // Note that the hex strings returned by the relayer do have have the 0x prefix
17279
- if (json.response.handles && json.response.handles.length > 0) {
17280
- const responseHandles = json.response.handles.map(fromHexString);
18310
+ if (result.handles && result.handles.length > 0) {
18311
+ const responseHandles = result.handles.map(hexToBytes);
17281
18312
  if (handles.length != responseHandles.length) {
17282
18313
  throw new Error(`Incorrect Handles list sizes: (expected) ${handles.length} != ${responseHandles.length} (received)`);
17283
18314
  }
@@ -17291,7 +18322,7 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
17291
18322
  }
17292
18323
  }
17293
18324
  }
17294
- const signatures = json.response.signatures;
18325
+ const signatures = result.signatures;
17295
18326
  // verify signatures for inputs:
17296
18327
  const domain = {
17297
18328
  name: 'InputVerification',
@@ -17334,12 +18365,27 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
17334
18365
  inputProof += extraData.slice(2);
17335
18366
  return {
17336
18367
  handles,
17337
- inputProof: fromHexString(inputProof),
18368
+ inputProof: hexToBytes(inputProof),
17338
18369
  };
17339
18370
  },
17340
18371
  };
17341
18372
  };
17342
18373
 
18374
+ function ensureError(e) {
18375
+ if (e instanceof Error) {
18376
+ return e;
18377
+ }
18378
+ const message = e.message ?? 'Non-Error value caught in exception handler';
18379
+ const name = e.name ?? 'ErrorWrapper';
18380
+ const cause = e.cause ?? e;
18381
+ const err = new Error(message, { cause });
18382
+ err.name = name;
18383
+ return err;
18384
+ }
18385
+ function assertNever(value, message) {
18386
+ throw new InternalError({ message });
18387
+ }
18388
+
17343
18389
  const aclABI = [
17344
18390
  'function isAllowedForDecryption(bytes32 handle) view returns (bool)',
17345
18391
  ];
@@ -17365,7 +18411,7 @@ function abiEncodeClearValues(clearValues) {
17365
18411
  const abiValues = [];
17366
18412
  for (let i = 0; i < handlesBytes32Hex.length; ++i) {
17367
18413
  const handle = handlesBytes32Hex[i];
17368
- const handleType = getHandleType(handle);
18414
+ const handleType = FhevmHandle.getFheTypeId(handle);
17369
18415
  let clearTextValue = clearValues[handle];
17370
18416
  if (typeof clearTextValue === 'boolean') {
17371
18417
  clearTextValue = clearTextValue ? '0x01' : '0x00';
@@ -17402,7 +18448,7 @@ function abiEncodeClearValues(clearValues) {
17402
18448
  break;
17403
18449
  }
17404
18450
  default: {
17405
- throw new Error(`Unsupported Fhevm primitive type id: ${handleType}`);
18451
+ assertNever(handleType, `Unsupported Fhevm primitive type id: ${handleType}`);
17406
18452
  }
17407
18453
  }
17408
18454
  }
@@ -17427,21 +18473,11 @@ function buildDecryptionProof(kmsSignatures, extraData) {
17427
18473
  ]);
17428
18474
  return decryptionProof;
17429
18475
  }
17430
- const CiphertextType = {
17431
- 0: 'bool',
17432
- 2: 'uint256',
17433
- 3: 'uint256',
17434
- 4: 'uint256',
17435
- 5: 'uint256',
17436
- 6: 'uint256',
17437
- 7: 'address',
17438
- 8: 'uint256',
17439
- };
17440
18476
  function deserializeClearValues(handles, decryptedResult) {
17441
18477
  let typesList = [];
17442
18478
  for (const handle of handles) {
17443
- const hexPair = handle.slice(-4, -2).toLowerCase();
17444
- const typeDiscriminant = parseInt(hexPair, 16);
18479
+ const typeDiscriminant = FhevmHandle.getFheTypeId(handle);
18480
+ assertRelayer(FhevmHandle.isFheTypeId(typeDiscriminant));
17445
18481
  typesList.push(typeDiscriminant);
17446
18482
  }
17447
18483
  const restoredEncoded = '0x' +
@@ -17449,7 +18485,7 @@ function deserializeClearValues(handles, decryptedResult) {
17449
18485
  decryptedResult.slice(2) +
17450
18486
  '00'.repeat(32); // dummy empty bytes[] length (ignored)
17451
18487
  const abiTypes = typesList.map((t) => {
17452
- const abiType = CiphertextType[t]; // all types are valid because this was supposedly checked already inside the `checkEncryptedBits` function
18488
+ const abiType = FhevmHandle.FheTypeIdToSolidityPrimitiveType[t]; // all types are valid because this was supposedly checked already inside the `checkEncryptedBits` function
17453
18489
  return abiType;
17454
18490
  });
17455
18491
  const coder = new AbiCoder();
@@ -17460,14 +18496,14 @@ function deserializeClearValues(handles, decryptedResult) {
17460
18496
  handles.forEach((handle, idx) => (results[handle] = rawValues[idx]));
17461
18497
  return results;
17462
18498
  }
17463
- const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress, relayerUrl, provider, instanceOptions) => async (_handles, options) => {
18499
+ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress, relayerProvider, provider, defaultOptions) => async (_handles, options) => {
17464
18500
  const extraData = '0x00';
17465
18501
  const acl = new ethers.Contract(aclContractAddress, aclABI, provider);
17466
18502
  let handles;
17467
18503
  try {
17468
18504
  handles = await Promise.all(_handles.map(async (_handle) => {
17469
18505
  const handle = typeof _handle === 'string'
17470
- ? toHexString(fromHexString(_handle), true)
18506
+ ? toHexString(hexToBytes(_handle), true)
17471
18507
  : toHexString(_handle, true);
17472
18508
  const isAllowedForDecryption = await acl.isAllowedForDecryption(handle);
17473
18509
  if (!isAllowedForDecryption) {
@@ -17485,7 +18521,10 @@ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, veri
17485
18521
  ciphertextHandles: handles,
17486
18522
  extraData,
17487
18523
  };
17488
- const json = await fetchRelayerJsonRpcPost('PUBLIC_DECRYPT', `${relayerUrl}/v1/public-decrypt`, payloadForRequest, options ?? instanceOptions);
18524
+ const json = await relayerProvider.fetchPostPublicDecrypt(payloadForRequest, {
18525
+ ...defaultOptions,
18526
+ ...options,
18527
+ });
17489
18528
  // verify signatures on decryption:
17490
18529
  const domain = {
17491
18530
  name: 'Decryption',
@@ -17500,10 +18539,11 @@ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, veri
17500
18539
  { name: 'extraData', type: 'bytes' },
17501
18540
  ],
17502
18541
  };
17503
- const result = json.response[0];
17504
- const decryptedResult = ensure0x(result.decrypted_value);
18542
+ //const result = json.response[0];
18543
+ const result = json;
18544
+ const decryptedResult = ensure0x(result.decryptedValue);
17505
18545
  const kmsSignatures = result.signatures.map(ensure0x);
17506
- // TODO result.extra_data (RelayerPublicDecryptJsonResponse)
18546
+ // TODO result.extraData (RelayerPublicDecryptJsonResponse)
17507
18547
  const signedExtraData = '0x';
17508
18548
  const recoveredAddresses = kmsSignatures.map((kmsSignature) => {
17509
18549
  const recoveredAddress = ethers.verifyTypedData(domain, types, { ctHandles: handles, decryptedResult, extraData: signedExtraData }, kmsSignature);
@@ -17538,12 +18578,12 @@ const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, veri
17538
18578
  */
17539
18579
  const createEIP712 = (verifyingContract, contractsChainId) => (publicKey, contractAddresses, startTimestamp, durationDays, delegatedAccount) => {
17540
18580
  const extraData = '0x00';
17541
- if (delegatedAccount && !isAddress(delegatedAccount))
18581
+ if (delegatedAccount && !isAddress$1(delegatedAccount))
17542
18582
  throw new Error('Invalid delegated account.');
17543
- if (!isAddress(verifyingContract)) {
18583
+ if (!isAddress$1(verifyingContract)) {
17544
18584
  throw new Error('Invalid verifying contract address.');
17545
18585
  }
17546
- if (!contractAddresses.every((c) => isAddress(c))) {
18586
+ if (!contractAddresses.every((c) => isAddress$1(c))) {
17547
18587
  throw new Error('Invalid contract address.');
17548
18588
  }
17549
18589
  // Format the public key based on its type
@@ -17627,89 +18667,2645 @@ const generateKeypair = () => {
17627
18667
  };
17628
18668
  };
17629
18669
 
17630
- global.fetch = fetchRetry(global.fetch, { retries: 5, retryDelay: 500 });
17631
- const MainnetConfig = {
17632
- aclContractAddress: '0xcA2E8f1F656CD25C01F05d0b243Ab1ecd4a8ffb6',
17633
- kmsContractAddress: '0x77627828a55156b04Ac0DC0eb30467f1a552BB03',
17634
- inputVerifierContractAddress: '0xCe0FC2e05CFff1B719EFF7169f7D80Af770c8EA2',
17635
- verifyingContractAddressDecryption: '0x0f6024a97684f7d90ddb0fAAD79cB15F2C888D24',
17636
- verifyingContractAddressInputVerification: '0xcB1bB072f38bdAF0F328CdEf1Fc6eDa1DF029287',
17637
- chainId: 1,
17638
- gatewayChainId: 261131,
17639
- network: 'https://ethereum-rpc.publicnode.com',
17640
- relayerUrl: 'https://relayer.mainnet.zama.org',
17641
- };
17642
- Object.freeze(MainnetConfig);
17643
- const SepoliaConfig = {
17644
- // ACL_CONTRACT_ADDRESS (FHEVM Host chain)
17645
- aclContractAddress: '0xf0Ffdc93b7E186bC2f8CB3dAA75D86d1930A433D',
17646
- // KMS_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
17647
- kmsContractAddress: '0xbE0E383937d564D7FF0BC3b46c51f0bF8d5C311A',
17648
- // INPUT_VERIFIER_CONTRACT_ADDRESS (FHEVM Host chain)
17649
- inputVerifierContractAddress: '0xBBC1fFCdc7C316aAAd72E807D9b0272BE8F84DA0',
17650
- // DECRYPTION_ADDRESS (Gateway chain)
17651
- verifyingContractAddressDecryption: '0x5D8BD78e2ea6bbE41f26dFe9fdaEAa349e077478',
17652
- // INPUT_VERIFICATION_ADDRESS (Gateway chain)
17653
- verifyingContractAddressInputVerification: '0x483b9dE06E4E4C7D35CCf5837A1668487406D955',
17654
- // FHEVM Host chain id
17655
- chainId: 11155111,
17656
- // Gateway chain id
17657
- gatewayChainId: 10901,
17658
- // Optional RPC provider to host chain
17659
- network: 'https://ethereum-sepolia-rpc.publicnode.com',
17660
- // Relayer URL
17661
- relayerUrl: 'https://relayer.testnet.zama.org',
17662
- };
17663
- const createInstance = async (config) => {
17664
- const { verifyingContractAddressDecryption, verifyingContractAddressInputVerification, publicKey, kmsContractAddress, aclContractAddress, gatewayChainId, auth, } = config;
17665
- if (!kmsContractAddress || !isAddress(kmsContractAddress)) {
17666
- throw new Error('KMS contract address is not valid or empty');
18670
+ class AbstractRelayerProvider {
18671
+ _relayerUrl;
18672
+ constructor(relayerUrl) {
18673
+ this._relayerUrl = relayerUrl;
17667
18674
  }
17668
- if (!verifyingContractAddressDecryption ||
17669
- !isAddress(verifyingContractAddressDecryption)) {
17670
- throw new Error('Verifying contract for Decryption address is not valid or empty');
18675
+ get url() {
18676
+ return this._relayerUrl;
17671
18677
  }
17672
- if (!verifyingContractAddressInputVerification ||
17673
- !isAddress(verifyingContractAddressInputVerification)) {
17674
- throw new Error('Verifying contract for InputVerification address is not valid or empty');
18678
+ get keyUrl() {
18679
+ return `${this.url}/keyurl`;
17675
18680
  }
17676
- if (!aclContractAddress || !isAddress(aclContractAddress)) {
17677
- throw new Error('ACL contract address is not valid or empty');
18681
+ get inputProof() {
18682
+ return `${this.url}/input-proof`;
17678
18683
  }
17679
- if (publicKey && !(publicKey.data instanceof Uint8Array))
17680
- throw new Error('publicKey must be a Uint8Array');
17681
- const provider = getProvider(config);
17682
- if (!provider) {
17683
- throw new Error('No network has been provided!');
18684
+ get publicDecrypt() {
18685
+ return `${this.url}/public-decrypt`;
17684
18686
  }
17685
- const chainId = await getChainId(provider, config);
17686
- const publicKeyData = await getTfheCompactPublicKey(config);
17687
- const publicParamsData = await getPublicParams(config);
17688
- const kmsSigners = await getKMSSigners(provider, config);
17689
- const thresholdKMSSigners = await getKMSSignersThreshold(provider, config);
17690
- const coprocessorSigners = await getCoprocessorSigners(provider, config);
17691
- const thresholdCoprocessorSigners = await getCoprocessorSignersThreshold(provider, config);
17692
- return {
17693
- createEncryptedInput: createRelayerEncryptedInput(aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, cleanURL(config.relayerUrl), publicKeyData.publicKey, publicParamsData, coprocessorSigners, thresholdCoprocessorSigners, auth && { auth }),
17694
- generateKeypair,
17695
- createEIP712: createEIP712(verifyingContractAddressDecryption, chainId),
17696
- publicDecrypt: publicDecryptRequest(kmsSigners, thresholdKMSSigners, gatewayChainId, verifyingContractAddressDecryption, aclContractAddress, cleanURL(config.relayerUrl), provider, auth && { auth }),
17697
- userDecrypt: userDecryptRequest(kmsSigners, gatewayChainId, chainId, verifyingContractAddressDecryption, aclContractAddress, cleanURL(config.relayerUrl), provider, auth && { auth }),
17698
- getPublicKey: () => publicKeyData.publicKey
17699
- ? {
17700
- publicKey: publicKeyData.publicKey.safe_serialize(SERIALIZED_SIZE_LIMIT_PK),
17701
- publicKeyId: publicKeyData.publicKeyId,
17702
- }
17703
- : null,
17704
- getPublicParams: (bits) => {
17705
- if (publicParamsData[bits]) {
17706
- return {
17707
- publicParams: publicParamsData[bits].publicParams.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS),
17708
- publicParamsId: publicParamsData[bits].publicParamsId,
17709
- };
17710
- }
17711
- return null;
17712
- },
18687
+ get userDecrypt() {
18688
+ return `${this.url}/user-decrypt`;
18689
+ }
18690
+ }
18691
+ function assertIsRelayerInputProofResult(value, name) {
18692
+ assertRecordBytes32HexArrayProperty(value, 'handles', name);
18693
+ assertRecordBytes65HexArrayProperty(value, 'signatures', name);
18694
+ }
18695
+ function assertIsRelayerPublicDecryptResult(value, name) {
18696
+ assertRecordBytesHexNo0xArrayProperty(value, 'signatures', name);
18697
+ assertRecordStringProperty(value, 'decryptedValue', name);
18698
+ assertRecordBytesHexProperty(value, 'extraData', name);
18699
+ }
18700
+ function assertIsRelayerUserDecryptResult(value, name) {
18701
+ if (!Array.isArray(value)) {
18702
+ throw InvalidPropertyError.invalidObject({
18703
+ objName: name,
18704
+ expectedType: 'Array',
18705
+ type: typeof value,
18706
+ });
18707
+ }
18708
+ for (let i = 0; i < value.length; ++i) {
18709
+ // Missing extraData
18710
+ assertRecordBytesHexNo0xProperty(value[i], 'payload', `${name}[i]`);
18711
+ assertRecordBytesHexNo0xProperty(value[i], 'signature', `${name}[i]`);
18712
+ }
18713
+ }
18714
+
18715
+ class RelayerV1Provider extends AbstractRelayerProvider {
18716
+ constructor(relayerUrl) {
18717
+ super(relayerUrl);
18718
+ }
18719
+ get version() {
18720
+ return 1;
18721
+ }
18722
+ async fetchGetKeyUrl() {
18723
+ const response = await fetchRelayerV1Get('KEY_URL', this.keyUrl);
18724
+ return response;
18725
+ }
18726
+ async fetchPostInputProof(payload, options) {
18727
+ /*
18728
+ Expected v1 format:
18729
+ ===================
18730
+ {
18731
+ "response": {
18732
+ "handles": [
18733
+ "0xb0b1af7734450c2b7d944571af7e5b438cc62a2a26000000000000aa36a70400"
18734
+ ],
18735
+ "signatures": [
18736
+ "0x70dcb78534f05c4448d3441b4704d3ff4a8478af56a3464497533c2e3c476d77165b09028847f0c3ed4b342b1e8b4252a93b521a3d8d07b724bcff740383e1361b"
18737
+ ]
18738
+ }
18739
+ }
18740
+ */
18741
+ const json = await fetchRelayerV1Post('INPUT_PROOF', this.inputProof, payload, options);
18742
+ assertIsRelayerInputProofResult(json.response, 'fetchPostInputProof()');
18743
+ return json.response;
18744
+ }
18745
+ async fetchPostPublicDecrypt(payload, options) {
18746
+ const json = await fetchRelayerV1Post('PUBLIC_DECRYPT', this.publicDecrypt, payload, options);
18747
+ const response = json.response[0];
18748
+ const result = {
18749
+ signatures: response.signatures,
18750
+ decryptedValue: response.decrypted_value,
18751
+ extraData: '0x',
18752
+ };
18753
+ assertIsRelayerPublicDecryptResult(result, 'fetchPostPublicDecrypt()');
18754
+ return result;
18755
+ }
18756
+ async fetchPostUserDecrypt(payload, options) {
18757
+ const json = await fetchRelayerV1Post('USER_DECRYPT', this.userDecrypt, payload, options);
18758
+ assertIsRelayerUserDecryptResult(json.response, 'RelayerUserDecryptResult()');
18759
+ return json.response;
18760
+ }
18761
+ }
18762
+
18763
+ class RelayerV2ProviderError extends RelayerErrorBase {
18764
+ _operation;
18765
+ constructor(params) {
18766
+ super(params);
18767
+ this._operation = params.operation;
18768
+ }
18769
+ get operation() {
18770
+ return this._operation;
18771
+ }
18772
+ }
18773
+ class RelayerV2GetKeyUrlError extends RelayerV2ProviderError {
18774
+ constructor({ cause }) {
18775
+ super({
18776
+ message: `Invalid relayer response.`,
18777
+ name: 'RelayerV2GetKeyUrlError',
18778
+ operation: 'KEY_URL',
18779
+ cause,
18780
+ });
18781
+ }
18782
+ }
18783
+ class RelayerV2GetKeyUrlInvalidResponseError extends RelayerV2GetKeyUrlError {
18784
+ constructor({ cause }) {
18785
+ super({ cause });
18786
+ }
18787
+ }
18788
+
18789
+ function assertIsRelayerV2ApiError400NoDetails(value, name) {
18790
+ assertRecordStringProperty(value, 'label', name);
18791
+ if (!(value.label === 'malformed_json' ||
18792
+ value.label === 'request_error' ||
18793
+ value.label === 'not_ready_for_decryption')) {
18794
+ throw new InvalidPropertyError({
18795
+ objName: name,
18796
+ property: 'label',
18797
+ expectedType: 'string',
18798
+ expectedValue: [
18799
+ 'malformed_json',
18800
+ 'request_error',
18801
+ 'not_ready_for_decryption',
18802
+ ],
18803
+ type: typeof value.label, // === "string"
18804
+ value: value.label,
18805
+ });
18806
+ }
18807
+ assertRecordStringProperty(value, 'message', name);
18808
+ }
18809
+
18810
+ /*
18811
+ type RelayerV2ApiError400WithDetails = {
18812
+ label: 'missing_fields' | 'validation_failed';
18813
+ message: string;
18814
+ details: Array<RelayerV2ErrorDetail>;
18815
+ };
18816
+ type RelayerV2ErrorDetail = {
18817
+ field: string;
18818
+ issue: string;
18819
+ }
18820
+ */
18821
+ function assertIsRelayerV2ApiError400WithDetails(value, name) {
18822
+ assertRecordStringProperty(value, 'label', name);
18823
+ if (!(value.label === 'missing_fields' || value.label === 'validation_failed')) {
18824
+ throw new InvalidPropertyError({
18825
+ objName: name,
18826
+ property: 'label',
18827
+ expectedType: 'string',
18828
+ expectedValue: ['missing_fields', 'validation_failed'],
18829
+ type: typeof value.label,
18830
+ value: value.label,
18831
+ });
18832
+ }
18833
+ assertRecordStringProperty(value, 'message', name);
18834
+ assertRecordArrayProperty(value, 'details', name);
18835
+ const arr = value.details;
18836
+ for (let i = 0; i < arr.length; ++i) {
18837
+ const detail = arr[i];
18838
+ assertRecordStringProperty(detail, 'field', `${name}.details[${i}]`);
18839
+ assertRecordStringProperty(detail, 'issue', `${name}.details[${i}]`);
18840
+ }
18841
+ }
18842
+
18843
+ /*
18844
+ export type RelayerV2ApiError429 = {
18845
+ label: 'rate_limited';
18846
+ message: string;
18847
+ };
18848
+ */
18849
+ function assertIsRelayerV2ApiError429(value, name) {
18850
+ assertRecordStringProperty(value, 'label', name, 'rate_limited');
18851
+ assertRecordStringProperty(value, 'message', name);
18852
+ }
18853
+
18854
+ /*
18855
+ export type RelayerV2ApiError500 = {
18856
+ label: 'internal_server_error';
18857
+ message: string;
18858
+ };
18859
+ */
18860
+ function assertIsRelayerV2ApiError500(value, name) {
18861
+ assertRecordStringProperty(value, 'label', name, 'internal_server_error');
18862
+ assertRecordStringProperty(value, 'message', name);
18863
+ }
18864
+
18865
+ /*
18866
+ export type RelayerV2ApiError404 = {
18867
+ label: 'not_found';
18868
+ message: string;
18869
+ };
18870
+ */
18871
+ function assertIsRelayerV2ApiError404(value, name) {
18872
+ assertRecordStringProperty(value, 'label', name, 'not_found');
18873
+ assertRecordStringProperty(value, 'message', name);
18874
+ }
18875
+
18876
+ /*
18877
+ export type RelayerV2ApiError503 = {
18878
+ label: "protocol_paused" | "gateway_not_reachable";
18879
+ message: string;
18880
+ };
18881
+ */
18882
+ function assertIsRelayerV2ApiError503(value, name) {
18883
+ assertRecordStringProperty(value, 'label', name, [
18884
+ 'protocol_paused',
18885
+ 'gateway_not_reachable',
18886
+ ]);
18887
+ assertRecordStringProperty(value, 'message', name);
18888
+ }
18889
+
18890
+ /*
18891
+ export type RelayerV2ApiError504 = {
18892
+ label: 'readiness_check_timedout' | 'response_timedout';
18893
+ message: string;
18894
+ };
18895
+ */
18896
+ function assertIsRelayerV2ApiError504(value, name) {
18897
+ assertRecordStringProperty(value, 'label', name, [
18898
+ 'readiness_check_timedout',
18899
+ 'response_timedout',
18900
+ ]);
18901
+ assertRecordStringProperty(value, 'message', name);
18902
+ }
18903
+
18904
+ function assertIsRelayerV2ResponseFailed(value, name) {
18905
+ assertRecordStringProperty(value, 'status', name, 'failed');
18906
+ assertNonNullableRecordProperty(value, 'error', name);
18907
+ assertIsRelayerV2ApiError(value.error, `${name}.error`);
18908
+ }
18909
+ function assertIsRelayerV2ApiError(value, name) {
18910
+ assertRecordStringProperty(value, 'label', name);
18911
+ // 400
18912
+ if (value.label === 'malformed_json' ||
18913
+ value.label === 'request_error' ||
18914
+ value.label === 'not_ready_for_decryption') {
18915
+ assertIsRelayerV2ApiError400NoDetails(value, name);
18916
+ }
18917
+ // 400 (with details)
18918
+ else if (value.label === 'missing_fields' ||
18919
+ value.label === 'validation_failed') {
18920
+ assertIsRelayerV2ApiError400WithDetails(value, name);
18921
+ }
18922
+ // 404
18923
+ else if (value.label === 'not_found') {
18924
+ assertIsRelayerV2ApiError404(value, name);
18925
+ }
18926
+ // 429
18927
+ else if (value.label === 'rate_limited') {
18928
+ assertIsRelayerV2ApiError429(value, name);
18929
+ }
18930
+ // 500
18931
+ else if (value.label === 'internal_server_error') {
18932
+ assertIsRelayerV2ApiError500(value, name);
18933
+ }
18934
+ // 503
18935
+ else if (value.label === 'protocol_paused' ||
18936
+ value.label === 'gateway_not_reachable') {
18937
+ assertIsRelayerV2ApiError503(value, name);
18938
+ }
18939
+ // 504
18940
+ else if (value.label === 'readiness_check_timedout' ||
18941
+ value.label === 'response_timedout') {
18942
+ assertIsRelayerV2ApiError504(value, name);
18943
+ }
18944
+ // Unsupported
18945
+ else {
18946
+ throw new InvalidPropertyError({
18947
+ objName: name,
18948
+ property: 'label',
18949
+ expectedType: 'string',
18950
+ expectedValue: [
18951
+ 'malformed_json',
18952
+ 'request_error',
18953
+ 'not_ready_for_decryption',
18954
+ 'missing_fields',
18955
+ 'validation_failed',
18956
+ 'rate_limited',
18957
+ 'internal_server_error',
18958
+ 'protocol_paused',
18959
+ 'gateway_not_reachable',
18960
+ 'readiness_check_timedout',
18961
+ 'response_timedout',
18962
+ ],
18963
+ type: typeof value.label,
18964
+ value: value.label,
18965
+ });
18966
+ }
18967
+ }
18968
+ ////////////////////////////////////////////////////////////////////////////////
18969
+ // 400
18970
+ ////////////////////////////////////////////////////////////////////////////////
18971
+ function assertIsRelayerV2ResponseFailedWithError400(value, name) {
18972
+ assertIsRelayerV2ResponseFailed(value, name);
18973
+ if (value.error.label === 'malformed_json' ||
18974
+ value.error.label === 'request_error' ||
18975
+ value.error.label === 'not_ready_for_decryption') {
18976
+ assertIsRelayerV2ApiError400NoDetails(value.error, `${name}.error`);
18977
+ }
18978
+ else if (value.error.label === 'missing_fields' ||
18979
+ value.error.label === 'validation_failed') {
18980
+ assertIsRelayerV2ApiError400WithDetails(value.error, `${name}.error`);
18981
+ }
18982
+ else {
18983
+ throw new InvalidPropertyError({
18984
+ objName: `${name}.error`,
18985
+ property: 'label',
18986
+ expectedType: 'string',
18987
+ expectedValue: [
18988
+ 'malformed_json',
18989
+ 'request_error',
18990
+ 'not_ready_for_decryption',
18991
+ 'missing_fields',
18992
+ 'validation_failed',
18993
+ ],
18994
+ type: typeof value.error.label,
18995
+ value: value.error.label,
18996
+ });
18997
+ }
18998
+ }
18999
+ ////////////////////////////////////////////////////////////////////////////////
19000
+ // 404
19001
+ ////////////////////////////////////////////////////////////////////////////////
19002
+ function assertIsRelayerV2ResponseFailedWithError404(value, name) {
19003
+ assertIsRelayerV2ResponseFailed(value, name);
19004
+ assertIsRelayerV2ApiError404(value.error, `${name}.error`);
19005
+ }
19006
+ ////////////////////////////////////////////////////////////////////////////////
19007
+ // 429
19008
+ ////////////////////////////////////////////////////////////////////////////////
19009
+ function assertIsRelayerV2ResponseFailedWithError429(value, name) {
19010
+ assertIsRelayerV2ResponseFailed(value, name);
19011
+ assertIsRelayerV2ApiError429(value.error, `${name}.error`);
19012
+ }
19013
+ ////////////////////////////////////////////////////////////////////////////////
19014
+ // 500
19015
+ ////////////////////////////////////////////////////////////////////////////////
19016
+ function assertIsRelayerV2ResponseFailedWithError500(value, name) {
19017
+ assertIsRelayerV2ResponseFailed(value, name);
19018
+ assertIsRelayerV2ApiError500(value.error, `${name}.error`);
19019
+ }
19020
+ ////////////////////////////////////////////////////////////////////////////////
19021
+ // 503
19022
+ ////////////////////////////////////////////////////////////////////////////////
19023
+ function assertIsRelayerV2ResponseFailedWithError503(value, name) {
19024
+ assertIsRelayerV2ResponseFailed(value, name);
19025
+ assertIsRelayerV2ApiError503(value.error, `${name}.error`);
19026
+ }
19027
+ ////////////////////////////////////////////////////////////////////////////////
19028
+ // 504
19029
+ ////////////////////////////////////////////////////////////////////////////////
19030
+ function assertIsRelayerV2ResponseFailedWithError504(value, name) {
19031
+ assertIsRelayerV2ResponseFailed(value, name);
19032
+ assertIsRelayerV2ApiError504(value.error, `${name}.error`);
19033
+ }
19034
+
19035
+ class RelayerV2FetchErrorBase extends RelayerErrorBase {
19036
+ _fetchMethod;
19037
+ _url;
19038
+ _jobId;
19039
+ _operation;
19040
+ _retryCount;
19041
+ _elapsed;
19042
+ _state;
19043
+ constructor(params) {
19044
+ super({
19045
+ ...params,
19046
+ name: params.name ?? 'RelayerV2FetchErrorBase',
19047
+ });
19048
+ this._fetchMethod = params.fetchMethod;
19049
+ this._url = params.url;
19050
+ this._operation = params.operation;
19051
+ this._elapsed = params.elapsed;
19052
+ this._retryCount = params.retryCount;
19053
+ this._state = params.state;
19054
+ this._jobId = params.jobId;
19055
+ }
19056
+ get url() {
19057
+ return this._url;
19058
+ }
19059
+ get operation() {
19060
+ return this._operation;
19061
+ }
19062
+ get fetchMethod() {
19063
+ return this._fetchMethod;
19064
+ }
19065
+ get jobId() {
19066
+ return this._jobId;
19067
+ }
19068
+ get retryCount() {
19069
+ return this._retryCount;
19070
+ }
19071
+ get elapsed() {
19072
+ return this._elapsed;
19073
+ }
19074
+ get state() {
19075
+ return this._state;
19076
+ }
19077
+ get isAbort() {
19078
+ // AbortError is not an instance of Error!
19079
+ return this.cause ? this.cause.name === 'AbortError' : false;
19080
+ }
19081
+ }
19082
+
19083
+ class RelayerV2ResponseErrorBase extends RelayerV2FetchErrorBase {
19084
+ _status;
19085
+ constructor(params) {
19086
+ super({
19087
+ ...params,
19088
+ name: params.name ?? 'RelayerV2ResponseErrorBase',
19089
+ });
19090
+ this._status = params.status;
19091
+ }
19092
+ get status() {
19093
+ return this._status;
19094
+ }
19095
+ }
19096
+
19097
+ class RelayerV2ResponseInvalidBodyError extends RelayerV2ResponseErrorBase {
19098
+ constructor(params) {
19099
+ super({
19100
+ ...params,
19101
+ cause: ensureError(params.cause),
19102
+ name: 'RelayerV2ResponseInvalidBodyError',
19103
+ message: `fetchMethod: ${params.fetchMethod} status:${params.status} url:${params.url} operation:${params.operation}`,
19104
+ });
19105
+ }
19106
+ }
19107
+
19108
+ class RelayerV2ResponseStatusError extends RelayerV2ResponseErrorBase {
19109
+ constructor(params) {
19110
+ super({
19111
+ ...params,
19112
+ name: 'RelayerV2ResponseStatusError',
19113
+ message: `fetchMethod: ${params.fetchMethod} status:${params.status} url:${params.url} operation:${params.operation}`,
19114
+ });
19115
+ }
19116
+ }
19117
+
19118
+ function assertIsRelayerV2ResultInputProof(value, name) {
19119
+ assertRecordBooleanProperty(value, 'accepted', name);
19120
+ if (value.accepted) {
19121
+ assertIsRelayerV2ResultInputProofAccepted(value, name);
19122
+ }
19123
+ else {
19124
+ assertIsRelayerV2ResultInputProofRejected(value, name);
19125
+ }
19126
+ }
19127
+ /*
19128
+ type RelayerV2ResultInputProofAccepted = {
19129
+ accepted: true;
19130
+ extra_data: BytesHex;
19131
+ handles: Bytes32Hex[];
19132
+ signatures: BytesHex[];
19133
+ }
19134
+ */
19135
+ function assertIsRelayerV2ResultInputProofAccepted(value, name) {
19136
+ assertRecordBooleanProperty(value, 'accepted', name, true);
19137
+ assertRecordBytes32HexArrayProperty(value, 'handles', name);
19138
+ assertRecordBytesHexArrayProperty(value, 'signatures', name);
19139
+ assertRecordBytesHexProperty(value, 'extraData', name);
19140
+ }
19141
+ /*
19142
+ type RelayerV2ResultInputProofRejected = {
19143
+ accepted: false;
19144
+ extra_data: BytesHex;
19145
+ }
19146
+ */
19147
+ function assertIsRelayerV2ResultInputProofRejected(value, name) {
19148
+ assertRecordBooleanProperty(value, 'accepted', name, false);
19149
+ assertRecordBytesHexProperty(value, 'extraData', name);
19150
+ }
19151
+
19152
+ function assertIsRelayerV2GetResponseInputProofSucceeded(value, name) {
19153
+ assertNonNullableRecordProperty(value, 'result', name);
19154
+ assertRecordStringProperty(value, 'status', name, 'succeeded');
19155
+ assertRecordStringProperty(value, 'requestId', name);
19156
+ assertIsRelayerV2ResultInputProof(value.result, `${name}.result`);
19157
+ }
19158
+
19159
+ function assertIsRelayerV2ResultPublicDecrypt(value, name) {
19160
+ assertRecordBytesHexNo0xArrayProperty(value, 'signatures', name);
19161
+ assertRecordBytesHexNo0xProperty(value, 'decryptedValue', name);
19162
+ assertRecordBytesHexProperty(value, 'extraData', name);
19163
+ }
19164
+
19165
+ function assertIsRelayerV2GetResponsePublicDecryptSucceeded(value, name) {
19166
+ assertNonNullableRecordProperty(value, 'result', name);
19167
+ assertRecordStringProperty(value, 'status', name, 'succeeded');
19168
+ assertRecordStringProperty(value, 'requestId', name);
19169
+ assertIsRelayerV2ResultPublicDecrypt(value.result, `${name}.result`);
19170
+ }
19171
+
19172
+ /**
19173
+ * Assertion function that validates a value is a valid `RelayerV2ResultUserDecrypt` object.
19174
+ * Validates the structure returned from the relayer for user decryption operations.
19175
+ * Throws an `InvalidPropertyError` if validation fails.
19176
+ *
19177
+ * @param value - The value to validate (can be any type)
19178
+ * @param name - The name of the value being validated (used in error messages)
19179
+ * @throws {InvalidPropertyError} When any required property is missing or has an invalid format
19180
+ * @throws {never} No other errors are thrown
19181
+ */
19182
+ function assertIsRelayerV2ResultUserDecrypt(value, name) {
19183
+ assertRecordArrayProperty(value, 'result', name);
19184
+ for (let i = 0; i < value.result.length; ++i) {
19185
+ // Missing extraData
19186
+ assertRecordBytesHexNo0xProperty(value.result[i], 'payload', `${name}.result[${i}]`);
19187
+ assertRecordBytesHexNo0xProperty(value.result[i], 'signature', `${name}.result[${i}]`);
19188
+ }
19189
+ }
19190
+
19191
+ function assertIsRelayerV2GetResponseUserDecryptSucceeded(value, name) {
19192
+ assertNonNullableRecordProperty(value, 'result', name);
19193
+ assertRecordStringProperty(value, 'status', name, 'succeeded');
19194
+ assertRecordStringProperty(value, 'requestId', name);
19195
+ assertIsRelayerV2ResultUserDecrypt(value.result, `${name}.result`);
19196
+ }
19197
+
19198
+ class RelayerV2RequestErrorBase extends RelayerErrorBase {
19199
+ _url;
19200
+ _operation;
19201
+ _jobId;
19202
+ constructor(params) {
19203
+ super({ ...params, name: params.name ?? 'RelayerV2RequestErrorBase' });
19204
+ this._url = params.url;
19205
+ this._operation = params.operation;
19206
+ this._jobId = params.jobId;
19207
+ }
19208
+ get url() {
19209
+ return this._url;
19210
+ }
19211
+ get jobId() {
19212
+ return this._jobId;
19213
+ }
19214
+ get operation() {
19215
+ return this._operation;
19216
+ }
19217
+ }
19218
+
19219
+ class RelayerV2RequestInternalError extends RelayerV2RequestErrorBase {
19220
+ _status;
19221
+ _state;
19222
+ constructor(params) {
19223
+ super({
19224
+ ...params,
19225
+ name: 'RelayerV2RequestInternalError',
19226
+ message: params.message ?? 'internal error',
19227
+ });
19228
+ this._status = params.status;
19229
+ this._state = params.state;
19230
+ }
19231
+ get status() {
19232
+ return this._status;
19233
+ }
19234
+ get state() {
19235
+ return this._state;
19236
+ }
19237
+ }
19238
+
19239
+ class RelayerV2ResponseApiError extends RelayerV2ResponseErrorBase {
19240
+ constructor(params) {
19241
+ super({
19242
+ ...params,
19243
+ name: 'RelayerV2ResponseApiError',
19244
+ message: params.relayerApiError.message,
19245
+ });
19246
+ }
19247
+ }
19248
+
19249
+ class RelayerV2FetchError extends RelayerV2FetchErrorBase {
19250
+ constructor(params) {
19251
+ super({
19252
+ ...params,
19253
+ name: 'RelayerV2FetchError',
19254
+ message: `Fetch ${params.fetchMethod} error`,
19255
+ cause: ensureError(params.cause),
19256
+ });
19257
+ }
19258
+ }
19259
+
19260
+ class RelayerV2ResponseInputProofRejectedError extends RelayerV2ResponseErrorBase {
19261
+ _result;
19262
+ constructor(params) {
19263
+ super({
19264
+ ...params,
19265
+ name: 'RelayerV2ResponseInputProofRejectedError',
19266
+ message: `InputProof rejected`,
19267
+ });
19268
+ this._result = params.result;
19269
+ }
19270
+ get result() {
19271
+ return this._result;
19272
+ }
19273
+ }
19274
+
19275
+ class RelayerV2StateError extends RelayerErrorBase {
19276
+ _state;
19277
+ constructor(params) {
19278
+ super({
19279
+ ...params,
19280
+ name: 'RelayerV2StateError',
19281
+ });
19282
+ this._state = { ...params.state };
19283
+ Object.freeze(this._state);
19284
+ }
19285
+ get state() {
19286
+ return this._state;
19287
+ }
19288
+ }
19289
+
19290
+ class RelayerV2MaxRetryError extends RelayerV2FetchErrorBase {
19291
+ constructor(params) {
19292
+ super({
19293
+ ...params,
19294
+ name: 'RelayerV2MaxRetryError',
19295
+ message: 'max retry error',
19296
+ });
19297
+ }
19298
+ }
19299
+
19300
+ function assertIsRelayerV2PostResultQueued(value, name) {
19301
+ assertRecordStringProperty(value, 'jobId', name);
19302
+ }
19303
+
19304
+ /*
19305
+ {
19306
+ status: 'queued';
19307
+ requestId: string;
19308
+ result: {
19309
+ jobId: string;
19310
+ };
19311
+ }
19312
+ */
19313
+ function assertIsRelayerV2PostResponseQueued(value, name) {
19314
+ assertRecordStringProperty(value, 'status', name, 'queued');
19315
+ assertRecordStringProperty(value, 'requestId', name);
19316
+ assertNonNullableRecordProperty(value, 'result', name);
19317
+ assertIsRelayerV2PostResultQueued(value.result, `${name}.result`);
19318
+ }
19319
+ /*
19320
+ {
19321
+ status: 'queued';
19322
+ requestId: string;
19323
+ }
19324
+ */
19325
+ function assertIsRelayerV2GetResponseQueued(value, name) {
19326
+ assertRecordStringProperty(value, 'status', name, 'queued');
19327
+ assertRecordStringProperty(value, 'requestId', name);
19328
+ }
19329
+
19330
+ class RelayerV2AsyncRequest {
19331
+ _jobId;
19332
+ _jobIdTimestamp;
19333
+ _state;
19334
+ _relayerOperation;
19335
+ _publicAPINoReentrancy;
19336
+ _internalAbortController;
19337
+ _internalAbortSignal;
19338
+ _externalAbortSignal;
19339
+ _terminateReason;
19340
+ _terminateError;
19341
+ _retryCount;
19342
+ _retryAfterTimeoutID;
19343
+ _url;
19344
+ _payload;
19345
+ _fhevmInstanceOptions;
19346
+ _retryAfterTimeoutPromiseFuncReject;
19347
+ _onProgress;
19348
+ _requestMaxDurationInSecs;
19349
+ _requestStartTimestamp;
19350
+ _requestGlobalTimeoutID;
19351
+ _throwErrorIfNoRetryAfter;
19352
+ static DEFAULT_RETRY_AFTER_SECS = 2;
19353
+ static DEFAULT_GLOBAL_REQUEST_TIMEOUT_SECS = 60 * 60;
19354
+ static MAX_GET_RETRY = 100;
19355
+ static MAX_POST_RETRY = 100;
19356
+ constructor(params) {
19357
+ if (params.relayerOperation !== 'INPUT_PROOF' &&
19358
+ params.relayerOperation !== 'PUBLIC_DECRYPT' &&
19359
+ params.relayerOperation !== 'USER_DECRYPT') {
19360
+ throw new InvalidPropertyError({
19361
+ objName: 'RelayerV2AsyncRequestParams',
19362
+ property: 'relayerOperation',
19363
+ expectedType: 'string',
19364
+ value: params.relayerOperation,
19365
+ expectedValue: 'INPUT_PROOF | PUBLIC_DECRYPT | USER_DECRYPT',
19366
+ });
19367
+ }
19368
+ this._relayerOperation = params.relayerOperation;
19369
+ this._internalAbortController = new AbortController();
19370
+ this._internalAbortSignal = this._internalAbortController.signal;
19371
+ this._internalAbortSignal.addEventListener('abort', this._handleInternalSignalAbort);
19372
+ this._externalAbortSignal = params.signal;
19373
+ if (this._externalAbortSignal) {
19374
+ this._externalAbortSignal.addEventListener('abort', this._handleExternalSignalAbort);
19375
+ }
19376
+ this._url = params.url;
19377
+ this._payload = params.payload;
19378
+ this._fhevmInstanceOptions = params.instanceOptions;
19379
+ this._onProgress = params.onProgress;
19380
+ this._state = {
19381
+ aborted: false,
19382
+ canceled: false,
19383
+ failed: false,
19384
+ fetching: false,
19385
+ running: false,
19386
+ succeeded: false,
19387
+ terminated: false,
19388
+ };
19389
+ this._retryCount = 0;
19390
+ this._retryAfterTimeoutID = undefined;
19391
+ this._requestGlobalTimeoutID = undefined;
19392
+ this._terminateReason = undefined;
19393
+ this._publicAPINoReentrancy = false;
19394
+ this._throwErrorIfNoRetryAfter = params.throwErrorIfNoRetryAfter ?? false;
19395
+ this._requestMaxDurationInSecs =
19396
+ params.timeoutInSeconds ??
19397
+ RelayerV2AsyncRequest.DEFAULT_GLOBAL_REQUEST_TIMEOUT_SECS;
19398
+ }
19399
+ //////////////////////////////////////////////////////////////////////////////
19400
+ // Public API: run
19401
+ //////////////////////////////////////////////////////////////////////////////
19402
+ async run() {
19403
+ if (this._publicAPINoReentrancy) {
19404
+ throw new RelayerV2StateError({
19405
+ message: `Relayer.run() failed. Call not permitted.`,
19406
+ state: { ...this._state },
19407
+ });
19408
+ }
19409
+ if (this._state.terminated) {
19410
+ throw new RelayerV2StateError({
19411
+ message: `Relayer.run() failed. Request already terminated.`,
19412
+ state: { ...this._state },
19413
+ });
19414
+ }
19415
+ if (this._state.canceled) {
19416
+ throw new RelayerV2StateError({
19417
+ message: `Relayer.run() failed. Request already canceled.`,
19418
+ state: { ...this._state },
19419
+ });
19420
+ }
19421
+ if (this._state.succeeded) {
19422
+ throw new RelayerV2StateError({
19423
+ message: `Relayer.run() failed. Request already succeeded.`,
19424
+ state: { ...this._state },
19425
+ });
19426
+ }
19427
+ if (this._state.failed) {
19428
+ throw new RelayerV2StateError({
19429
+ message: `Relayer.run() failed. Request already failed.`,
19430
+ state: { ...this._state },
19431
+ });
19432
+ }
19433
+ if (this._state.aborted) {
19434
+ throw new RelayerV2StateError({
19435
+ message: `Relayer.run() failed. Request already aborted.`,
19436
+ state: { ...this._state },
19437
+ });
19438
+ }
19439
+ if (this._externalAbortSignal?.aborted === true) {
19440
+ throw new RelayerV2StateError({
19441
+ message: `Relayer.run() failed. External AbortSignal already aborted (reason:${this._externalAbortSignal?.reason}).`,
19442
+ state: { ...this._state },
19443
+ });
19444
+ }
19445
+ if (this._internalAbortSignal?.aborted === true) {
19446
+ throw new RelayerV2StateError({
19447
+ message: `Relayer.run() failed. Internal AbortSignal already aborted (reason:${this._internalAbortSignal?.reason}).`,
19448
+ state: { ...this._state },
19449
+ });
19450
+ }
19451
+ if (this._state.running) {
19452
+ throw new RelayerV2StateError({
19453
+ message: `Relayer.run() failed. Request already running.`,
19454
+ state: { ...this._state },
19455
+ });
19456
+ }
19457
+ this._state.running = true;
19458
+ this._requestStartTimestamp = Date.now();
19459
+ this._setGlobalRequestTimeout(this._requestMaxDurationInSecs * 1000);
19460
+ try {
19461
+ const json = await this._runPostLoop();
19462
+ this._state.succeeded = true;
19463
+ this._terminate('succeeded');
19464
+ return json;
19465
+ }
19466
+ catch (e) {
19467
+ this._state.failed = true;
19468
+ if (e.name === 'AbortError') {
19469
+ this._assert(this._state.aborted, 'this._state.aborted');
19470
+ this._assert(this._state.terminated, 'this._state.terminated');
19471
+ }
19472
+ // Ignored if already terminated. For example, if abort has been previously called.
19473
+ this._terminate('failed', e);
19474
+ throw e;
19475
+ }
19476
+ }
19477
+ //////////////////////////////////////////////////////////////////////////////
19478
+ // Public API: cancel
19479
+ //////////////////////////////////////////////////////////////////////////////
19480
+ _canContinue() {
19481
+ return !(this._state.canceled ||
19482
+ this._state.terminated ||
19483
+ this._state.succeeded ||
19484
+ this._state.aborted);
19485
+ }
19486
+ cancel() {
19487
+ if (this._publicAPINoReentrancy) {
19488
+ throw new RelayerV2StateError({
19489
+ message: `Relayer.cancel() failed. Call not permitted.`,
19490
+ state: { ...this._state },
19491
+ });
19492
+ }
19493
+ if (!this._canContinue()) {
19494
+ this._trace('cancel', '!this._canContinue()');
19495
+ return;
19496
+ }
19497
+ this._state.canceled = true;
19498
+ this._internalAbortController?.abort('cancel');
19499
+ // Debug
19500
+ this._assert(this._state.aborted, 'this._state.aborted');
19501
+ this._assert(this._state.terminated, 'this._state.terminated');
19502
+ }
19503
+ //////////////////////////////////////////////////////////////////////////////
19504
+ // Public API: getters
19505
+ //////////////////////////////////////////////////////////////////////////////
19506
+ get state() {
19507
+ return { ...this._state };
19508
+ }
19509
+ get canceled() {
19510
+ return this._state.canceled;
19511
+ }
19512
+ get terminated() {
19513
+ return this._state.terminated;
19514
+ }
19515
+ get terminateReason() {
19516
+ return this._terminateReason;
19517
+ }
19518
+ get terminateError() {
19519
+ return this._terminateError;
19520
+ }
19521
+ get running() {
19522
+ return this._state.running;
19523
+ }
19524
+ get fetching() {
19525
+ return this._state.fetching;
19526
+ }
19527
+ get failed() {
19528
+ return this._state.failed;
19529
+ }
19530
+ get succeeded() {
19531
+ return this._state.succeeded;
19532
+ }
19533
+ get startTimeMs() {
19534
+ return this._requestStartTimestamp;
19535
+ }
19536
+ get elapsedTimeMs() {
19537
+ if (this._requestStartTimestamp === undefined) {
19538
+ return undefined;
19539
+ }
19540
+ return Date.now() - this._requestStartTimestamp;
19541
+ }
19542
+ get retryCount() {
19543
+ return this._retryCount;
19544
+ }
19545
+ //////////////////////////////////////////////////////////////////////////////
19546
+ // Post Loop
19547
+ //////////////////////////////////////////////////////////////////////////////
19548
+ // POST : 202 | 400 | 429 | 500 | 503
19549
+ async _runPostLoop() {
19550
+ // No infinite loop!
19551
+ let i = 0;
19552
+ while (i < RelayerV2AsyncRequest.MAX_POST_RETRY) {
19553
+ ++i;
19554
+ this._assertCanContinueAfterAwait();
19555
+ // At this stage: `terminated` is guaranteed to be `false`.
19556
+ // However, the `fetch` call can potentially throw an `AbortError`. In this case
19557
+ // in the error catch the `terminated` flag will be `true`! But, that's ok because the
19558
+ // next part of the function will never be executed (thrown error).
19559
+ const elapsed = this._jobId ? Date.now() - this._jobIdTimestamp : 0;
19560
+ const response = await this._fetchPost(elapsed);
19561
+ // At this stage: `terminated` is guaranteed to be `false`.
19562
+ const responseStatus = response.status;
19563
+ switch (responseStatus) {
19564
+ // RelayerV2ResponseQueued
19565
+ case 202: {
19566
+ // response.json() errors:
19567
+ // 1. if body is already read (call json() 2 times)
19568
+ // - TypeError: Body is unusable: Body has already been read
19569
+ // 2. if body is invalid JSON
19570
+ // - SyntaxError: Unexpected end of JSON input
19571
+ // - SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2) at JSON.parse (<anonymous>)
19572
+ const bodyJson = await this._getResponseJson(response);
19573
+ try {
19574
+ assertIsRelayerV2PostResponseQueued(bodyJson, 'body');
19575
+ }
19576
+ catch (cause) {
19577
+ this._throwResponseInvalidBodyError({
19578
+ fetchMethod: 'POST',
19579
+ status: responseStatus,
19580
+ cause: cause,
19581
+ elapsed,
19582
+ bodyJson: safeJSONstringify(bodyJson),
19583
+ });
19584
+ }
19585
+ let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
19586
+ if (retry_after_sec < 1)
19587
+ retry_after_sec = 1;
19588
+ // Debug: will throw an assert failed error if jobId has already been set
19589
+ this._setJobIdOnce(bodyJson.result.jobId);
19590
+ // Async onProgress callback
19591
+ this._postAsyncOnProgressCallback({
19592
+ type: 'queued',
19593
+ url: this._url,
19594
+ method: 'POST',
19595
+ status: responseStatus,
19596
+ requestId: bodyJson.requestId,
19597
+ jobId: this.jobId,
19598
+ operation: this._relayerOperation,
19599
+ retryCount: this._retryCount,
19600
+ retryAfter: retry_after_sec,
19601
+ elapsed,
19602
+ });
19603
+ // Wait if needed (minimum 1s)
19604
+ await this._setRetryAfterTimeout(retry_after_sec * 1000);
19605
+ const json = await this._runGetLoop();
19606
+ return json;
19607
+ }
19608
+ // RelayerV2ResponseFailed
19609
+ // RelayerV2ApiError400
19610
+ // RelayerV2ApiError400WithDetails
19611
+ case 400: {
19612
+ const bodyJson = await this._getResponseJson(response);
19613
+ try {
19614
+ assertIsRelayerV2ResponseFailedWithError400(bodyJson, 'body');
19615
+ }
19616
+ catch (cause) {
19617
+ this._throwResponseInvalidBodyError({
19618
+ fetchMethod: 'POST',
19619
+ status: responseStatus,
19620
+ cause: cause,
19621
+ elapsed,
19622
+ bodyJson: safeJSONstringify(bodyJson),
19623
+ });
19624
+ }
19625
+ this._throwRelayerV2ResponseApiError({
19626
+ fetchMethod: 'POST',
19627
+ status: responseStatus,
19628
+ relayerApiError: bodyJson.error,
19629
+ elapsed,
19630
+ });
19631
+ }
19632
+ // RelayerV2ResponseFailed
19633
+ // RelayerV2ApiError429
19634
+ case 429: {
19635
+ // Retry
19636
+ // Rate Limit error (Cloudflare/Kong/Relayer), reason in message
19637
+ const bodyJson = await this._getResponseJson(response);
19638
+ try {
19639
+ assertIsRelayerV2ResponseFailedWithError429(bodyJson, 'body');
19640
+ }
19641
+ catch (cause) {
19642
+ this._throwResponseInvalidBodyError({
19643
+ fetchMethod: 'POST',
19644
+ status: responseStatus,
19645
+ cause: cause,
19646
+ elapsed,
19647
+ bodyJson: safeJSONstringify(bodyJson),
19648
+ });
19649
+ }
19650
+ let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
19651
+ if (retry_after_sec < 1)
19652
+ retry_after_sec = 1;
19653
+ // Async onProgress callback
19654
+ this._postAsyncOnProgressCallback({
19655
+ type: 'ratelimited',
19656
+ url: this._url,
19657
+ method: 'POST',
19658
+ status: responseStatus,
19659
+ retryAfter: retry_after_sec,
19660
+ retryCount: this._retryCount,
19661
+ elapsed,
19662
+ message: bodyJson.error.message,
19663
+ });
19664
+ // Wait if needed (minimum 1s)
19665
+ await this._setRetryAfterTimeout(retry_after_sec * 1000);
19666
+ continue;
19667
+ }
19668
+ // RelayerV2ResponseFailed
19669
+ // RelayerV2ApiError500
19670
+ case 500: {
19671
+ // Abort
19672
+ // Relayer internal error
19673
+ const bodyJson = await this._getResponseJson(response);
19674
+ try {
19675
+ assertIsRelayerV2ResponseFailedWithError500(bodyJson, 'body');
19676
+ }
19677
+ catch (cause) {
19678
+ this._throwResponseInvalidBodyError({
19679
+ fetchMethod: 'POST',
19680
+ status: responseStatus,
19681
+ cause: cause,
19682
+ elapsed,
19683
+ bodyJson: safeJSONstringify(bodyJson),
19684
+ });
19685
+ }
19686
+ this._throwRelayerV2ResponseApiError({
19687
+ fetchMethod: 'POST',
19688
+ status: responseStatus,
19689
+ relayerApiError: bodyJson.error,
19690
+ elapsed,
19691
+ });
19692
+ }
19693
+ // RelayerV2ResponseFailed
19694
+ // RelayerV2ApiError503
19695
+ case 503: {
19696
+ // Abort
19697
+ // Possible Reasons: Gateway has some internal error (unknown)
19698
+ const bodyJson = await this._getResponseJson(response);
19699
+ try {
19700
+ assertIsRelayerV2ResponseFailedWithError503(bodyJson, 'body');
19701
+ }
19702
+ catch (cause) {
19703
+ this._throwResponseInvalidBodyError({
19704
+ fetchMethod: 'POST',
19705
+ status: responseStatus,
19706
+ cause: cause,
19707
+ elapsed,
19708
+ bodyJson: safeJSONstringify(bodyJson),
19709
+ });
19710
+ }
19711
+ this._throwRelayerV2ResponseApiError({
19712
+ fetchMethod: 'POST',
19713
+ status: responseStatus,
19714
+ relayerApiError: bodyJson.error,
19715
+ elapsed,
19716
+ });
19717
+ }
19718
+ default: {
19719
+ // Use TS compiler + `never` to guarantee the switch integrity
19720
+ const throwUnsupportedStatus = (unsupportedStatus) => {
19721
+ throw new RelayerV2ResponseStatusError({
19722
+ fetchMethod: 'POST',
19723
+ status: unsupportedStatus,
19724
+ url: this._url,
19725
+ operation: this._relayerOperation,
19726
+ elapsed,
19727
+ retryCount: this._retryCount,
19728
+ state: { ...this._state },
19729
+ });
19730
+ };
19731
+ throwUnsupportedStatus(responseStatus);
19732
+ }
19733
+ }
19734
+ }
19735
+ // Max retry error
19736
+ this._throwMaxRetryError({ fetchMethod: 'POST' });
19737
+ }
19738
+ //////////////////////////////////////////////////////////////////////////////
19739
+ // Get Loop
19740
+ //////////////////////////////////////////////////////////////////////////////
19741
+ // GET: 200 | 202 | 404 | 500 | 503 | 504
19742
+ // GET is not rate-limited, therefore there is not 429 error
19743
+ async _runGetLoop() {
19744
+ this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
19745
+ this._assert(this._jobIdTimestamp !== undefined, 'this._jobIdTimestamp !== undefined');
19746
+ let i = 0;
19747
+ while (i < RelayerV2AsyncRequest.MAX_GET_RETRY) {
19748
+ ++i;
19749
+ this._assertCanContinueAfterAwait();
19750
+ const elapsed = Date.now() - this._jobIdTimestamp;
19751
+ const response = await this._fetchGet(elapsed);
19752
+ // At this stage: `terminated` is guaranteed to be `false`.
19753
+ const responseStatus = response.status;
19754
+ switch (responseStatus) {
19755
+ // RelayerV2GetResponseSucceeded
19756
+ case 200: {
19757
+ const bodyJson = await this._getResponseJson(response);
19758
+ try {
19759
+ if (this._relayerOperation === 'INPUT_PROOF') {
19760
+ assertIsRelayerV2GetResponseInputProofSucceeded(bodyJson, 'body');
19761
+ // Async onProgress callback
19762
+ this._postAsyncOnProgressCallback({
19763
+ type: 'succeeded',
19764
+ url: this._url,
19765
+ method: 'GET',
19766
+ status: responseStatus,
19767
+ jobId: this.jobId,
19768
+ requestId: bodyJson.requestId,
19769
+ operation: this._relayerOperation,
19770
+ retryCount: this._retryCount,
19771
+ elapsed,
19772
+ result: bodyJson.result,
19773
+ });
19774
+ if (!bodyJson.result.accepted) {
19775
+ const e = new RelayerV2ResponseInputProofRejectedError({
19776
+ url: this._url,
19777
+ fetchMethod: 'GET',
19778
+ jobId: this.jobId,
19779
+ operation: this._relayerOperation,
19780
+ retryCount: this._retryCount,
19781
+ status: responseStatus,
19782
+ state: { ...this._state },
19783
+ elapsed,
19784
+ result: bodyJson.result,
19785
+ });
19786
+ throw e;
19787
+ }
19788
+ }
19789
+ else if (this._relayerOperation === 'PUBLIC_DECRYPT') {
19790
+ assertIsRelayerV2GetResponsePublicDecryptSucceeded(bodyJson, 'body');
19791
+ // Async onProgress callback
19792
+ this._postAsyncOnProgressCallback({
19793
+ type: 'succeeded',
19794
+ url: this._url,
19795
+ method: 'GET',
19796
+ status: responseStatus,
19797
+ jobId: this.jobId,
19798
+ requestId: bodyJson.requestId,
19799
+ operation: this._relayerOperation,
19800
+ retryCount: this._retryCount,
19801
+ elapsed,
19802
+ result: bodyJson.result,
19803
+ });
19804
+ }
19805
+ else if (this._relayerOperation === 'USER_DECRYPT') {
19806
+ assertIsRelayerV2GetResponseUserDecryptSucceeded(bodyJson, 'body');
19807
+ // Async onProgress callback
19808
+ this._postAsyncOnProgressCallback({
19809
+ type: 'succeeded',
19810
+ url: this._url,
19811
+ method: 'GET',
19812
+ status: responseStatus,
19813
+ jobId: this.jobId,
19814
+ requestId: bodyJson.requestId,
19815
+ operation: this._relayerOperation,
19816
+ retryCount: this._retryCount,
19817
+ elapsed,
19818
+ result: bodyJson.result,
19819
+ });
19820
+ }
19821
+ }
19822
+ catch (cause) {
19823
+ // Special case for InputProof rejected
19824
+ if (cause instanceof RelayerV2ResponseInputProofRejectedError) {
19825
+ throw cause;
19826
+ }
19827
+ this._throwResponseInvalidBodyError({
19828
+ fetchMethod: 'GET',
19829
+ status: responseStatus,
19830
+ elapsed,
19831
+ cause: cause,
19832
+ bodyJson: safeJSONstringify(bodyJson),
19833
+ });
19834
+ }
19835
+ // RelayerV2ResultPublicDecrypt
19836
+ // RelayerV2ResultUserDecrypt
19837
+ // RelayerV2ResultInputProof;
19838
+ return bodyJson.result;
19839
+ }
19840
+ // RelayerV2ResponseQueued
19841
+ case 202: {
19842
+ const bodyJson = await this._getResponseJson(response);
19843
+ try {
19844
+ assertIsRelayerV2GetResponseQueued(bodyJson, 'body');
19845
+ }
19846
+ catch (cause) {
19847
+ this._throwResponseInvalidBodyError({
19848
+ fetchMethod: 'GET',
19849
+ status: responseStatus,
19850
+ elapsed,
19851
+ cause: cause,
19852
+ bodyJson: safeJSONstringify(bodyJson),
19853
+ });
19854
+ }
19855
+ let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
19856
+ if (retry_after_sec < 1)
19857
+ retry_after_sec = 1;
19858
+ // Async onProgress callback
19859
+ this._postAsyncOnProgressCallback({
19860
+ type: 'queued',
19861
+ url: this._url,
19862
+ method: 'GET',
19863
+ status: responseStatus,
19864
+ requestId: bodyJson.requestId,
19865
+ operation: this._relayerOperation,
19866
+ jobId: this.jobId,
19867
+ retryAfter: retry_after_sec,
19868
+ retryCount: this._retryCount,
19869
+ elapsed,
19870
+ });
19871
+ // Wait if needed (minimum 1s)
19872
+ await this._setRetryAfterTimeout(retry_after_sec * 1000);
19873
+ continue;
19874
+ }
19875
+ case 400: {
19876
+ // Abort
19877
+ // Wrong jobId, incorrect format or unknown value etc.
19878
+ const bodyJson = await this._getResponseJson(response);
19879
+ try {
19880
+ assertIsRelayerV2ResponseFailedWithError400(bodyJson, 'body');
19881
+ }
19882
+ catch (cause) {
19883
+ this._throwResponseInvalidBodyError({
19884
+ fetchMethod: 'GET',
19885
+ status: responseStatus,
19886
+ elapsed,
19887
+ cause: cause,
19888
+ bodyJson: safeJSONstringify(bodyJson),
19889
+ });
19890
+ }
19891
+ this._throwRelayerV2ResponseApiError({
19892
+ fetchMethod: 'GET',
19893
+ status: responseStatus,
19894
+ relayerApiError: bodyJson.error,
19895
+ elapsed,
19896
+ });
19897
+ }
19898
+ case 404: {
19899
+ // Abort
19900
+ // Wrong jobId, incorrect format or unknown value etc.
19901
+ const bodyJson = await this._getResponseJson(response);
19902
+ try {
19903
+ assertIsRelayerV2ResponseFailedWithError404(bodyJson, 'body');
19904
+ }
19905
+ catch (cause) {
19906
+ this._throwResponseInvalidBodyError({
19907
+ fetchMethod: 'GET',
19908
+ status: responseStatus,
19909
+ elapsed,
19910
+ cause: cause,
19911
+ bodyJson: safeJSONstringify(bodyJson),
19912
+ });
19913
+ }
19914
+ this._throwRelayerV2ResponseApiError({
19915
+ fetchMethod: 'GET',
19916
+ status: responseStatus,
19917
+ relayerApiError: bodyJson.error,
19918
+ elapsed,
19919
+ });
19920
+ }
19921
+ // RelayerV2ResponseFailed
19922
+ // RelayerV2ApiError500
19923
+ case 500: {
19924
+ // Abort
19925
+ // Relayer internal error
19926
+ const bodyJson = await this._getResponseJson(response);
19927
+ try {
19928
+ assertIsRelayerV2ResponseFailedWithError500(bodyJson, 'body');
19929
+ }
19930
+ catch (cause) {
19931
+ this._throwResponseInvalidBodyError({
19932
+ fetchMethod: 'GET',
19933
+ status: responseStatus,
19934
+ elapsed,
19935
+ cause: cause,
19936
+ bodyJson: safeJSONstringify(bodyJson),
19937
+ });
19938
+ }
19939
+ this._throwRelayerV2ResponseApiError({
19940
+ fetchMethod: 'GET',
19941
+ status: responseStatus,
19942
+ relayerApiError: bodyJson.error,
19943
+ elapsed,
19944
+ });
19945
+ }
19946
+ // RelayerV2ResponseFailed
19947
+ // RelayerV2ApiError503
19948
+ case 503: {
19949
+ // Abort
19950
+ // Possible Reasons: Gateway has some internal error (unknown)
19951
+ const bodyJson = await this._getResponseJson(response);
19952
+ try {
19953
+ assertIsRelayerV2ResponseFailedWithError503(bodyJson, 'body');
19954
+ }
19955
+ catch (cause) {
19956
+ this._throwResponseInvalidBodyError({
19957
+ fetchMethod: 'GET',
19958
+ status: responseStatus,
19959
+ elapsed,
19960
+ cause: cause,
19961
+ bodyJson: safeJSONstringify(bodyJson),
19962
+ });
19963
+ }
19964
+ this._throwRelayerV2ResponseApiError({
19965
+ fetchMethod: 'GET',
19966
+ status: responseStatus,
19967
+ relayerApiError: bodyJson.error,
19968
+ elapsed,
19969
+ });
19970
+ }
19971
+ // RelayerV2ResponseFailed
19972
+ // RelayerV2ApiError504
19973
+ case 504: {
19974
+ // Abort
19975
+ // Possible Reasons: Gateway has not responded in time (gateway timeout)
19976
+ const bodyJson = await this._getResponseJson(response);
19977
+ try {
19978
+ assertIsRelayerV2ResponseFailedWithError504(bodyJson, 'body');
19979
+ }
19980
+ catch (cause) {
19981
+ this._throwResponseInvalidBodyError({
19982
+ fetchMethod: 'GET',
19983
+ status: responseStatus,
19984
+ elapsed,
19985
+ cause: cause,
19986
+ bodyJson: safeJSONstringify(bodyJson),
19987
+ });
19988
+ }
19989
+ this._throwRelayerV2ResponseApiError({
19990
+ fetchMethod: 'GET',
19991
+ status: responseStatus,
19992
+ relayerApiError: bodyJson.error,
19993
+ elapsed,
19994
+ });
19995
+ }
19996
+ default: {
19997
+ // Use TS compiler + `never` to guarantee the switch integrity
19998
+ const throwUnsupportedStatus = (unsupportedStatus) => {
19999
+ throw new RelayerV2ResponseStatusError({
20000
+ fetchMethod: 'GET',
20001
+ status: unsupportedStatus,
20002
+ url: this._url,
20003
+ jobId: this.jobId,
20004
+ operation: this._relayerOperation,
20005
+ elapsed,
20006
+ retryCount: this._retryCount,
20007
+ state: { ...this._state },
20008
+ });
20009
+ };
20010
+ throwUnsupportedStatus(responseStatus);
20011
+ }
20012
+ }
20013
+ }
20014
+ // Max retry error
20015
+ this._throwMaxRetryError({ fetchMethod: 'GET' });
20016
+ }
20017
+ //////////////////////////////////////////////////////////////////////////////
20018
+ async _getResponseJson(response) {
20019
+ const bodyJson = await response.json();
20020
+ this._assertCanContinueAfterAwait();
20021
+ return bodyJson;
20022
+ }
20023
+ //////////////////////////////////////////////////////////////////////////////
20024
+ _getRetryAfterHeaderValueInSecs(response) {
20025
+ if (!response.headers.has('Retry-After')) {
20026
+ if (this._throwErrorIfNoRetryAfter) {
20027
+ throw new Error(`Missing 'Retry-After' header key`);
20028
+ }
20029
+ return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_SECS;
20030
+ }
20031
+ try {
20032
+ const n = Number.parseInt(response.headers.get('Retry-After'));
20033
+ if (isUint(n)) {
20034
+ return n;
20035
+ }
20036
+ }
20037
+ catch {
20038
+ //
20039
+ }
20040
+ if (this._throwErrorIfNoRetryAfter) {
20041
+ throw new Error(`Invalid 'Retry-After' header key`);
20042
+ }
20043
+ return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_SECS;
20044
+ }
20045
+ //////////////////////////////////////////////////////////////////////////////
20046
+ // JobId
20047
+ //////////////////////////////////////////////////////////////////////////////
20048
+ /**
20049
+ * Sets the unique job identifier for this request.
20050
+ *
20051
+ * This function enforces a strict initialization constraint: the jobId must be
20052
+ * set exactly once during the entire lifecycle of the state machine instance.
20053
+ *
20054
+ * This immutability ensures that all subsequent operations, logging, and state
20055
+ * transitions are consistently associated with the correct external request.
20056
+ *
20057
+ * @param jobId - The unique identifier associated with the asynchronous job request.
20058
+ * @private
20059
+ * @throws {RelayerV2RequestInternalError} Thrown if jobId is undefined or if the jobId has already been set.
20060
+ */
20061
+ _setJobIdOnce(jobId) {
20062
+ this._assert(jobId !== undefined, 'jobId !== undefined');
20063
+ this._assert(this._jobId === undefined, 'this._jobId === undefined');
20064
+ this._jobId = jobId;
20065
+ this._jobIdTimestamp = Date.now();
20066
+ }
20067
+ get jobId() {
20068
+ this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
20069
+ return this._jobId;
20070
+ }
20071
+ //////////////////////////////////////////////////////////////////////////////
20072
+ // Fetch functions
20073
+ //////////////////////////////////////////////////////////////////////////////
20074
+ async _fetchPost(elapsed) {
20075
+ // Debug state-check guards:
20076
+ // - the jobId is guaranteed to be undefined.
20077
+ // - `terminated` is guaranteed to be `false`
20078
+ // - `fetching` is guaranteed to be `false`
20079
+ this._assert(this._jobId === undefined, 'this._jobId === undefined');
20080
+ this._assert(!this._state.terminated, '!this._state.terminated');
20081
+ this._assert(!this._state.fetching, '!this._state.fetching');
20082
+ this._trace('_fetchPost', 'enter');
20083
+ const init = setAuth({
20084
+ method: 'POST',
20085
+ headers: {
20086
+ 'Content-Type': 'application/json',
20087
+ 'ZAMA-SDK-VERSION': `${version}`,
20088
+ 'ZAMA-SDK-NAME': `${sdkName}`,
20089
+ },
20090
+ body: JSON.stringify(this._payload),
20091
+ ...(this._internalAbortSignal
20092
+ ? { signal: this._internalAbortSignal }
20093
+ : {}),
20094
+ }, this._fhevmInstanceOptions?.auth);
20095
+ this._state.fetching = true;
20096
+ let response;
20097
+ try {
20098
+ response = await fetch(this._url, init);
20099
+ }
20100
+ catch (cause) {
20101
+ this._state.fetching = false;
20102
+ // Warning: `terminated` can be `true` here!
20103
+ // (ex: if `controller.abort()` has been called from the outside while still executing `fetch`)
20104
+ this._trace('_fetchPost', 'catch(e) + throw e: ' + cause);
20105
+ // Keep the standard 'AbortError'
20106
+ if (cause.name === 'AbortError') {
20107
+ throw cause;
20108
+ }
20109
+ else {
20110
+ this._throwFetchError({
20111
+ fetchMethod: 'POST',
20112
+ cause,
20113
+ elapsed,
20114
+ });
20115
+ }
20116
+ }
20117
+ this._state.fetching = false;
20118
+ // Debug state-check guards:
20119
+ // - the jobId is guaranteed to be undefined.
20120
+ // - `terminated` is guaranteed to be `false`
20121
+ this._assert(this._jobId === undefined, 'this._jobId === undefined');
20122
+ this._assert(!this._state.terminated, '!this._state.terminated');
20123
+ // Debug
20124
+ this._assertCanContinueAfterAwait();
20125
+ this._trace('_fetchPost', 'return response Ok');
20126
+ return response;
20127
+ }
20128
+ //////////////////////////////////////////////////////////////////////////////
20129
+ async _fetchGet(elapsed) {
20130
+ // Debug state-check guards:
20131
+ // - the jobId is guaranteed to be set.
20132
+ // - `terminated` is guaranteed to be `false`
20133
+ // - `fetching` is guaranteed to be `false`
20134
+ this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
20135
+ this._assert(!this._state.terminated, '!this._state.terminated');
20136
+ this._assert(!this._state.fetching, '!this._state.fetching');
20137
+ this._trace('_fetchGet', `jobId=${this.jobId}`);
20138
+ const init = {
20139
+ method: 'GET',
20140
+ headers: {
20141
+ 'ZAMA-SDK-VERSION': `${version}`,
20142
+ 'ZAMA-SDK-NAME': `${sdkName}`,
20143
+ },
20144
+ ...(this._internalAbortSignal
20145
+ ? { signal: this._internalAbortSignal }
20146
+ : {}),
20147
+ };
20148
+ this._state.fetching = true;
20149
+ let response;
20150
+ try {
20151
+ response = await fetch(`${this._url}/${this.jobId}`, init);
20152
+ }
20153
+ catch (cause) {
20154
+ this._state.fetching = false;
20155
+ // Warning: `terminated` can be `true` here!
20156
+ // (ex: if `controller.abort()` has been called from the outside while still executing `fetch`)
20157
+ this._trace('_fetchGet', `jobId=${this.jobId}, catch(e) + throw e: ${cause}`);
20158
+ // Keep the standard 'AbortError'
20159
+ if (cause.name === 'AbortError') {
20160
+ throw cause;
20161
+ }
20162
+ else {
20163
+ this._throwFetchError({
20164
+ fetchMethod: 'GET',
20165
+ cause,
20166
+ elapsed,
20167
+ });
20168
+ }
20169
+ }
20170
+ this._state.fetching = false;
20171
+ // Debug state-check guards:
20172
+ // - the jobId is guaranteed to be set.
20173
+ // - `terminated` is guaranteed to be `false`
20174
+ this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
20175
+ this._assert(!this._state.terminated, '!this._state.terminated');
20176
+ // Debug
20177
+ this._assertCanContinueAfterAwait();
20178
+ this._trace('_fetchGet', `jobId=${this.jobId}, return response Ok, status=${response.status}`);
20179
+ return response;
20180
+ }
20181
+ //////////////////////////////////////////////////////////////////////////////
20182
+ // AbortSignal
20183
+ //////////////////////////////////////////////////////////////////////////////
20184
+ // Warning: Use arrow function only!
20185
+ _handleExternalSignalAbort = (ev) => {
20186
+ const signal = ev.currentTarget;
20187
+ // TESTING: the following sequences must be extensively tested:
20188
+ // ============================================================
20189
+ //
20190
+ // Each steps could potentially be called synchronously one after the other
20191
+ // or asynchronously: step 2 is called from the next microtick
20192
+ //
20193
+ // 1. externalSignal.abort();
20194
+ // 2. request.cancel();
20195
+ //
20196
+ // 1. externalSignal.abort();
20197
+ // 2. externalSignal.abort();
20198
+ //
20199
+ // 1. request.cancel();
20200
+ // 2. externalSignal.abort();
20201
+ // Debug state-check guards:
20202
+ this._assert(this instanceof RelayerV2AsyncRequest, `this instanceof RelayerV2AsyncRequest`);
20203
+ this._assert(signal === this._externalAbortSignal, 'signal === this._externalAbortSignal');
20204
+ this._assert(!this._state.terminated, `!this._state.terminated`);
20205
+ this._assert(!this._state.aborted, '!this._state.aborted');
20206
+ this._assert(!this._state.canceled, '!this._state.canceled');
20207
+ this.cancel();
20208
+ };
20209
+ // Warning: Use arrow function only!
20210
+ _handleInternalSignalAbort = (ev) => {
20211
+ const signal = ev.currentTarget;
20212
+ // Debug state-check guards:
20213
+ this._assert(this instanceof RelayerV2AsyncRequest, `this instanceof RelayerV2AsyncRequest`);
20214
+ this._assert(signal === this._internalAbortSignal, 'signal === this._internalAbortSignal');
20215
+ this._assert(!this._state.terminated, `!this._state.terminated`);
20216
+ this._assert(!this._state.aborted, '!this._state.aborted');
20217
+ this._state.aborted = true;
20218
+ if (signal.reason !== 'cancel') {
20219
+ this._assert(!this._state.canceled, '!this._state.canceled');
20220
+ }
20221
+ this._terminate('abort');
20222
+ };
20223
+ //////////////////////////////////////////////////////////////////////////////
20224
+ // Terminate
20225
+ //////////////////////////////////////////////////////////////////////////////
20226
+ /**
20227
+ * Can be called multiple times
20228
+ */
20229
+ _terminate(reason, error) {
20230
+ // Warning: this._state.fetching can be true
20231
+ // ex: call cancel while fetch is running
20232
+ if (this._state.terminated) {
20233
+ this._trace(`_terminate`, `reason=${reason}. Already terminated with reason='${this._terminateReason}'. IGNORE`);
20234
+ this._assert(this._terminateReason !== undefined, 'this._terminateReason !== undefined');
20235
+ this._assert(this._internalAbortSignal === undefined, 'this._signal === undefined');
20236
+ this._assert(this._requestGlobalTimeoutID === undefined, 'this._requestGlobalTimeoutID === undefined');
20237
+ this._assert(this._retryAfterTimeoutID === undefined, 'this._retryAfterTimeoutID === undefined');
20238
+ this._assert(this._retryAfterTimeoutPromiseFuncReject === undefined, 'this._retryAfterTimeoutPromiseFuncReject === undefined');
20239
+ return;
20240
+ }
20241
+ this._trace('_terminate', `reason=${reason}`);
20242
+ this._terminateReason = reason;
20243
+ this._terminateError = error;
20244
+ this._state.terminated = true;
20245
+ this._tryClearRetryAfterTimeout();
20246
+ this._tryClearGlobalRequestTimeout();
20247
+ const is = this._internalAbortSignal;
20248
+ const es = this._externalAbortSignal;
20249
+ this._externalAbortSignal = undefined;
20250
+ this._internalAbortSignal = undefined;
20251
+ this._internalAbortController = undefined;
20252
+ if (es) {
20253
+ es.removeEventListener('abort', this._handleExternalSignalAbort);
20254
+ }
20255
+ if (is) {
20256
+ is.removeEventListener('abort', this._handleInternalSignalAbort);
20257
+ }
20258
+ this._trace('_terminate', `reason=${reason} completed.`);
20259
+ }
20260
+ //////////////////////////////////////////////////////////////////////////////
20261
+ // Retry-After timeout
20262
+ //////////////////////////////////////////////////////////////////////////////
20263
+ async _setRetryAfterTimeout(delayMs) {
20264
+ // Debug
20265
+ this._assert(!this._state.terminated, '!this._state.terminated');
20266
+ this._assert(this._retryAfterTimeoutID === undefined, 'this._retryAfterTimeoutID === undefined');
20267
+ this._assert(delayMs >= 1000, 'delayMs >= 1000');
20268
+ this._trace('_setRetryAfterTimeout', `delayMs=${delayMs}`);
20269
+ if (this._retryAfterTimeoutID !== undefined) {
20270
+ return Promise.reject(new Error(`retry-after already running.`));
20271
+ }
20272
+ const p = new Promise((resolve, reject) => {
20273
+ this._retryAfterTimeoutPromiseFuncReject = reject;
20274
+ const callback = () => {
20275
+ this._retryAfterTimeoutID = undefined;
20276
+ this._retryAfterTimeoutPromiseFuncReject = undefined;
20277
+ resolve();
20278
+ };
20279
+ this._retryCount++;
20280
+ this._retryAfterTimeoutID = setTimeout(callback, delayMs);
20281
+ });
20282
+ this._assert(this._retryAfterTimeoutID !== undefined, 'this._retryAfterTimeoutID !== undefined');
20283
+ this._assert(this._retryAfterTimeoutPromiseFuncReject !== undefined, 'this._retryAfterTimeoutPromiseFuncReject !== undefined');
20284
+ return p;
20285
+ }
20286
+ //////////////////////////////////////////////////////////////////////////////
20287
+ _tryClearRetryAfterTimeout() {
20288
+ if (this._retryAfterTimeoutID === undefined) {
20289
+ // Debug
20290
+ this._assert(this._retryAfterTimeoutPromiseFuncReject === undefined, 'this._retryAfterTimeoutPromiseFuncReject === undefined');
20291
+ return;
20292
+ }
20293
+ const reject = this._retryAfterTimeoutPromiseFuncReject;
20294
+ const tid = this._retryAfterTimeoutID;
20295
+ this._retryAfterTimeoutID = undefined;
20296
+ this._retryAfterTimeoutPromiseFuncReject = undefined;
20297
+ clearTimeout(tid);
20298
+ reject(new Error('_tryClearRetryAfterTimeout'));
20299
+ }
20300
+ //////////////////////////////////////////////////////////////////////////////
20301
+ // Global Request Timeout
20302
+ //////////////////////////////////////////////////////////////////////////////
20303
+ _setGlobalRequestTimeout(delayMs) {
20304
+ // Debug
20305
+ this._assert(this._requestGlobalTimeoutID === undefined, 'this._requestGlobalTimeoutID === undefined');
20306
+ const callback = () => {
20307
+ this._requestGlobalTimeoutID = undefined;
20308
+ this._handleGlobalRequestTimeout();
20309
+ };
20310
+ this._requestGlobalTimeoutID = setTimeout(callback, delayMs);
20311
+ }
20312
+ _handleGlobalRequestTimeout() {
20313
+ this._terminate('timeout');
20314
+ }
20315
+ _tryClearGlobalRequestTimeout() {
20316
+ if (this._requestGlobalTimeoutID === undefined) {
20317
+ return;
20318
+ }
20319
+ const tid = this._requestGlobalTimeoutID;
20320
+ this._requestGlobalTimeoutID = undefined;
20321
+ clearTimeout(tid);
20322
+ }
20323
+ //////////////////////////////////////////////////////////////////////////////
20324
+ // Progress
20325
+ //////////////////////////////////////////////////////////////////////////////
20326
+ _postAsyncOnProgressCallback(args) {
20327
+ const onProgressFunc = this._onProgress;
20328
+ if (onProgressFunc) {
20329
+ // setTimeout(() => {
20330
+ // onProgressFunc(args);
20331
+ // }, 0);
20332
+ // onProgressFunc() will execute asynchronously in the next cycle of
20333
+ // the JavaScript event loop (the microtask queue).
20334
+ // Promise.resolve().then(() => {
20335
+ // onProgressFunc(args);
20336
+ // });
20337
+ queueMicrotask(() => {
20338
+ onProgressFunc(args);
20339
+ });
20340
+ }
20341
+ }
20342
+ //////////////////////////////////////////////////////////////////////////////
20343
+ // Errors
20344
+ //////////////////////////////////////////////////////////////////////////////
20345
+ _throwRelayerV2ResponseApiError(params) {
20346
+ // Clone
20347
+ const clonedRelayerApiError = JSON.parse(JSON.stringify(params.relayerApiError));
20348
+ // Async onProgress callback
20349
+ this._postAsyncOnProgressCallback({
20350
+ type: 'failed',
20351
+ url: this._url,
20352
+ method: params.fetchMethod,
20353
+ status: params.status,
20354
+ ...(this._jobId ? { jobId: this._jobId } : {}),
20355
+ operation: this._relayerOperation,
20356
+ retryCount: this._retryCount,
20357
+ elapsed: params.elapsed,
20358
+ relayerApiError: clonedRelayerApiError,
20359
+ });
20360
+ throw new RelayerV2ResponseApiError({
20361
+ url: this._url,
20362
+ fetchMethod: params.fetchMethod,
20363
+ status: params.status,
20364
+ jobId: this._jobId,
20365
+ operation: this._relayerOperation,
20366
+ retryCount: this._retryCount,
20367
+ elapsed: params.elapsed,
20368
+ relayerApiError: params.relayerApiError,
20369
+ state: { ...this._state },
20370
+ });
20371
+ }
20372
+ _assert(condition, message) {
20373
+ if (!condition) {
20374
+ this._throwInternalError(`Assertion failed: ${message}`);
20375
+ }
20376
+ }
20377
+ _throwInternalError(message) {
20378
+ throw new RelayerV2RequestInternalError({
20379
+ operation: this._relayerOperation,
20380
+ url: this._url,
20381
+ message,
20382
+ state: JSON.stringify(this._state),
20383
+ jobId: this._jobId, // internal value
20384
+ });
20385
+ }
20386
+ _throwMaxRetryError(params) {
20387
+ const elapsed = this._jobIdTimestamp
20388
+ ? Date.now() - this._jobIdTimestamp
20389
+ : 0;
20390
+ throw new RelayerV2MaxRetryError({
20391
+ operation: this._relayerOperation,
20392
+ url: this._url,
20393
+ state: { ...this._state },
20394
+ retryCount: this._retryCount,
20395
+ jobId: this._jobId, // internal value
20396
+ fetchMethod: params.fetchMethod,
20397
+ elapsed,
20398
+ });
20399
+ }
20400
+ _throwResponseInvalidBodyError(params) {
20401
+ throw new RelayerV2ResponseInvalidBodyError({
20402
+ ...params,
20403
+ url: this._url,
20404
+ jobId: this._jobId,
20405
+ operation: this._relayerOperation,
20406
+ state: { ...this._state },
20407
+ retryCount: this._retryCount,
20408
+ });
20409
+ }
20410
+ _throwFetchError(params) {
20411
+ throw new RelayerV2FetchError({
20412
+ ...params,
20413
+ url: this._url,
20414
+ jobId: this._jobId,
20415
+ operation: this._relayerOperation,
20416
+ state: { ...this._state },
20417
+ retryCount: this._retryCount,
20418
+ });
20419
+ }
20420
+ /**
20421
+ * Assert Continuation Guard
20422
+ *
20423
+ * This internal method implements a state-check guard to ensure the state machine
20424
+ * can safely proceed after an asynchronous operation has completed.
20425
+ *
20426
+ * In a state machine with asynchronous calls (e.g., fetch, timer delays), the system's
20427
+ * state (e.g., this._state) might change externally during the 'await' pause
20428
+ * (e.g., due to a timeout, an external abort signal, or a concurrent state transition).
20429
+ *
20430
+ * If the internal check (this._canContinue()) returns false, it means the current
20431
+ * operation is no longer valid, and execution must stop immediately to prevent state corruption.
20432
+ * This pattern is essential for reliable asynchronous state machines.
20433
+ *
20434
+ * @throws {RelayerV2RequestInternalError} Thrown if the state check fails (i.e., this._canContinue() is false).
20435
+ * The error includes relevant state information (like current state and jobId)
20436
+ * to aid in debugging the exact point of the integrity failure.
20437
+ */
20438
+ _assertCanContinueAfterAwait() {
20439
+ if (!this._canContinue()) {
20440
+ this._throwInternalError('cannot continue.');
20441
+ }
20442
+ }
20443
+ //////////////////////////////////////////////////////////////////////////////
20444
+ // Trace
20445
+ //////////////////////////////////////////////////////////////////////////////
20446
+ _trace(functionName, message) {
20447
+ console.log(`[RelayerV2AsyncRequest]:${functionName}: ${message}`);
20448
+ }
20449
+ }
20450
+
20451
+ function assertIsRelayerV2GetResponseKeyUrl(value, name) {
20452
+ assertNonNullableRecordProperty(value, 'response', name);
20453
+ // crs
20454
+ assertNonNullableRecordProperty(value.response, 'crs', `${name}.response`);
20455
+ const crs = value.response.crs;
20456
+ const keys = Object.keys(crs);
20457
+ for (let i = 0; i < keys.length; ++i) {
20458
+ // RelayerV2KeyData
20459
+ assertIsRelayerV2KeyData(crs[keys[i]], `${name}.response.crs.${keys[i]}`);
20460
+ }
20461
+ // fhe_key_info
20462
+ assertRecordArrayProperty(value.response, 'fheKeyInfo', `${name}.response`);
20463
+ const fheKeyInfo = value.response.fheKeyInfo;
20464
+ for (let i = 0; i < fheKeyInfo.length; ++i) {
20465
+ const ki = fheKeyInfo[i];
20466
+ const kiName = `${name}.response.fheKeyInfo[${i}]`;
20467
+ assertNonNullableRecordProperty(ki, 'fhePublicKey', kiName);
20468
+ assertIsRelayerV2KeyData(ki.fhePublicKey, `${kiName}.fhePublicKey`);
20469
+ }
20470
+ }
20471
+ function assertIsRelayerV2KeyData(value, name) {
20472
+ assertRecordStringProperty(value, 'dataId', name);
20473
+ assertRecordStringArrayProperty(value, 'urls', name);
20474
+ }
20475
+ function toRelayerV1KeyUrlResponse(response) {
20476
+ const fheKeyInfoV1 = response.response.fheKeyInfo.map((v2Info) => ({
20477
+ fhe_public_key: {
20478
+ data_id: v2Info.fhePublicKey.dataId,
20479
+ urls: v2Info.fhePublicKey.urls,
20480
+ },
20481
+ }));
20482
+ const crsV1 = {};
20483
+ for (const [key, v2Data] of Object.entries(response.response.crs)) {
20484
+ crsV1[key] = {
20485
+ data_id: v2Data.dataId,
20486
+ urls: v2Data.urls,
20487
+ };
20488
+ }
20489
+ return {
20490
+ response: {
20491
+ fhe_key_info: fheKeyInfoV1,
20492
+ crs: crsV1,
20493
+ },
20494
+ };
20495
+ }
20496
+
20497
+ class RelayerV2Provider extends AbstractRelayerProvider {
20498
+ constructor(relayerUrl) {
20499
+ super(relayerUrl);
20500
+ }
20501
+ get version() {
20502
+ return 2;
20503
+ }
20504
+ async fetchGetKeyUrlV2() {
20505
+ const response = await fetchRelayerV1Get('KEY_URL', this.keyUrl);
20506
+ // Relayer error
20507
+ try {
20508
+ assertIsRelayerV2GetResponseKeyUrl(response, 'fetchGetKeyUrl()');
20509
+ }
20510
+ catch (e) {
20511
+ throw new RelayerV2GetKeyUrlInvalidResponseError({
20512
+ cause: ensureError(e),
20513
+ });
20514
+ }
20515
+ return response;
20516
+ }
20517
+ async fetchGetKeyUrl() {
20518
+ const response = await this.fetchGetKeyUrlV2();
20519
+ return toRelayerV1KeyUrlResponse(response);
20520
+ }
20521
+ async fetchPostInputProof(payload, instanceOptions, fetchOptions) {
20522
+ const request = new RelayerV2AsyncRequest({
20523
+ relayerOperation: 'INPUT_PROOF',
20524
+ url: this.inputProof,
20525
+ payload,
20526
+ instanceOptions,
20527
+ ...fetchOptions,
20528
+ });
20529
+ const result = (await request.run());
20530
+ assertIsRelayerInputProofResult(result, 'fetchPostInputProof()');
20531
+ return result;
20532
+ }
20533
+ async fetchPostPublicDecrypt(payload, instanceOptions, fetchOptions) {
20534
+ const request = new RelayerV2AsyncRequest({
20535
+ relayerOperation: 'PUBLIC_DECRYPT',
20536
+ url: this.publicDecrypt,
20537
+ payload,
20538
+ instanceOptions,
20539
+ ...fetchOptions,
20540
+ });
20541
+ const result = await request.run();
20542
+ assertIsRelayerPublicDecryptResult(result, 'fetchPostPublicDecrypt()');
20543
+ return result;
20544
+ }
20545
+ async fetchPostUserDecrypt(payload, instanceOptions, fetchOptions) {
20546
+ const request = new RelayerV2AsyncRequest({
20547
+ relayerOperation: 'USER_DECRYPT',
20548
+ url: this.userDecrypt,
20549
+ payload,
20550
+ instanceOptions,
20551
+ ...fetchOptions,
20552
+ });
20553
+ const result = (await request.run());
20554
+ assertIsRelayerUserDecryptResult(result.result, 'fetchPostUserDecrypt()');
20555
+ return result.result;
20556
+ }
20557
+ }
20558
+
20559
+ class AbstractRelayerFhevm {
20560
+ }
20561
+
20562
+ class TFHECrs {
20563
+ _id;
20564
+ _compactPkeCrs;
20565
+ _bits;
20566
+ _srcUrl;
20567
+ constructor(params) {
20568
+ this._id = params.id;
20569
+ this._compactPkeCrs = params.compactPkeCrs;
20570
+ this._bits = params.bits;
20571
+ this._srcUrl = params.srcUrl;
20572
+ }
20573
+ /*
20574
+ {
20575
+ id: string,
20576
+ data: Uint8Array,
20577
+ bits: number
20578
+ srcUrl?: string
20579
+ }
20580
+ */
20581
+ static isKeyBytesType(value) {
20582
+ try {
20583
+ TFHECrs.assertKeyBytesType(value, '');
20584
+ return true;
20585
+ }
20586
+ catch {
20587
+ return false;
20588
+ }
20589
+ }
20590
+ /*
20591
+ {
20592
+ id: string,
20593
+ bits: number
20594
+ srcUrl: string
20595
+ }
20596
+ */
20597
+ static isKeyUrlType(value) {
20598
+ try {
20599
+ TFHECrs.assertKeyUrlType(value, '');
20600
+ return true;
20601
+ }
20602
+ catch {
20603
+ return false;
20604
+ }
20605
+ }
20606
+ /*
20607
+ {
20608
+ id: string,
20609
+ data: Uint8Array,
20610
+ bits: number
20611
+ srcUrl?: string
20612
+ }
20613
+ */
20614
+ static assertKeyBytesType(value, name) {
20615
+ assertRecordStringProperty(value, 'id', name);
20616
+ assertUint8ArrayProperty(value, 'data', name);
20617
+ assertRecordUintProperty(value, 'bits', name);
20618
+ if (isNonNullableRecordProperty(value, 'srcUrl')) {
20619
+ assertRecordStringProperty(value, 'srcUrl', name);
20620
+ }
20621
+ }
20622
+ /*
20623
+ {
20624
+ id: string,
20625
+ bits: number
20626
+ srcUrl: string
20627
+ }
20628
+ */
20629
+ static assertKeyUrlType(value, name) {
20630
+ assertRecordStringProperty(value, 'id', name);
20631
+ assertRecordUintProperty(value, 'bits', name);
20632
+ assertRecordStringProperty(value, 'srcUrl', name);
20633
+ }
20634
+ /*
20635
+ {
20636
+ 2048: {
20637
+ publicParamsId: string,
20638
+ publicParams: Uint8Array
20639
+ }
20640
+ }
20641
+ */
20642
+ static assertIsPublicParams2048BytesType(value, name) {
20643
+ assertNonNullableRecordProperty(value, '2048', name);
20644
+ assertRecordStringProperty(value['2048'], 'publicParamsId', `${name}.2048`);
20645
+ assertUint8ArrayProperty(value['2048'], 'publicParams', `${name}.2048`);
20646
+ }
20647
+ /*
20648
+ {
20649
+ 2048: {
20650
+ publicParamsId: string,
20651
+ publicParams: Uint8Array
20652
+ }
20653
+ }
20654
+ */
20655
+ static isPublicParams2048BytesType(value) {
20656
+ try {
20657
+ TFHECrs.assertIsPublicParams2048BytesType(value, '');
20658
+ return true;
20659
+ }
20660
+ catch {
20661
+ return false;
20662
+ }
20663
+ }
20664
+ static async fromBytesOrUrl(params) {
20665
+ if (TFHECrs.isKeyBytesType(params)) {
20666
+ return TFHECrs._fromBytes(params);
20667
+ }
20668
+ else if (TFHECrs.isPublicParams2048BytesType(params)) {
20669
+ return TFHECrs._fromPublicParamsBytes(params);
20670
+ }
20671
+ else if (TFHECrs.isKeyUrlType(params)) {
20672
+ return TFHECrs._fromUrl(params);
20673
+ }
20674
+ else {
20675
+ throw new Error('Invalid public key (deserialization failed)');
20676
+ }
20677
+ }
20678
+ static fromBytes(params) {
20679
+ try {
20680
+ TFHECrs.assertKeyBytesType(params, 'arg');
20681
+ return TFHECrs._fromBytes(params);
20682
+ }
20683
+ catch (e) {
20684
+ throw new Error('Invalid public key (deserialization failed)', {
20685
+ cause: e,
20686
+ });
20687
+ }
20688
+ }
20689
+ static _fromBytes(params) {
20690
+ const _params = {
20691
+ compactPkeCrs: TFHE.CompactPkeCrs.safe_deserialize(params.data, SERIALIZED_SIZE_LIMIT_CRS),
20692
+ id: params.id,
20693
+ bits: params.bits,
20694
+ srcUrl: params.srcUrl,
20695
+ };
20696
+ return new TFHECrs(_params);
20697
+ }
20698
+ static fromPublicParamsBytes(params) {
20699
+ try {
20700
+ TFHECrs.assertIsPublicParams2048BytesType(params, 'arg');
20701
+ return TFHECrs._fromPublicParamsBytes(params);
20702
+ }
20703
+ catch (e) {
20704
+ throw new Error('Invalid public key (deserialization failed)', {
20705
+ cause: e,
20706
+ });
20707
+ }
20708
+ }
20709
+ static _fromPublicParamsBytes(params) {
20710
+ return TFHECrs._fromBytes({
20711
+ bits: 2048,
20712
+ data: params['2048'].publicParams,
20713
+ id: params['2048'].publicParamsId,
20714
+ });
20715
+ }
20716
+ static async fromUrl(params) {
20717
+ try {
20718
+ TFHECrs.assertKeyUrlType(params, 'arg');
20719
+ return TFHECrs._fromUrl(params);
20720
+ }
20721
+ catch (e) {
20722
+ throw new Error('Impossible to fetch public key: wrong relayer url.', {
20723
+ cause: e,
20724
+ });
20725
+ }
20726
+ }
20727
+ static async _fromUrl(params) {
20728
+ TFHECrs.assertKeyUrlType(params, 'arg');
20729
+ const compactPkeCrsBytes = await fetchBytes(params.srcUrl);
20730
+ return TFHECrs.fromBytes({
20731
+ data: compactPkeCrsBytes,
20732
+ id: params.id,
20733
+ bits: params.bits,
20734
+ srcUrl: params.srcUrl,
20735
+ });
20736
+ }
20737
+ /*
20738
+ {
20739
+ id: string,
20740
+ bits: number,
20741
+ data: Uint8Array,
20742
+ srcUrl?: string
20743
+ }
20744
+ */
20745
+ toBytes() {
20746
+ return {
20747
+ data: this._compactPkeCrs.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS),
20748
+ id: this._id,
20749
+ bits: this._bits,
20750
+ ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
20751
+ };
20752
+ }
20753
+ /*
20754
+ {
20755
+ 2048: {
20756
+ publicParamsId: string,
20757
+ publicParams: TFHE.CompactPkeCrs
20758
+ }
20759
+ }
20760
+ */
20761
+ toPublicParamsWasm() {
20762
+ if (this._bits !== 2048) {
20763
+ throw new Error(`Unsupported PublicParams bits format ${this._bits}`);
20764
+ }
20765
+ const pp = {
20766
+ 2048: {
20767
+ publicParams: this._compactPkeCrs,
20768
+ publicParamsId: this._id,
20769
+ },
20770
+ };
20771
+ return pp;
20772
+ }
20773
+ /*
20774
+ {
20775
+ 2048: {
20776
+ publicParamsId: string,
20777
+ publicParams: Uint8Array
20778
+ }
20779
+ }
20780
+ */
20781
+ toPublicParamsBytes() {
20782
+ if (this._bits !== 2048) {
20783
+ throw new Error(`Unsupported PublicParams bits format ${this._bits}`);
20784
+ }
20785
+ const pp = {
20786
+ 2048: {
20787
+ publicParams: this.toBytes().data,
20788
+ publicParamsId: this._id,
20789
+ },
20790
+ };
20791
+ return pp;
20792
+ }
20793
+ }
20794
+
20795
+ class TFHEPublicKey {
20796
+ _id;
20797
+ _tfheCompactPublicKey;
20798
+ _srcUrl;
20799
+ constructor(params) {
20800
+ this._id = params.id;
20801
+ this._tfheCompactPublicKey = params.tfheCompactPublicKey;
20802
+ this._srcUrl = params.srcUrl;
20803
+ }
20804
+ /*
20805
+ {
20806
+ id: string,
20807
+ data: Uint8Array,
20808
+ srcUrl?: string
20809
+ }
20810
+ */
20811
+ static isKeyBytesType(value) {
20812
+ try {
20813
+ TFHEPublicKey.assertKeyBytesType(value, '');
20814
+ return true;
20815
+ }
20816
+ catch {
20817
+ return false;
20818
+ }
20819
+ }
20820
+ /*
20821
+ {
20822
+ id: string,
20823
+ srcUrl: string
20824
+ }
20825
+ */
20826
+ static isKeyUrlType(value) {
20827
+ try {
20828
+ TFHEPublicKey.assertKeyUrlType(value, '');
20829
+ return true;
20830
+ }
20831
+ catch {
20832
+ return false;
20833
+ }
20834
+ }
20835
+ /*
20836
+ {
20837
+ id: string,
20838
+ data: Uint8Array,
20839
+ srcUrl?: string
20840
+ }
20841
+ */
20842
+ static assertKeyBytesType(value, name) {
20843
+ assertRecordStringProperty(value, 'id', name);
20844
+ assertUint8ArrayProperty(value, 'data', name);
20845
+ if (isNonNullableRecordProperty(value, 'srcUrl')) {
20846
+ assertRecordStringProperty(value, 'srcUrl', name);
20847
+ }
20848
+ }
20849
+ /*
20850
+ {
20851
+ id: string,
20852
+ srcUrl: string
20853
+ }
20854
+ */
20855
+ static assertKeyUrlType(value, name) {
20856
+ assertRecordStringProperty(value, 'id', name);
20857
+ assertRecordStringProperty(value, 'srcUrl', name);
20858
+ }
20859
+ static async fromBytesOrUrl(params) {
20860
+ if (TFHEPublicKey.isKeyBytesType(params)) {
20861
+ return TFHEPublicKey._fromBytes(params);
20862
+ }
20863
+ else if (TFHEPublicKey.isKeyUrlType(params)) {
20864
+ return TFHEPublicKey._fromUrl(params);
20865
+ }
20866
+ else {
20867
+ throw new Error('Invalid public key (deserialization failed)');
20868
+ }
20869
+ }
20870
+ /*
20871
+ {
20872
+ id: string,
20873
+ data: Uint8Array,
20874
+ srcUrl?: string
20875
+ }
20876
+ */
20877
+ static fromBytes(params) {
20878
+ try {
20879
+ TFHEPublicKey.assertKeyBytesType(params, 'arg');
20880
+ return TFHEPublicKey._fromBytes(params);
20881
+ }
20882
+ catch (e) {
20883
+ throw new Error('Invalid public key (deserialization failed)', {
20884
+ cause: e,
20885
+ });
20886
+ }
20887
+ }
20888
+ /*
20889
+ {
20890
+ id: string,
20891
+ data: Uint8Array,
20892
+ srcUrl?: string
20893
+ }
20894
+ */
20895
+ static _fromBytes(params) {
20896
+ const _params = {
20897
+ tfheCompactPublicKey: TFHE.TfheCompactPublicKey.safe_deserialize(params.data, SERIALIZED_SIZE_LIMIT_PK),
20898
+ id: params.id,
20899
+ srcUrl: params.srcUrl,
20900
+ };
20901
+ return new TFHEPublicKey(_params);
20902
+ }
20903
+ /*
20904
+ {
20905
+ id: string,
20906
+ srcUrl: string
20907
+ }
20908
+ */
20909
+ static async fromUrl(params) {
20910
+ try {
20911
+ TFHEPublicKey.assertKeyUrlType(params, 'arg');
20912
+ return TFHEPublicKey._fromUrl(params);
20913
+ }
20914
+ catch (e) {
20915
+ throw new Error('Impossible to fetch public key: wrong relayer url.', {
20916
+ cause: e,
20917
+ });
20918
+ }
20919
+ }
20920
+ /*
20921
+ {
20922
+ id: string,
20923
+ srcUrl: string
20924
+ }
20925
+ */
20926
+ static async _fromUrl(params) {
20927
+ const tfheCompactPublicKeyBytes = await fetchBytes(params.srcUrl);
20928
+ return TFHEPublicKey.fromBytes({
20929
+ data: tfheCompactPublicKeyBytes,
20930
+ id: params.id,
20931
+ srcUrl: params.srcUrl,
20932
+ });
20933
+ }
20934
+ /*
20935
+ {
20936
+ id: string,
20937
+ data: Uint8Array,
20938
+ srcUrl?: string
20939
+ }
20940
+ */
20941
+ toBytes() {
20942
+ return {
20943
+ data: this._tfheCompactPublicKey.safe_serialize(SERIALIZED_SIZE_LIMIT_PK),
20944
+ id: this._id,
20945
+ ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
20946
+ };
20947
+ }
20948
+ /*
20949
+ {
20950
+ publicKey: TFHE.TfheCompactPublicKey
20951
+ publicKeyId: string
20952
+ }
20953
+ */
20954
+ toPublicKeyWasm() {
20955
+ return {
20956
+ publicKey: this._tfheCompactPublicKey,
20957
+ publicKeyId: this._id,
20958
+ };
20959
+ }
20960
+ /*
20961
+ {
20962
+ publicKey: Uint8Array
20963
+ publicKeyId: string
20964
+ }
20965
+ */
20966
+ toPublicKeyBytes() {
20967
+ return {
20968
+ publicKey: this.toBytes().data,
20969
+ publicKeyId: this._id,
20970
+ };
20971
+ }
20972
+ }
20973
+
20974
+ //const __KEY_URL_CACHE__: Record<string, RelayerV2PublicKey> = {};
20975
+ class RelayerV2PublicKey {
20976
+ _crs2048;
20977
+ _publicKey;
20978
+ constructor(params) {
20979
+ this._publicKey = params.publicKey;
20980
+ this._crs2048 = params.crs2048;
20981
+ }
20982
+ static tryFromBytes(value) {
20983
+ if (!isNonNullableRecordProperty(value, 'publicParams')) {
20984
+ return null;
20985
+ }
20986
+ if (!TFHECrs.isPublicParams2048BytesType(value.publicParams)) {
20987
+ return null;
20988
+ }
20989
+ if (!isNonNullableRecordProperty(value, 'publicKey')) {
20990
+ return null;
20991
+ }
20992
+ if (!TFHEPublicKey.isKeyBytesType(value.publicKey)) {
20993
+ return null;
20994
+ }
20995
+ const publicKey = value.publicKey;
20996
+ const publicParams = value.publicParams;
20997
+ return RelayerV2PublicKey.fromBytes({
20998
+ publicKey,
20999
+ publicParams,
21000
+ });
21001
+ }
21002
+ static fromBytes(params) {
21003
+ TFHECrs.assertIsPublicParams2048BytesType(params.publicParams, 'arg.publicParams');
21004
+ const publicKey = TFHEPublicKey.fromBytes(params.publicKey);
21005
+ const crs2048 = TFHECrs.fromBytes({
21006
+ id: params.publicParams[2048].publicParamsId,
21007
+ data: params.publicParams[2048].publicParams,
21008
+ bits: 2048,
21009
+ });
21010
+ return new RelayerV2PublicKey({ publicKey, crs2048 });
21011
+ }
21012
+ static async fromRelayerResponse(response) {
21013
+ try {
21014
+ assertIsRelayerV2GetResponseKeyUrl(response, 'RelayerV2GetResponseKeyUrl');
21015
+ const pub_key_0 = response.response.fheKeyInfo[0].fhePublicKey;
21016
+ const tfheCompactPublicKeyId = pub_key_0.dataId;
21017
+ const tfheCompactPublicKeyUrl = pub_key_0.urls[0];
21018
+ const crs_2048 = response.response.crs['2048'];
21019
+ const compactPkeCrs2048Id = crs_2048.dataId;
21020
+ const compactPkeCrs2048Url = crs_2048.urls[0];
21021
+ const publicKey = await TFHEPublicKey.fromUrl({
21022
+ id: tfheCompactPublicKeyId,
21023
+ srcUrl: tfheCompactPublicKeyUrl,
21024
+ });
21025
+ const crs = await TFHECrs.fromUrl({
21026
+ id: compactPkeCrs2048Id,
21027
+ bits: 2048,
21028
+ srcUrl: compactPkeCrs2048Url,
21029
+ });
21030
+ return new RelayerV2PublicKey({ publicKey, crs2048: crs });
21031
+ }
21032
+ catch (e) {
21033
+ throw new Error('Impossible to fetch public key: wrong relayer url.', {
21034
+ cause: e,
21035
+ });
21036
+ }
21037
+ }
21038
+ getTFHEPublicKey() {
21039
+ return this._publicKey;
21040
+ }
21041
+ getTFHECrs() {
21042
+ return this._crs2048;
21043
+ }
21044
+ toBytes() {
21045
+ return {
21046
+ publicKey: this._publicKey.toBytes(),
21047
+ publicParams: this._crs2048.toPublicParamsBytes(),
21048
+ };
21049
+ }
21050
+ }
21051
+
21052
+ class RelayerV2Fhevm extends AbstractRelayerFhevm {
21053
+ _relayerProvider;
21054
+ _relayerPublicKey;
21055
+ constructor(params) {
21056
+ super();
21057
+ this._relayerProvider = params.relayerProvider;
21058
+ this._relayerPublicKey = params.relayerPublicKey;
21059
+ }
21060
+ get version() {
21061
+ return 2;
21062
+ }
21063
+ get relayerVersionUrl() {
21064
+ return this.relayerProvider.url;
21065
+ }
21066
+ static async fromConfig(config) {
21067
+ const relayerProvider = new RelayerV2Provider(config.relayerVersionUrl);
21068
+ let relayerPublicKey = RelayerV2PublicKey.tryFromBytes(config);
21069
+ if (!relayerPublicKey) {
21070
+ const response = await relayerProvider.fetchGetKeyUrlV2();
21071
+ relayerPublicKey = await RelayerV2PublicKey.fromRelayerResponse(response);
21072
+ }
21073
+ return new RelayerV2Fhevm({
21074
+ relayerProvider,
21075
+ relayerPublicKey,
21076
+ });
21077
+ }
21078
+ get relayerProvider() {
21079
+ return this._relayerProvider;
21080
+ }
21081
+ getPublicKeyBytes() {
21082
+ return this._relayerPublicKey.getTFHEPublicKey().toPublicKeyBytes();
21083
+ }
21084
+ getPublicKeyWasm() {
21085
+ return this._relayerPublicKey.getTFHEPublicKey().toPublicKeyWasm();
21086
+ }
21087
+ getPublicParamsBytes(bits) {
21088
+ if (bits !== 2048) {
21089
+ throw new Error(`Unsupported PublicParams bits format ${bits}`);
21090
+ }
21091
+ return this._relayerPublicKey.getTFHECrs().toPublicParamsBytes()['2048'];
21092
+ }
21093
+ getPublicParamsWasm(bits) {
21094
+ if (bits !== 2048) {
21095
+ throw new Error(`Unsupported PublicParams bits format ${bits}`);
21096
+ }
21097
+ return this._relayerPublicKey.getTFHECrs().toPublicParamsWasm()['2048'];
21098
+ }
21099
+ }
21100
+
21101
+ class RelayerV1Fhevm extends AbstractRelayerFhevm {
21102
+ _relayerProvider;
21103
+ _publicKeyData;
21104
+ _publicParamsData;
21105
+ constructor(params) {
21106
+ super();
21107
+ this._relayerProvider = params.relayerProvider;
21108
+ this._publicKeyData = params.publicKeyData;
21109
+ this._publicParamsData = params.publicParamsData;
21110
+ }
21111
+ get version() {
21112
+ return 1;
21113
+ }
21114
+ get relayerVersionUrl() {
21115
+ return this.relayerProvider.url;
21116
+ }
21117
+ static async fromConfig(config) {
21118
+ const relayerProvider = new RelayerV1Provider(config.relayerVersionUrl);
21119
+ const publicKeyData = await getTfheCompactPublicKey(config);
21120
+ const publicParamsData = await getPublicParams(config);
21121
+ return new RelayerV1Fhevm({
21122
+ relayerProvider,
21123
+ publicKeyData,
21124
+ publicParamsData,
21125
+ });
21126
+ }
21127
+ get relayerProvider() {
21128
+ return this._relayerProvider;
21129
+ }
21130
+ getPublicKeyBytes() {
21131
+ return {
21132
+ publicKey: this._publicKeyData.publicKey.safe_serialize(SERIALIZED_SIZE_LIMIT_PK),
21133
+ publicKeyId: this._publicKeyData.publicKeyId,
21134
+ };
21135
+ }
21136
+ getPublicKeyWasm() {
21137
+ return {
21138
+ publicKey: this._publicKeyData.publicKey,
21139
+ publicKeyId: this._publicKeyData.publicKeyId,
21140
+ };
21141
+ }
21142
+ getPublicParamsBytes(bits) {
21143
+ if (bits !== 2048) {
21144
+ throw new Error(`Unsupported PublicParams bits format ${bits}`);
21145
+ }
21146
+ return {
21147
+ publicParams: this._publicParamsData['2048'].publicParams.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS),
21148
+ publicParamsId: this._publicParamsData['2048'].publicParamsId,
21149
+ };
21150
+ }
21151
+ getPublicParamsWasm(bits) {
21152
+ if (bits !== 2048) {
21153
+ throw new Error(`Unsupported PublicParams bits format ${bits}`);
21154
+ }
21155
+ return {
21156
+ publicParams: this._publicParamsData['2048'].publicParams,
21157
+ publicParamsId: this._publicParamsData['2048'].publicParamsId,
21158
+ };
21159
+ }
21160
+ }
21161
+
21162
+ async function createRelayerFhevm(config) {
21163
+ const resolved = _resolveRelayerUrl(config.relayerUrl, config.defaultRelayerVersion);
21164
+ if (!resolved) {
21165
+ throw new Error(`Invalid relayerUrl: ${config.relayerUrl}`);
21166
+ }
21167
+ if (resolved.version === 2) {
21168
+ return RelayerV2Fhevm.fromConfig({
21169
+ relayerVersionUrl: resolved.url,
21170
+ publicKey: config.publicKey,
21171
+ publicParams: config.publicParams,
21172
+ });
21173
+ }
21174
+ return RelayerV1Fhevm.fromConfig({
21175
+ relayerVersionUrl: resolved.url,
21176
+ publicKey: config.publicKey,
21177
+ publicParams: config.publicParams,
21178
+ });
21179
+ }
21180
+ function _resolveRelayerUrl(value, defaultVersion) {
21181
+ if (!value || typeof value !== 'string') {
21182
+ return null;
21183
+ }
21184
+ const urlNoSlash = removeSuffix(value, '/');
21185
+ if (!URL.canParse(urlNoSlash)) {
21186
+ return null;
21187
+ }
21188
+ if (urlNoSlash.endsWith('/v1')) {
21189
+ return {
21190
+ url: value,
21191
+ version: 1,
21192
+ };
21193
+ }
21194
+ if (urlNoSlash.endsWith('/v2')) {
21195
+ return {
21196
+ url: value,
21197
+ version: 2,
21198
+ };
21199
+ }
21200
+ if (typeof defaultVersion !== 'number') {
21201
+ throw new Error(`relayerUrl cannot be resolved. (value=${value})`);
21202
+ }
21203
+ return {
21204
+ url: `${urlNoSlash}/v${defaultVersion}`,
21205
+ version: defaultVersion,
21206
+ };
21207
+ }
21208
+
21209
+ ////////////////////////////////////////////////////////////////////////////////
21210
+ // MainnetConfig
21211
+ ////////////////////////////////////////////////////////////////////////////////
21212
+ const MainnetConfig = {
21213
+ aclContractAddress: '0xcA2E8f1F656CD25C01F05d0b243Ab1ecd4a8ffb6',
21214
+ kmsContractAddress: '0x77627828a55156b04Ac0DC0eb30467f1a552BB03',
21215
+ inputVerifierContractAddress: '0xCe0FC2e05CFff1B719EFF7169f7D80Af770c8EA2',
21216
+ verifyingContractAddressDecryption: '0x0f6024a97684f7d90ddb0fAAD79cB15F2C888D24',
21217
+ verifyingContractAddressInputVerification: '0xcB1bB072f38bdAF0F328CdEf1Fc6eDa1DF029287',
21218
+ chainId: 1,
21219
+ gatewayChainId: 261131,
21220
+ network: 'https://ethereum-rpc.publicnode.com',
21221
+ relayerUrl: 'https://relayer.mainnet.zama.org',
21222
+ };
21223
+ Object.freeze(MainnetConfig);
21224
+ ////////////////////////////////////////////////////////////////////////////////
21225
+ // SepoliaConfig
21226
+ ////////////////////////////////////////////////////////////////////////////////
21227
+ const SepoliaConfig = {
21228
+ aclContractAddress: '0xf0Ffdc93b7E186bC2f8CB3dAA75D86d1930A433D',
21229
+ kmsContractAddress: '0xbE0E383937d564D7FF0BC3b46c51f0bF8d5C311A',
21230
+ inputVerifierContractAddress: '0xBBC1fFCdc7C316aAAd72E807D9b0272BE8F84DA0',
21231
+ verifyingContractAddressDecryption: '0x5D8BD78e2ea6bbE41f26dFe9fdaEAa349e077478',
21232
+ verifyingContractAddressInputVerification: '0x483b9dE06E4E4C7D35CCf5837A1668487406D955',
21233
+ chainId: 11155111,
21234
+ gatewayChainId: 10901,
21235
+ network: 'https://ethereum-sepolia-rpc.publicnode.com',
21236
+ relayerUrl: 'https://relayer.testnet.zama.org',
21237
+ };
21238
+ Object.freeze(SepoliaConfig);
21239
+ ////////////////////////////////////////////////////////////////////////////////
21240
+ // createInstance
21241
+ ////////////////////////////////////////////////////////////////////////////////
21242
+ const createInstance = async (config) => {
21243
+ const { verifyingContractAddressDecryption, verifyingContractAddressInputVerification, publicKey, inputVerifierContractAddress, kmsContractAddress, aclContractAddress, gatewayChainId, auth, } = config;
21244
+ if (!isChecksummedAddress(aclContractAddress)) {
21245
+ throw new Error('ACL contract address is not valid or empty');
21246
+ }
21247
+ if (!isChecksummedAddress(inputVerifierContractAddress)) {
21248
+ throw new Error('InputVerifier contract address is not valid or empty');
21249
+ }
21250
+ if (!isChecksummedAddress(kmsContractAddress)) {
21251
+ throw new Error('KMS contract address is not valid or empty');
21252
+ }
21253
+ if (!isChecksummedAddress(verifyingContractAddressDecryption)) {
21254
+ throw new Error('Verifying contract for Decryption address is not valid or empty');
21255
+ }
21256
+ if (!isChecksummedAddress(verifyingContractAddressInputVerification)) {
21257
+ throw new Error('Verifying contract for InputVerification address is not valid or empty');
21258
+ }
21259
+ if (publicKey && !(publicKey.data instanceof Uint8Array)) {
21260
+ throw new Error('publicKey must be a Uint8Array');
21261
+ }
21262
+ // TODO change argument
21263
+ // provider is never undefined | null here!
21264
+ const provider = getProvider(config.network);
21265
+ const relayerUrl = config.relayerUrl ?? SepoliaConfig.relayerUrl;
21266
+ const relayerFhevm = await createRelayerFhevm({
21267
+ relayerUrl,
21268
+ publicKey: config.publicKey,
21269
+ publicParams: config.publicParams,
21270
+ defaultRelayerVersion: 1,
21271
+ });
21272
+ const chainId = await getChainId(provider, config);
21273
+ const kmsSigners = await getKMSSigners(provider, kmsContractAddress);
21274
+ const thresholdKMSSigners = await getKMSSignersThreshold(provider, kmsContractAddress);
21275
+ const coprocessorSigners = await getCoprocessorSigners(provider, inputVerifierContractAddress);
21276
+ const thresholdCoprocessorSigners = await getCoprocessorSignersThreshold(provider, inputVerifierContractAddress);
21277
+ return {
21278
+ createEncryptedInput: createRelayerEncryptedInput(aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerFhevm.relayerProvider, relayerFhevm.getPublicKeyWasm().publicKey, { 2048: relayerFhevm.getPublicParamsWasm(2048) }, coprocessorSigners, thresholdCoprocessorSigners, auth && { auth }),
21279
+ generateKeypair,
21280
+ createEIP712: createEIP712(verifyingContractAddressDecryption, chainId),
21281
+ publicDecrypt: publicDecryptRequest(kmsSigners, thresholdKMSSigners, gatewayChainId, verifyingContractAddressDecryption, aclContractAddress,
21282
+ //cleanURL(config.relayerUrl),
21283
+ relayerFhevm.relayerProvider, provider, auth && { auth }),
21284
+ userDecrypt: userDecryptRequest(kmsSigners, gatewayChainId, chainId, verifyingContractAddressDecryption, aclContractAddress,
21285
+ //cleanURL(config.relayerUrl),
21286
+ relayerFhevm.relayerProvider, provider, auth && { auth }),
21287
+ getPublicKey: () => relayerFhevm.getPublicKeyBytes(),
21288
+ getPublicParams: (bits) => relayerFhevm.getPublicParamsBytes(bits),
21289
+ // getPublicKey: () =>
21290
+ // publicKeyData.publicKey
21291
+ // ? {
21292
+ // publicKey: publicKeyData.publicKey.safe_serialize(
21293
+ // SERIALIZED_SIZE_LIMIT_PK,
21294
+ // ),
21295
+ // publicKeyId: publicKeyData.publicKeyId,
21296
+ // }
21297
+ // : null,
21298
+ // getPublicParams: (bits: keyof PublicParams) => {
21299
+ // if (publicParamsData[bits]) {
21300
+ // return {
21301
+ // publicParams: publicParamsData[bits]!.publicParams.safe_serialize(
21302
+ // SERIALIZED_SIZE_LIMIT_CRS,
21303
+ // ),
21304
+ // publicParamsId: publicParamsData[bits]!.publicParamsId,
21305
+ // };
21306
+ // }
21307
+ // return null;
21308
+ // },
17713
21309
  };
17714
21310
  };
17715
21311
 
@@ -17759,4 +21355,4 @@ window.TKMS = {
17759
21355
  ml_kem_pke_get_pk,
17760
21356
  };
17761
21357
 
17762
- export { ENCRYPTION_TYPES, MainnetConfig, SepoliaConfig, createEIP712, createInstance, generateKeypair, getErrorCauseCode, getErrorCauseStatus, initSDK };
21358
+ export { MainnetConfig, SepoliaConfig, createEIP712, createInstance, generateKeypair, getErrorCauseCode, getErrorCauseStatus, initSDK };