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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/node.cjs CHANGED
@@ -29,7 +29,7 @@ const SERIALIZED_SIZE_LIMIT_PK = BigInt(1024 * 1024 * 512);
29
29
  const SERIALIZED_SIZE_LIMIT_CRS = BigInt(1024 * 1024 * 512);
30
30
 
31
31
  // This file is auto-generated
32
- const version = '0.4.0-alpha.1';
32
+ const version = '0.4.0-alpha.2';
33
33
  const sdkName = '@zama-fhe/relayer-sdk';
34
34
 
35
35
  function getErrorCause(e) {
@@ -831,9 +831,9 @@ function assertRecordStringArrayProperty(o, property, objName) {
831
831
  }
832
832
  }
833
833
  }
834
- function safeJSONstringify(o) {
834
+ function safeJSONstringify(o, space) {
835
835
  try {
836
- return JSON.stringify(o, (_, v) => typeof v === 'bigint' ? v.toString() : v);
836
+ return JSON.stringify(o, (_, v) => (typeof v === 'bigint' ? v.toString() : v), space);
837
837
  }
838
838
  catch {
839
839
  return '';
@@ -949,6 +949,17 @@ const NumEncryptedBits = {
949
949
  7: 160, // eaddress
950
950
  8: 256, // euint256
951
951
  };
952
+ // export function getHandleType(handle: `0x${string}`): number {
953
+ // if (handle.length !== 66) {
954
+ // throw new Error(`Handle ${handle} is not of valid length`);
955
+ // }
956
+ // const hexPair = handle.slice(-4, -2).toLowerCase();
957
+ // const typeDiscriminant = parseInt(hexPair, 16);
958
+ // if (!(typeDiscriminant in NumEncryptedBits)) {
959
+ // throw new Error(`Handle ${handle} is not of valid type`);
960
+ // }
961
+ // return typeDiscriminant;
962
+ // }
952
963
  function checkEncryptedBits(handles) {
953
964
  let total = 0;
954
965
  for (const handle of handles) {
@@ -1006,7 +1017,7 @@ function isBytes(value, bytewidth) {
1006
1017
  if (!(value instanceof Uint8Array)) {
1007
1018
  return false;
1008
1019
  }
1009
- return value.length === bytewidth ;
1020
+ return bytewidth ? value.length === bytewidth : true;
1010
1021
  }
1011
1022
  function isBytesHex(value, bytewidth) {
1012
1023
  if (!is0x(value)) {
@@ -1239,6 +1250,54 @@ function assertUint8ArrayProperty(o, property, objName) {
1239
1250
  });
1240
1251
  }
1241
1252
  }
1253
+ ////////////////////////////////////////////////////////////////////////////////
1254
+ // Hex
1255
+ ////////////////////////////////////////////////////////////////////////////////
1256
+ const HEX_CHARS = {
1257
+ '0': 0,
1258
+ '1': 1,
1259
+ '2': 2,
1260
+ '3': 3,
1261
+ '4': 4,
1262
+ '5': 5,
1263
+ '6': 6,
1264
+ '7': 7,
1265
+ '8': 8,
1266
+ '9': 9,
1267
+ a: 10,
1268
+ b: 11,
1269
+ c: 12,
1270
+ d: 13,
1271
+ e: 14,
1272
+ f: 15,
1273
+ A: 10,
1274
+ B: 11,
1275
+ C: 12,
1276
+ D: 13,
1277
+ E: 14,
1278
+ F: 15,
1279
+ };
1280
+ Object.freeze(HEX_CHARS);
1281
+ const HEX_BYTES = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
1282
+ Object.freeze(HEX_BYTES);
1283
+ const HEX_CHARS_CODES = new Uint8Array([
1284
+ 48,
1285
+ 49,
1286
+ 50,
1287
+ 51,
1288
+ 52,
1289
+ 53,
1290
+ 54,
1291
+ 55,
1292
+ 56,
1293
+ 57, // '0'-'9'
1294
+ 97,
1295
+ 98,
1296
+ 99,
1297
+ 100,
1298
+ 101,
1299
+ 102, // 'a'-'f'
1300
+ ]);
1242
1301
  /**
1243
1302
  * Convert a Uint8Array to a hex string (without 0x prefix).
1244
1303
  */
@@ -1258,15 +1317,49 @@ function bytesToHexNo0x(bytes) {
1258
1317
  function bytesToHex(bytes) {
1259
1318
  return `0x${bytesToHexNo0x(bytes)}`;
1260
1319
  }
1320
+ function bytesToHexLarge(bytes) {
1321
+ const out = new Uint8Array(2 + bytes.length * 2);
1322
+ out[0] = 48; // '0'
1323
+ out[1] = 120; // 'x'
1324
+ for (let i = 0; i < bytes.length; i++) {
1325
+ const j = 2 + i * 2;
1326
+ out[j] = HEX_CHARS_CODES[bytes[i] >> 4];
1327
+ out[j + 1] = HEX_CHARS_CODES[bytes[i] & 0xf];
1328
+ }
1329
+ return new TextDecoder().decode(out);
1330
+ }
1261
1331
  /**
1262
1332
  * Convert a hex string prefixed by 0x or not to a Uint8Array
1333
+ * Any invalid byte string is converted to 0
1334
+ * "0xzzff" = [0, 255]
1335
+ * "0xzfff" = [0, 255]
1263
1336
  */
1264
1337
  function hexToBytes(hexString) {
1338
+ if (hexString.length % 2 !== 0) {
1339
+ throw new Error('Invalid hex string: odd length');
1340
+ }
1265
1341
  const arr = hexString.replace(/^(0x)/, '').match(/.{1,2}/g);
1266
1342
  if (!arr)
1267
1343
  return new Uint8Array();
1268
1344
  return Uint8Array.from(arr.map((byte) => parseInt(byte, 16)));
1269
1345
  }
1346
+ function hexToBytesFaster(hexString, strict = false) {
1347
+ const offset = hexString[0] === '0' && hexString[1] === 'x' ? 2 : 0;
1348
+ const len = hexString.length - offset;
1349
+ if (len % 2 !== 0) {
1350
+ throw new Error('Invalid hex string: odd length');
1351
+ }
1352
+ const bytes = new Uint8Array(len / 2);
1353
+ for (let i = 0; i < bytes.length; i++) {
1354
+ const hi = HEX_CHARS[hexString[offset + i * 2]];
1355
+ const lo = HEX_CHARS[hexString[offset + i * 2 + 1]];
1356
+ if ((hi === undefined || lo === undefined) && strict) {
1357
+ throw new Error(`Invalid hex character at position ${offset + i * 2}`);
1358
+ }
1359
+ bytes[i] = (hi << 4) | lo;
1360
+ }
1361
+ return bytes;
1362
+ }
1270
1363
  /**
1271
1364
  * Convert a Uint8Array to a bigint
1272
1365
  */
@@ -1280,9 +1373,6 @@ function bytesToBigInt(byteArray) {
1280
1373
  }
1281
1374
  return result;
1282
1375
  }
1283
- function toHexString(bytes, with0x = false) {
1284
- return `${with0x ? '0x' : ''}${bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')}`;
1285
- }
1286
1376
  async function fetchBytes(url) {
1287
1377
  const response = await fetch(url);
1288
1378
  if (!response.ok) {
@@ -1374,8 +1464,8 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
1374
1464
  const publicKeySanitized = publicKey.replace(/^(0x)/, '');
1375
1465
  const handleContractPairs = _handles.map((h) => ({
1376
1466
  handle: typeof h.handle === 'string'
1377
- ? toHexString(hexToBytes(h.handle), true)
1378
- : toHexString(h.handle, true),
1467
+ ? bytesToHex(hexToBytes(h.handle))
1468
+ : bytesToHex(h.handle),
1379
1469
  contractAddress: getAddress$1(h.contractAddress),
1380
1470
  }));
1381
1471
  checkEncryptedBits(handleContractPairs.map((h) => h.handle));
@@ -1591,9 +1681,8 @@ const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKe
1591
1681
 
1592
1682
  const MAX_UINT64 = BigInt('18446744073709551615'); // 2^64 - 1
1593
1683
  BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
1594
- const MAX_UINT32 = 0xffffffff;
1595
1684
  const MAX_UINT8 = 0xff;
1596
- function numberToHex(num) {
1685
+ function numberToHexNo0x(num) {
1597
1686
  let hex = num.toString(16);
1598
1687
  return hex.length % 2 ? '0' + hex : hex;
1599
1688
  }
@@ -1627,25 +1716,19 @@ function isUint8(value) {
1627
1716
  }
1628
1717
  return value <= MAX_UINT8;
1629
1718
  }
1630
- function isUint32(value) {
1631
- if (!isUint(value)) {
1632
- return false;
1633
- }
1634
- return value <= MAX_UINT32;
1635
- }
1636
1719
  function isUint64(value) {
1637
1720
  if (!isUint(value)) {
1638
1721
  return false;
1639
1722
  }
1640
1723
  return value <= MAX_UINT64;
1641
1724
  }
1642
- function uint32ToBytes32(uint32) {
1643
- if (!isUint32(uint32)) {
1644
- throw new InvalidTypeError({ expectedType: 'Uint32' });
1725
+ function numberToBytes32(num) {
1726
+ if (!isUintNumber(num)) {
1727
+ throw new InvalidTypeError({ expectedType: 'Uint' });
1645
1728
  }
1646
1729
  const buffer = new ArrayBuffer(32);
1647
1730
  const view = new DataView(buffer);
1648
- view.setUint32(28, Number(uint32), false);
1731
+ view.setBigUint64(24, BigInt(num), false);
1649
1732
  return new Uint8Array(buffer);
1650
1733
  }
1651
1734
  function assertIsUint8(value) {
@@ -1743,63 +1826,213 @@ function checksummedAddressToBytes20(address) {
1743
1826
  class FhevmHandleError extends RelayerErrorBase {
1744
1827
  constructor({ handle, message }) {
1745
1828
  super({
1746
- message: message ?? `FHEVM Handle "${handle}" is invalid.`,
1829
+ message: message ??
1830
+ (handle
1831
+ ? `FHEVM Handle "${handle}" is invalid.`
1832
+ : `FHEVM Handle is invalid.`),
1747
1833
  name: 'FhevmHandleError',
1748
1834
  });
1749
1835
  }
1750
1836
  }
1751
1837
 
1838
+ class FheTypeError extends RelayerErrorBase {
1839
+ constructor({ fheTypeId, message, }) {
1840
+ super({
1841
+ message: message ??
1842
+ (fheTypeId
1843
+ ? `FheTypeId "${fheTypeId}" is invalid.`
1844
+ : `FheTypeId is invalid.`),
1845
+ name: 'FheTypeError',
1846
+ });
1847
+ }
1848
+ }
1849
+
1850
+ const FheTypeIdToName = {
1851
+ 0: 'ebool',
1852
+ //1: 'euint4', has been deprecated
1853
+ 2: 'euint8',
1854
+ 3: 'euint16',
1855
+ 4: 'euint32',
1856
+ 5: 'euint64',
1857
+ 6: 'euint128',
1858
+ 7: 'eaddress',
1859
+ 8: 'euint256',
1860
+ };
1861
+ const FheTypeIdToEncryptionBitwidth = {
1862
+ 0: 2,
1863
+ //1:?, euint4 has been deprecated
1864
+ 2: 8,
1865
+ 3: 16,
1866
+ 4: 32,
1867
+ 5: 64,
1868
+ 6: 128,
1869
+ 7: 160,
1870
+ 8: 256,
1871
+ };
1872
+ const EncryptionBitwidthToFheTypeId = {
1873
+ 2: 0,
1874
+ //?:1, euint4 has been deprecated
1875
+ 8: 2,
1876
+ 16: 3,
1877
+ 32: 4,
1878
+ 64: 5,
1879
+ 128: 6,
1880
+ 160: 7,
1881
+ 256: 8,
1882
+ };
1883
+ const FheTypeIdToSolidityPrimitiveTypeName = {
1884
+ 0: 'bool',
1885
+ //1:'uint256', euint4 has been deprecated
1886
+ 2: 'uint256',
1887
+ 3: 'uint256',
1888
+ 4: 'uint256',
1889
+ 5: 'uint256',
1890
+ 6: 'uint256',
1891
+ 7: 'address',
1892
+ 8: 'uint256',
1893
+ };
1894
+ Object.freeze(FheTypeIdToEncryptionBitwidth);
1895
+ Object.freeze(EncryptionBitwidthToFheTypeId);
1896
+ Object.freeze(FheTypeIdToSolidityPrimitiveTypeName);
1897
+ ////////////////////////////////////////////////////////////////////////////////
1898
+ // Type Guards
1899
+ ////////////////////////////////////////////////////////////////////////////////
1900
+ /**
1901
+ * Checks if a value is a valid FheTypeId.
1902
+ * @example isFheTypeId(2) // true (euint8)
1903
+ * @example isFheTypeId(1) // false (euint4 is deprecated)
1904
+ */
1905
+ function isFheTypeId(value) {
1906
+ switch (value) {
1907
+ case 0:
1908
+ // 1: euint4 is deprecated
1909
+ case 2:
1910
+ case 3:
1911
+ case 4:
1912
+ case 5:
1913
+ case 6:
1914
+ case 7:
1915
+ case 8:
1916
+ return true;
1917
+ default:
1918
+ return false;
1919
+ }
1920
+ }
1921
+ /**
1922
+ * Checks if a value is a valid encryption bit width.
1923
+ * @example isEncryptionBits(8) // true
1924
+ * @example isEncryptionBits(4) // false (euint4 is deprecated)
1925
+ */
1926
+ function isEncryptionBits(value) {
1927
+ if (typeof value !== 'number') {
1928
+ return false;
1929
+ }
1930
+ return value in EncryptionBitwidthToFheTypeId;
1931
+ }
1932
+ ////////////////////////////////////////////////////////////////////////////////
1933
+ // FheTypeId extractors
1934
+ ////////////////////////////////////////////////////////////////////////////////
1935
+ /**
1936
+ * Converts an encryption bit width to its corresponding FheTypeId.
1937
+ * @throws {FheTypeError} If bitwidth is not a valid encryption bit width.
1938
+ * @example fheTypeIdFromEncryptionBits(8) // 2 (euint8)
1939
+ */
1940
+ function fheTypeIdFromEncryptionBits(bitwidth) {
1941
+ if (!isEncryptionBits(bitwidth)) {
1942
+ throw new FheTypeError({
1943
+ message: `Invalid encryption bits ${bitwidth}`,
1944
+ });
1945
+ }
1946
+ return EncryptionBitwidthToFheTypeId[bitwidth];
1947
+ }
1948
+ /**
1949
+ * Converts an FheTypeId to its corresponding FheTypeName.
1950
+ * @throws {FheTypeError} If id is not a valid FheTypeId.
1951
+ * @example fheTypeNameFromId(2) // 'euint8'
1952
+ */
1953
+ function fheTypeNameFromId(id) {
1954
+ if (!isFheTypeId(id)) {
1955
+ throw new FheTypeError({
1956
+ message: `Invalid FheType id '${id}'`,
1957
+ });
1958
+ }
1959
+ return FheTypeIdToName[id];
1960
+ }
1961
+ ////////////////////////////////////////////////////////////////////////////////
1962
+ // Solidity primitive type names
1963
+ ////////////////////////////////////////////////////////////////////////////////
1964
+ /**
1965
+ * Returns the Solidity primitive type name for an FheTypeId.
1966
+ * @example solidityPrimitiveTypeNameFromFheTypeId(0) // 'bool'
1967
+ * @example solidityPrimitiveTypeNameFromFheTypeId(7) // 'address'
1968
+ * @example solidityPrimitiveTypeNameFromFheTypeId(2) // 'uint256'
1969
+ */
1970
+ function solidityPrimitiveTypeNameFromFheTypeId(typeId) {
1971
+ if (!isFheTypeId(typeId)) {
1972
+ throw new FheTypeError({
1973
+ message: `Invalid FheType id '${typeId}'`,
1974
+ });
1975
+ }
1976
+ return FheTypeIdToSolidityPrimitiveTypeName[typeId];
1977
+ }
1978
+ ////////////////////////////////////////////////////////////////////////////////
1979
+ // Encryption Bits
1980
+ ////////////////////////////////////////////////////////////////////////////////
1981
+ /**
1982
+ * Returns the encryption bit width for an FheTypeId.
1983
+ * @example encryptionBitsFromFheTypeId(2) // 8 (euint8)
1984
+ * @example encryptionBitsFromFheTypeId(7) // 160 (eaddress)
1985
+ */
1986
+ function encryptionBitsFromFheTypeId(typeId) {
1987
+ if (!isFheTypeId(typeId)) {
1988
+ throw new FheTypeError({
1989
+ message: `Invalid FheType id '${typeId}'`,
1990
+ });
1991
+ }
1992
+ return FheTypeIdToEncryptionBitwidth[typeId];
1993
+ }
1994
+
1752
1995
  ////////////////////////////////////////////////////////////////////////////////
1753
1996
  // FhevmHandle
1754
1997
  ////////////////////////////////////////////////////////////////////////////////
1755
1998
  class FhevmHandle {
1999
+ //////////////////////////////////////////////////////////////////////////////
2000
+ // Instance Properties
2001
+ //////////////////////////////////////////////////////////////////////////////
1756
2002
  _hash21;
1757
2003
  _chainId;
1758
2004
  _fheTypeId;
1759
2005
  _version;
1760
2006
  _computed;
1761
2007
  _index;
2008
+ _handleBytes32Hex;
2009
+ _handleBytes32;
2010
+ //////////////////////////////////////////////////////////////////////////////
2011
+ // Static Constants
2012
+ //////////////////////////////////////////////////////////////////////////////
1762
2013
  static RAW_CT_HASH_DOMAIN_SEPARATOR = 'ZK-w_rct';
1763
2014
  static HANDLE_HASH_DOMAIN_SEPARATOR = 'ZK-w_hdl';
1764
- static FheTypeIdToEncryptionBitwidths = {
1765
- 0: 2,
1766
- 2: 8,
1767
- 3: 16,
1768
- 4: 32,
1769
- 5: 64,
1770
- 6: 128,
1771
- 7: 160,
1772
- 8: 256,
1773
- };
1774
- static FheTypeEncryptionBitwidthsToId = {
1775
- 2: 0,
1776
- 8: 2,
1777
- 16: 3,
1778
- 32: 4,
1779
- 64: 5,
1780
- 128: 6,
1781
- 160: 7,
1782
- 256: 8,
1783
- };
1784
- static FheTypeIdToSolidityPrimitiveType = {
1785
- 0: 'bool',
1786
- 2: 'uint256',
1787
- 3: 'uint256',
1788
- 4: 'uint256',
1789
- 5: 'uint256',
1790
- 6: 'uint256',
1791
- 7: 'address',
1792
- 8: 'uint256',
1793
- };
1794
- static {
1795
- Object.freeze(FhevmHandle.FheTypeIdToEncryptionBitwidths);
1796
- Object.freeze(FhevmHandle.FheTypeEncryptionBitwidthsToId);
1797
- }
1798
- constructor(hash21, chainId, fheTypeId, version, computed, index) {
2015
+ static CURRENT_CIPHERTEXT_VERSION = 0;
2016
+ //////////////////////////////////////////////////////////////////////////////
2017
+ // Constructor
2018
+ //////////////////////////////////////////////////////////////////////////////
2019
+ constructor({ hash21, chainId, fheTypeId, version, computed, index, handleBytes32, handleBytes32Hex, }) {
2020
+ if (!isUintNumber(chainId)) {
2021
+ throw new FhevmHandleError({
2022
+ message: 'ChainId must be a positive integer',
2023
+ });
2024
+ }
1799
2025
  if (BigInt(chainId) > MAX_UINT64) {
1800
2026
  // fhevm assumes chainID is only taking up to 8 bytes
1801
- throw new Error('ChainId exceeds maximum allowed value (8 bytes)');
2027
+ throw new FhevmHandleError({
2028
+ message: 'ChainId exceeds maximum allowed value (8 bytes)',
2029
+ });
2030
+ }
2031
+ if (!isBytesHex(hash21, 21)) {
2032
+ throw new FhevmHandleError({ message: 'Hash21 should be 21 bytes long' });
1802
2033
  }
2034
+ this._handleBytes32 = handleBytes32;
2035
+ this._handleBytes32Hex = handleBytes32Hex;
1803
2036
  this._hash21 = hash21;
1804
2037
  this._chainId = chainId;
1805
2038
  this._fheTypeId = fheTypeId;
@@ -1809,6 +2042,9 @@ class FhevmHandle {
1809
2042
  this._index = index;
1810
2043
  }
1811
2044
  }
2045
+ //////////////////////////////////////////////////////////////////////////////
2046
+ // Instance Getters
2047
+ //////////////////////////////////////////////////////////////////////////////
1812
2048
  get hash21() {
1813
2049
  return this._hash21;
1814
2050
  }
@@ -1818,6 +2054,9 @@ class FhevmHandle {
1818
2054
  get fheTypeId() {
1819
2055
  return this._fheTypeId;
1820
2056
  }
2057
+ get fheTypeName() {
2058
+ return fheTypeNameFromId(this._fheTypeId);
2059
+ }
1821
2060
  get version() {
1822
2061
  return this._version;
1823
2062
  }
@@ -1827,6 +2066,106 @@ class FhevmHandle {
1827
2066
  get index() {
1828
2067
  return this._index;
1829
2068
  }
2069
+ get encryptedBitwidth() {
2070
+ return encryptionBitsFromFheTypeId(this._fheTypeId);
2071
+ }
2072
+ get solidityPrimitiveTypeName() {
2073
+ return solidityPrimitiveTypeNameFromFheTypeId(this._fheTypeId);
2074
+ }
2075
+ toJSON() {
2076
+ return {
2077
+ handle: this.toBytes32Hex(),
2078
+ fheTypeName: this.fheTypeName,
2079
+ fheTypeId: this.fheTypeId,
2080
+ chainId: this.chainId,
2081
+ index: this.index,
2082
+ computed: this.computed,
2083
+ encryptedBitwidth: this.encryptedBitwidth,
2084
+ version: this.version,
2085
+ solidityPrimitiveTypeName: this.solidityPrimitiveTypeName,
2086
+ hash21: this.hash21,
2087
+ };
2088
+ }
2089
+ //////////////////////////////////////////////////////////////////////////////
2090
+ // Instance Serialization
2091
+ //////////////////////////////////////////////////////////////////////////////
2092
+ toBytes32() {
2093
+ if (this._handleBytes32 === undefined) {
2094
+ assertRelayer((this._index === undefined && this._computed) ||
2095
+ (this._index !== undefined && this._index < 255 && !this._computed));
2096
+ const chainId32Bytes = numberToBytes32(this._chainId);
2097
+ const chainId8Bytes = chainId32Bytes.subarray(24, 32);
2098
+ const handleHash21 = hexToBytes(this._hash21);
2099
+ assertRelayer(handleHash21.length === 21);
2100
+ const handleBytes32AsBytes = new Uint8Array(32);
2101
+ handleBytes32AsBytes.set(handleHash21, 0);
2102
+ handleBytes32AsBytes[21] = this._index === undefined ? 255 : this._index;
2103
+ handleBytes32AsBytes.set(chainId8Bytes, 22);
2104
+ handleBytes32AsBytes[30] = this._fheTypeId;
2105
+ handleBytes32AsBytes[31] = this._version;
2106
+ this._handleBytes32 = handleBytes32AsBytes;
2107
+ }
2108
+ return this._handleBytes32;
2109
+ }
2110
+ toBytes32Hex() {
2111
+ if (this._handleBytes32Hex === undefined) {
2112
+ this._handleBytes32Hex = bytesToHex(this.toBytes32());
2113
+ }
2114
+ return this._handleBytes32Hex;
2115
+ }
2116
+ //////////////////////////////////////////////////////////////////////////////
2117
+ // Static Factory Methods
2118
+ //////////////////////////////////////////////////////////////////////////////
2119
+ static fromComponents(params) {
2120
+ return new FhevmHandle(params);
2121
+ }
2122
+ static fromBytes32(handle) {
2123
+ if (!isBytes32(handle)) {
2124
+ throw new FhevmHandleError({
2125
+ message: `FHEVM Handle is not a valid bytes32 array.`,
2126
+ });
2127
+ }
2128
+ const bytes = handle;
2129
+ // Extract hash21 (bytes 0-20)
2130
+ const hash21 = bytesToHex(bytes.slice(0, 21));
2131
+ // Extract index (byte 21) - 255 means computed
2132
+ const indexByte = bytes[21];
2133
+ const computed = indexByte === 255;
2134
+ const index = computed ? undefined : indexByte;
2135
+ // Extract chainId (bytes 22-29, 8 bytes as big-endian uint64)
2136
+ let chainId = 0;
2137
+ for (let i = 22; i < 30; i++) {
2138
+ chainId = chainId * 256 + bytes[i];
2139
+ }
2140
+ // Extract fheTypeId (byte 30)
2141
+ const fheTypeIdByte = bytes[30];
2142
+ if (!isFheTypeId(fheTypeIdByte)) {
2143
+ throw new FhevmHandleError({
2144
+ handle,
2145
+ message: `FHEVM Handle "${handle}" is invalid. Unknown FheType: ${fheTypeIdByte}`,
2146
+ });
2147
+ }
2148
+ // Extract version (byte 31)
2149
+ const version = bytes[31];
2150
+ return new FhevmHandle({
2151
+ hash21,
2152
+ chainId,
2153
+ fheTypeId: fheTypeIdByte,
2154
+ version,
2155
+ computed,
2156
+ index,
2157
+ handleBytes32: handle,
2158
+ });
2159
+ }
2160
+ static fromBytes32Hex(handle) {
2161
+ if (!isBytes32Hex(handle)) {
2162
+ throw new FhevmHandleError({ handle });
2163
+ }
2164
+ const bytes = hexToBytes(handle);
2165
+ const h = FhevmHandle.fromBytes32(bytes);
2166
+ h._handleBytes32Hex = handle;
2167
+ return h;
2168
+ }
1830
2169
  static fromZKProof(params) {
1831
2170
  assertIsChecksummedAddress(params.aclAddress);
1832
2171
  assertIsUint64(params.chainId);
@@ -1836,7 +2175,7 @@ class FhevmHandle {
1836
2175
  fheTypeIds = params.fheTypeIds;
1837
2176
  }
1838
2177
  else if (params.fheTypeEncryptionBitwidths !== undefined) {
1839
- fheTypeIds = params.fheTypeEncryptionBitwidths.map((w) => FhevmHandle.FheTypeEncryptionBitwidthsToId[w]);
2178
+ fheTypeIds = params.fheTypeEncryptionBitwidths.map((w) => fheTypeIdFromEncryptionBits(w));
1840
2179
  }
1841
2180
  else {
1842
2181
  throw new InternalError({
@@ -1867,22 +2206,69 @@ class FhevmHandle {
1867
2206
  const handles = [];
1868
2207
  for (let i = 0; i < fheTypeIds.length; ++i) {
1869
2208
  const hash21 = FhevmHandle._computeInputHash21(hexToBytes(blobHashBytes32Hex), params.aclAddress, params.chainId, i);
1870
- handles.push(new FhevmHandle(hash21, params.chainId, fheTypeIds[i], params.ciphertextVersion, false, i));
2209
+ handles.push(new FhevmHandle({
2210
+ hash21,
2211
+ chainId: params.chainId,
2212
+ fheTypeId: fheTypeIds[i],
2213
+ version: params.ciphertextVersion,
2214
+ computed: false,
2215
+ index: i,
2216
+ }));
1871
2217
  }
1872
2218
  return handles;
1873
2219
  }
2220
+ //////////////////////////////////////////////////////////////////////////////
2221
+ // Static Parsing
2222
+ //////////////////////////////////////////////////////////////////////////////
2223
+ static parse(handle) {
2224
+ if (isBytes(handle)) {
2225
+ return FhevmHandle.fromBytes32(handle);
2226
+ }
2227
+ return FhevmHandle.fromBytes32Hex(handle);
2228
+ }
2229
+ static canParse(handle) {
2230
+ try {
2231
+ FhevmHandle.parse(handle);
2232
+ return true;
2233
+ }
2234
+ catch {
2235
+ return false;
2236
+ }
2237
+ }
2238
+ //////////////////////////////////////////////////////////////////////////////
2239
+ // Static Assertions
2240
+ //////////////////////////////////////////////////////////////////////////////
2241
+ static assertIsHandleHex(handle) {
2242
+ if (typeof handle !== 'string') {
2243
+ throw new FhevmHandleError({
2244
+ message: 'Invalid bytes32 hexadecimal string',
2245
+ });
2246
+ }
2247
+ if (!FhevmHandle.canParse(handle)) {
2248
+ throw new FhevmHandleError({ handle });
2249
+ }
2250
+ }
2251
+ //////////////////////////////////////////////////////////////////////////////
2252
+ // Static Helpers
2253
+ //////////////////////////////////////////////////////////////////////////////
2254
+ static currentCiphertextVersion() {
2255
+ return FhevmHandle.CURRENT_CIPHERTEXT_VERSION;
2256
+ }
2257
+ //////////////////////////////////////////////////////////////////////////////
2258
+ // Private Helpers
2259
+ //////////////////////////////////////////////////////////////////////////////
1874
2260
  /**
1875
2261
  * blobHashBytes32 = keccak256(ciphertextWithZKProof)
1876
2262
  */
1877
2263
  static _computeInputHash21(blobHashBytes32, aclAddress, chainId, index) {
1878
2264
  /*
1879
2265
  https://github.com/zama-ai/fhevm/blob/8ffbd5906ab3d57af178e049930e3fc065c9d4b3/coprocessor/fhevm-engine/zkproof-worker/src/verifier.rs#L431C7-L431C8
1880
-
2266
+
1881
2267
  handle_hash = Bytes("ZK-w_hdl") + blobHash 32 Bytes + index 1 Byte + aclAddress 20 Bytes + chainId 32 bytes
1882
2268
  ===========================================================================================================
1883
2269
 
1884
2270
  const HANDLE_HASH_DOMAIN_SEPARATOR: [u8; 8] = *b"ZK-w_hdl";
1885
-
2271
+
1886
2272
  let mut handle_hash = Keccak256::new();
1887
2273
  handle_hash.update(HANDLE_HASH_DOMAIN_SEPARATOR);
1888
2274
  handle_hash.update(blob_hash);
@@ -1903,62 +2289,12 @@ class FhevmHandle {
1903
2289
  assertIsUint64(chainId);
1904
2290
  const encryptionIndexByte1 = new Uint8Array([index]);
1905
2291
  const aclContractAddressBytes20 = checksummedAddressToBytes20(aclAddress);
1906
- const chainIdBytes32 = uint32ToBytes32(chainId);
2292
+ const chainIdBytes32 = numberToBytes32(chainId);
1907
2293
  const encoder = new TextEncoder();
1908
2294
  const domainSepBytes = encoder.encode(FhevmHandle.HANDLE_HASH_DOMAIN_SEPARATOR);
1909
- return ethers.keccak256(concatBytes(domainSepBytes, blobHashBytes32, encryptionIndexByte1, aclContractAddressBytes20, chainIdBytes32));
1910
- }
1911
- toBytes32() {
1912
- assertRelayer((this._index === undefined && this._computed) ||
1913
- (this._index !== undefined && this._index < 255 && !this._computed));
1914
- const chainId32Bytes = uint32ToBytes32(this._chainId);
1915
- const chainId8Bytes = chainId32Bytes.subarray(24, 32);
1916
- const handleHash = hexToBytes(this._hash21);
1917
- const handleBytes32AsBytes = new Uint8Array(32);
1918
- handleBytes32AsBytes.set(handleHash, 0);
1919
- handleBytes32AsBytes[21] = this._index === undefined ? 255 : this._index;
1920
- handleBytes32AsBytes.set(chainId8Bytes, 22);
1921
- handleBytes32AsBytes[30] = this._fheTypeId;
1922
- handleBytes32AsBytes[31] = this._version;
1923
- return handleBytes32AsBytes;
1924
- }
1925
- toBytes32Hex() {
1926
- return bytesToHex(this.toBytes32());
1927
- }
1928
- static checkHandleHex(handle) {
1929
- if (!isBytes32Hex(handle)) {
1930
- throw new FhevmHandleError({ handle });
1931
- }
1932
- }
1933
- static isFheTypeId(value) {
1934
- switch (value) {
1935
- case 0:
1936
- // 1: euint4 is deprecated
1937
- case 2:
1938
- case 3:
1939
- case 4:
1940
- case 5:
1941
- case 6:
1942
- case 7:
1943
- case 8:
1944
- return true;
1945
- default:
1946
- return false;
1947
- }
1948
- }
1949
- static getFheTypeId(handle) {
1950
- if (!isBytes32Hex(handle)) {
1951
- throw new FhevmHandleError({ handle });
1952
- }
1953
- const hexPair = handle.slice(-4, -2).toLowerCase();
1954
- const typeDiscriminant = parseInt(hexPair, 16);
1955
- if (!FhevmHandle.isFheTypeId(typeDiscriminant)) {
1956
- throw new FhevmHandleError({
1957
- handle,
1958
- message: `FHEVM Handle "${handle}" is invalid. Unknown FheType: ${typeDiscriminant}`,
1959
- });
1960
- }
1961
- return typeDiscriminant;
2295
+ const hashBytes32Hex = ethers.keccak256(concatBytes(domainSepBytes, blobHashBytes32, encryptionIndexByte1, aclContractAddressBytes20, chainIdBytes32));
2296
+ // Truncate to 21 bytes (0x + 42 hex chars)
2297
+ return hashBytes32Hex.slice(0, 2 + 2 * 21);
1962
2298
  }
1963
2299
  }
1964
2300
 
@@ -1967,6 +2303,89 @@ const getAddress = (value) => ethers.getAddress(value);
1967
2303
  const currentCiphertextVersion = () => {
1968
2304
  return 0;
1969
2305
  };
2306
+ async function requestCiphertextWithZKProofVerification({ bits, ciphertext, contractAddress, userAddress, aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerProvider, coprocessorSigners, thresholdCoprocessorSigners, extraData, options, }) {
2307
+ const payload = {
2308
+ contractAddress,
2309
+ userAddress,
2310
+ ciphertextWithInputVerification: bytesToHexNo0x(ciphertext),
2311
+ contractChainId: ('0x' + chainId.toString(16)),
2312
+ extraData,
2313
+ };
2314
+ const json = await relayerProvider.fetchPostInputProof(payload, options);
2315
+ if (!isFhevmRelayerInputProofResponse(json)) {
2316
+ throwRelayerInternalError('INPUT_PROOF', json);
2317
+ }
2318
+ const fhevmHandles = FhevmHandle.fromZKProof({
2319
+ ciphertextWithZKProof: ciphertext,
2320
+ chainId,
2321
+ aclAddress: aclContractAddress,
2322
+ ciphertextVersion: currentCiphertextVersion(),
2323
+ fheTypeEncryptionBitwidths: bits,
2324
+ });
2325
+ const handles = fhevmHandles.map((h) => h.toBytes32());
2326
+ const result = json;
2327
+ // Note that the hex strings returned by the relayer do have have the 0x prefix
2328
+ if (result.handles && result.handles.length > 0) {
2329
+ const responseHandles = result.handles.map(hexToBytes);
2330
+ if (handles.length != responseHandles.length) {
2331
+ throw new Error(`Incorrect Handles list sizes: (expected) ${handles.length} != ${responseHandles.length} (received)`);
2332
+ }
2333
+ for (let index = 0; index < handles.length; index += 1) {
2334
+ let handle = handles[index];
2335
+ let responseHandle = responseHandles[index];
2336
+ let expected = bytesToHexNo0x(handle);
2337
+ let current = bytesToHexNo0x(responseHandle);
2338
+ if (expected !== current) {
2339
+ throw new Error(`Incorrect Handle ${index}: (expected) ${expected} != ${current} (received)`);
2340
+ }
2341
+ }
2342
+ }
2343
+ const signatures = result.signatures;
2344
+ // verify signatures for inputs:
2345
+ const domain = {
2346
+ name: 'InputVerification',
2347
+ version: '1',
2348
+ chainId: gatewayChainId,
2349
+ verifyingContract: verifyingContractAddressInputVerification,
2350
+ };
2351
+ const types = {
2352
+ CiphertextVerification: [
2353
+ { name: 'ctHandles', type: 'bytes32[]' },
2354
+ { name: 'userAddress', type: 'address' },
2355
+ { name: 'contractAddress', type: 'address' },
2356
+ { name: 'contractChainId', type: 'uint256' },
2357
+ { name: 'extraData', type: 'bytes' },
2358
+ ],
2359
+ };
2360
+ const recoveredAddresses = signatures.map((signature) => {
2361
+ const sig = signature.startsWith('0x') ? signature : `0x${signature}`;
2362
+ const recoveredAddress = ethers.ethers.verifyTypedData(domain, types, {
2363
+ ctHandles: handles,
2364
+ userAddress,
2365
+ contractAddress,
2366
+ contractChainId: chainId,
2367
+ extraData,
2368
+ }, sig);
2369
+ return recoveredAddress;
2370
+ });
2371
+ const thresholdReached = isThresholdReached$1(coprocessorSigners, recoveredAddresses, thresholdCoprocessorSigners);
2372
+ if (!thresholdReached) {
2373
+ throw Error('Coprocessor signers threshold is not reached');
2374
+ }
2375
+ // inputProof is len(list_handles) + numCoprocessorSigners + list_handles + signatureCoprocessorSigners (1+1+NUM_HANDLES*32+65*numSigners)
2376
+ let inputProof = numberToHexNo0x(handles.length);
2377
+ const numSigners = signatures.length;
2378
+ inputProof += numberToHexNo0x(numSigners);
2379
+ const listHandlesStr = handles.map((i) => bytesToHexNo0x(i));
2380
+ listHandlesStr.map((handle) => (inputProof += handle));
2381
+ signatures.map((signature) => (inputProof += signature.slice(2))); // removes the '0x' prefix from the `signature` string
2382
+ // Append the extra data to the input proof
2383
+ inputProof += extraData.slice(2);
2384
+ return {
2385
+ handles,
2386
+ inputProof: hexToBytes(inputProof),
2387
+ };
2388
+ }
1970
2389
  function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold) {
1971
2390
  const addressMap = new Map();
1972
2391
  recoveredAddresses.forEach((address, index) => {
@@ -2050,8 +2469,15 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
2050
2469
  getBits() {
2051
2470
  return input.getBits();
2052
2471
  },
2053
- getCiphertextWithInputVerification() {
2054
- return input.encrypt();
2472
+ generateZKProof() {
2473
+ return {
2474
+ chainId,
2475
+ aclContractAddress: aclContractAddress,
2476
+ userAddress: userAddress,
2477
+ contractAddress: contractAddress,
2478
+ ciphertextWithZkProof: input.encrypt(),
2479
+ bits: input.getBits(),
2480
+ };
2055
2481
  },
2056
2482
  encrypt: async (options) => {
2057
2483
  const extraData = '0x00';
@@ -2060,7 +2486,7 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
2060
2486
  const payload = {
2061
2487
  contractAddress: getAddress(contractAddress),
2062
2488
  userAddress: getAddress(userAddress),
2063
- ciphertextWithInputVerification: toHexString(ciphertext),
2489
+ ciphertextWithInputVerification: bytesToHexNo0x(ciphertext),
2064
2490
  contractChainId: ('0x' + chainId.toString(16)),
2065
2491
  extraData,
2066
2492
  };
@@ -2089,8 +2515,8 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
2089
2515
  for (let index = 0; index < handles.length; index += 1) {
2090
2516
  let handle = handles[index];
2091
2517
  let responseHandle = responseHandles[index];
2092
- let expected = toHexString(handle);
2093
- let current = toHexString(responseHandle);
2518
+ let expected = bytesToHexNo0x(handle);
2519
+ let current = bytesToHexNo0x(responseHandle);
2094
2520
  if (expected !== current) {
2095
2521
  throw new Error(`Incorrect Handle ${index}: (expected) ${expected} != ${current} (received)`);
2096
2522
  }
@@ -2129,10 +2555,10 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
2129
2555
  throw Error('Coprocessor signers threshold is not reached');
2130
2556
  }
2131
2557
  // inputProof is len(list_handles) + numCoprocessorSigners + list_handles + signatureCoprocessorSigners (1+1+NUM_HANDLES*32+65*numSigners)
2132
- let inputProof = numberToHex(handles.length);
2558
+ let inputProof = numberToHexNo0x(handles.length);
2133
2559
  const numSigners = signatures.length;
2134
- inputProof += numberToHex(numSigners);
2135
- const listHandlesStr = handles.map((i) => toHexString(i));
2560
+ inputProof += numberToHexNo0x(numSigners);
2561
+ const listHandlesStr = handles.map((i) => bytesToHexNo0x(i));
2136
2562
  listHandlesStr.map((handle) => (inputProof += handle));
2137
2563
  signatures.map((signature) => (inputProof += signature.slice(2))); // removes the '0x' prefix from the `signature` string
2138
2564
  // Append the extra data to the input proof
@@ -2185,7 +2611,7 @@ function abiEncodeClearValues(clearValues) {
2185
2611
  const abiValues = [];
2186
2612
  for (let i = 0; i < handlesBytes32Hex.length; ++i) {
2187
2613
  const handle = handlesBytes32Hex[i];
2188
- const handleType = FhevmHandle.getFheTypeId(handle);
2614
+ const handleType = FhevmHandle.parse(handle).fheTypeId;
2189
2615
  let clearTextValue = clearValues[handle];
2190
2616
  if (typeof clearTextValue === 'boolean') {
2191
2617
  clearTextValue = clearTextValue ? '0x01' : '0x00';
@@ -2248,24 +2674,23 @@ function buildDecryptionProof(kmsSignatures, extraData) {
2248
2674
  return decryptionProof;
2249
2675
  }
2250
2676
  function deserializeClearValues(handles, decryptedResult) {
2251
- let typesList = [];
2677
+ let fheTypeIdList = [];
2252
2678
  for (const handle of handles) {
2253
- const typeDiscriminant = FhevmHandle.getFheTypeId(handle);
2254
- assertRelayer(FhevmHandle.isFheTypeId(typeDiscriminant));
2255
- typesList.push(typeDiscriminant);
2679
+ const typeDiscriminant = FhevmHandle.parse(handle).fheTypeId;
2680
+ fheTypeIdList.push(typeDiscriminant);
2256
2681
  }
2257
2682
  const restoredEncoded = '0x' +
2258
2683
  '00'.repeat(32) + // dummy requestID (ignored)
2259
2684
  decryptedResult.slice(2) +
2260
2685
  '00'.repeat(32); // dummy empty bytes[] length (ignored)
2261
- const abiTypes = typesList.map((t) => {
2262
- const abiType = FhevmHandle.FheTypeIdToSolidityPrimitiveType[t]; // all types are valid because this was supposedly checked already inside the `checkEncryptedBits` function
2686
+ const abiTypes = fheTypeIdList.map((t) => {
2687
+ const abiType = solidityPrimitiveTypeNameFromFheTypeId(t); // all types are valid because this was supposedly checked already inside the `checkEncryptedBits` function
2263
2688
  return abiType;
2264
2689
  });
2265
2690
  const coder = new ethers.AbiCoder();
2266
2691
  const decoded = coder.decode(['uint256', ...abiTypes, 'bytes[]'], restoredEncoded);
2267
2692
  // strip dummy first/last element
2268
- const rawValues = decoded.slice(1, 1 + typesList.length);
2693
+ const rawValues = decoded.slice(1, 1 + fheTypeIdList.length);
2269
2694
  const results = {};
2270
2695
  handles.forEach((handle, idx) => (results[handle] = rawValues[idx]));
2271
2696
  return results;
@@ -2273,12 +2698,13 @@ function deserializeClearValues(handles, decryptedResult) {
2273
2698
  const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress, relayerProvider, provider, defaultOptions) => async (_handles, options) => {
2274
2699
  const extraData = '0x00';
2275
2700
  const acl = new ethers.ethers.Contract(aclContractAddress, aclABI, provider);
2701
+ // This will be replaced by new sanitize classes
2276
2702
  let handles;
2277
2703
  try {
2278
2704
  handles = await Promise.all(_handles.map(async (_handle) => {
2279
2705
  const handle = typeof _handle === 'string'
2280
- ? toHexString(hexToBytes(_handle), true)
2281
- : toHexString(_handle, true);
2706
+ ? bytesToHex(hexToBytes(_handle))
2707
+ : bytesToHex(_handle);
2282
2708
  const isAllowedForDecryption = await acl.isAllowedForDecryption(handle);
2283
2709
  if (!isAllowedForDecryption) {
2284
2710
  throw new Error(`Handle ${handle} is not allowed for public decryption!`);
@@ -2436,8 +2862,8 @@ const createEIP712 = (verifyingContract, contractsChainId) => (publicKey, contra
2436
2862
  const generateKeypair = () => {
2437
2863
  const keypair = TKMS.ml_kem_pke_keygen();
2438
2864
  return {
2439
- publicKey: toHexString(TKMS.ml_kem_pke_pk_to_u8vec(TKMS.ml_kem_pke_get_pk(keypair))),
2440
- privateKey: toHexString(TKMS.ml_kem_pke_sk_to_u8vec(keypair)),
2865
+ publicKey: bytesToHexNo0x(TKMS.ml_kem_pke_pk_to_u8vec(TKMS.ml_kem_pke_get_pk(keypair))),
2866
+ privateKey: bytesToHexNo0x(TKMS.ml_kem_pke_sk_to_u8vec(keypair)),
2441
2867
  };
2442
2868
  };
2443
2869
 
@@ -3101,7 +3527,28 @@ function assertIsRelayerV2GetResponseQueued(value, name) {
3101
3527
  assertRecordStringProperty(value, 'requestId', name);
3102
3528
  }
3103
3529
 
3530
+ class RelayerV2TimeoutError extends RelayerV2RequestErrorBase {
3531
+ constructor(params) {
3532
+ super({
3533
+ ...params,
3534
+ name: 'RelayerV2TimeoutError',
3535
+ message: `Request timed out after ${params.timeoutMs}ms`,
3536
+ });
3537
+ }
3538
+ }
3539
+
3540
+ class RelayerV2AbortError extends RelayerV2RequestErrorBase {
3541
+ constructor(params) {
3542
+ super({
3543
+ ...params,
3544
+ name: 'RelayerV2AbortError',
3545
+ message: `Request aborted`,
3546
+ });
3547
+ }
3548
+ }
3549
+
3104
3550
  class RelayerV2AsyncRequest {
3551
+ _fetchMethod;
3105
3552
  _jobId;
3106
3553
  _jobIdTimestamp;
3107
3554
  _state;
@@ -3116,17 +3563,18 @@ class RelayerV2AsyncRequest {
3116
3563
  _retryAfterTimeoutID;
3117
3564
  _url;
3118
3565
  _payload;
3119
- _fhevmInstanceOptions;
3566
+ _fhevmAuth;
3120
3567
  _retryAfterTimeoutPromiseFuncReject;
3121
3568
  _onProgress;
3122
- _requestMaxDurationInSecs;
3569
+ _requestMaxDurationInMs;
3123
3570
  _requestStartTimestamp;
3124
3571
  _requestGlobalTimeoutID;
3125
3572
  _throwErrorIfNoRetryAfter;
3126
- static DEFAULT_RETRY_AFTER_SECS = 2;
3127
- static DEFAULT_GLOBAL_REQUEST_TIMEOUT_SECS = 60 * 60;
3128
- static MAX_GET_RETRY = 100;
3129
- static MAX_POST_RETRY = 100;
3573
+ static DEFAULT_RETRY_AFTER_MS = 2500;
3574
+ static MINIMUM_RETRY_AFTER_MS = 1000;
3575
+ static DEFAULT_GLOBAL_REQUEST_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour
3576
+ static MAX_GET_RETRY = 60 * 30; // number of default retries in 1 hour (30 retries/min)
3577
+ static MAX_POST_RETRY = RelayerV2AsyncRequest.MAX_GET_RETRY;
3130
3578
  constructor(params) {
3131
3579
  if (params.relayerOperation !== 'INPUT_PROOF' &&
3132
3580
  params.relayerOperation !== 'PUBLIC_DECRYPT' &&
@@ -3143,14 +3591,14 @@ class RelayerV2AsyncRequest {
3143
3591
  this._internalAbortController = new AbortController();
3144
3592
  this._internalAbortSignal = this._internalAbortController.signal;
3145
3593
  this._internalAbortSignal.addEventListener('abort', this._handleInternalSignalAbort);
3146
- this._externalAbortSignal = params.signal;
3594
+ this._externalAbortSignal = params.options?.signal;
3147
3595
  if (this._externalAbortSignal) {
3148
3596
  this._externalAbortSignal.addEventListener('abort', this._handleExternalSignalAbort);
3149
3597
  }
3150
3598
  this._url = params.url;
3151
3599
  this._payload = params.payload;
3152
- this._fhevmInstanceOptions = params.instanceOptions;
3153
- this._onProgress = params.onProgress;
3600
+ this._fhevmAuth = params.options?.auth;
3601
+ this._onProgress = params.options?.onProgress;
3154
3602
  this._state = {
3155
3603
  aborted: false,
3156
3604
  canceled: false,
@@ -3159,6 +3607,7 @@ class RelayerV2AsyncRequest {
3159
3607
  running: false,
3160
3608
  succeeded: false,
3161
3609
  terminated: false,
3610
+ timeout: false,
3162
3611
  };
3163
3612
  this._retryCount = 0;
3164
3613
  this._retryAfterTimeoutID = undefined;
@@ -3166,9 +3615,9 @@ class RelayerV2AsyncRequest {
3166
3615
  this._terminateReason = undefined;
3167
3616
  this._publicAPINoReentrancy = false;
3168
3617
  this._throwErrorIfNoRetryAfter = params.throwErrorIfNoRetryAfter ?? false;
3169
- this._requestMaxDurationInSecs =
3170
- params.timeoutInSeconds ??
3171
- RelayerV2AsyncRequest.DEFAULT_GLOBAL_REQUEST_TIMEOUT_SECS;
3618
+ this._requestMaxDurationInMs =
3619
+ params.options?.timeout ??
3620
+ RelayerV2AsyncRequest.DEFAULT_GLOBAL_REQUEST_TIMEOUT_MS;
3172
3621
  }
3173
3622
  //////////////////////////////////////////////////////////////////////////////
3174
3623
  // Public API: run
@@ -3210,6 +3659,12 @@ class RelayerV2AsyncRequest {
3210
3659
  state: { ...this._state },
3211
3660
  });
3212
3661
  }
3662
+ if (this._state.timeout) {
3663
+ throw new RelayerV2StateError({
3664
+ message: `Relayer.run() failed. Request already timeout.`,
3665
+ state: { ...this._state },
3666
+ });
3667
+ }
3213
3668
  if (this._externalAbortSignal?.aborted === true) {
3214
3669
  throw new RelayerV2StateError({
3215
3670
  message: `Relayer.run() failed. External AbortSignal already aborted (reason:${this._externalAbortSignal?.reason}).`,
@@ -3230,7 +3685,7 @@ class RelayerV2AsyncRequest {
3230
3685
  }
3231
3686
  this._state.running = true;
3232
3687
  this._requestStartTimestamp = Date.now();
3233
- this._setGlobalRequestTimeout(this._requestMaxDurationInSecs * 1000);
3688
+ this._setGlobalRequestTimeout(this._requestMaxDurationInMs);
3234
3689
  try {
3235
3690
  const json = await this._runPostLoop();
3236
3691
  this._state.succeeded = true;
@@ -3301,6 +3756,12 @@ class RelayerV2AsyncRequest {
3301
3756
  get failed() {
3302
3757
  return this._state.failed;
3303
3758
  }
3759
+ get aborted() {
3760
+ return this._state.aborted;
3761
+ }
3762
+ get timeout() {
3763
+ return this._state.timeout;
3764
+ }
3304
3765
  get succeeded() {
3305
3766
  return this._state.succeeded;
3306
3767
  }
@@ -3321,6 +3782,8 @@ class RelayerV2AsyncRequest {
3321
3782
  //////////////////////////////////////////////////////////////////////////////
3322
3783
  // POST : 202 | 400 | 429 | 500 | 503
3323
3784
  async _runPostLoop() {
3785
+ this._assert(this._fetchMethod === undefined, 'this._fetchMethod === undefined');
3786
+ this._fetchMethod = 'POST';
3324
3787
  // No infinite loop!
3325
3788
  let i = 0;
3326
3789
  while (i < RelayerV2AsyncRequest.MAX_POST_RETRY) {
@@ -3356,9 +3819,7 @@ class RelayerV2AsyncRequest {
3356
3819
  bodyJson: safeJSONstringify(bodyJson),
3357
3820
  });
3358
3821
  }
3359
- let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
3360
- if (retry_after_sec < 1)
3361
- retry_after_sec = 1;
3822
+ const retryAfterMs = this._getRetryAfterHeaderValueInMs(response);
3362
3823
  // Debug: will throw an assert failed error if jobId has already been set
3363
3824
  this._setJobIdOnce(bodyJson.result.jobId);
3364
3825
  // Async onProgress callback
@@ -3371,11 +3832,10 @@ class RelayerV2AsyncRequest {
3371
3832
  jobId: this.jobId,
3372
3833
  operation: this._relayerOperation,
3373
3834
  retryCount: this._retryCount,
3374
- retryAfter: retry_after_sec,
3835
+ retryAfterMs,
3375
3836
  elapsed,
3376
3837
  });
3377
- // Wait if needed (minimum 1s)
3378
- await this._setRetryAfterTimeout(retry_after_sec * 1000);
3838
+ await this._setRetryAfterTimeout(retryAfterMs);
3379
3839
  const json = await this._runGetLoop();
3380
3840
  return json;
3381
3841
  }
@@ -3421,22 +3881,20 @@ class RelayerV2AsyncRequest {
3421
3881
  bodyJson: safeJSONstringify(bodyJson),
3422
3882
  });
3423
3883
  }
3424
- let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
3425
- if (retry_after_sec < 1)
3426
- retry_after_sec = 1;
3884
+ const retryAfterMs = this._getRetryAfterHeaderValueInMs(response);
3427
3885
  // Async onProgress callback
3428
3886
  this._postAsyncOnProgressCallback({
3429
3887
  type: 'ratelimited',
3430
3888
  url: this._url,
3431
3889
  method: 'POST',
3432
3890
  status: responseStatus,
3433
- retryAfter: retry_after_sec,
3891
+ retryAfterMs,
3434
3892
  retryCount: this._retryCount,
3435
3893
  elapsed,
3436
- message: bodyJson.error.message,
3894
+ relayerApiError: bodyJson.error,
3437
3895
  });
3438
3896
  // Wait if needed (minimum 1s)
3439
- await this._setRetryAfterTimeout(retry_after_sec * 1000);
3897
+ await this._setRetryAfterTimeout(retryAfterMs);
3440
3898
  continue;
3441
3899
  }
3442
3900
  // RelayerV2ResponseFailed
@@ -3515,8 +3973,10 @@ class RelayerV2AsyncRequest {
3515
3973
  // GET: 200 | 202 | 404 | 500 | 503 | 504
3516
3974
  // GET is not rate-limited, therefore there is not 429 error
3517
3975
  async _runGetLoop() {
3976
+ this._assert(this._fetchMethod === 'POST', "this._fetchMethod === 'POST'");
3518
3977
  this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
3519
3978
  this._assert(this._jobIdTimestamp !== undefined, 'this._jobIdTimestamp !== undefined');
3979
+ this._fetchMethod = 'GET';
3520
3980
  let i = 0;
3521
3981
  while (i < RelayerV2AsyncRequest.MAX_GET_RETRY) {
3522
3982
  ++i;
@@ -3626,9 +4086,7 @@ class RelayerV2AsyncRequest {
3626
4086
  bodyJson: safeJSONstringify(bodyJson),
3627
4087
  });
3628
4088
  }
3629
- let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
3630
- if (retry_after_sec < 1)
3631
- retry_after_sec = 1;
4089
+ const retryAfterMs = this._getRetryAfterHeaderValueInMs(response);
3632
4090
  // Async onProgress callback
3633
4091
  this._postAsyncOnProgressCallback({
3634
4092
  type: 'queued',
@@ -3638,12 +4096,12 @@ class RelayerV2AsyncRequest {
3638
4096
  requestId: bodyJson.requestId,
3639
4097
  operation: this._relayerOperation,
3640
4098
  jobId: this.jobId,
3641
- retryAfter: retry_after_sec,
4099
+ retryAfterMs,
3642
4100
  retryCount: this._retryCount,
3643
4101
  elapsed,
3644
4102
  });
3645
4103
  // Wait if needed (minimum 1s)
3646
- await this._setRetryAfterTimeout(retry_after_sec * 1000);
4104
+ await this._setRetryAfterTimeout(retryAfterMs);
3647
4105
  continue;
3648
4106
  }
3649
4107
  case 400: {
@@ -3795,17 +4253,20 @@ class RelayerV2AsyncRequest {
3795
4253
  return bodyJson;
3796
4254
  }
3797
4255
  //////////////////////////////////////////////////////////////////////////////
3798
- _getRetryAfterHeaderValueInSecs(response) {
4256
+ _getRetryAfterHeaderValueInMs(response) {
3799
4257
  if (!response.headers.has('Retry-After')) {
3800
4258
  if (this._throwErrorIfNoRetryAfter) {
3801
4259
  throw new Error(`Missing 'Retry-After' header key`);
3802
4260
  }
3803
- return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_SECS;
4261
+ return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_MS;
3804
4262
  }
3805
4263
  try {
3806
4264
  const n = Number.parseInt(response.headers.get('Retry-After'));
3807
4265
  if (isUint(n)) {
3808
- return n;
4266
+ const ms = n * 1000;
4267
+ return ms < RelayerV2AsyncRequest.MINIMUM_RETRY_AFTER_MS
4268
+ ? RelayerV2AsyncRequest.MINIMUM_RETRY_AFTER_MS
4269
+ : ms;
3809
4270
  }
3810
4271
  }
3811
4272
  catch {
@@ -3814,7 +4275,7 @@ class RelayerV2AsyncRequest {
3814
4275
  if (this._throwErrorIfNoRetryAfter) {
3815
4276
  throw new Error(`Invalid 'Retry-After' header key`);
3816
4277
  }
3817
- return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_SECS;
4278
+ return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_MS;
3818
4279
  }
3819
4280
  //////////////////////////////////////////////////////////////////////////////
3820
4281
  // JobId
@@ -3853,7 +4314,7 @@ class RelayerV2AsyncRequest {
3853
4314
  this._assert(this._jobId === undefined, 'this._jobId === undefined');
3854
4315
  this._assert(!this._state.terminated, '!this._state.terminated');
3855
4316
  this._assert(!this._state.fetching, '!this._state.fetching');
3856
- this._trace('_fetchPost', 'enter');
4317
+ this._trace('_fetchPost', this._url);
3857
4318
  const init = setAuth({
3858
4319
  method: 'POST',
3859
4320
  headers: {
@@ -3865,7 +4326,7 @@ class RelayerV2AsyncRequest {
3865
4326
  ...(this._internalAbortSignal
3866
4327
  ? { signal: this._internalAbortSignal }
3867
4328
  : {}),
3868
- }, this._fhevmInstanceOptions?.auth);
4329
+ }, this._fhevmAuth);
3869
4330
  this._state.fetching = true;
3870
4331
  let response;
3871
4332
  try {
@@ -3992,7 +4453,18 @@ class RelayerV2AsyncRequest {
3992
4453
  if (signal.reason !== 'cancel') {
3993
4454
  this._assert(!this._state.canceled, '!this._state.canceled');
3994
4455
  }
3995
- this._terminate('abort');
4456
+ this._postAsyncOnProgressCallback({
4457
+ type: 'abort',
4458
+ url: this._url,
4459
+ ...(this._jobId ? { jobId: this._jobId } : {}),
4460
+ operation: this._relayerOperation,
4461
+ retryCount: this._retryCount,
4462
+ });
4463
+ this._terminate('abort', new RelayerV2AbortError({
4464
+ operation: this._relayerOperation,
4465
+ jobId: this._jobId,
4466
+ url: this._url,
4467
+ }));
3996
4468
  };
3997
4469
  //////////////////////////////////////////////////////////////////////////////
3998
4470
  // Terminate
@@ -4016,7 +4488,7 @@ class RelayerV2AsyncRequest {
4016
4488
  this._terminateReason = reason;
4017
4489
  this._terminateError = error;
4018
4490
  this._state.terminated = true;
4019
- this._tryClearRetryAfterTimeout();
4491
+ this._tryClearRetryAfterTimeout(error);
4020
4492
  this._tryClearGlobalRequestTimeout();
4021
4493
  const is = this._internalAbortSignal;
4022
4494
  const es = this._externalAbortSignal;
@@ -4038,7 +4510,7 @@ class RelayerV2AsyncRequest {
4038
4510
  // Debug
4039
4511
  this._assert(!this._state.terminated, '!this._state.terminated');
4040
4512
  this._assert(this._retryAfterTimeoutID === undefined, 'this._retryAfterTimeoutID === undefined');
4041
- this._assert(delayMs >= 1000, 'delayMs >= 1000');
4513
+ this._assert(delayMs >= RelayerV2AsyncRequest.MINIMUM_RETRY_AFTER_MS, `delayMs >= ${RelayerV2AsyncRequest.MINIMUM_RETRY_AFTER_MS}`);
4042
4514
  this._trace('_setRetryAfterTimeout', `delayMs=${delayMs}`);
4043
4515
  if (this._retryAfterTimeoutID !== undefined) {
4044
4516
  return Promise.reject(new Error(`retry-after already running.`));
@@ -4058,7 +4530,7 @@ class RelayerV2AsyncRequest {
4058
4530
  return p;
4059
4531
  }
4060
4532
  //////////////////////////////////////////////////////////////////////////////
4061
- _tryClearRetryAfterTimeout() {
4533
+ _tryClearRetryAfterTimeout(error) {
4062
4534
  if (this._retryAfterTimeoutID === undefined) {
4063
4535
  // Debug
4064
4536
  this._assert(this._retryAfterTimeoutPromiseFuncReject === undefined, 'this._retryAfterTimeoutPromiseFuncReject === undefined');
@@ -4069,7 +4541,8 @@ class RelayerV2AsyncRequest {
4069
4541
  this._retryAfterTimeoutID = undefined;
4070
4542
  this._retryAfterTimeoutPromiseFuncReject = undefined;
4071
4543
  clearTimeout(tid);
4072
- reject(new Error('_tryClearRetryAfterTimeout'));
4544
+ // Calling reject will
4545
+ reject(error ?? new Error('_tryClearRetryAfterTimeout'));
4073
4546
  }
4074
4547
  //////////////////////////////////////////////////////////////////////////////
4075
4548
  // Global Request Timeout
@@ -4084,7 +4557,24 @@ class RelayerV2AsyncRequest {
4084
4557
  this._requestGlobalTimeoutID = setTimeout(callback, delayMs);
4085
4558
  }
4086
4559
  _handleGlobalRequestTimeout() {
4087
- this._terminate('timeout');
4560
+ this._state.timeout = true;
4561
+ // Debug state-check guards:
4562
+ this._assert(this instanceof RelayerV2AsyncRequest, `this instanceof RelayerV2AsyncRequest`);
4563
+ this._assert(!this._state.terminated, `!this._state.terminated`);
4564
+ this._assert(!this._state.timeout, '!this._state.timeout');
4565
+ this._postAsyncOnProgressCallback({
4566
+ type: 'timeout',
4567
+ url: this._url,
4568
+ ...(this._jobId ? { jobId: this._jobId } : {}),
4569
+ operation: this._relayerOperation,
4570
+ retryCount: this._retryCount,
4571
+ });
4572
+ this._terminate('timeout', new RelayerV2TimeoutError({
4573
+ operation: this._relayerOperation,
4574
+ jobId: this._jobId,
4575
+ url: this._url,
4576
+ timeoutMs: this._requestMaxDurationInMs,
4577
+ }));
4088
4578
  }
4089
4579
  _tryClearGlobalRequestTimeout() {
4090
4580
  if (this._requestGlobalTimeoutID === undefined) {
@@ -4292,37 +4782,34 @@ class RelayerV2Provider extends AbstractRelayerProvider {
4292
4782
  const response = await this.fetchGetKeyUrlV2();
4293
4783
  return toRelayerV1KeyUrlResponse(response);
4294
4784
  }
4295
- async fetchPostInputProof(payload, instanceOptions, fetchOptions) {
4785
+ async fetchPostInputProof(payload, options) {
4296
4786
  const request = new RelayerV2AsyncRequest({
4297
4787
  relayerOperation: 'INPUT_PROOF',
4298
4788
  url: this.inputProof,
4299
4789
  payload,
4300
- instanceOptions,
4301
- ...fetchOptions,
4790
+ options,
4302
4791
  });
4303
4792
  const result = (await request.run());
4304
4793
  assertIsRelayerInputProofResult(result, 'fetchPostInputProof()');
4305
4794
  return result;
4306
4795
  }
4307
- async fetchPostPublicDecrypt(payload, instanceOptions, fetchOptions) {
4796
+ async fetchPostPublicDecrypt(payload, options) {
4308
4797
  const request = new RelayerV2AsyncRequest({
4309
4798
  relayerOperation: 'PUBLIC_DECRYPT',
4310
4799
  url: this.publicDecrypt,
4311
4800
  payload,
4312
- instanceOptions,
4313
- ...fetchOptions,
4801
+ options,
4314
4802
  });
4315
4803
  const result = await request.run();
4316
4804
  assertIsRelayerPublicDecryptResult(result, 'fetchPostPublicDecrypt()');
4317
4805
  return result;
4318
4806
  }
4319
- async fetchPostUserDecrypt(payload, instanceOptions, fetchOptions) {
4807
+ async fetchPostUserDecrypt(payload, options) {
4320
4808
  const request = new RelayerV2AsyncRequest({
4321
4809
  relayerOperation: 'USER_DECRYPT',
4322
4810
  url: this.userDecrypt,
4323
4811
  payload,
4324
- instanceOptions,
4325
- ...fetchOptions,
4812
+ options,
4326
4813
  });
4327
4814
  const result = (await request.run());
4328
4815
  assertIsRelayerUserDecryptResult(result.result, 'fetchPostUserDecrypt()');
@@ -4330,6 +4817,16 @@ class RelayerV2Provider extends AbstractRelayerProvider {
4330
4817
  }
4331
4818
  }
4332
4819
 
4820
+ class TFHECrsError extends RelayerErrorBase {
4821
+ constructor({ message, cause }) {
4822
+ super({
4823
+ message,
4824
+ name: 'TFHECrsError',
4825
+ ...(cause ? { cause: ensureError(cause) } : {}),
4826
+ });
4827
+ }
4828
+ }
4829
+
4333
4830
  class AbstractRelayerFhevm {
4334
4831
  }
4335
4832
 
@@ -4344,6 +4841,15 @@ class TFHECrs {
4344
4841
  this._bits = params.bits;
4345
4842
  this._srcUrl = params.srcUrl;
4346
4843
  }
4844
+ get id() {
4845
+ return this._id;
4846
+ }
4847
+ get bits() {
4848
+ return this._bits;
4849
+ }
4850
+ get srcUrl() {
4851
+ return this._srcUrl;
4852
+ }
4347
4853
  /*
4348
4854
  {
4349
4855
  id: string,
@@ -4446,19 +4952,57 @@ class TFHECrs {
4446
4952
  return TFHECrs._fromUrl(params);
4447
4953
  }
4448
4954
  else {
4449
- throw new Error('Invalid public key (deserialization failed)');
4955
+ throw new TFHECrsError({
4956
+ message: 'Invalid public key (deserialization failed)',
4957
+ });
4450
4958
  }
4451
4959
  }
4960
+ /*
4961
+ {
4962
+ id: string;
4963
+ data: Uint8Array;
4964
+ bits: number;
4965
+ srcUrl?: string;
4966
+ }
4967
+ */
4452
4968
  static fromBytes(params) {
4453
4969
  try {
4454
4970
  TFHECrs.assertKeyBytesType(params, 'arg');
4455
4971
  return TFHECrs._fromBytes(params);
4456
4972
  }
4457
4973
  catch (e) {
4458
- throw new Error('Invalid public key (deserialization failed)', {
4974
+ throw new TFHECrsError({
4975
+ message: 'Invalid public key (deserialization failed)',
4976
+ cause: e,
4977
+ });
4978
+ }
4979
+ }
4980
+ /*
4981
+ {
4982
+ id: string;
4983
+ data: BytesHex;
4984
+ bits: number;
4985
+ srcUrl?: string;
4986
+ }
4987
+ */
4988
+ static fromBytesHex(params) {
4989
+ let data;
4990
+ try {
4991
+ assertRecordStringProperty(params, 'data', 'arg');
4992
+ data = hexToBytesFaster(params.data, true /* strict */);
4993
+ }
4994
+ catch (e) {
4995
+ throw new TFHECrsError({
4996
+ message: 'Invalid public key (deserialization failed)',
4459
4997
  cause: e,
4460
4998
  });
4461
4999
  }
5000
+ return TFHECrs.fromBytes({
5001
+ id: params?.id,
5002
+ bits: params?.bits,
5003
+ srcUrl: params?.srcUrl,
5004
+ data,
5005
+ });
4462
5006
  }
4463
5007
  static _fromBytes(params) {
4464
5008
  const _params = {
@@ -4475,7 +5019,50 @@ class TFHECrs {
4475
5019
  return TFHECrs._fromPublicParamsBytes(params);
4476
5020
  }
4477
5021
  catch (e) {
4478
- throw new Error('Invalid public key (deserialization failed)', {
5022
+ throw new TFHECrsError({
5023
+ message: 'Invalid public key (deserialization failed)',
5024
+ cause: e,
5025
+ });
5026
+ }
5027
+ }
5028
+ static fromBitsPublicParamsBytes(bits, params) {
5029
+ if (bits === undefined) {
5030
+ throw new TFHECrsError({ message: 'Missing PublicParams bits format' });
5031
+ }
5032
+ if (bits !== 2048) {
5033
+ throw new TFHECrsError({
5034
+ message: `Unsupported PublicParams bits format '${bits}'`,
5035
+ });
5036
+ }
5037
+ try {
5038
+ assertRecordStringProperty(params, 'publicParamsId', `arg`);
5039
+ assertUint8ArrayProperty(params, 'publicParams', `arg`);
5040
+ return TFHECrs._fromPublicParamsBytes({
5041
+ 2048: params,
5042
+ });
5043
+ }
5044
+ catch (e) {
5045
+ throw new TFHECrsError({
5046
+ message: 'Invalid public key (deserialization failed)',
5047
+ cause: e,
5048
+ });
5049
+ }
5050
+ }
5051
+ static fromPublicParamsBytesHex(params) {
5052
+ try {
5053
+ assertNonNullableRecordProperty(params, '2048', 'arg');
5054
+ assertRecordStringProperty(params['2048'], 'publicParamsId', `arg.2048`);
5055
+ assertRecordStringProperty(params['2048'], 'publicParams', `arg.2048`);
5056
+ return TFHECrs._fromPublicParamsBytes({
5057
+ 2048: {
5058
+ publicParams: hexToBytesFaster(params['2048'].publicParams, true /* strict */),
5059
+ publicParamsId: params['2048'].publicParamsId,
5060
+ },
5061
+ });
5062
+ }
5063
+ catch (e) {
5064
+ throw new TFHECrsError({
5065
+ message: 'Invalid public key (deserialization failed)',
4479
5066
  cause: e,
4480
5067
  });
4481
5068
  }
@@ -4493,7 +5080,8 @@ class TFHECrs {
4493
5080
  return TFHECrs._fromUrl(params);
4494
5081
  }
4495
5082
  catch (e) {
4496
- throw new Error('Impossible to fetch public key: wrong relayer url.', {
5083
+ throw new TFHECrsError({
5084
+ message: 'Impossible to fetch public key: wrong relayer url.',
4497
5085
  cause: e,
4498
5086
  });
4499
5087
  }
@@ -4524,6 +5112,22 @@ class TFHECrs {
4524
5112
  ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
4525
5113
  };
4526
5114
  }
5115
+ /*
5116
+ {
5117
+ id: string,
5118
+ bits: number,
5119
+ data: BytesHex,
5120
+ srcUrl?: string
5121
+ }
5122
+ */
5123
+ toBytesHex() {
5124
+ return {
5125
+ data: bytesToHexLarge(this._compactPkeCrs.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS)),
5126
+ id: this._id,
5127
+ bits: this._bits,
5128
+ ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
5129
+ };
5130
+ }
4527
5131
  /*
4528
5132
  {
4529
5133
  2048: {
@@ -4532,9 +5136,11 @@ class TFHECrs {
4532
5136
  }
4533
5137
  }
4534
5138
  */
4535
- toPublicParamsWasm() {
5139
+ toPublicParams2048Wasm() {
4536
5140
  if (this._bits !== 2048) {
4537
- throw new Error(`Unsupported PublicParams bits format ${this._bits}`);
5141
+ throw new TFHECrsError({
5142
+ message: `Unsupported PublicParams bits format '2048'`,
5143
+ });
4538
5144
  }
4539
5145
  const pp = {
4540
5146
  2048: {
@@ -4552,9 +5158,11 @@ class TFHECrs {
4552
5158
  }
4553
5159
  }
4554
5160
  */
4555
- toPublicParamsBytes() {
5161
+ toPublicParams2048Bytes() {
4556
5162
  if (this._bits !== 2048) {
4557
- throw new Error(`Unsupported PublicParams bits format ${this._bits}`);
5163
+ throw new TFHECrsError({
5164
+ message: `Unsupported PublicParams bits format '2048'`,
5165
+ });
4558
5166
  }
4559
5167
  const pp = {
4560
5168
  2048: {
@@ -4564,6 +5172,64 @@ class TFHECrs {
4564
5172
  };
4565
5173
  return pp;
4566
5174
  }
5175
+ /*
5176
+ {
5177
+ 2048: {
5178
+ publicParamsId: string,
5179
+ publicParams: BytesHex
5180
+ }
5181
+ }
5182
+ */
5183
+ toPublicParams2048BytesHex() {
5184
+ if (this._bits === undefined) {
5185
+ throw new TFHECrsError({ message: 'Missing PublicParams bits format' });
5186
+ }
5187
+ if (this._bits !== 2048) {
5188
+ throw new TFHECrsError({
5189
+ message: `Unsupported PublicParams bits format '${this._bits}'`,
5190
+ });
5191
+ }
5192
+ const pp = {
5193
+ 2048: {
5194
+ publicParams: this.toBytesHex().data,
5195
+ publicParamsId: this._id,
5196
+ },
5197
+ };
5198
+ return pp;
5199
+ }
5200
+ //////////////////////////////////////////////////////////////////////////////
5201
+ // JSON
5202
+ //////////////////////////////////////////////////////////////////////////////
5203
+ /*
5204
+ {
5205
+ __type: 'TFHECrs',
5206
+ id: string,
5207
+ data: BytesHex,
5208
+ srcUrl?: string
5209
+ }
5210
+ */
5211
+ toJSON() {
5212
+ return {
5213
+ __type: 'TFHECrs',
5214
+ ...this.toBytesHex(),
5215
+ };
5216
+ }
5217
+ static fromJSON(json) {
5218
+ if (json.__type !== 'TFHECrs') {
5219
+ throw new TFHECrsError({ message: 'Invalid TFHECrs JSON.' });
5220
+ }
5221
+ return TFHECrs.fromBytesHex(json);
5222
+ }
5223
+ }
5224
+
5225
+ class TFHEPublicKeyError extends RelayerErrorBase {
5226
+ constructor({ message, cause }) {
5227
+ super({
5228
+ message,
5229
+ name: 'TFHEPublicKeyError',
5230
+ ...(cause ? { cause: ensureError(cause) } : {}),
5231
+ });
5232
+ }
4567
5233
  }
4568
5234
 
4569
5235
  class TFHEPublicKey {
@@ -4575,6 +5241,12 @@ class TFHEPublicKey {
4575
5241
  this._tfheCompactPublicKey = params.tfheCompactPublicKey;
4576
5242
  this._srcUrl = params.srcUrl;
4577
5243
  }
5244
+ get id() {
5245
+ return this._id;
5246
+ }
5247
+ get srcUrl() {
5248
+ return this._srcUrl;
5249
+ }
4578
5250
  /*
4579
5251
  {
4580
5252
  id: string,
@@ -4659,6 +5331,30 @@ class TFHEPublicKey {
4659
5331
  });
4660
5332
  }
4661
5333
  }
5334
+ /*
5335
+ {
5336
+ id: string,
5337
+ data: BytesHex,
5338
+ srcUrl?: string
5339
+ }
5340
+ */
5341
+ static fromBytesHex(params) {
5342
+ let data;
5343
+ try {
5344
+ assertRecordStringProperty(params, 'data', 'arg');
5345
+ data = hexToBytesFaster(params.data, true /* strict */);
5346
+ }
5347
+ catch (e) {
5348
+ throw new Error('Invalid public key (deserialization failed)', {
5349
+ cause: e,
5350
+ });
5351
+ }
5352
+ return TFHEPublicKey.fromBytes({
5353
+ id: params?.id,
5354
+ srcUrl: params?.srcUrl,
5355
+ data,
5356
+ });
5357
+ }
4662
5358
  /*
4663
5359
  {
4664
5360
  id: string,
@@ -4719,6 +5415,20 @@ class TFHEPublicKey {
4719
5415
  ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
4720
5416
  };
4721
5417
  }
5418
+ /*
5419
+ {
5420
+ id: string,
5421
+ data: BytesHex,
5422
+ srcUrl?: string
5423
+ }
5424
+ */
5425
+ toBytesHex() {
5426
+ return {
5427
+ data: bytesToHexLarge(this._tfheCompactPublicKey.safe_serialize(SERIALIZED_SIZE_LIMIT_PK)),
5428
+ id: this._id,
5429
+ ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
5430
+ };
5431
+ }
4722
5432
  /*
4723
5433
  {
4724
5434
  publicKey: TFHE.TfheCompactPublicKey
@@ -4743,6 +5453,75 @@ class TFHEPublicKey {
4743
5453
  publicKeyId: this._id,
4744
5454
  };
4745
5455
  }
5456
+ /*
5457
+ {
5458
+ publicKey: Uint8Array
5459
+ publicKeyId: string
5460
+ }
5461
+ */
5462
+ toPublicKeyBytesHex() {
5463
+ return {
5464
+ publicKey: this.toBytesHex().data,
5465
+ publicKeyId: this._id,
5466
+ };
5467
+ }
5468
+ static _fromPublicKeyBytes(params) {
5469
+ return TFHEPublicKey._fromBytes({
5470
+ data: params.publicKey,
5471
+ id: params.publicKeyId,
5472
+ srcUrl: params.srcUrl,
5473
+ });
5474
+ }
5475
+ static fromPublicKeyBytesHex(params) {
5476
+ try {
5477
+ assertRecordStringProperty(params, 'publicKey', `arg`);
5478
+ assertRecordStringProperty(params, 'publicKeyId', `arg`);
5479
+ return TFHEPublicKey._fromPublicKeyBytes({
5480
+ publicKey: hexToBytesFaster(params.publicKey, true /* strict */),
5481
+ publicKeyId: params.publicKeyId,
5482
+ });
5483
+ }
5484
+ catch (e) {
5485
+ throw new Error('Invalid public key (deserialization failed)', {
5486
+ cause: e,
5487
+ });
5488
+ }
5489
+ }
5490
+ static fromPublicKeyBytes(params) {
5491
+ try {
5492
+ assertUint8ArrayProperty(params, 'publicKey', `arg`);
5493
+ assertRecordStringProperty(params, 'publicKeyId', `arg`);
5494
+ return TFHEPublicKey._fromPublicKeyBytes(params);
5495
+ }
5496
+ catch (e) {
5497
+ throw new Error('Invalid public key (deserialization failed)', {
5498
+ cause: e,
5499
+ });
5500
+ }
5501
+ }
5502
+ //////////////////////////////////////////////////////////////////////////////
5503
+ // JSON
5504
+ //////////////////////////////////////////////////////////////////////////////
5505
+ /*
5506
+ {
5507
+ __type: 'TFHEPublicKey',
5508
+ id: string,
5509
+ data: BytesHex,
5510
+ srcUrl?: string
5511
+ }
5512
+ */
5513
+ toJSON() {
5514
+ return {
5515
+ __type: 'TFHEPublicKey',
5516
+ ...this.toBytesHex(),
5517
+ };
5518
+ }
5519
+ static fromJSON(json) {
5520
+ if (json.__type !== 'TFHEPublicKey') {
5521
+ throw new TFHEPublicKeyError({ message: 'Invalid TFHEPublicKey JSON.' });
5522
+ }
5523
+ return TFHEPublicKey.fromBytesHex(json);
5524
+ }
4746
5525
  }
4747
5526
 
4748
5527
  //const __KEY_URL_CACHE__: Record<string, RelayerV2PublicKey> = {};
@@ -4818,7 +5597,7 @@ class RelayerV2PublicKey {
4818
5597
  toBytes() {
4819
5598
  return {
4820
5599
  publicKey: this._publicKey.toBytes(),
4821
- publicParams: this._crs2048.toPublicParamsBytes(),
5600
+ publicParams: this._crs2048.toPublicParams2048Bytes(),
4822
5601
  };
4823
5602
  }
4824
5603
  }
@@ -4852,23 +5631,46 @@ class RelayerV2Fhevm extends AbstractRelayerFhevm {
4852
5631
  get relayerProvider() {
4853
5632
  return this._relayerProvider;
4854
5633
  }
5634
+ getPublicKeyInfo() {
5635
+ return {
5636
+ id: this._relayerPublicKey.getTFHEPublicKey().id,
5637
+ srcUrl: this._relayerPublicKey.getTFHEPublicKey().srcUrl,
5638
+ };
5639
+ }
4855
5640
  getPublicKeyBytes() {
4856
5641
  return this._relayerPublicKey.getTFHEPublicKey().toPublicKeyBytes();
4857
5642
  }
4858
5643
  getPublicKeyWasm() {
4859
5644
  return this._relayerPublicKey.getTFHEPublicKey().toPublicKeyWasm();
4860
5645
  }
4861
- getPublicParamsBytes(bits) {
5646
+ getPublicParamsBytesForBits(bits) {
5647
+ if (bits === undefined) {
5648
+ throw new TFHECrsError({ message: `Missing PublicParams bits format` });
5649
+ }
4862
5650
  if (bits !== 2048) {
4863
- throw new Error(`Unsupported PublicParams bits format ${bits}`);
5651
+ throw new TFHECrsError({
5652
+ message: `Unsupported PublicParams bits format '${bits}'`,
5653
+ });
4864
5654
  }
4865
- return this._relayerPublicKey.getTFHECrs().toPublicParamsBytes()['2048'];
5655
+ return this._relayerPublicKey.getTFHECrs().toPublicParams2048Bytes()['2048'];
4866
5656
  }
4867
- getPublicParamsWasm(bits) {
5657
+ getPublicParamsWasmForBits(bits) {
5658
+ if (bits === undefined) {
5659
+ throw new TFHECrsError({ message: `Missing PublicParams bits format` });
5660
+ }
4868
5661
  if (bits !== 2048) {
4869
- throw new Error(`Unsupported PublicParams bits format ${bits}`);
5662
+ throw new TFHECrsError({
5663
+ message: `Unsupported PublicParams bits format '${bits}'`,
5664
+ });
4870
5665
  }
4871
- return this._relayerPublicKey.getTFHECrs().toPublicParamsWasm()['2048'];
5666
+ return this._relayerPublicKey.getTFHECrs().toPublicParams2048Wasm()['2048'];
5667
+ }
5668
+ getPublicParamsInfo() {
5669
+ return {
5670
+ id: this._relayerPublicKey.getTFHECrs().id,
5671
+ bits: this._relayerPublicKey.getTFHECrs().bits,
5672
+ srcUrl: this._relayerPublicKey.getTFHECrs().srcUrl,
5673
+ };
4872
5674
  }
4873
5675
  }
4874
5676
 
@@ -4907,24 +5709,42 @@ class RelayerV1Fhevm extends AbstractRelayerFhevm {
4907
5709
  publicKeyId: this._publicKeyData.publicKeyId,
4908
5710
  };
4909
5711
  }
5712
+ getPublicKeyInfo() {
5713
+ return {
5714
+ id: this._publicKeyData.publicKeyId,
5715
+ };
5716
+ }
5717
+ getPublicParamsInfo() {
5718
+ return {
5719
+ id: this._publicParamsData['2048'].publicParamsId,
5720
+ bits: 2048,
5721
+ };
5722
+ }
4910
5723
  getPublicKeyWasm() {
4911
5724
  return {
4912
5725
  publicKey: this._publicKeyData.publicKey,
4913
5726
  publicKeyId: this._publicKeyData.publicKeyId,
4914
5727
  };
4915
5728
  }
4916
- getPublicParamsBytes(bits) {
5729
+ getPublicParamsBytesForBits(bits) {
5730
+ if (bits === undefined) {
5731
+ throw new Error(`Missing PublicParams bits format`);
5732
+ }
4917
5733
  if (bits !== 2048) {
4918
- throw new Error(`Unsupported PublicParams bits format ${bits}`);
5734
+ throw new Error(`Unsupported PublicParams bits format '${bits}'`);
4919
5735
  }
4920
- return {
5736
+ const res = {
4921
5737
  publicParams: this._publicParamsData['2048'].publicParams.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS),
4922
5738
  publicParamsId: this._publicParamsData['2048'].publicParamsId,
4923
5739
  };
5740
+ return res;
4924
5741
  }
4925
- getPublicParamsWasm(bits) {
5742
+ getPublicParamsWasmForBits(bits) {
5743
+ if (bits === undefined) {
5744
+ throw new Error(`Missing PublicParams bits format`);
5745
+ }
4926
5746
  if (bits !== 2048) {
4927
- throw new Error(`Unsupported PublicParams bits format ${bits}`);
5747
+ throw new Error(`Unsupported PublicParams bits format '${bits}'`);
4928
5748
  }
4929
5749
  return {
4930
5750
  publicParams: this._publicParamsData['2048'].publicParams,
@@ -4945,11 +5765,16 @@ async function createRelayerFhevm(config) {
4945
5765
  publicParams: config.publicParams,
4946
5766
  });
4947
5767
  }
4948
- return RelayerV1Fhevm.fromConfig({
4949
- relayerVersionUrl: resolved.url,
4950
- publicKey: config.publicKey,
4951
- publicParams: config.publicParams,
4952
- });
5768
+ else if (resolved.version === 1) {
5769
+ return RelayerV1Fhevm.fromConfig({
5770
+ relayerVersionUrl: resolved.url,
5771
+ publicKey: config.publicKey,
5772
+ publicParams: config.publicParams,
5773
+ });
5774
+ }
5775
+ else {
5776
+ throw new Error(`Invalid relayerUrl: ${config.relayerUrl}`);
5777
+ }
4953
5778
  }
4954
5779
  function _resolveRelayerUrl(value, defaultVersion) {
4955
5780
  if (!value || typeof value !== 'string') {
@@ -5049,7 +5874,28 @@ const createInstance = async (config) => {
5049
5874
  const coprocessorSigners = await getCoprocessorSigners(provider, inputVerifierContractAddress);
5050
5875
  const thresholdCoprocessorSigners = await getCoprocessorSignersThreshold(provider, inputVerifierContractAddress);
5051
5876
  return {
5052
- createEncryptedInput: createRelayerEncryptedInput(aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerFhevm.relayerProvider, relayerFhevm.getPublicKeyWasm().publicKey, { 2048: relayerFhevm.getPublicParamsWasm(2048) }, coprocessorSigners, thresholdCoprocessorSigners, auth && { auth }),
5877
+ createEncryptedInput: createRelayerEncryptedInput(aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerFhevm.relayerProvider, relayerFhevm.getPublicKeyWasm().publicKey, { 2048: relayerFhevm.getPublicParamsWasmForBits(2048) }, coprocessorSigners, thresholdCoprocessorSigners, auth && { auth }),
5878
+ requestZKProofVerification: (zkProof, options) => {
5879
+ if (zkProof.chainId !== chainId ||
5880
+ zkProof.aclContractAddress !== aclContractAddress) {
5881
+ throw new Error('Invalid ZKProof');
5882
+ }
5883
+ return requestCiphertextWithZKProofVerification({
5884
+ ciphertext: zkProof.ciphertextWithZkProof,
5885
+ aclContractAddress: aclContractAddress,
5886
+ contractAddress: zkProof.contractAddress,
5887
+ userAddress: zkProof.userAddress,
5888
+ chainId,
5889
+ gatewayChainId,
5890
+ bits: zkProof.bits,
5891
+ coprocessorSigners,
5892
+ extraData: '0x00',
5893
+ thresholdCoprocessorSigners,
5894
+ relayerProvider: relayerFhevm.relayerProvider,
5895
+ verifyingContractAddressInputVerification: verifyingContractAddressInputVerification,
5896
+ options,
5897
+ });
5898
+ },
5053
5899
  generateKeypair,
5054
5900
  createEIP712: createEIP712(verifyingContractAddressDecryption, chainId),
5055
5901
  publicDecrypt: publicDecryptRequest(kmsSigners, thresholdKMSSigners, gatewayChainId, verifyingContractAddressDecryption, aclContractAddress,
@@ -5059,7 +5905,7 @@ const createInstance = async (config) => {
5059
5905
  //cleanURL(config.relayerUrl),
5060
5906
  relayerFhevm.relayerProvider, provider, auth && { auth }),
5061
5907
  getPublicKey: () => relayerFhevm.getPublicKeyBytes(),
5062
- getPublicParams: (bits) => relayerFhevm.getPublicParamsBytes(bits),
5908
+ getPublicParams: (bits) => relayerFhevm.getPublicParamsBytesForBits(bits),
5063
5909
  // getPublicKey: () =>
5064
5910
  // publicKeyData.publicKey
5065
5911
  // ? {
@@ -5097,7 +5943,7 @@ const createTfheKeypair = () => {
5097
5943
  };
5098
5944
  const createTfhePublicKey = () => {
5099
5945
  const { publicKey } = createTfheKeypair();
5100
- return toHexString(publicKey.serialize());
5946
+ return bytesToHexNo0x(publicKey.serialize());
5101
5947
  };
5102
5948
 
5103
5949
  global.TFHE = TFHEPkg__namespace;