@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.js CHANGED
@@ -8,7 +8,7 @@ const SERIALIZED_SIZE_LIMIT_PK = BigInt(1024 * 1024 * 512);
8
8
  const SERIALIZED_SIZE_LIMIT_CRS = BigInt(1024 * 1024 * 512);
9
9
 
10
10
  // This file is auto-generated
11
- const version = '0.4.0-alpha.1';
11
+ const version = '0.4.0-alpha.2';
12
12
  const sdkName = '@zama-fhe/relayer-sdk';
13
13
 
14
14
  function getErrorCause(e) {
@@ -810,9 +810,9 @@ function assertRecordStringArrayProperty(o, property, objName) {
810
810
  }
811
811
  }
812
812
  }
813
- function safeJSONstringify(o) {
813
+ function safeJSONstringify(o, space) {
814
814
  try {
815
- return JSON.stringify(o, (_, v) => typeof v === 'bigint' ? v.toString() : v);
815
+ return JSON.stringify(o, (_, v) => (typeof v === 'bigint' ? v.toString() : v), space);
816
816
  }
817
817
  catch {
818
818
  return '';
@@ -928,6 +928,17 @@ const NumEncryptedBits = {
928
928
  7: 160, // eaddress
929
929
  8: 256, // euint256
930
930
  };
931
+ // export function getHandleType(handle: `0x${string}`): number {
932
+ // if (handle.length !== 66) {
933
+ // throw new Error(`Handle ${handle} is not of valid length`);
934
+ // }
935
+ // const hexPair = handle.slice(-4, -2).toLowerCase();
936
+ // const typeDiscriminant = parseInt(hexPair, 16);
937
+ // if (!(typeDiscriminant in NumEncryptedBits)) {
938
+ // throw new Error(`Handle ${handle} is not of valid type`);
939
+ // }
940
+ // return typeDiscriminant;
941
+ // }
931
942
  function checkEncryptedBits(handles) {
932
943
  let total = 0;
933
944
  for (const handle of handles) {
@@ -985,7 +996,7 @@ function isBytes(value, bytewidth) {
985
996
  if (!(value instanceof Uint8Array)) {
986
997
  return false;
987
998
  }
988
- return value.length === bytewidth ;
999
+ return bytewidth ? value.length === bytewidth : true;
989
1000
  }
990
1001
  function isBytesHex(value, bytewidth) {
991
1002
  if (!is0x(value)) {
@@ -1218,6 +1229,54 @@ function assertUint8ArrayProperty(o, property, objName) {
1218
1229
  });
1219
1230
  }
1220
1231
  }
1232
+ ////////////////////////////////////////////////////////////////////////////////
1233
+ // Hex
1234
+ ////////////////////////////////////////////////////////////////////////////////
1235
+ const HEX_CHARS = {
1236
+ '0': 0,
1237
+ '1': 1,
1238
+ '2': 2,
1239
+ '3': 3,
1240
+ '4': 4,
1241
+ '5': 5,
1242
+ '6': 6,
1243
+ '7': 7,
1244
+ '8': 8,
1245
+ '9': 9,
1246
+ a: 10,
1247
+ b: 11,
1248
+ c: 12,
1249
+ d: 13,
1250
+ e: 14,
1251
+ f: 15,
1252
+ A: 10,
1253
+ B: 11,
1254
+ C: 12,
1255
+ D: 13,
1256
+ E: 14,
1257
+ F: 15,
1258
+ };
1259
+ Object.freeze(HEX_CHARS);
1260
+ const HEX_BYTES = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
1261
+ Object.freeze(HEX_BYTES);
1262
+ const HEX_CHARS_CODES = new Uint8Array([
1263
+ 48,
1264
+ 49,
1265
+ 50,
1266
+ 51,
1267
+ 52,
1268
+ 53,
1269
+ 54,
1270
+ 55,
1271
+ 56,
1272
+ 57, // '0'-'9'
1273
+ 97,
1274
+ 98,
1275
+ 99,
1276
+ 100,
1277
+ 101,
1278
+ 102, // 'a'-'f'
1279
+ ]);
1221
1280
  /**
1222
1281
  * Convert a Uint8Array to a hex string (without 0x prefix).
1223
1282
  */
@@ -1237,15 +1296,49 @@ function bytesToHexNo0x(bytes) {
1237
1296
  function bytesToHex(bytes) {
1238
1297
  return `0x${bytesToHexNo0x(bytes)}`;
1239
1298
  }
1299
+ function bytesToHexLarge(bytes) {
1300
+ const out = new Uint8Array(2 + bytes.length * 2);
1301
+ out[0] = 48; // '0'
1302
+ out[1] = 120; // 'x'
1303
+ for (let i = 0; i < bytes.length; i++) {
1304
+ const j = 2 + i * 2;
1305
+ out[j] = HEX_CHARS_CODES[bytes[i] >> 4];
1306
+ out[j + 1] = HEX_CHARS_CODES[bytes[i] & 0xf];
1307
+ }
1308
+ return new TextDecoder().decode(out);
1309
+ }
1240
1310
  /**
1241
1311
  * Convert a hex string prefixed by 0x or not to a Uint8Array
1312
+ * Any invalid byte string is converted to 0
1313
+ * "0xzzff" = [0, 255]
1314
+ * "0xzfff" = [0, 255]
1242
1315
  */
1243
1316
  function hexToBytes(hexString) {
1317
+ if (hexString.length % 2 !== 0) {
1318
+ throw new Error('Invalid hex string: odd length');
1319
+ }
1244
1320
  const arr = hexString.replace(/^(0x)/, '').match(/.{1,2}/g);
1245
1321
  if (!arr)
1246
1322
  return new Uint8Array();
1247
1323
  return Uint8Array.from(arr.map((byte) => parseInt(byte, 16)));
1248
1324
  }
1325
+ function hexToBytesFaster(hexString, strict = false) {
1326
+ const offset = hexString[0] === '0' && hexString[1] === 'x' ? 2 : 0;
1327
+ const len = hexString.length - offset;
1328
+ if (len % 2 !== 0) {
1329
+ throw new Error('Invalid hex string: odd length');
1330
+ }
1331
+ const bytes = new Uint8Array(len / 2);
1332
+ for (let i = 0; i < bytes.length; i++) {
1333
+ const hi = HEX_CHARS[hexString[offset + i * 2]];
1334
+ const lo = HEX_CHARS[hexString[offset + i * 2 + 1]];
1335
+ if ((hi === undefined || lo === undefined) && strict) {
1336
+ throw new Error(`Invalid hex character at position ${offset + i * 2}`);
1337
+ }
1338
+ bytes[i] = (hi << 4) | lo;
1339
+ }
1340
+ return bytes;
1341
+ }
1249
1342
  /**
1250
1343
  * Convert a Uint8Array to a bigint
1251
1344
  */
@@ -1259,9 +1352,6 @@ function bytesToBigInt(byteArray) {
1259
1352
  }
1260
1353
  return result;
1261
1354
  }
1262
- function toHexString(bytes, with0x = false) {
1263
- return `${with0x ? '0x' : ''}${bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')}`;
1264
- }
1265
1355
  async function fetchBytes(url) {
1266
1356
  const response = await fetch(url);
1267
1357
  if (!response.ok) {
@@ -1353,8 +1443,8 @@ const userDecryptRequest = (kmsSigners, gatewayChainId, chainId, verifyingContra
1353
1443
  const publicKeySanitized = publicKey.replace(/^(0x)/, '');
1354
1444
  const handleContractPairs = _handles.map((h) => ({
1355
1445
  handle: typeof h.handle === 'string'
1356
- ? toHexString(hexToBytes(h.handle), true)
1357
- : toHexString(h.handle, true),
1446
+ ? bytesToHex(hexToBytes(h.handle))
1447
+ : bytesToHex(h.handle),
1358
1448
  contractAddress: getAddress$1(h.contractAddress),
1359
1449
  }));
1360
1450
  checkEncryptedBits(handleContractPairs.map((h) => h.handle));
@@ -1570,9 +1660,8 @@ const createEncryptedInput = ({ aclContractAddress, chainId, tfheCompactPublicKe
1570
1660
 
1571
1661
  const MAX_UINT64 = BigInt('18446744073709551615'); // 2^64 - 1
1572
1662
  BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
1573
- const MAX_UINT32 = 0xffffffff;
1574
1663
  const MAX_UINT8 = 0xff;
1575
- function numberToHex(num) {
1664
+ function numberToHexNo0x(num) {
1576
1665
  let hex = num.toString(16);
1577
1666
  return hex.length % 2 ? '0' + hex : hex;
1578
1667
  }
@@ -1606,25 +1695,19 @@ function isUint8(value) {
1606
1695
  }
1607
1696
  return value <= MAX_UINT8;
1608
1697
  }
1609
- function isUint32(value) {
1610
- if (!isUint(value)) {
1611
- return false;
1612
- }
1613
- return value <= MAX_UINT32;
1614
- }
1615
1698
  function isUint64(value) {
1616
1699
  if (!isUint(value)) {
1617
1700
  return false;
1618
1701
  }
1619
1702
  return value <= MAX_UINT64;
1620
1703
  }
1621
- function uint32ToBytes32(uint32) {
1622
- if (!isUint32(uint32)) {
1623
- throw new InvalidTypeError({ expectedType: 'Uint32' });
1704
+ function numberToBytes32(num) {
1705
+ if (!isUintNumber(num)) {
1706
+ throw new InvalidTypeError({ expectedType: 'Uint' });
1624
1707
  }
1625
1708
  const buffer = new ArrayBuffer(32);
1626
1709
  const view = new DataView(buffer);
1627
- view.setUint32(28, Number(uint32), false);
1710
+ view.setBigUint64(24, BigInt(num), false);
1628
1711
  return new Uint8Array(buffer);
1629
1712
  }
1630
1713
  function assertIsUint8(value) {
@@ -1722,63 +1805,213 @@ function checksummedAddressToBytes20(address) {
1722
1805
  class FhevmHandleError extends RelayerErrorBase {
1723
1806
  constructor({ handle, message }) {
1724
1807
  super({
1725
- message: message ?? `FHEVM Handle "${handle}" is invalid.`,
1808
+ message: message ??
1809
+ (handle
1810
+ ? `FHEVM Handle "${handle}" is invalid.`
1811
+ : `FHEVM Handle is invalid.`),
1726
1812
  name: 'FhevmHandleError',
1727
1813
  });
1728
1814
  }
1729
1815
  }
1730
1816
 
1817
+ class FheTypeError extends RelayerErrorBase {
1818
+ constructor({ fheTypeId, message, }) {
1819
+ super({
1820
+ message: message ??
1821
+ (fheTypeId
1822
+ ? `FheTypeId "${fheTypeId}" is invalid.`
1823
+ : `FheTypeId is invalid.`),
1824
+ name: 'FheTypeError',
1825
+ });
1826
+ }
1827
+ }
1828
+
1829
+ const FheTypeIdToName = {
1830
+ 0: 'ebool',
1831
+ //1: 'euint4', has been deprecated
1832
+ 2: 'euint8',
1833
+ 3: 'euint16',
1834
+ 4: 'euint32',
1835
+ 5: 'euint64',
1836
+ 6: 'euint128',
1837
+ 7: 'eaddress',
1838
+ 8: 'euint256',
1839
+ };
1840
+ const FheTypeIdToEncryptionBitwidth = {
1841
+ 0: 2,
1842
+ //1:?, euint4 has been deprecated
1843
+ 2: 8,
1844
+ 3: 16,
1845
+ 4: 32,
1846
+ 5: 64,
1847
+ 6: 128,
1848
+ 7: 160,
1849
+ 8: 256,
1850
+ };
1851
+ const EncryptionBitwidthToFheTypeId = {
1852
+ 2: 0,
1853
+ //?:1, euint4 has been deprecated
1854
+ 8: 2,
1855
+ 16: 3,
1856
+ 32: 4,
1857
+ 64: 5,
1858
+ 128: 6,
1859
+ 160: 7,
1860
+ 256: 8,
1861
+ };
1862
+ const FheTypeIdToSolidityPrimitiveTypeName = {
1863
+ 0: 'bool',
1864
+ //1:'uint256', euint4 has been deprecated
1865
+ 2: 'uint256',
1866
+ 3: 'uint256',
1867
+ 4: 'uint256',
1868
+ 5: 'uint256',
1869
+ 6: 'uint256',
1870
+ 7: 'address',
1871
+ 8: 'uint256',
1872
+ };
1873
+ Object.freeze(FheTypeIdToEncryptionBitwidth);
1874
+ Object.freeze(EncryptionBitwidthToFheTypeId);
1875
+ Object.freeze(FheTypeIdToSolidityPrimitiveTypeName);
1876
+ ////////////////////////////////////////////////////////////////////////////////
1877
+ // Type Guards
1878
+ ////////////////////////////////////////////////////////////////////////////////
1879
+ /**
1880
+ * Checks if a value is a valid FheTypeId.
1881
+ * @example isFheTypeId(2) // true (euint8)
1882
+ * @example isFheTypeId(1) // false (euint4 is deprecated)
1883
+ */
1884
+ function isFheTypeId(value) {
1885
+ switch (value) {
1886
+ case 0:
1887
+ // 1: euint4 is deprecated
1888
+ case 2:
1889
+ case 3:
1890
+ case 4:
1891
+ case 5:
1892
+ case 6:
1893
+ case 7:
1894
+ case 8:
1895
+ return true;
1896
+ default:
1897
+ return false;
1898
+ }
1899
+ }
1900
+ /**
1901
+ * Checks if a value is a valid encryption bit width.
1902
+ * @example isEncryptionBits(8) // true
1903
+ * @example isEncryptionBits(4) // false (euint4 is deprecated)
1904
+ */
1905
+ function isEncryptionBits(value) {
1906
+ if (typeof value !== 'number') {
1907
+ return false;
1908
+ }
1909
+ return value in EncryptionBitwidthToFheTypeId;
1910
+ }
1911
+ ////////////////////////////////////////////////////////////////////////////////
1912
+ // FheTypeId extractors
1913
+ ////////////////////////////////////////////////////////////////////////////////
1914
+ /**
1915
+ * Converts an encryption bit width to its corresponding FheTypeId.
1916
+ * @throws {FheTypeError} If bitwidth is not a valid encryption bit width.
1917
+ * @example fheTypeIdFromEncryptionBits(8) // 2 (euint8)
1918
+ */
1919
+ function fheTypeIdFromEncryptionBits(bitwidth) {
1920
+ if (!isEncryptionBits(bitwidth)) {
1921
+ throw new FheTypeError({
1922
+ message: `Invalid encryption bits ${bitwidth}`,
1923
+ });
1924
+ }
1925
+ return EncryptionBitwidthToFheTypeId[bitwidth];
1926
+ }
1927
+ /**
1928
+ * Converts an FheTypeId to its corresponding FheTypeName.
1929
+ * @throws {FheTypeError} If id is not a valid FheTypeId.
1930
+ * @example fheTypeNameFromId(2) // 'euint8'
1931
+ */
1932
+ function fheTypeNameFromId(id) {
1933
+ if (!isFheTypeId(id)) {
1934
+ throw new FheTypeError({
1935
+ message: `Invalid FheType id '${id}'`,
1936
+ });
1937
+ }
1938
+ return FheTypeIdToName[id];
1939
+ }
1940
+ ////////////////////////////////////////////////////////////////////////////////
1941
+ // Solidity primitive type names
1942
+ ////////////////////////////////////////////////////////////////////////////////
1943
+ /**
1944
+ * Returns the Solidity primitive type name for an FheTypeId.
1945
+ * @example solidityPrimitiveTypeNameFromFheTypeId(0) // 'bool'
1946
+ * @example solidityPrimitiveTypeNameFromFheTypeId(7) // 'address'
1947
+ * @example solidityPrimitiveTypeNameFromFheTypeId(2) // 'uint256'
1948
+ */
1949
+ function solidityPrimitiveTypeNameFromFheTypeId(typeId) {
1950
+ if (!isFheTypeId(typeId)) {
1951
+ throw new FheTypeError({
1952
+ message: `Invalid FheType id '${typeId}'`,
1953
+ });
1954
+ }
1955
+ return FheTypeIdToSolidityPrimitiveTypeName[typeId];
1956
+ }
1957
+ ////////////////////////////////////////////////////////////////////////////////
1958
+ // Encryption Bits
1959
+ ////////////////////////////////////////////////////////////////////////////////
1960
+ /**
1961
+ * Returns the encryption bit width for an FheTypeId.
1962
+ * @example encryptionBitsFromFheTypeId(2) // 8 (euint8)
1963
+ * @example encryptionBitsFromFheTypeId(7) // 160 (eaddress)
1964
+ */
1965
+ function encryptionBitsFromFheTypeId(typeId) {
1966
+ if (!isFheTypeId(typeId)) {
1967
+ throw new FheTypeError({
1968
+ message: `Invalid FheType id '${typeId}'`,
1969
+ });
1970
+ }
1971
+ return FheTypeIdToEncryptionBitwidth[typeId];
1972
+ }
1973
+
1731
1974
  ////////////////////////////////////////////////////////////////////////////////
1732
1975
  // FhevmHandle
1733
1976
  ////////////////////////////////////////////////////////////////////////////////
1734
1977
  class FhevmHandle {
1978
+ //////////////////////////////////////////////////////////////////////////////
1979
+ // Instance Properties
1980
+ //////////////////////////////////////////////////////////////////////////////
1735
1981
  _hash21;
1736
1982
  _chainId;
1737
1983
  _fheTypeId;
1738
1984
  _version;
1739
1985
  _computed;
1740
1986
  _index;
1987
+ _handleBytes32Hex;
1988
+ _handleBytes32;
1989
+ //////////////////////////////////////////////////////////////////////////////
1990
+ // Static Constants
1991
+ //////////////////////////////////////////////////////////////////////////////
1741
1992
  static RAW_CT_HASH_DOMAIN_SEPARATOR = 'ZK-w_rct';
1742
1993
  static HANDLE_HASH_DOMAIN_SEPARATOR = 'ZK-w_hdl';
1743
- static FheTypeIdToEncryptionBitwidths = {
1744
- 0: 2,
1745
- 2: 8,
1746
- 3: 16,
1747
- 4: 32,
1748
- 5: 64,
1749
- 6: 128,
1750
- 7: 160,
1751
- 8: 256,
1752
- };
1753
- static FheTypeEncryptionBitwidthsToId = {
1754
- 2: 0,
1755
- 8: 2,
1756
- 16: 3,
1757
- 32: 4,
1758
- 64: 5,
1759
- 128: 6,
1760
- 160: 7,
1761
- 256: 8,
1762
- };
1763
- static FheTypeIdToSolidityPrimitiveType = {
1764
- 0: 'bool',
1765
- 2: 'uint256',
1766
- 3: 'uint256',
1767
- 4: 'uint256',
1768
- 5: 'uint256',
1769
- 6: 'uint256',
1770
- 7: 'address',
1771
- 8: 'uint256',
1772
- };
1773
- static {
1774
- Object.freeze(FhevmHandle.FheTypeIdToEncryptionBitwidths);
1775
- Object.freeze(FhevmHandle.FheTypeEncryptionBitwidthsToId);
1776
- }
1777
- constructor(hash21, chainId, fheTypeId, version, computed, index) {
1994
+ static CURRENT_CIPHERTEXT_VERSION = 0;
1995
+ //////////////////////////////////////////////////////////////////////////////
1996
+ // Constructor
1997
+ //////////////////////////////////////////////////////////////////////////////
1998
+ constructor({ hash21, chainId, fheTypeId, version, computed, index, handleBytes32, handleBytes32Hex, }) {
1999
+ if (!isUintNumber(chainId)) {
2000
+ throw new FhevmHandleError({
2001
+ message: 'ChainId must be a positive integer',
2002
+ });
2003
+ }
1778
2004
  if (BigInt(chainId) > MAX_UINT64) {
1779
2005
  // fhevm assumes chainID is only taking up to 8 bytes
1780
- throw new Error('ChainId exceeds maximum allowed value (8 bytes)');
2006
+ throw new FhevmHandleError({
2007
+ message: 'ChainId exceeds maximum allowed value (8 bytes)',
2008
+ });
2009
+ }
2010
+ if (!isBytesHex(hash21, 21)) {
2011
+ throw new FhevmHandleError({ message: 'Hash21 should be 21 bytes long' });
1781
2012
  }
2013
+ this._handleBytes32 = handleBytes32;
2014
+ this._handleBytes32Hex = handleBytes32Hex;
1782
2015
  this._hash21 = hash21;
1783
2016
  this._chainId = chainId;
1784
2017
  this._fheTypeId = fheTypeId;
@@ -1788,6 +2021,9 @@ class FhevmHandle {
1788
2021
  this._index = index;
1789
2022
  }
1790
2023
  }
2024
+ //////////////////////////////////////////////////////////////////////////////
2025
+ // Instance Getters
2026
+ //////////////////////////////////////////////////////////////////////////////
1791
2027
  get hash21() {
1792
2028
  return this._hash21;
1793
2029
  }
@@ -1797,6 +2033,9 @@ class FhevmHandle {
1797
2033
  get fheTypeId() {
1798
2034
  return this._fheTypeId;
1799
2035
  }
2036
+ get fheTypeName() {
2037
+ return fheTypeNameFromId(this._fheTypeId);
2038
+ }
1800
2039
  get version() {
1801
2040
  return this._version;
1802
2041
  }
@@ -1806,6 +2045,106 @@ class FhevmHandle {
1806
2045
  get index() {
1807
2046
  return this._index;
1808
2047
  }
2048
+ get encryptedBitwidth() {
2049
+ return encryptionBitsFromFheTypeId(this._fheTypeId);
2050
+ }
2051
+ get solidityPrimitiveTypeName() {
2052
+ return solidityPrimitiveTypeNameFromFheTypeId(this._fheTypeId);
2053
+ }
2054
+ toJSON() {
2055
+ return {
2056
+ handle: this.toBytes32Hex(),
2057
+ fheTypeName: this.fheTypeName,
2058
+ fheTypeId: this.fheTypeId,
2059
+ chainId: this.chainId,
2060
+ index: this.index,
2061
+ computed: this.computed,
2062
+ encryptedBitwidth: this.encryptedBitwidth,
2063
+ version: this.version,
2064
+ solidityPrimitiveTypeName: this.solidityPrimitiveTypeName,
2065
+ hash21: this.hash21,
2066
+ };
2067
+ }
2068
+ //////////////////////////////////////////////////////////////////////////////
2069
+ // Instance Serialization
2070
+ //////////////////////////////////////////////////////////////////////////////
2071
+ toBytes32() {
2072
+ if (this._handleBytes32 === undefined) {
2073
+ assertRelayer((this._index === undefined && this._computed) ||
2074
+ (this._index !== undefined && this._index < 255 && !this._computed));
2075
+ const chainId32Bytes = numberToBytes32(this._chainId);
2076
+ const chainId8Bytes = chainId32Bytes.subarray(24, 32);
2077
+ const handleHash21 = hexToBytes(this._hash21);
2078
+ assertRelayer(handleHash21.length === 21);
2079
+ const handleBytes32AsBytes = new Uint8Array(32);
2080
+ handleBytes32AsBytes.set(handleHash21, 0);
2081
+ handleBytes32AsBytes[21] = this._index === undefined ? 255 : this._index;
2082
+ handleBytes32AsBytes.set(chainId8Bytes, 22);
2083
+ handleBytes32AsBytes[30] = this._fheTypeId;
2084
+ handleBytes32AsBytes[31] = this._version;
2085
+ this._handleBytes32 = handleBytes32AsBytes;
2086
+ }
2087
+ return this._handleBytes32;
2088
+ }
2089
+ toBytes32Hex() {
2090
+ if (this._handleBytes32Hex === undefined) {
2091
+ this._handleBytes32Hex = bytesToHex(this.toBytes32());
2092
+ }
2093
+ return this._handleBytes32Hex;
2094
+ }
2095
+ //////////////////////////////////////////////////////////////////////////////
2096
+ // Static Factory Methods
2097
+ //////////////////////////////////////////////////////////////////////////////
2098
+ static fromComponents(params) {
2099
+ return new FhevmHandle(params);
2100
+ }
2101
+ static fromBytes32(handle) {
2102
+ if (!isBytes32(handle)) {
2103
+ throw new FhevmHandleError({
2104
+ message: `FHEVM Handle is not a valid bytes32 array.`,
2105
+ });
2106
+ }
2107
+ const bytes = handle;
2108
+ // Extract hash21 (bytes 0-20)
2109
+ const hash21 = bytesToHex(bytes.slice(0, 21));
2110
+ // Extract index (byte 21) - 255 means computed
2111
+ const indexByte = bytes[21];
2112
+ const computed = indexByte === 255;
2113
+ const index = computed ? undefined : indexByte;
2114
+ // Extract chainId (bytes 22-29, 8 bytes as big-endian uint64)
2115
+ let chainId = 0;
2116
+ for (let i = 22; i < 30; i++) {
2117
+ chainId = chainId * 256 + bytes[i];
2118
+ }
2119
+ // Extract fheTypeId (byte 30)
2120
+ const fheTypeIdByte = bytes[30];
2121
+ if (!isFheTypeId(fheTypeIdByte)) {
2122
+ throw new FhevmHandleError({
2123
+ handle,
2124
+ message: `FHEVM Handle "${handle}" is invalid. Unknown FheType: ${fheTypeIdByte}`,
2125
+ });
2126
+ }
2127
+ // Extract version (byte 31)
2128
+ const version = bytes[31];
2129
+ return new FhevmHandle({
2130
+ hash21,
2131
+ chainId,
2132
+ fheTypeId: fheTypeIdByte,
2133
+ version,
2134
+ computed,
2135
+ index,
2136
+ handleBytes32: handle,
2137
+ });
2138
+ }
2139
+ static fromBytes32Hex(handle) {
2140
+ if (!isBytes32Hex(handle)) {
2141
+ throw new FhevmHandleError({ handle });
2142
+ }
2143
+ const bytes = hexToBytes(handle);
2144
+ const h = FhevmHandle.fromBytes32(bytes);
2145
+ h._handleBytes32Hex = handle;
2146
+ return h;
2147
+ }
1809
2148
  static fromZKProof(params) {
1810
2149
  assertIsChecksummedAddress(params.aclAddress);
1811
2150
  assertIsUint64(params.chainId);
@@ -1815,7 +2154,7 @@ class FhevmHandle {
1815
2154
  fheTypeIds = params.fheTypeIds;
1816
2155
  }
1817
2156
  else if (params.fheTypeEncryptionBitwidths !== undefined) {
1818
- fheTypeIds = params.fheTypeEncryptionBitwidths.map((w) => FhevmHandle.FheTypeEncryptionBitwidthsToId[w]);
2157
+ fheTypeIds = params.fheTypeEncryptionBitwidths.map((w) => fheTypeIdFromEncryptionBits(w));
1819
2158
  }
1820
2159
  else {
1821
2160
  throw new InternalError({
@@ -1846,22 +2185,69 @@ class FhevmHandle {
1846
2185
  const handles = [];
1847
2186
  for (let i = 0; i < fheTypeIds.length; ++i) {
1848
2187
  const hash21 = FhevmHandle._computeInputHash21(hexToBytes(blobHashBytes32Hex), params.aclAddress, params.chainId, i);
1849
- handles.push(new FhevmHandle(hash21, params.chainId, fheTypeIds[i], params.ciphertextVersion, false, i));
2188
+ handles.push(new FhevmHandle({
2189
+ hash21,
2190
+ chainId: params.chainId,
2191
+ fheTypeId: fheTypeIds[i],
2192
+ version: params.ciphertextVersion,
2193
+ computed: false,
2194
+ index: i,
2195
+ }));
1850
2196
  }
1851
2197
  return handles;
1852
2198
  }
2199
+ //////////////////////////////////////////////////////////////////////////////
2200
+ // Static Parsing
2201
+ //////////////////////////////////////////////////////////////////////////////
2202
+ static parse(handle) {
2203
+ if (isBytes(handle)) {
2204
+ return FhevmHandle.fromBytes32(handle);
2205
+ }
2206
+ return FhevmHandle.fromBytes32Hex(handle);
2207
+ }
2208
+ static canParse(handle) {
2209
+ try {
2210
+ FhevmHandle.parse(handle);
2211
+ return true;
2212
+ }
2213
+ catch {
2214
+ return false;
2215
+ }
2216
+ }
2217
+ //////////////////////////////////////////////////////////////////////////////
2218
+ // Static Assertions
2219
+ //////////////////////////////////////////////////////////////////////////////
2220
+ static assertIsHandleHex(handle) {
2221
+ if (typeof handle !== 'string') {
2222
+ throw new FhevmHandleError({
2223
+ message: 'Invalid bytes32 hexadecimal string',
2224
+ });
2225
+ }
2226
+ if (!FhevmHandle.canParse(handle)) {
2227
+ throw new FhevmHandleError({ handle });
2228
+ }
2229
+ }
2230
+ //////////////////////////////////////////////////////////////////////////////
2231
+ // Static Helpers
2232
+ //////////////////////////////////////////////////////////////////////////////
2233
+ static currentCiphertextVersion() {
2234
+ return FhevmHandle.CURRENT_CIPHERTEXT_VERSION;
2235
+ }
2236
+ //////////////////////////////////////////////////////////////////////////////
2237
+ // Private Helpers
2238
+ //////////////////////////////////////////////////////////////////////////////
1853
2239
  /**
1854
2240
  * blobHashBytes32 = keccak256(ciphertextWithZKProof)
1855
2241
  */
1856
2242
  static _computeInputHash21(blobHashBytes32, aclAddress, chainId, index) {
1857
2243
  /*
1858
2244
  https://github.com/zama-ai/fhevm/blob/8ffbd5906ab3d57af178e049930e3fc065c9d4b3/coprocessor/fhevm-engine/zkproof-worker/src/verifier.rs#L431C7-L431C8
1859
-
2245
+
1860
2246
  handle_hash = Bytes("ZK-w_hdl") + blobHash 32 Bytes + index 1 Byte + aclAddress 20 Bytes + chainId 32 bytes
1861
2247
  ===========================================================================================================
1862
2248
 
1863
2249
  const HANDLE_HASH_DOMAIN_SEPARATOR: [u8; 8] = *b"ZK-w_hdl";
1864
-
2250
+
1865
2251
  let mut handle_hash = Keccak256::new();
1866
2252
  handle_hash.update(HANDLE_HASH_DOMAIN_SEPARATOR);
1867
2253
  handle_hash.update(blob_hash);
@@ -1882,62 +2268,12 @@ class FhevmHandle {
1882
2268
  assertIsUint64(chainId);
1883
2269
  const encryptionIndexByte1 = new Uint8Array([index]);
1884
2270
  const aclContractAddressBytes20 = checksummedAddressToBytes20(aclAddress);
1885
- const chainIdBytes32 = uint32ToBytes32(chainId);
2271
+ const chainIdBytes32 = numberToBytes32(chainId);
1886
2272
  const encoder = new TextEncoder();
1887
2273
  const domainSepBytes = encoder.encode(FhevmHandle.HANDLE_HASH_DOMAIN_SEPARATOR);
1888
- return keccak256(concatBytes(domainSepBytes, blobHashBytes32, encryptionIndexByte1, aclContractAddressBytes20, chainIdBytes32));
1889
- }
1890
- toBytes32() {
1891
- assertRelayer((this._index === undefined && this._computed) ||
1892
- (this._index !== undefined && this._index < 255 && !this._computed));
1893
- const chainId32Bytes = uint32ToBytes32(this._chainId);
1894
- const chainId8Bytes = chainId32Bytes.subarray(24, 32);
1895
- const handleHash = hexToBytes(this._hash21);
1896
- const handleBytes32AsBytes = new Uint8Array(32);
1897
- handleBytes32AsBytes.set(handleHash, 0);
1898
- handleBytes32AsBytes[21] = this._index === undefined ? 255 : this._index;
1899
- handleBytes32AsBytes.set(chainId8Bytes, 22);
1900
- handleBytes32AsBytes[30] = this._fheTypeId;
1901
- handleBytes32AsBytes[31] = this._version;
1902
- return handleBytes32AsBytes;
1903
- }
1904
- toBytes32Hex() {
1905
- return bytesToHex(this.toBytes32());
1906
- }
1907
- static checkHandleHex(handle) {
1908
- if (!isBytes32Hex(handle)) {
1909
- throw new FhevmHandleError({ handle });
1910
- }
1911
- }
1912
- static isFheTypeId(value) {
1913
- switch (value) {
1914
- case 0:
1915
- // 1: euint4 is deprecated
1916
- case 2:
1917
- case 3:
1918
- case 4:
1919
- case 5:
1920
- case 6:
1921
- case 7:
1922
- case 8:
1923
- return true;
1924
- default:
1925
- return false;
1926
- }
1927
- }
1928
- static getFheTypeId(handle) {
1929
- if (!isBytes32Hex(handle)) {
1930
- throw new FhevmHandleError({ handle });
1931
- }
1932
- const hexPair = handle.slice(-4, -2).toLowerCase();
1933
- const typeDiscriminant = parseInt(hexPair, 16);
1934
- if (!FhevmHandle.isFheTypeId(typeDiscriminant)) {
1935
- throw new FhevmHandleError({
1936
- handle,
1937
- message: `FHEVM Handle "${handle}" is invalid. Unknown FheType: ${typeDiscriminant}`,
1938
- });
1939
- }
1940
- return typeDiscriminant;
2274
+ const hashBytes32Hex = keccak256(concatBytes(domainSepBytes, blobHashBytes32, encryptionIndexByte1, aclContractAddressBytes20, chainIdBytes32));
2275
+ // Truncate to 21 bytes (0x + 42 hex chars)
2276
+ return hashBytes32Hex.slice(0, 2 + 2 * 21);
1941
2277
  }
1942
2278
  }
1943
2279
 
@@ -1946,6 +2282,89 @@ const getAddress = (value) => getAddress$2(value);
1946
2282
  const currentCiphertextVersion = () => {
1947
2283
  return 0;
1948
2284
  };
2285
+ async function requestCiphertextWithZKProofVerification({ bits, ciphertext, contractAddress, userAddress, aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerProvider, coprocessorSigners, thresholdCoprocessorSigners, extraData, options, }) {
2286
+ const payload = {
2287
+ contractAddress,
2288
+ userAddress,
2289
+ ciphertextWithInputVerification: bytesToHexNo0x(ciphertext),
2290
+ contractChainId: ('0x' + chainId.toString(16)),
2291
+ extraData,
2292
+ };
2293
+ const json = await relayerProvider.fetchPostInputProof(payload, options);
2294
+ if (!isFhevmRelayerInputProofResponse(json)) {
2295
+ throwRelayerInternalError('INPUT_PROOF', json);
2296
+ }
2297
+ const fhevmHandles = FhevmHandle.fromZKProof({
2298
+ ciphertextWithZKProof: ciphertext,
2299
+ chainId,
2300
+ aclAddress: aclContractAddress,
2301
+ ciphertextVersion: currentCiphertextVersion(),
2302
+ fheTypeEncryptionBitwidths: bits,
2303
+ });
2304
+ const handles = fhevmHandles.map((h) => h.toBytes32());
2305
+ const result = json;
2306
+ // Note that the hex strings returned by the relayer do have have the 0x prefix
2307
+ if (result.handles && result.handles.length > 0) {
2308
+ const responseHandles = result.handles.map(hexToBytes);
2309
+ if (handles.length != responseHandles.length) {
2310
+ throw new Error(`Incorrect Handles list sizes: (expected) ${handles.length} != ${responseHandles.length} (received)`);
2311
+ }
2312
+ for (let index = 0; index < handles.length; index += 1) {
2313
+ let handle = handles[index];
2314
+ let responseHandle = responseHandles[index];
2315
+ let expected = bytesToHexNo0x(handle);
2316
+ let current = bytesToHexNo0x(responseHandle);
2317
+ if (expected !== current) {
2318
+ throw new Error(`Incorrect Handle ${index}: (expected) ${expected} != ${current} (received)`);
2319
+ }
2320
+ }
2321
+ }
2322
+ const signatures = result.signatures;
2323
+ // verify signatures for inputs:
2324
+ const domain = {
2325
+ name: 'InputVerification',
2326
+ version: '1',
2327
+ chainId: gatewayChainId,
2328
+ verifyingContract: verifyingContractAddressInputVerification,
2329
+ };
2330
+ const types = {
2331
+ CiphertextVerification: [
2332
+ { name: 'ctHandles', type: 'bytes32[]' },
2333
+ { name: 'userAddress', type: 'address' },
2334
+ { name: 'contractAddress', type: 'address' },
2335
+ { name: 'contractChainId', type: 'uint256' },
2336
+ { name: 'extraData', type: 'bytes' },
2337
+ ],
2338
+ };
2339
+ const recoveredAddresses = signatures.map((signature) => {
2340
+ const sig = signature.startsWith('0x') ? signature : `0x${signature}`;
2341
+ const recoveredAddress = ethers.verifyTypedData(domain, types, {
2342
+ ctHandles: handles,
2343
+ userAddress,
2344
+ contractAddress,
2345
+ contractChainId: chainId,
2346
+ extraData,
2347
+ }, sig);
2348
+ return recoveredAddress;
2349
+ });
2350
+ const thresholdReached = isThresholdReached$1(coprocessorSigners, recoveredAddresses, thresholdCoprocessorSigners);
2351
+ if (!thresholdReached) {
2352
+ throw Error('Coprocessor signers threshold is not reached');
2353
+ }
2354
+ // inputProof is len(list_handles) + numCoprocessorSigners + list_handles + signatureCoprocessorSigners (1+1+NUM_HANDLES*32+65*numSigners)
2355
+ let inputProof = numberToHexNo0x(handles.length);
2356
+ const numSigners = signatures.length;
2357
+ inputProof += numberToHexNo0x(numSigners);
2358
+ const listHandlesStr = handles.map((i) => bytesToHexNo0x(i));
2359
+ listHandlesStr.map((handle) => (inputProof += handle));
2360
+ signatures.map((signature) => (inputProof += signature.slice(2))); // removes the '0x' prefix from the `signature` string
2361
+ // Append the extra data to the input proof
2362
+ inputProof += extraData.slice(2);
2363
+ return {
2364
+ handles,
2365
+ inputProof: hexToBytes(inputProof),
2366
+ };
2367
+ }
1949
2368
  function isThresholdReached$1(coprocessorSigners, recoveredAddresses, threshold) {
1950
2369
  const addressMap = new Map();
1951
2370
  recoveredAddresses.forEach((address, index) => {
@@ -2029,8 +2448,15 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
2029
2448
  getBits() {
2030
2449
  return input.getBits();
2031
2450
  },
2032
- getCiphertextWithInputVerification() {
2033
- return input.encrypt();
2451
+ generateZKProof() {
2452
+ return {
2453
+ chainId,
2454
+ aclContractAddress: aclContractAddress,
2455
+ userAddress: userAddress,
2456
+ contractAddress: contractAddress,
2457
+ ciphertextWithZkProof: input.encrypt(),
2458
+ bits: input.getBits(),
2459
+ };
2034
2460
  },
2035
2461
  encrypt: async (options) => {
2036
2462
  const extraData = '0x00';
@@ -2039,7 +2465,7 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
2039
2465
  const payload = {
2040
2466
  contractAddress: getAddress(contractAddress),
2041
2467
  userAddress: getAddress(userAddress),
2042
- ciphertextWithInputVerification: toHexString(ciphertext),
2468
+ ciphertextWithInputVerification: bytesToHexNo0x(ciphertext),
2043
2469
  contractChainId: ('0x' + chainId.toString(16)),
2044
2470
  extraData,
2045
2471
  };
@@ -2068,8 +2494,8 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
2068
2494
  for (let index = 0; index < handles.length; index += 1) {
2069
2495
  let handle = handles[index];
2070
2496
  let responseHandle = responseHandles[index];
2071
- let expected = toHexString(handle);
2072
- let current = toHexString(responseHandle);
2497
+ let expected = bytesToHexNo0x(handle);
2498
+ let current = bytesToHexNo0x(responseHandle);
2073
2499
  if (expected !== current) {
2074
2500
  throw new Error(`Incorrect Handle ${index}: (expected) ${expected} != ${current} (received)`);
2075
2501
  }
@@ -2108,10 +2534,10 @@ const createRelayerEncryptedInput = (aclContractAddress, verifyingContractAddres
2108
2534
  throw Error('Coprocessor signers threshold is not reached');
2109
2535
  }
2110
2536
  // inputProof is len(list_handles) + numCoprocessorSigners + list_handles + signatureCoprocessorSigners (1+1+NUM_HANDLES*32+65*numSigners)
2111
- let inputProof = numberToHex(handles.length);
2537
+ let inputProof = numberToHexNo0x(handles.length);
2112
2538
  const numSigners = signatures.length;
2113
- inputProof += numberToHex(numSigners);
2114
- const listHandlesStr = handles.map((i) => toHexString(i));
2539
+ inputProof += numberToHexNo0x(numSigners);
2540
+ const listHandlesStr = handles.map((i) => bytesToHexNo0x(i));
2115
2541
  listHandlesStr.map((handle) => (inputProof += handle));
2116
2542
  signatures.map((signature) => (inputProof += signature.slice(2))); // removes the '0x' prefix from the `signature` string
2117
2543
  // Append the extra data to the input proof
@@ -2164,7 +2590,7 @@ function abiEncodeClearValues(clearValues) {
2164
2590
  const abiValues = [];
2165
2591
  for (let i = 0; i < handlesBytes32Hex.length; ++i) {
2166
2592
  const handle = handlesBytes32Hex[i];
2167
- const handleType = FhevmHandle.getFheTypeId(handle);
2593
+ const handleType = FhevmHandle.parse(handle).fheTypeId;
2168
2594
  let clearTextValue = clearValues[handle];
2169
2595
  if (typeof clearTextValue === 'boolean') {
2170
2596
  clearTextValue = clearTextValue ? '0x01' : '0x00';
@@ -2227,24 +2653,23 @@ function buildDecryptionProof(kmsSignatures, extraData) {
2227
2653
  return decryptionProof;
2228
2654
  }
2229
2655
  function deserializeClearValues(handles, decryptedResult) {
2230
- let typesList = [];
2656
+ let fheTypeIdList = [];
2231
2657
  for (const handle of handles) {
2232
- const typeDiscriminant = FhevmHandle.getFheTypeId(handle);
2233
- assertRelayer(FhevmHandle.isFheTypeId(typeDiscriminant));
2234
- typesList.push(typeDiscriminant);
2658
+ const typeDiscriminant = FhevmHandle.parse(handle).fheTypeId;
2659
+ fheTypeIdList.push(typeDiscriminant);
2235
2660
  }
2236
2661
  const restoredEncoded = '0x' +
2237
2662
  '00'.repeat(32) + // dummy requestID (ignored)
2238
2663
  decryptedResult.slice(2) +
2239
2664
  '00'.repeat(32); // dummy empty bytes[] length (ignored)
2240
- const abiTypes = typesList.map((t) => {
2241
- const abiType = FhevmHandle.FheTypeIdToSolidityPrimitiveType[t]; // all types are valid because this was supposedly checked already inside the `checkEncryptedBits` function
2665
+ const abiTypes = fheTypeIdList.map((t) => {
2666
+ const abiType = solidityPrimitiveTypeNameFromFheTypeId(t); // all types are valid because this was supposedly checked already inside the `checkEncryptedBits` function
2242
2667
  return abiType;
2243
2668
  });
2244
2669
  const coder = new AbiCoder();
2245
2670
  const decoded = coder.decode(['uint256', ...abiTypes, 'bytes[]'], restoredEncoded);
2246
2671
  // strip dummy first/last element
2247
- const rawValues = decoded.slice(1, 1 + typesList.length);
2672
+ const rawValues = decoded.slice(1, 1 + fheTypeIdList.length);
2248
2673
  const results = {};
2249
2674
  handles.forEach((handle, idx) => (results[handle] = rawValues[idx]));
2250
2675
  return results;
@@ -2252,12 +2677,13 @@ function deserializeClearValues(handles, decryptedResult) {
2252
2677
  const publicDecryptRequest = (kmsSigners, thresholdSigners, gatewayChainId, verifyingContractAddress, aclContractAddress, relayerProvider, provider, defaultOptions) => async (_handles, options) => {
2253
2678
  const extraData = '0x00';
2254
2679
  const acl = new ethers.Contract(aclContractAddress, aclABI, provider);
2680
+ // This will be replaced by new sanitize classes
2255
2681
  let handles;
2256
2682
  try {
2257
2683
  handles = await Promise.all(_handles.map(async (_handle) => {
2258
2684
  const handle = typeof _handle === 'string'
2259
- ? toHexString(hexToBytes(_handle), true)
2260
- : toHexString(_handle, true);
2685
+ ? bytesToHex(hexToBytes(_handle))
2686
+ : bytesToHex(_handle);
2261
2687
  const isAllowedForDecryption = await acl.isAllowedForDecryption(handle);
2262
2688
  if (!isAllowedForDecryption) {
2263
2689
  throw new Error(`Handle ${handle} is not allowed for public decryption!`);
@@ -2415,8 +2841,8 @@ const createEIP712 = (verifyingContract, contractsChainId) => (publicKey, contra
2415
2841
  const generateKeypair = () => {
2416
2842
  const keypair = TKMS.ml_kem_pke_keygen();
2417
2843
  return {
2418
- publicKey: toHexString(TKMS.ml_kem_pke_pk_to_u8vec(TKMS.ml_kem_pke_get_pk(keypair))),
2419
- privateKey: toHexString(TKMS.ml_kem_pke_sk_to_u8vec(keypair)),
2844
+ publicKey: bytesToHexNo0x(TKMS.ml_kem_pke_pk_to_u8vec(TKMS.ml_kem_pke_get_pk(keypair))),
2845
+ privateKey: bytesToHexNo0x(TKMS.ml_kem_pke_sk_to_u8vec(keypair)),
2420
2846
  };
2421
2847
  };
2422
2848
 
@@ -3080,7 +3506,28 @@ function assertIsRelayerV2GetResponseQueued(value, name) {
3080
3506
  assertRecordStringProperty(value, 'requestId', name);
3081
3507
  }
3082
3508
 
3509
+ class RelayerV2TimeoutError extends RelayerV2RequestErrorBase {
3510
+ constructor(params) {
3511
+ super({
3512
+ ...params,
3513
+ name: 'RelayerV2TimeoutError',
3514
+ message: `Request timed out after ${params.timeoutMs}ms`,
3515
+ });
3516
+ }
3517
+ }
3518
+
3519
+ class RelayerV2AbortError extends RelayerV2RequestErrorBase {
3520
+ constructor(params) {
3521
+ super({
3522
+ ...params,
3523
+ name: 'RelayerV2AbortError',
3524
+ message: `Request aborted`,
3525
+ });
3526
+ }
3527
+ }
3528
+
3083
3529
  class RelayerV2AsyncRequest {
3530
+ _fetchMethod;
3084
3531
  _jobId;
3085
3532
  _jobIdTimestamp;
3086
3533
  _state;
@@ -3095,17 +3542,18 @@ class RelayerV2AsyncRequest {
3095
3542
  _retryAfterTimeoutID;
3096
3543
  _url;
3097
3544
  _payload;
3098
- _fhevmInstanceOptions;
3545
+ _fhevmAuth;
3099
3546
  _retryAfterTimeoutPromiseFuncReject;
3100
3547
  _onProgress;
3101
- _requestMaxDurationInSecs;
3548
+ _requestMaxDurationInMs;
3102
3549
  _requestStartTimestamp;
3103
3550
  _requestGlobalTimeoutID;
3104
3551
  _throwErrorIfNoRetryAfter;
3105
- static DEFAULT_RETRY_AFTER_SECS = 2;
3106
- static DEFAULT_GLOBAL_REQUEST_TIMEOUT_SECS = 60 * 60;
3107
- static MAX_GET_RETRY = 100;
3108
- static MAX_POST_RETRY = 100;
3552
+ static DEFAULT_RETRY_AFTER_MS = 2500;
3553
+ static MINIMUM_RETRY_AFTER_MS = 1000;
3554
+ static DEFAULT_GLOBAL_REQUEST_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour
3555
+ static MAX_GET_RETRY = 60 * 30; // number of default retries in 1 hour (30 retries/min)
3556
+ static MAX_POST_RETRY = RelayerV2AsyncRequest.MAX_GET_RETRY;
3109
3557
  constructor(params) {
3110
3558
  if (params.relayerOperation !== 'INPUT_PROOF' &&
3111
3559
  params.relayerOperation !== 'PUBLIC_DECRYPT' &&
@@ -3122,14 +3570,14 @@ class RelayerV2AsyncRequest {
3122
3570
  this._internalAbortController = new AbortController();
3123
3571
  this._internalAbortSignal = this._internalAbortController.signal;
3124
3572
  this._internalAbortSignal.addEventListener('abort', this._handleInternalSignalAbort);
3125
- this._externalAbortSignal = params.signal;
3573
+ this._externalAbortSignal = params.options?.signal;
3126
3574
  if (this._externalAbortSignal) {
3127
3575
  this._externalAbortSignal.addEventListener('abort', this._handleExternalSignalAbort);
3128
3576
  }
3129
3577
  this._url = params.url;
3130
3578
  this._payload = params.payload;
3131
- this._fhevmInstanceOptions = params.instanceOptions;
3132
- this._onProgress = params.onProgress;
3579
+ this._fhevmAuth = params.options?.auth;
3580
+ this._onProgress = params.options?.onProgress;
3133
3581
  this._state = {
3134
3582
  aborted: false,
3135
3583
  canceled: false,
@@ -3138,6 +3586,7 @@ class RelayerV2AsyncRequest {
3138
3586
  running: false,
3139
3587
  succeeded: false,
3140
3588
  terminated: false,
3589
+ timeout: false,
3141
3590
  };
3142
3591
  this._retryCount = 0;
3143
3592
  this._retryAfterTimeoutID = undefined;
@@ -3145,9 +3594,9 @@ class RelayerV2AsyncRequest {
3145
3594
  this._terminateReason = undefined;
3146
3595
  this._publicAPINoReentrancy = false;
3147
3596
  this._throwErrorIfNoRetryAfter = params.throwErrorIfNoRetryAfter ?? false;
3148
- this._requestMaxDurationInSecs =
3149
- params.timeoutInSeconds ??
3150
- RelayerV2AsyncRequest.DEFAULT_GLOBAL_REQUEST_TIMEOUT_SECS;
3597
+ this._requestMaxDurationInMs =
3598
+ params.options?.timeout ??
3599
+ RelayerV2AsyncRequest.DEFAULT_GLOBAL_REQUEST_TIMEOUT_MS;
3151
3600
  }
3152
3601
  //////////////////////////////////////////////////////////////////////////////
3153
3602
  // Public API: run
@@ -3189,6 +3638,12 @@ class RelayerV2AsyncRequest {
3189
3638
  state: { ...this._state },
3190
3639
  });
3191
3640
  }
3641
+ if (this._state.timeout) {
3642
+ throw new RelayerV2StateError({
3643
+ message: `Relayer.run() failed. Request already timeout.`,
3644
+ state: { ...this._state },
3645
+ });
3646
+ }
3192
3647
  if (this._externalAbortSignal?.aborted === true) {
3193
3648
  throw new RelayerV2StateError({
3194
3649
  message: `Relayer.run() failed. External AbortSignal already aborted (reason:${this._externalAbortSignal?.reason}).`,
@@ -3209,7 +3664,7 @@ class RelayerV2AsyncRequest {
3209
3664
  }
3210
3665
  this._state.running = true;
3211
3666
  this._requestStartTimestamp = Date.now();
3212
- this._setGlobalRequestTimeout(this._requestMaxDurationInSecs * 1000);
3667
+ this._setGlobalRequestTimeout(this._requestMaxDurationInMs);
3213
3668
  try {
3214
3669
  const json = await this._runPostLoop();
3215
3670
  this._state.succeeded = true;
@@ -3280,6 +3735,12 @@ class RelayerV2AsyncRequest {
3280
3735
  get failed() {
3281
3736
  return this._state.failed;
3282
3737
  }
3738
+ get aborted() {
3739
+ return this._state.aborted;
3740
+ }
3741
+ get timeout() {
3742
+ return this._state.timeout;
3743
+ }
3283
3744
  get succeeded() {
3284
3745
  return this._state.succeeded;
3285
3746
  }
@@ -3300,6 +3761,8 @@ class RelayerV2AsyncRequest {
3300
3761
  //////////////////////////////////////////////////////////////////////////////
3301
3762
  // POST : 202 | 400 | 429 | 500 | 503
3302
3763
  async _runPostLoop() {
3764
+ this._assert(this._fetchMethod === undefined, 'this._fetchMethod === undefined');
3765
+ this._fetchMethod = 'POST';
3303
3766
  // No infinite loop!
3304
3767
  let i = 0;
3305
3768
  while (i < RelayerV2AsyncRequest.MAX_POST_RETRY) {
@@ -3335,9 +3798,7 @@ class RelayerV2AsyncRequest {
3335
3798
  bodyJson: safeJSONstringify(bodyJson),
3336
3799
  });
3337
3800
  }
3338
- let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
3339
- if (retry_after_sec < 1)
3340
- retry_after_sec = 1;
3801
+ const retryAfterMs = this._getRetryAfterHeaderValueInMs(response);
3341
3802
  // Debug: will throw an assert failed error if jobId has already been set
3342
3803
  this._setJobIdOnce(bodyJson.result.jobId);
3343
3804
  // Async onProgress callback
@@ -3350,11 +3811,10 @@ class RelayerV2AsyncRequest {
3350
3811
  jobId: this.jobId,
3351
3812
  operation: this._relayerOperation,
3352
3813
  retryCount: this._retryCount,
3353
- retryAfter: retry_after_sec,
3814
+ retryAfterMs,
3354
3815
  elapsed,
3355
3816
  });
3356
- // Wait if needed (minimum 1s)
3357
- await this._setRetryAfterTimeout(retry_after_sec * 1000);
3817
+ await this._setRetryAfterTimeout(retryAfterMs);
3358
3818
  const json = await this._runGetLoop();
3359
3819
  return json;
3360
3820
  }
@@ -3400,22 +3860,20 @@ class RelayerV2AsyncRequest {
3400
3860
  bodyJson: safeJSONstringify(bodyJson),
3401
3861
  });
3402
3862
  }
3403
- let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
3404
- if (retry_after_sec < 1)
3405
- retry_after_sec = 1;
3863
+ const retryAfterMs = this._getRetryAfterHeaderValueInMs(response);
3406
3864
  // Async onProgress callback
3407
3865
  this._postAsyncOnProgressCallback({
3408
3866
  type: 'ratelimited',
3409
3867
  url: this._url,
3410
3868
  method: 'POST',
3411
3869
  status: responseStatus,
3412
- retryAfter: retry_after_sec,
3870
+ retryAfterMs,
3413
3871
  retryCount: this._retryCount,
3414
3872
  elapsed,
3415
- message: bodyJson.error.message,
3873
+ relayerApiError: bodyJson.error,
3416
3874
  });
3417
3875
  // Wait if needed (minimum 1s)
3418
- await this._setRetryAfterTimeout(retry_after_sec * 1000);
3876
+ await this._setRetryAfterTimeout(retryAfterMs);
3419
3877
  continue;
3420
3878
  }
3421
3879
  // RelayerV2ResponseFailed
@@ -3494,8 +3952,10 @@ class RelayerV2AsyncRequest {
3494
3952
  // GET: 200 | 202 | 404 | 500 | 503 | 504
3495
3953
  // GET is not rate-limited, therefore there is not 429 error
3496
3954
  async _runGetLoop() {
3955
+ this._assert(this._fetchMethod === 'POST', "this._fetchMethod === 'POST'");
3497
3956
  this._assert(this._jobId !== undefined, 'this._jobId !== undefined');
3498
3957
  this._assert(this._jobIdTimestamp !== undefined, 'this._jobIdTimestamp !== undefined');
3958
+ this._fetchMethod = 'GET';
3499
3959
  let i = 0;
3500
3960
  while (i < RelayerV2AsyncRequest.MAX_GET_RETRY) {
3501
3961
  ++i;
@@ -3605,9 +4065,7 @@ class RelayerV2AsyncRequest {
3605
4065
  bodyJson: safeJSONstringify(bodyJson),
3606
4066
  });
3607
4067
  }
3608
- let retry_after_sec = this._getRetryAfterHeaderValueInSecs(response);
3609
- if (retry_after_sec < 1)
3610
- retry_after_sec = 1;
4068
+ const retryAfterMs = this._getRetryAfterHeaderValueInMs(response);
3611
4069
  // Async onProgress callback
3612
4070
  this._postAsyncOnProgressCallback({
3613
4071
  type: 'queued',
@@ -3617,12 +4075,12 @@ class RelayerV2AsyncRequest {
3617
4075
  requestId: bodyJson.requestId,
3618
4076
  operation: this._relayerOperation,
3619
4077
  jobId: this.jobId,
3620
- retryAfter: retry_after_sec,
4078
+ retryAfterMs,
3621
4079
  retryCount: this._retryCount,
3622
4080
  elapsed,
3623
4081
  });
3624
4082
  // Wait if needed (minimum 1s)
3625
- await this._setRetryAfterTimeout(retry_after_sec * 1000);
4083
+ await this._setRetryAfterTimeout(retryAfterMs);
3626
4084
  continue;
3627
4085
  }
3628
4086
  case 400: {
@@ -3774,17 +4232,20 @@ class RelayerV2AsyncRequest {
3774
4232
  return bodyJson;
3775
4233
  }
3776
4234
  //////////////////////////////////////////////////////////////////////////////
3777
- _getRetryAfterHeaderValueInSecs(response) {
4235
+ _getRetryAfterHeaderValueInMs(response) {
3778
4236
  if (!response.headers.has('Retry-After')) {
3779
4237
  if (this._throwErrorIfNoRetryAfter) {
3780
4238
  throw new Error(`Missing 'Retry-After' header key`);
3781
4239
  }
3782
- return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_SECS;
4240
+ return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_MS;
3783
4241
  }
3784
4242
  try {
3785
4243
  const n = Number.parseInt(response.headers.get('Retry-After'));
3786
4244
  if (isUint(n)) {
3787
- return n;
4245
+ const ms = n * 1000;
4246
+ return ms < RelayerV2AsyncRequest.MINIMUM_RETRY_AFTER_MS
4247
+ ? RelayerV2AsyncRequest.MINIMUM_RETRY_AFTER_MS
4248
+ : ms;
3788
4249
  }
3789
4250
  }
3790
4251
  catch {
@@ -3793,7 +4254,7 @@ class RelayerV2AsyncRequest {
3793
4254
  if (this._throwErrorIfNoRetryAfter) {
3794
4255
  throw new Error(`Invalid 'Retry-After' header key`);
3795
4256
  }
3796
- return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_SECS;
4257
+ return RelayerV2AsyncRequest.DEFAULT_RETRY_AFTER_MS;
3797
4258
  }
3798
4259
  //////////////////////////////////////////////////////////////////////////////
3799
4260
  // JobId
@@ -3832,7 +4293,7 @@ class RelayerV2AsyncRequest {
3832
4293
  this._assert(this._jobId === undefined, 'this._jobId === undefined');
3833
4294
  this._assert(!this._state.terminated, '!this._state.terminated');
3834
4295
  this._assert(!this._state.fetching, '!this._state.fetching');
3835
- this._trace('_fetchPost', 'enter');
4296
+ this._trace('_fetchPost', this._url);
3836
4297
  const init = setAuth({
3837
4298
  method: 'POST',
3838
4299
  headers: {
@@ -3844,7 +4305,7 @@ class RelayerV2AsyncRequest {
3844
4305
  ...(this._internalAbortSignal
3845
4306
  ? { signal: this._internalAbortSignal }
3846
4307
  : {}),
3847
- }, this._fhevmInstanceOptions?.auth);
4308
+ }, this._fhevmAuth);
3848
4309
  this._state.fetching = true;
3849
4310
  let response;
3850
4311
  try {
@@ -3971,7 +4432,18 @@ class RelayerV2AsyncRequest {
3971
4432
  if (signal.reason !== 'cancel') {
3972
4433
  this._assert(!this._state.canceled, '!this._state.canceled');
3973
4434
  }
3974
- this._terminate('abort');
4435
+ this._postAsyncOnProgressCallback({
4436
+ type: 'abort',
4437
+ url: this._url,
4438
+ ...(this._jobId ? { jobId: this._jobId } : {}),
4439
+ operation: this._relayerOperation,
4440
+ retryCount: this._retryCount,
4441
+ });
4442
+ this._terminate('abort', new RelayerV2AbortError({
4443
+ operation: this._relayerOperation,
4444
+ jobId: this._jobId,
4445
+ url: this._url,
4446
+ }));
3975
4447
  };
3976
4448
  //////////////////////////////////////////////////////////////////////////////
3977
4449
  // Terminate
@@ -3995,7 +4467,7 @@ class RelayerV2AsyncRequest {
3995
4467
  this._terminateReason = reason;
3996
4468
  this._terminateError = error;
3997
4469
  this._state.terminated = true;
3998
- this._tryClearRetryAfterTimeout();
4470
+ this._tryClearRetryAfterTimeout(error);
3999
4471
  this._tryClearGlobalRequestTimeout();
4000
4472
  const is = this._internalAbortSignal;
4001
4473
  const es = this._externalAbortSignal;
@@ -4017,7 +4489,7 @@ class RelayerV2AsyncRequest {
4017
4489
  // Debug
4018
4490
  this._assert(!this._state.terminated, '!this._state.terminated');
4019
4491
  this._assert(this._retryAfterTimeoutID === undefined, 'this._retryAfterTimeoutID === undefined');
4020
- this._assert(delayMs >= 1000, 'delayMs >= 1000');
4492
+ this._assert(delayMs >= RelayerV2AsyncRequest.MINIMUM_RETRY_AFTER_MS, `delayMs >= ${RelayerV2AsyncRequest.MINIMUM_RETRY_AFTER_MS}`);
4021
4493
  this._trace('_setRetryAfterTimeout', `delayMs=${delayMs}`);
4022
4494
  if (this._retryAfterTimeoutID !== undefined) {
4023
4495
  return Promise.reject(new Error(`retry-after already running.`));
@@ -4037,7 +4509,7 @@ class RelayerV2AsyncRequest {
4037
4509
  return p;
4038
4510
  }
4039
4511
  //////////////////////////////////////////////////////////////////////////////
4040
- _tryClearRetryAfterTimeout() {
4512
+ _tryClearRetryAfterTimeout(error) {
4041
4513
  if (this._retryAfterTimeoutID === undefined) {
4042
4514
  // Debug
4043
4515
  this._assert(this._retryAfterTimeoutPromiseFuncReject === undefined, 'this._retryAfterTimeoutPromiseFuncReject === undefined');
@@ -4048,7 +4520,8 @@ class RelayerV2AsyncRequest {
4048
4520
  this._retryAfterTimeoutID = undefined;
4049
4521
  this._retryAfterTimeoutPromiseFuncReject = undefined;
4050
4522
  clearTimeout(tid);
4051
- reject(new Error('_tryClearRetryAfterTimeout'));
4523
+ // Calling reject will
4524
+ reject(error ?? new Error('_tryClearRetryAfterTimeout'));
4052
4525
  }
4053
4526
  //////////////////////////////////////////////////////////////////////////////
4054
4527
  // Global Request Timeout
@@ -4063,7 +4536,24 @@ class RelayerV2AsyncRequest {
4063
4536
  this._requestGlobalTimeoutID = setTimeout(callback, delayMs);
4064
4537
  }
4065
4538
  _handleGlobalRequestTimeout() {
4066
- this._terminate('timeout');
4539
+ this._state.timeout = true;
4540
+ // Debug state-check guards:
4541
+ this._assert(this instanceof RelayerV2AsyncRequest, `this instanceof RelayerV2AsyncRequest`);
4542
+ this._assert(!this._state.terminated, `!this._state.terminated`);
4543
+ this._assert(!this._state.timeout, '!this._state.timeout');
4544
+ this._postAsyncOnProgressCallback({
4545
+ type: 'timeout',
4546
+ url: this._url,
4547
+ ...(this._jobId ? { jobId: this._jobId } : {}),
4548
+ operation: this._relayerOperation,
4549
+ retryCount: this._retryCount,
4550
+ });
4551
+ this._terminate('timeout', new RelayerV2TimeoutError({
4552
+ operation: this._relayerOperation,
4553
+ jobId: this._jobId,
4554
+ url: this._url,
4555
+ timeoutMs: this._requestMaxDurationInMs,
4556
+ }));
4067
4557
  }
4068
4558
  _tryClearGlobalRequestTimeout() {
4069
4559
  if (this._requestGlobalTimeoutID === undefined) {
@@ -4271,37 +4761,34 @@ class RelayerV2Provider extends AbstractRelayerProvider {
4271
4761
  const response = await this.fetchGetKeyUrlV2();
4272
4762
  return toRelayerV1KeyUrlResponse(response);
4273
4763
  }
4274
- async fetchPostInputProof(payload, instanceOptions, fetchOptions) {
4764
+ async fetchPostInputProof(payload, options) {
4275
4765
  const request = new RelayerV2AsyncRequest({
4276
4766
  relayerOperation: 'INPUT_PROOF',
4277
4767
  url: this.inputProof,
4278
4768
  payload,
4279
- instanceOptions,
4280
- ...fetchOptions,
4769
+ options,
4281
4770
  });
4282
4771
  const result = (await request.run());
4283
4772
  assertIsRelayerInputProofResult(result, 'fetchPostInputProof()');
4284
4773
  return result;
4285
4774
  }
4286
- async fetchPostPublicDecrypt(payload, instanceOptions, fetchOptions) {
4775
+ async fetchPostPublicDecrypt(payload, options) {
4287
4776
  const request = new RelayerV2AsyncRequest({
4288
4777
  relayerOperation: 'PUBLIC_DECRYPT',
4289
4778
  url: this.publicDecrypt,
4290
4779
  payload,
4291
- instanceOptions,
4292
- ...fetchOptions,
4780
+ options,
4293
4781
  });
4294
4782
  const result = await request.run();
4295
4783
  assertIsRelayerPublicDecryptResult(result, 'fetchPostPublicDecrypt()');
4296
4784
  return result;
4297
4785
  }
4298
- async fetchPostUserDecrypt(payload, instanceOptions, fetchOptions) {
4786
+ async fetchPostUserDecrypt(payload, options) {
4299
4787
  const request = new RelayerV2AsyncRequest({
4300
4788
  relayerOperation: 'USER_DECRYPT',
4301
4789
  url: this.userDecrypt,
4302
4790
  payload,
4303
- instanceOptions,
4304
- ...fetchOptions,
4791
+ options,
4305
4792
  });
4306
4793
  const result = (await request.run());
4307
4794
  assertIsRelayerUserDecryptResult(result.result, 'fetchPostUserDecrypt()');
@@ -4309,6 +4796,16 @@ class RelayerV2Provider extends AbstractRelayerProvider {
4309
4796
  }
4310
4797
  }
4311
4798
 
4799
+ class TFHECrsError extends RelayerErrorBase {
4800
+ constructor({ message, cause }) {
4801
+ super({
4802
+ message,
4803
+ name: 'TFHECrsError',
4804
+ ...(cause ? { cause: ensureError(cause) } : {}),
4805
+ });
4806
+ }
4807
+ }
4808
+
4312
4809
  class AbstractRelayerFhevm {
4313
4810
  }
4314
4811
 
@@ -4323,6 +4820,15 @@ class TFHECrs {
4323
4820
  this._bits = params.bits;
4324
4821
  this._srcUrl = params.srcUrl;
4325
4822
  }
4823
+ get id() {
4824
+ return this._id;
4825
+ }
4826
+ get bits() {
4827
+ return this._bits;
4828
+ }
4829
+ get srcUrl() {
4830
+ return this._srcUrl;
4831
+ }
4326
4832
  /*
4327
4833
  {
4328
4834
  id: string,
@@ -4425,19 +4931,57 @@ class TFHECrs {
4425
4931
  return TFHECrs._fromUrl(params);
4426
4932
  }
4427
4933
  else {
4428
- throw new Error('Invalid public key (deserialization failed)');
4934
+ throw new TFHECrsError({
4935
+ message: 'Invalid public key (deserialization failed)',
4936
+ });
4429
4937
  }
4430
4938
  }
4939
+ /*
4940
+ {
4941
+ id: string;
4942
+ data: Uint8Array;
4943
+ bits: number;
4944
+ srcUrl?: string;
4945
+ }
4946
+ */
4431
4947
  static fromBytes(params) {
4432
4948
  try {
4433
4949
  TFHECrs.assertKeyBytesType(params, 'arg');
4434
4950
  return TFHECrs._fromBytes(params);
4435
4951
  }
4436
4952
  catch (e) {
4437
- throw new Error('Invalid public key (deserialization failed)', {
4953
+ throw new TFHECrsError({
4954
+ message: 'Invalid public key (deserialization failed)',
4955
+ cause: e,
4956
+ });
4957
+ }
4958
+ }
4959
+ /*
4960
+ {
4961
+ id: string;
4962
+ data: BytesHex;
4963
+ bits: number;
4964
+ srcUrl?: string;
4965
+ }
4966
+ */
4967
+ static fromBytesHex(params) {
4968
+ let data;
4969
+ try {
4970
+ assertRecordStringProperty(params, 'data', 'arg');
4971
+ data = hexToBytesFaster(params.data, true /* strict */);
4972
+ }
4973
+ catch (e) {
4974
+ throw new TFHECrsError({
4975
+ message: 'Invalid public key (deserialization failed)',
4438
4976
  cause: e,
4439
4977
  });
4440
4978
  }
4979
+ return TFHECrs.fromBytes({
4980
+ id: params?.id,
4981
+ bits: params?.bits,
4982
+ srcUrl: params?.srcUrl,
4983
+ data,
4984
+ });
4441
4985
  }
4442
4986
  static _fromBytes(params) {
4443
4987
  const _params = {
@@ -4454,7 +4998,50 @@ class TFHECrs {
4454
4998
  return TFHECrs._fromPublicParamsBytes(params);
4455
4999
  }
4456
5000
  catch (e) {
4457
- throw new Error('Invalid public key (deserialization failed)', {
5001
+ throw new TFHECrsError({
5002
+ message: 'Invalid public key (deserialization failed)',
5003
+ cause: e,
5004
+ });
5005
+ }
5006
+ }
5007
+ static fromBitsPublicParamsBytes(bits, params) {
5008
+ if (bits === undefined) {
5009
+ throw new TFHECrsError({ message: 'Missing PublicParams bits format' });
5010
+ }
5011
+ if (bits !== 2048) {
5012
+ throw new TFHECrsError({
5013
+ message: `Unsupported PublicParams bits format '${bits}'`,
5014
+ });
5015
+ }
5016
+ try {
5017
+ assertRecordStringProperty(params, 'publicParamsId', `arg`);
5018
+ assertUint8ArrayProperty(params, 'publicParams', `arg`);
5019
+ return TFHECrs._fromPublicParamsBytes({
5020
+ 2048: params,
5021
+ });
5022
+ }
5023
+ catch (e) {
5024
+ throw new TFHECrsError({
5025
+ message: 'Invalid public key (deserialization failed)',
5026
+ cause: e,
5027
+ });
5028
+ }
5029
+ }
5030
+ static fromPublicParamsBytesHex(params) {
5031
+ try {
5032
+ assertNonNullableRecordProperty(params, '2048', 'arg');
5033
+ assertRecordStringProperty(params['2048'], 'publicParamsId', `arg.2048`);
5034
+ assertRecordStringProperty(params['2048'], 'publicParams', `arg.2048`);
5035
+ return TFHECrs._fromPublicParamsBytes({
5036
+ 2048: {
5037
+ publicParams: hexToBytesFaster(params['2048'].publicParams, true /* strict */),
5038
+ publicParamsId: params['2048'].publicParamsId,
5039
+ },
5040
+ });
5041
+ }
5042
+ catch (e) {
5043
+ throw new TFHECrsError({
5044
+ message: 'Invalid public key (deserialization failed)',
4458
5045
  cause: e,
4459
5046
  });
4460
5047
  }
@@ -4472,7 +5059,8 @@ class TFHECrs {
4472
5059
  return TFHECrs._fromUrl(params);
4473
5060
  }
4474
5061
  catch (e) {
4475
- throw new Error('Impossible to fetch public key: wrong relayer url.', {
5062
+ throw new TFHECrsError({
5063
+ message: 'Impossible to fetch public key: wrong relayer url.',
4476
5064
  cause: e,
4477
5065
  });
4478
5066
  }
@@ -4503,6 +5091,22 @@ class TFHECrs {
4503
5091
  ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
4504
5092
  };
4505
5093
  }
5094
+ /*
5095
+ {
5096
+ id: string,
5097
+ bits: number,
5098
+ data: BytesHex,
5099
+ srcUrl?: string
5100
+ }
5101
+ */
5102
+ toBytesHex() {
5103
+ return {
5104
+ data: bytesToHexLarge(this._compactPkeCrs.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS)),
5105
+ id: this._id,
5106
+ bits: this._bits,
5107
+ ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
5108
+ };
5109
+ }
4506
5110
  /*
4507
5111
  {
4508
5112
  2048: {
@@ -4511,9 +5115,11 @@ class TFHECrs {
4511
5115
  }
4512
5116
  }
4513
5117
  */
4514
- toPublicParamsWasm() {
5118
+ toPublicParams2048Wasm() {
4515
5119
  if (this._bits !== 2048) {
4516
- throw new Error(`Unsupported PublicParams bits format ${this._bits}`);
5120
+ throw new TFHECrsError({
5121
+ message: `Unsupported PublicParams bits format '2048'`,
5122
+ });
4517
5123
  }
4518
5124
  const pp = {
4519
5125
  2048: {
@@ -4531,9 +5137,11 @@ class TFHECrs {
4531
5137
  }
4532
5138
  }
4533
5139
  */
4534
- toPublicParamsBytes() {
5140
+ toPublicParams2048Bytes() {
4535
5141
  if (this._bits !== 2048) {
4536
- throw new Error(`Unsupported PublicParams bits format ${this._bits}`);
5142
+ throw new TFHECrsError({
5143
+ message: `Unsupported PublicParams bits format '2048'`,
5144
+ });
4537
5145
  }
4538
5146
  const pp = {
4539
5147
  2048: {
@@ -4543,6 +5151,64 @@ class TFHECrs {
4543
5151
  };
4544
5152
  return pp;
4545
5153
  }
5154
+ /*
5155
+ {
5156
+ 2048: {
5157
+ publicParamsId: string,
5158
+ publicParams: BytesHex
5159
+ }
5160
+ }
5161
+ */
5162
+ toPublicParams2048BytesHex() {
5163
+ if (this._bits === undefined) {
5164
+ throw new TFHECrsError({ message: 'Missing PublicParams bits format' });
5165
+ }
5166
+ if (this._bits !== 2048) {
5167
+ throw new TFHECrsError({
5168
+ message: `Unsupported PublicParams bits format '${this._bits}'`,
5169
+ });
5170
+ }
5171
+ const pp = {
5172
+ 2048: {
5173
+ publicParams: this.toBytesHex().data,
5174
+ publicParamsId: this._id,
5175
+ },
5176
+ };
5177
+ return pp;
5178
+ }
5179
+ //////////////////////////////////////////////////////////////////////////////
5180
+ // JSON
5181
+ //////////////////////////////////////////////////////////////////////////////
5182
+ /*
5183
+ {
5184
+ __type: 'TFHECrs',
5185
+ id: string,
5186
+ data: BytesHex,
5187
+ srcUrl?: string
5188
+ }
5189
+ */
5190
+ toJSON() {
5191
+ return {
5192
+ __type: 'TFHECrs',
5193
+ ...this.toBytesHex(),
5194
+ };
5195
+ }
5196
+ static fromJSON(json) {
5197
+ if (json.__type !== 'TFHECrs') {
5198
+ throw new TFHECrsError({ message: 'Invalid TFHECrs JSON.' });
5199
+ }
5200
+ return TFHECrs.fromBytesHex(json);
5201
+ }
5202
+ }
5203
+
5204
+ class TFHEPublicKeyError extends RelayerErrorBase {
5205
+ constructor({ message, cause }) {
5206
+ super({
5207
+ message,
5208
+ name: 'TFHEPublicKeyError',
5209
+ ...(cause ? { cause: ensureError(cause) } : {}),
5210
+ });
5211
+ }
4546
5212
  }
4547
5213
 
4548
5214
  class TFHEPublicKey {
@@ -4554,6 +5220,12 @@ class TFHEPublicKey {
4554
5220
  this._tfheCompactPublicKey = params.tfheCompactPublicKey;
4555
5221
  this._srcUrl = params.srcUrl;
4556
5222
  }
5223
+ get id() {
5224
+ return this._id;
5225
+ }
5226
+ get srcUrl() {
5227
+ return this._srcUrl;
5228
+ }
4557
5229
  /*
4558
5230
  {
4559
5231
  id: string,
@@ -4638,6 +5310,30 @@ class TFHEPublicKey {
4638
5310
  });
4639
5311
  }
4640
5312
  }
5313
+ /*
5314
+ {
5315
+ id: string,
5316
+ data: BytesHex,
5317
+ srcUrl?: string
5318
+ }
5319
+ */
5320
+ static fromBytesHex(params) {
5321
+ let data;
5322
+ try {
5323
+ assertRecordStringProperty(params, 'data', 'arg');
5324
+ data = hexToBytesFaster(params.data, true /* strict */);
5325
+ }
5326
+ catch (e) {
5327
+ throw new Error('Invalid public key (deserialization failed)', {
5328
+ cause: e,
5329
+ });
5330
+ }
5331
+ return TFHEPublicKey.fromBytes({
5332
+ id: params?.id,
5333
+ srcUrl: params?.srcUrl,
5334
+ data,
5335
+ });
5336
+ }
4641
5337
  /*
4642
5338
  {
4643
5339
  id: string,
@@ -4698,6 +5394,20 @@ class TFHEPublicKey {
4698
5394
  ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
4699
5395
  };
4700
5396
  }
5397
+ /*
5398
+ {
5399
+ id: string,
5400
+ data: BytesHex,
5401
+ srcUrl?: string
5402
+ }
5403
+ */
5404
+ toBytesHex() {
5405
+ return {
5406
+ data: bytesToHexLarge(this._tfheCompactPublicKey.safe_serialize(SERIALIZED_SIZE_LIMIT_PK)),
5407
+ id: this._id,
5408
+ ...(this._srcUrl ? { srcUrl: this._srcUrl } : {}),
5409
+ };
5410
+ }
4701
5411
  /*
4702
5412
  {
4703
5413
  publicKey: TFHE.TfheCompactPublicKey
@@ -4722,6 +5432,75 @@ class TFHEPublicKey {
4722
5432
  publicKeyId: this._id,
4723
5433
  };
4724
5434
  }
5435
+ /*
5436
+ {
5437
+ publicKey: Uint8Array
5438
+ publicKeyId: string
5439
+ }
5440
+ */
5441
+ toPublicKeyBytesHex() {
5442
+ return {
5443
+ publicKey: this.toBytesHex().data,
5444
+ publicKeyId: this._id,
5445
+ };
5446
+ }
5447
+ static _fromPublicKeyBytes(params) {
5448
+ return TFHEPublicKey._fromBytes({
5449
+ data: params.publicKey,
5450
+ id: params.publicKeyId,
5451
+ srcUrl: params.srcUrl,
5452
+ });
5453
+ }
5454
+ static fromPublicKeyBytesHex(params) {
5455
+ try {
5456
+ assertRecordStringProperty(params, 'publicKey', `arg`);
5457
+ assertRecordStringProperty(params, 'publicKeyId', `arg`);
5458
+ return TFHEPublicKey._fromPublicKeyBytes({
5459
+ publicKey: hexToBytesFaster(params.publicKey, true /* strict */),
5460
+ publicKeyId: params.publicKeyId,
5461
+ });
5462
+ }
5463
+ catch (e) {
5464
+ throw new Error('Invalid public key (deserialization failed)', {
5465
+ cause: e,
5466
+ });
5467
+ }
5468
+ }
5469
+ static fromPublicKeyBytes(params) {
5470
+ try {
5471
+ assertUint8ArrayProperty(params, 'publicKey', `arg`);
5472
+ assertRecordStringProperty(params, 'publicKeyId', `arg`);
5473
+ return TFHEPublicKey._fromPublicKeyBytes(params);
5474
+ }
5475
+ catch (e) {
5476
+ throw new Error('Invalid public key (deserialization failed)', {
5477
+ cause: e,
5478
+ });
5479
+ }
5480
+ }
5481
+ //////////////////////////////////////////////////////////////////////////////
5482
+ // JSON
5483
+ //////////////////////////////////////////////////////////////////////////////
5484
+ /*
5485
+ {
5486
+ __type: 'TFHEPublicKey',
5487
+ id: string,
5488
+ data: BytesHex,
5489
+ srcUrl?: string
5490
+ }
5491
+ */
5492
+ toJSON() {
5493
+ return {
5494
+ __type: 'TFHEPublicKey',
5495
+ ...this.toBytesHex(),
5496
+ };
5497
+ }
5498
+ static fromJSON(json) {
5499
+ if (json.__type !== 'TFHEPublicKey') {
5500
+ throw new TFHEPublicKeyError({ message: 'Invalid TFHEPublicKey JSON.' });
5501
+ }
5502
+ return TFHEPublicKey.fromBytesHex(json);
5503
+ }
4725
5504
  }
4726
5505
 
4727
5506
  //const __KEY_URL_CACHE__: Record<string, RelayerV2PublicKey> = {};
@@ -4797,7 +5576,7 @@ class RelayerV2PublicKey {
4797
5576
  toBytes() {
4798
5577
  return {
4799
5578
  publicKey: this._publicKey.toBytes(),
4800
- publicParams: this._crs2048.toPublicParamsBytes(),
5579
+ publicParams: this._crs2048.toPublicParams2048Bytes(),
4801
5580
  };
4802
5581
  }
4803
5582
  }
@@ -4831,23 +5610,46 @@ class RelayerV2Fhevm extends AbstractRelayerFhevm {
4831
5610
  get relayerProvider() {
4832
5611
  return this._relayerProvider;
4833
5612
  }
5613
+ getPublicKeyInfo() {
5614
+ return {
5615
+ id: this._relayerPublicKey.getTFHEPublicKey().id,
5616
+ srcUrl: this._relayerPublicKey.getTFHEPublicKey().srcUrl,
5617
+ };
5618
+ }
4834
5619
  getPublicKeyBytes() {
4835
5620
  return this._relayerPublicKey.getTFHEPublicKey().toPublicKeyBytes();
4836
5621
  }
4837
5622
  getPublicKeyWasm() {
4838
5623
  return this._relayerPublicKey.getTFHEPublicKey().toPublicKeyWasm();
4839
5624
  }
4840
- getPublicParamsBytes(bits) {
5625
+ getPublicParamsBytesForBits(bits) {
5626
+ if (bits === undefined) {
5627
+ throw new TFHECrsError({ message: `Missing PublicParams bits format` });
5628
+ }
4841
5629
  if (bits !== 2048) {
4842
- throw new Error(`Unsupported PublicParams bits format ${bits}`);
5630
+ throw new TFHECrsError({
5631
+ message: `Unsupported PublicParams bits format '${bits}'`,
5632
+ });
4843
5633
  }
4844
- return this._relayerPublicKey.getTFHECrs().toPublicParamsBytes()['2048'];
5634
+ return this._relayerPublicKey.getTFHECrs().toPublicParams2048Bytes()['2048'];
4845
5635
  }
4846
- getPublicParamsWasm(bits) {
5636
+ getPublicParamsWasmForBits(bits) {
5637
+ if (bits === undefined) {
5638
+ throw new TFHECrsError({ message: `Missing PublicParams bits format` });
5639
+ }
4847
5640
  if (bits !== 2048) {
4848
- throw new Error(`Unsupported PublicParams bits format ${bits}`);
5641
+ throw new TFHECrsError({
5642
+ message: `Unsupported PublicParams bits format '${bits}'`,
5643
+ });
4849
5644
  }
4850
- return this._relayerPublicKey.getTFHECrs().toPublicParamsWasm()['2048'];
5645
+ return this._relayerPublicKey.getTFHECrs().toPublicParams2048Wasm()['2048'];
5646
+ }
5647
+ getPublicParamsInfo() {
5648
+ return {
5649
+ id: this._relayerPublicKey.getTFHECrs().id,
5650
+ bits: this._relayerPublicKey.getTFHECrs().bits,
5651
+ srcUrl: this._relayerPublicKey.getTFHECrs().srcUrl,
5652
+ };
4851
5653
  }
4852
5654
  }
4853
5655
 
@@ -4886,24 +5688,42 @@ class RelayerV1Fhevm extends AbstractRelayerFhevm {
4886
5688
  publicKeyId: this._publicKeyData.publicKeyId,
4887
5689
  };
4888
5690
  }
5691
+ getPublicKeyInfo() {
5692
+ return {
5693
+ id: this._publicKeyData.publicKeyId,
5694
+ };
5695
+ }
5696
+ getPublicParamsInfo() {
5697
+ return {
5698
+ id: this._publicParamsData['2048'].publicParamsId,
5699
+ bits: 2048,
5700
+ };
5701
+ }
4889
5702
  getPublicKeyWasm() {
4890
5703
  return {
4891
5704
  publicKey: this._publicKeyData.publicKey,
4892
5705
  publicKeyId: this._publicKeyData.publicKeyId,
4893
5706
  };
4894
5707
  }
4895
- getPublicParamsBytes(bits) {
5708
+ getPublicParamsBytesForBits(bits) {
5709
+ if (bits === undefined) {
5710
+ throw new Error(`Missing PublicParams bits format`);
5711
+ }
4896
5712
  if (bits !== 2048) {
4897
- throw new Error(`Unsupported PublicParams bits format ${bits}`);
5713
+ throw new Error(`Unsupported PublicParams bits format '${bits}'`);
4898
5714
  }
4899
- return {
5715
+ const res = {
4900
5716
  publicParams: this._publicParamsData['2048'].publicParams.safe_serialize(SERIALIZED_SIZE_LIMIT_CRS),
4901
5717
  publicParamsId: this._publicParamsData['2048'].publicParamsId,
4902
5718
  };
5719
+ return res;
4903
5720
  }
4904
- getPublicParamsWasm(bits) {
5721
+ getPublicParamsWasmForBits(bits) {
5722
+ if (bits === undefined) {
5723
+ throw new Error(`Missing PublicParams bits format`);
5724
+ }
4905
5725
  if (bits !== 2048) {
4906
- throw new Error(`Unsupported PublicParams bits format ${bits}`);
5726
+ throw new Error(`Unsupported PublicParams bits format '${bits}'`);
4907
5727
  }
4908
5728
  return {
4909
5729
  publicParams: this._publicParamsData['2048'].publicParams,
@@ -4924,11 +5744,16 @@ async function createRelayerFhevm(config) {
4924
5744
  publicParams: config.publicParams,
4925
5745
  });
4926
5746
  }
4927
- return RelayerV1Fhevm.fromConfig({
4928
- relayerVersionUrl: resolved.url,
4929
- publicKey: config.publicKey,
4930
- publicParams: config.publicParams,
4931
- });
5747
+ else if (resolved.version === 1) {
5748
+ return RelayerV1Fhevm.fromConfig({
5749
+ relayerVersionUrl: resolved.url,
5750
+ publicKey: config.publicKey,
5751
+ publicParams: config.publicParams,
5752
+ });
5753
+ }
5754
+ else {
5755
+ throw new Error(`Invalid relayerUrl: ${config.relayerUrl}`);
5756
+ }
4932
5757
  }
4933
5758
  function _resolveRelayerUrl(value, defaultVersion) {
4934
5759
  if (!value || typeof value !== 'string') {
@@ -5028,7 +5853,28 @@ const createInstance = async (config) => {
5028
5853
  const coprocessorSigners = await getCoprocessorSigners(provider, inputVerifierContractAddress);
5029
5854
  const thresholdCoprocessorSigners = await getCoprocessorSignersThreshold(provider, inputVerifierContractAddress);
5030
5855
  return {
5031
- createEncryptedInput: createRelayerEncryptedInput(aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerFhevm.relayerProvider, relayerFhevm.getPublicKeyWasm().publicKey, { 2048: relayerFhevm.getPublicParamsWasm(2048) }, coprocessorSigners, thresholdCoprocessorSigners, auth && { auth }),
5856
+ createEncryptedInput: createRelayerEncryptedInput(aclContractAddress, verifyingContractAddressInputVerification, chainId, gatewayChainId, relayerFhevm.relayerProvider, relayerFhevm.getPublicKeyWasm().publicKey, { 2048: relayerFhevm.getPublicParamsWasmForBits(2048) }, coprocessorSigners, thresholdCoprocessorSigners, auth && { auth }),
5857
+ requestZKProofVerification: (zkProof, options) => {
5858
+ if (zkProof.chainId !== chainId ||
5859
+ zkProof.aclContractAddress !== aclContractAddress) {
5860
+ throw new Error('Invalid ZKProof');
5861
+ }
5862
+ return requestCiphertextWithZKProofVerification({
5863
+ ciphertext: zkProof.ciphertextWithZkProof,
5864
+ aclContractAddress: aclContractAddress,
5865
+ contractAddress: zkProof.contractAddress,
5866
+ userAddress: zkProof.userAddress,
5867
+ chainId,
5868
+ gatewayChainId,
5869
+ bits: zkProof.bits,
5870
+ coprocessorSigners,
5871
+ extraData: '0x00',
5872
+ thresholdCoprocessorSigners,
5873
+ relayerProvider: relayerFhevm.relayerProvider,
5874
+ verifyingContractAddressInputVerification: verifyingContractAddressInputVerification,
5875
+ options,
5876
+ });
5877
+ },
5032
5878
  generateKeypair,
5033
5879
  createEIP712: createEIP712(verifyingContractAddressDecryption, chainId),
5034
5880
  publicDecrypt: publicDecryptRequest(kmsSigners, thresholdKMSSigners, gatewayChainId, verifyingContractAddressDecryption, aclContractAddress,
@@ -5038,7 +5884,7 @@ const createInstance = async (config) => {
5038
5884
  //cleanURL(config.relayerUrl),
5039
5885
  relayerFhevm.relayerProvider, provider, auth && { auth }),
5040
5886
  getPublicKey: () => relayerFhevm.getPublicKeyBytes(),
5041
- getPublicParams: (bits) => relayerFhevm.getPublicParamsBytes(bits),
5887
+ getPublicParams: (bits) => relayerFhevm.getPublicParamsBytesForBits(bits),
5042
5888
  // getPublicKey: () =>
5043
5889
  // publicKeyData.publicKey
5044
5890
  // ? {
@@ -5076,7 +5922,7 @@ const createTfheKeypair = () => {
5076
5922
  };
5077
5923
  const createTfhePublicKey = () => {
5078
5924
  const { publicKey } = createTfheKeypair();
5079
- return toHexString(publicKey.serialize());
5925
+ return bytesToHexNo0x(publicKey.serialize());
5080
5926
  };
5081
5927
 
5082
5928
  global.TFHE = TFHEPkg;