@cofhe/sdk 0.3.2 → 0.5.0

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.
Files changed (97) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/adapters/{ethers5.test.ts → test/ethers5.test.ts} +2 -2
  3. package/adapters/{ethers6.test.ts → test/ethers6.test.ts} +2 -2
  4. package/adapters/{hardhat.hh2.test.ts → test/hardhat.hh2.test.ts} +2 -2
  5. package/adapters/{index.test.ts → test/index.test.ts} +1 -1
  6. package/adapters/{wagmi.test.ts → test/wagmi.test.ts} +1 -1
  7. package/chains/{chains.test.ts → test/chains.test.ts} +1 -1
  8. package/core/client.ts +15 -5
  9. package/core/clientTypes.ts +7 -5
  10. package/core/consts.ts +9 -0
  11. package/core/decrypt/cofheMocksDecryptForTx.ts +14 -3
  12. package/core/decrypt/decryptForTxBuilder.ts +24 -10
  13. package/core/decrypt/decryptForViewBuilder.ts +14 -7
  14. package/core/decrypt/polling.ts +14 -0
  15. package/core/decrypt/tnDecryptUtils.ts +65 -0
  16. package/core/decrypt/{tnDecrypt.ts → tnDecryptV1.ts} +7 -70
  17. package/core/decrypt/tnDecryptV2.ts +483 -0
  18. package/core/decrypt/tnSealOutputV2.ts +245 -104
  19. package/core/decrypt/verifyDecryptResult.ts +65 -0
  20. package/core/encrypt/cofheMocksZkVerifySign.ts +6 -6
  21. package/core/encrypt/zkPackProveVerify.ts +10 -19
  22. package/core/fetchKeys.ts +0 -2
  23. package/core/index.ts +9 -1
  24. package/core/keyStore.ts +5 -2
  25. package/core/permits.ts +8 -3
  26. package/core/{client.test.ts → test/client.test.ts} +7 -7
  27. package/core/{config.test.ts → test/config.test.ts} +1 -1
  28. package/core/test/decrypt.test.ts +252 -0
  29. package/core/test/decryptBuilders.test.ts +390 -0
  30. package/core/{encrypt → test}/encryptInputsBuilder.test.ts +61 -6
  31. package/core/{fetchKeys.test.ts → test/fetchKeys.test.ts} +3 -3
  32. package/core/{keyStore.test.ts → test/keyStore.test.ts} +5 -3
  33. package/core/{permits.test.ts → test/permits.test.ts} +42 -1
  34. package/core/test/pollCallbacks.test.ts +563 -0
  35. package/core/types.ts +21 -0
  36. package/dist/chains.d.cts +2 -2
  37. package/dist/chains.d.ts +2 -2
  38. package/dist/chunk-4FP4V35O.js +13 -0
  39. package/dist/{chunk-NWDKXBIP.js → chunk-MRCKUMOS.js} +62 -22
  40. package/dist/{chunk-LWMRB6SD.js → chunk-S7OKGLFD.js} +615 -198
  41. package/dist/{clientTypes-Y43CKbOz.d.cts → clientTypes-BSbwairE.d.cts} +38 -13
  42. package/dist/{clientTypes-PQha8zes.d.ts → clientTypes-DDmcgZ0a.d.ts} +38 -13
  43. package/dist/core.cjs +691 -235
  44. package/dist/core.d.cts +24 -6
  45. package/dist/core.d.ts +24 -6
  46. package/dist/core.js +3 -2
  47. package/dist/node.cjs +696 -237
  48. package/dist/node.d.cts +3 -3
  49. package/dist/node.d.ts +3 -3
  50. package/dist/node.js +14 -7
  51. package/dist/{permit-MZ502UBl.d.ts → permit-DnVMDT5h.d.cts} +34 -4
  52. package/dist/{permit-MZ502UBl.d.cts → permit-DnVMDT5h.d.ts} +34 -4
  53. package/dist/permits.cjs +66 -29
  54. package/dist/permits.d.cts +18 -13
  55. package/dist/permits.d.ts +18 -13
  56. package/dist/permits.js +2 -1
  57. package/dist/web.cjs +718 -242
  58. package/dist/web.d.cts +8 -4
  59. package/dist/web.d.ts +8 -4
  60. package/dist/web.js +34 -11
  61. package/dist/zkProve.worker.cjs +6 -3
  62. package/dist/zkProve.worker.js +5 -3
  63. package/node/index.ts +13 -4
  64. package/node/test/client.test.ts +25 -0
  65. package/node/test/config.test.ts +16 -0
  66. package/node/test/inherited.test.ts +244 -0
  67. package/node/test/tfheinit.test.ts +56 -0
  68. package/package.json +24 -22
  69. package/permits/permit.ts +31 -5
  70. package/permits/sealing.ts +1 -1
  71. package/permits/{localstorage.test.ts → test/localstorage.test.ts} +2 -2
  72. package/permits/{permit.test.ts → test/permit.test.ts} +35 -1
  73. package/permits/{sealing.test.ts → test/sealing.test.ts} +1 -1
  74. package/permits/{store.test.ts → test/store.test.ts} +2 -2
  75. package/permits/{validation.test.ts → test/validation.test.ts} +82 -6
  76. package/permits/types.ts +1 -1
  77. package/permits/validation.ts +42 -2
  78. package/web/const.ts +2 -0
  79. package/web/index.ts +20 -6
  80. package/web/storage.ts +18 -3
  81. package/web/{client.web.test.ts → test/client.web.test.ts} +13 -1
  82. package/web/test/config.web.test.ts +16 -0
  83. package/web/test/inherited.web.test.ts +245 -0
  84. package/web/test/tfheinit.web.test.ts +62 -0
  85. package/web/{worker.config.web.test.ts → test/worker.config.web.test.ts} +1 -1
  86. package/web/{worker.output.web.test.ts → test/worker.output.web.test.ts} +1 -1
  87. package/web/{workerManager.test.ts → test/workerManager.test.ts} +1 -1
  88. package/web/{workerManager.web.test.ts → test/workerManager.web.test.ts} +1 -1
  89. package/web/zkProve.worker.ts +4 -3
  90. package/node/client.test.ts +0 -147
  91. package/node/config.test.ts +0 -68
  92. package/node/encryptInputs.test.ts +0 -155
  93. package/web/config.web.test.ts +0 -69
  94. package/web/encryptInputs.web.test.ts +0 -172
  95. package/web/worker.builder.web.test.ts +0 -148
  96. /package/dist/{types-YiAC4gig.d.cts → types-C07FK-cL.d.cts} +0 -0
  97. /package/dist/{types-YiAC4gig.d.ts → types-C07FK-cL.d.ts} +0 -0
package/dist/web.cjs CHANGED
@@ -14,25 +14,7 @@ var init = require('tfhe');
14
14
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
15
15
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
16
 
17
- function _interopNamespace(e) {
18
- if (e && e.__esModule) return e;
19
- var n = Object.create(null);
20
- if (e) {
21
- Object.keys(e).forEach(function (k) {
22
- if (k !== 'default') {
23
- var d = Object.getOwnPropertyDescriptor(e, k);
24
- Object.defineProperty(n, k, d.get ? d : {
25
- enumerable: true,
26
- get: function () { return e[k]; }
27
- });
28
- }
29
- });
30
- }
31
- n.default = e;
32
- return Object.freeze(n);
33
- }
34
-
35
- var nacl__namespace = /*#__PURE__*/_interopNamespace(nacl);
17
+ var nacl__default = /*#__PURE__*/_interopDefault(nacl);
36
18
  var init__default = /*#__PURE__*/_interopDefault(init);
37
19
 
38
20
  // core/client.ts
@@ -126,6 +108,16 @@ var bigintSafeJsonStringify = (value) => {
126
108
  };
127
109
  var isCofheError = (error) => error instanceof CofheError;
128
110
 
111
+ // core/consts.ts
112
+ var TASK_MANAGER_ADDRESS = "0xeA30c4B8b44078Bbf8a6ef5b9f1eC1626C7848D9";
113
+ var MOCKS_ZK_VERIFIER_ADDRESS = "0x0000000000000000000000000000000000005001";
114
+ var MOCKS_THRESHOLD_NETWORK_ADDRESS = "0x0000000000000000000000000000000000005002";
115
+ var MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY = "0x6C8D7F768A6BB4AAFE85E8A2F5A9680355239C7E14646ED62B044E39DE154512";
116
+ var MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
117
+ var TFHE_RS_ZK_MAX_BITS = 2048;
118
+ var TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT = BigInt(1 << 30);
119
+ var TFHE_RS_KEY_VERSION = 2;
120
+
129
121
  // core/types.ts
130
122
  var FheUintUTypes = [
131
123
  2 /* Uint8 */,
@@ -248,7 +240,6 @@ var MAX_UINT32 = 4294967295n;
248
240
  var MAX_UINT64 = 18446744073709551615n;
249
241
  var MAX_UINT128 = 340282366920938463463374607431768211455n;
250
242
  var MAX_UINT160 = 1461501637330902918203684832716283019655932542975n;
251
- var MAX_ENCRYPTABLE_BITS = 2048;
252
243
  var zkPack = (items, builder) => {
253
244
  let totalBits = 0;
254
245
  for (const item of items) {
@@ -312,14 +303,14 @@ var zkPack = (items, builder) => {
312
303
  }
313
304
  }
314
305
  }
315
- if (totalBits > MAX_ENCRYPTABLE_BITS) {
306
+ if (totalBits > TFHE_RS_ZK_MAX_BITS) {
316
307
  throw new CofheError({
317
308
  code: "ZK_PACK_FAILED" /* ZkPackFailed */,
318
- message: `Total bits ${totalBits} exceeds ${MAX_ENCRYPTABLE_BITS}`,
319
- hint: `Ensure that the total bits of the items to encrypt does not exceed ${MAX_ENCRYPTABLE_BITS}`,
309
+ message: `Total bits ${totalBits} exceeds ${TFHE_RS_ZK_MAX_BITS}`,
310
+ hint: `Ensure that the total bits of the items to encrypt does not exceed ${TFHE_RS_ZK_MAX_BITS}`,
320
311
  context: {
321
312
  totalBits,
322
- maxBits: MAX_ENCRYPTABLE_BITS,
313
+ maxBits: TFHE_RS_ZK_MAX_BITS,
323
314
  items
324
315
  }
325
316
  });
@@ -338,7 +329,7 @@ var zkProve = async (builder, crs, metadata) => {
338
329
  1
339
330
  // ZkComputeLoad.Verify
340
331
  );
341
- resolve(compactList.serialize());
332
+ resolve(compactList.safe_serialize(TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT));
342
333
  }, 0);
343
334
  });
344
335
  };
@@ -514,15 +505,6 @@ var MockZkVerifierAbi = [
514
505
  },
515
506
  { type: "error", name: "InvalidInputs", inputs: [] }
516
507
  ];
517
-
518
- // core/consts.ts
519
- var TASK_MANAGER_ADDRESS = "0xeA30c4B8b44078Bbf8a6ef5b9f1eC1626C7848D9";
520
- var MOCKS_ZK_VERIFIER_ADDRESS = "0x0000000000000000000000000000000000005001";
521
- var MOCKS_THRESHOLD_NETWORK_ADDRESS = "0x0000000000000000000000000000000000005002";
522
- var MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY = "0x6C8D7F768A6BB4AAFE85E8A2F5A9680355239C7E14646ED62B044E39DE154512";
523
- var MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
524
-
525
- // core/encrypt/cofheMocksZkVerifySign.ts
526
508
  function createMockZkVerifierSigner() {
527
509
  return viem.createWalletClient({
528
510
  chain: chains.hardhat,
@@ -564,14 +546,14 @@ async function cofheMocksCheckEncryptableBits(items) {
564
546
  }
565
547
  }
566
548
  }
567
- if (totalBits > MAX_ENCRYPTABLE_BITS) {
549
+ if (totalBits > TFHE_RS_ZK_MAX_BITS) {
568
550
  throw new CofheError({
569
551
  code: "ZK_PACK_FAILED" /* ZkPackFailed */,
570
- message: `Total bits ${totalBits} exceeds ${MAX_ENCRYPTABLE_BITS}`,
571
- hint: `Ensure that the total bits of the items to encrypt does not exceed ${MAX_ENCRYPTABLE_BITS}`,
552
+ message: `Total bits ${totalBits} exceeds ${TFHE_RS_ZK_MAX_BITS}`,
553
+ hint: `Ensure that the total bits of the items to encrypt does not exceed ${TFHE_RS_ZK_MAX_BITS}`,
572
554
  context: {
573
555
  totalBits,
574
- maxBits: MAX_ENCRYPTABLE_BITS,
556
+ maxBits: TFHE_RS_ZK_MAX_BITS,
575
557
  items
576
558
  }
577
559
  });
@@ -842,6 +824,7 @@ function getThresholdNetworkUrlOrThrow(config, chainId) {
842
824
  }
843
825
  return url;
844
826
  }
827
+ var KEYSTORE_NAME = `cofhesdk-keys-v${TFHE_RS_KEY_VERSION}`;
845
828
  function isValidPersistedState(state) {
846
829
  if (state && typeof state === "object") {
847
830
  if ("fhe" in state && "crs" in state) {
@@ -896,7 +879,7 @@ function createKeysStore(storage) {
896
879
  };
897
880
  const clearKeysStorage = async () => {
898
881
  if (storage) {
899
- await storage.removeItem("cofhesdk-keys");
882
+ await storage.removeItem(KEYSTORE_NAME);
900
883
  }
901
884
  };
902
885
  const rehydrateKeysStore = async () => {
@@ -926,7 +909,7 @@ function createStoreWithPersit(storage) {
926
909
  if (_error)
927
910
  throw new Error(`onRehydrateStorage: Error rehydrating keys store: ${_error}`);
928
911
  },
929
- name: "cofhesdk-keys",
912
+ name: KEYSTORE_NAME,
930
913
  storage: middleware.createJSONStorage(() => storage),
931
914
  merge: (persistedState, currentState) => {
932
915
  const persisted = isValidPersistedState(persistedState) ? persistedState : DEFAULT_KEYS_STORE;
@@ -1651,7 +1634,7 @@ var SealingKey = class _SealingKey {
1651
1634
  const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
1652
1635
  const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
1653
1636
  const privateKeyBytes = fromHexString(this.privateKey);
1654
- const decryptedMessage = nacl__namespace.box.open(dataToDecrypt, nonce, ephemPublicKey, privateKeyBytes);
1637
+ const decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, privateKeyBytes);
1655
1638
  if (!decryptedMessage) {
1656
1639
  throw new Error("Failed to decrypt message");
1657
1640
  }
@@ -1684,9 +1667,9 @@ var SealingKey = class _SealingKey {
1684
1667
  static seal = (value, publicKey) => {
1685
1668
  isString(publicKey);
1686
1669
  isBigIntOrNumber(value);
1687
- const ephemeralKeyPair = nacl__namespace.box.keyPair();
1688
- const nonce = nacl__namespace.randomBytes(nacl__namespace.box.nonceLength);
1689
- const encryptedMessage = nacl__namespace.box(toBeArray(value), nonce, fromHexString(publicKey), ephemeralKeyPair.secretKey);
1670
+ const ephemeralKeyPair = nacl__default.default.box.keyPair();
1671
+ const nonce = nacl__default.default.randomBytes(nacl__default.default.box.nonceLength);
1672
+ const encryptedMessage = nacl__default.default.box(toBeArray(value), nonce, fromHexString(publicKey), ephemeralKeyPair.secretKey);
1690
1673
  return {
1691
1674
  data: encryptedMessage,
1692
1675
  public_key: ephemeralKeyPair.publicKey,
@@ -1695,7 +1678,7 @@ var SealingKey = class _SealingKey {
1695
1678
  };
1696
1679
  };
1697
1680
  var GenerateSealingKey = () => {
1698
- const sodiumKeypair = nacl__namespace.box.keyPair();
1681
+ const sodiumKeypair = nacl__default.default.box.keyPair();
1699
1682
  return new SealingKey(toHexString2(sodiumKeypair.secretKey), toHexString2(sodiumKeypair.publicKey));
1700
1683
  };
1701
1684
  var SerializedSealingPair = zod.z.object({
@@ -1853,9 +1836,9 @@ var ValidationUtils = {
1853
1836
  return false;
1854
1837
  },
1855
1838
  /**
1856
- * Overall validity checker of a permit
1839
+ * Checks that a permit is signed and not expired.
1857
1840
  */
1858
- isValid: (permit) => {
1841
+ isSignedAndNotExpired: (permit) => {
1859
1842
  if (ValidationUtils.isExpired(permit)) {
1860
1843
  return { valid: false, error: "expired" };
1861
1844
  }
@@ -1863,6 +1846,34 @@ var ValidationUtils = {
1863
1846
  return { valid: false, error: "not-signed" };
1864
1847
  }
1865
1848
  return { valid: true, error: null };
1849
+ },
1850
+ /**
1851
+ * Asserts that a permit is signed and not expired.
1852
+ *
1853
+ * Throws `Error` with message:
1854
+ * - `Permit is expired`
1855
+ * - `Permit is not signed`
1856
+ */
1857
+ assertSignedAndNotExpired: (permit) => {
1858
+ const result = ValidationUtils.isSignedAndNotExpired(permit);
1859
+ if (result.valid)
1860
+ return;
1861
+ if (result.error === "expired") {
1862
+ throw new Error("Permit is expired");
1863
+ }
1864
+ if (result.error === "not-signed") {
1865
+ throw new Error("Permit is not signed");
1866
+ }
1867
+ throw new Error("Permit is invalid");
1868
+ },
1869
+ isValid: (permit) => {
1870
+ const schema = permit.type === "self" ? SelfPermitValidator : permit.type === "sharing" ? SharingPermitValidator : permit.type === "recipient" ? ImportPermitValidator : null;
1871
+ if (schema == null)
1872
+ return { valid: false, error: "invalid-schema" };
1873
+ const schemaResult = schema.safeParse(permit);
1874
+ if (!schemaResult.success)
1875
+ return { valid: false, error: "invalid-schema" };
1876
+ return ValidationUtils.isSignedAndNotExpired(permit);
1866
1877
  }
1867
1878
  };
1868
1879
 
@@ -2244,9 +2255,9 @@ var PermitUtils = {
2244
2255
  };
2245
2256
  },
2246
2257
  /**
2247
- * Validate a permit
2258
+ * Validate a permit (schema-level validation)
2248
2259
  */
2249
- validate: (permit) => {
2260
+ validateSchema: (permit) => {
2250
2261
  if (permit.type === "self") {
2251
2262
  return validateSelfPermit(permit);
2252
2263
  } else if (permit.type === "sharing") {
@@ -2257,12 +2268,27 @@ var PermitUtils = {
2257
2268
  throw new Error("Invalid permit type");
2258
2269
  }
2259
2270
  },
2271
+ /**
2272
+ * Validate a permit (holistic validation).
2273
+ *
2274
+ * This validates:
2275
+ * - Permit schema (shape + invariants)
2276
+ * - Permit is signed
2277
+ * - Permit is not expired
2278
+ *
2279
+ * For schema-only validation, use `validateSchema(permit)`.
2280
+ */
2281
+ validate: (permit) => {
2282
+ const validated = PermitUtils.validateSchema(permit);
2283
+ ValidationUtils.assertSignedAndNotExpired(validated);
2284
+ return validated;
2285
+ },
2260
2286
  /**
2261
2287
  * Get the permission object from a permit (for use in contracts)
2262
2288
  */
2263
2289
  getPermission: (permit, skipValidation = false) => {
2264
2290
  if (!skipValidation) {
2265
- PermitUtils.validate(permit);
2291
+ PermitUtils.validateSchema(permit);
2266
2292
  }
2267
2293
  return {
2268
2294
  issuer: permit.issuer,
@@ -2328,8 +2354,17 @@ var PermitUtils = {
2328
2354
  return ValidationUtils.isSigned(permit);
2329
2355
  },
2330
2356
  /**
2331
- * Check if permit is valid
2357
+ * Check if permit is signed and not expired
2358
+ */
2359
+ isSignedAndNotExpired: (permit) => {
2360
+ return ValidationUtils.isSignedAndNotExpired(permit);
2361
+ },
2362
+ /**
2363
+ * Assert that permit is signed and not expired
2332
2364
  */
2365
+ assertSignedAndNotExpired: (permit) => {
2366
+ return ValidationUtils.assertSignedAndNotExpired(permit);
2367
+ },
2333
2368
  isValid: (permit) => {
2334
2369
  return ValidationUtils.isValid(permit);
2335
2370
  },
@@ -2507,19 +2542,22 @@ var importShared = async (options, publicClient, walletClient) => {
2507
2542
  var getHash = (permit) => {
2508
2543
  return PermitUtils.getHash(permit);
2509
2544
  };
2545
+ var exportShared = (permit) => {
2546
+ return PermitUtils.export(permit);
2547
+ };
2510
2548
  var serialize = (permit) => {
2511
2549
  return PermitUtils.serialize(permit);
2512
2550
  };
2513
2551
  var deserialize = (serialized) => {
2514
2552
  return PermitUtils.deserialize(serialized);
2515
2553
  };
2516
- var getPermit2 = async (chainId, account, hash) => {
2554
+ var getPermit2 = (chainId, account, hash) => {
2517
2555
  return permitStore.getPermit(chainId, account, hash);
2518
2556
  };
2519
- var getPermits2 = async (chainId, account) => {
2557
+ var getPermits2 = (chainId, account) => {
2520
2558
  return permitStore.getPermits(chainId, account);
2521
2559
  };
2522
- var getActivePermit2 = async (chainId, account) => {
2560
+ var getActivePermit2 = (chainId, account) => {
2523
2561
  return permitStore.getActivePermit(chainId, account);
2524
2562
  };
2525
2563
  var getActivePermitHash2 = (chainId, account) => {
@@ -2557,6 +2595,7 @@ var permits = {
2557
2595
  getOrCreateSelfPermit,
2558
2596
  getOrCreateSharingPermit,
2559
2597
  getHash,
2598
+ export: exportShared,
2560
2599
  serialize,
2561
2600
  deserialize,
2562
2601
  getPermit: getPermit2,
@@ -2799,9 +2838,19 @@ async function cofheMocksDecryptForView(ctHash, utype, permit, publicClient) {
2799
2838
  return unsealed;
2800
2839
  }
2801
2840
 
2841
+ // core/decrypt/polling.ts
2842
+ function computeMinuteRampPollIntervalMs(elapsedMs, params) {
2843
+ const elapsedSeconds = Math.floor(elapsedMs / 1e3);
2844
+ const intervalSeconds = 1 + Math.floor(elapsedSeconds / 60);
2845
+ const intervalMs = intervalSeconds * 1e3;
2846
+ return Math.min(params.maxIntervalMs, Math.max(params.minIntervalMs, intervalMs));
2847
+ }
2848
+
2802
2849
  // core/decrypt/tnSealOutputV2.ts
2803
2850
  var POLL_INTERVAL_MS = 1e3;
2804
- var POLL_TIMEOUT_MS = 5 * 60 * 1e3;
2851
+ var POLL_MAX_INTERVAL_MS = 1e4;
2852
+ var SEAL_OUTPUT_TIMEOUT_MS = 5 * 60 * 1e3;
2853
+ var SUBMIT_RETRY_INTERVAL_MS = 1e3;
2805
2854
  function numberArrayToUint8Array(arr) {
2806
2855
  return new Uint8Array(arr);
2807
2856
  }
@@ -2818,93 +2867,193 @@ function convertSealedData(sealed) {
2818
2867
  nonce: numberArrayToUint8Array(sealed.nonce)
2819
2868
  };
2820
2869
  }
2821
- async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission) {
2822
- const body = {
2823
- ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
2824
- host_chain_id: chainId,
2825
- permit: permission
2826
- };
2827
- let response;
2828
- try {
2829
- response = await fetch(`${thresholdNetworkUrl}/v2/sealoutput`, {
2830
- method: "POST",
2831
- headers: {
2832
- "Content-Type": "application/json"
2833
- },
2834
- body: JSON.stringify(body)
2835
- });
2836
- } catch (e) {
2837
- throw new CofheError({
2838
- code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2839
- message: `sealOutput request failed`,
2840
- hint: "Ensure the threshold network URL is valid and reachable.",
2841
- cause: e instanceof Error ? e : void 0,
2842
- context: {
2843
- thresholdNetworkUrl,
2844
- body
2845
- }
2846
- });
2870
+ function getSealedDataFromSubmitResponse(value) {
2871
+ if (value.sealed)
2872
+ return value.sealed;
2873
+ if (Array.isArray(value.sealed_data) && Array.isArray(value.ephemeral_public_key) && Array.isArray(value.nonce)) {
2874
+ return {
2875
+ data: value.sealed_data,
2876
+ public_key: value.ephemeral_public_key,
2877
+ nonce: value.nonce
2878
+ };
2847
2879
  }
2848
- if (!response.ok) {
2849
- let errorMessage = `HTTP ${response.status}`;
2850
- try {
2851
- const errorBody = await response.json();
2852
- errorMessage = errorBody.error_message || errorBody.message || errorMessage;
2853
- } catch {
2854
- errorMessage = response.statusText || errorMessage;
2855
- }
2880
+ return void 0;
2881
+ }
2882
+ function parseCompletedSealOutputResponse(params) {
2883
+ const { value, thresholdNetworkUrl, requestId } = params;
2884
+ if (value.is_succeed === false) {
2885
+ const errorMessage = value.error_message || "Unknown error";
2856
2886
  throw new CofheError({
2857
2887
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2858
2888
  message: `sealOutput request failed: ${errorMessage}`,
2859
- hint: "Check the threshold network URL and request parameters.",
2860
2889
  context: {
2861
2890
  thresholdNetworkUrl,
2862
- status: response.status,
2863
- statusText: response.statusText,
2864
- body
2891
+ requestId,
2892
+ response: value
2865
2893
  }
2866
2894
  });
2867
2895
  }
2868
- let submitResponse;
2869
- try {
2870
- submitResponse = await response.json();
2871
- } catch (e) {
2896
+ const sealed = "sealed" in value ? value.sealed : getSealedDataFromSubmitResponse(value);
2897
+ if (!sealed) {
2872
2898
  throw new CofheError({
2873
- code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2874
- message: `Failed to parse sealOutput submit response`,
2875
- cause: e instanceof Error ? e : void 0,
2899
+ code: "SEAL_OUTPUT_RETURNED_NULL" /* SealOutputReturnedNull */,
2900
+ message: `sealOutput request completed but returned no sealed data`,
2876
2901
  context: {
2877
2902
  thresholdNetworkUrl,
2878
- body
2903
+ requestId,
2904
+ response: value
2879
2905
  }
2880
2906
  });
2881
2907
  }
2882
- if (!submitResponse.request_id) {
2908
+ return convertSealedData(sealed);
2909
+ }
2910
+ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
2911
+ const body = {
2912
+ ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
2913
+ host_chain_id: chainId,
2914
+ permit: permission
2915
+ };
2916
+ let attemptIndex = 0;
2917
+ for (; ; ) {
2918
+ let response;
2919
+ try {
2920
+ response = await fetch(`${thresholdNetworkUrl}/v2/sealoutput`, {
2921
+ method: "POST",
2922
+ headers: {
2923
+ "Content-Type": "application/json"
2924
+ },
2925
+ body: JSON.stringify(body)
2926
+ });
2927
+ } catch (e) {
2928
+ throw new CofheError({
2929
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2930
+ message: `sealOutput request failed`,
2931
+ hint: "Ensure the threshold network URL is valid and reachable.",
2932
+ cause: e instanceof Error ? e : void 0,
2933
+ context: {
2934
+ thresholdNetworkUrl,
2935
+ body,
2936
+ attemptIndex
2937
+ }
2938
+ });
2939
+ }
2940
+ if (!response.ok) {
2941
+ let errorMessage = `HTTP ${response.status}`;
2942
+ try {
2943
+ const errorBody = await response.json();
2944
+ errorMessage = errorBody.error_message || errorBody.message || errorMessage;
2945
+ } catch {
2946
+ errorMessage = response.statusText || errorMessage;
2947
+ }
2948
+ throw new CofheError({
2949
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2950
+ message: `sealOutput request failed: ${errorMessage}`,
2951
+ hint: "Check the threshold network URL and request parameters.",
2952
+ context: {
2953
+ thresholdNetworkUrl,
2954
+ status: response.status,
2955
+ statusText: response.statusText,
2956
+ body,
2957
+ attemptIndex
2958
+ }
2959
+ });
2960
+ }
2961
+ let submitResponse;
2962
+ if (response.status !== 204) {
2963
+ try {
2964
+ submitResponse = await response.json();
2965
+ } catch (e) {
2966
+ throw new CofheError({
2967
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2968
+ message: `Failed to parse sealOutput submit response`,
2969
+ cause: e instanceof Error ? e : void 0,
2970
+ context: {
2971
+ thresholdNetworkUrl,
2972
+ body,
2973
+ attemptIndex
2974
+ }
2975
+ });
2976
+ }
2977
+ if (getSealedDataFromSubmitResponse(submitResponse)) {
2978
+ return {
2979
+ kind: "completed",
2980
+ sealed: parseCompletedSealOutputResponse({
2981
+ value: submitResponse,
2982
+ thresholdNetworkUrl,
2983
+ requestId: submitResponse.request_id
2984
+ })
2985
+ };
2986
+ }
2987
+ if (submitResponse.request_id) {
2988
+ return { kind: "request_id", requestId: submitResponse.request_id };
2989
+ }
2990
+ }
2991
+ if (response.status === 204) {
2992
+ const elapsedMs = Date.now() - overallStartTime;
2993
+ if (elapsedMs > SEAL_OUTPUT_TIMEOUT_MS) {
2994
+ throw new CofheError({
2995
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2996
+ message: `sealOutput submit retried without receiving request_id for ${SEAL_OUTPUT_TIMEOUT_MS}ms`,
2997
+ hint: "The ciphertext may still be propagating. Try again later.",
2998
+ context: {
2999
+ thresholdNetworkUrl,
3000
+ body,
3001
+ attemptIndex,
3002
+ timeoutMs: SEAL_OUTPUT_TIMEOUT_MS,
3003
+ submitResponse,
3004
+ status: response.status
3005
+ }
3006
+ });
3007
+ }
3008
+ onPoll?.({
3009
+ operation: "sealoutput",
3010
+ requestId: "",
3011
+ attemptIndex,
3012
+ elapsedMs,
3013
+ intervalMs: SUBMIT_RETRY_INTERVAL_MS,
3014
+ timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
3015
+ });
3016
+ await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS));
3017
+ attemptIndex += 1;
3018
+ continue;
3019
+ }
2883
3020
  throw new CofheError({
2884
3021
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2885
3022
  message: `sealOutput submit response missing request_id`,
2886
3023
  context: {
2887
3024
  thresholdNetworkUrl,
2888
3025
  body,
2889
- submitResponse
3026
+ submitResponse,
3027
+ attemptIndex
2890
3028
  }
2891
3029
  });
2892
3030
  }
2893
- return submitResponse.request_id;
2894
3031
  }
2895
- async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
2896
- const startTime = Date.now();
2897
- let completed = false;
2898
- while (!completed) {
2899
- if (Date.now() - startTime > POLL_TIMEOUT_MS) {
3032
+ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
3033
+ let attemptIndex = 0;
3034
+ while (true) {
3035
+ const elapsedMs = Date.now() - overallStartTime;
3036
+ const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
3037
+ minIntervalMs: POLL_INTERVAL_MS,
3038
+ maxIntervalMs: POLL_MAX_INTERVAL_MS
3039
+ });
3040
+ onPoll?.({
3041
+ operation: "sealoutput",
3042
+ requestId,
3043
+ attemptIndex,
3044
+ elapsedMs,
3045
+ intervalMs,
3046
+ timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
3047
+ });
3048
+ if (elapsedMs > SEAL_OUTPUT_TIMEOUT_MS) {
2900
3049
  throw new CofheError({
2901
3050
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2902
- message: `sealOutput polling timed out after ${POLL_TIMEOUT_MS}ms`,
3051
+ message: `sealOutput polling timed out after ${SEAL_OUTPUT_TIMEOUT_MS}ms`,
2903
3052
  hint: "The request may still be processing. Try again later.",
2904
3053
  context: {
2905
3054
  thresholdNetworkUrl,
2906
3055
  requestId,
2907
- timeoutMs: POLL_TIMEOUT_MS
3056
+ timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
2908
3057
  }
2909
3058
  });
2910
3059
  }
@@ -2973,32 +3122,14 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
2973
3122
  });
2974
3123
  }
2975
3124
  if (statusResponse.status === "COMPLETED") {
2976
- if (statusResponse.is_succeed === false) {
2977
- const errorMessage = statusResponse.error_message || "Unknown error";
2978
- throw new CofheError({
2979
- code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2980
- message: `sealOutput request failed: ${errorMessage}`,
2981
- context: {
2982
- thresholdNetworkUrl,
2983
- requestId,
2984
- statusResponse
2985
- }
2986
- });
2987
- }
2988
- if (!statusResponse.sealed) {
2989
- throw new CofheError({
2990
- code: "SEAL_OUTPUT_RETURNED_NULL" /* SealOutputReturnedNull */,
2991
- message: `sealOutput request completed but returned no sealed data`,
2992
- context: {
2993
- thresholdNetworkUrl,
2994
- requestId,
2995
- statusResponse
2996
- }
2997
- });
2998
- }
2999
- return convertSealedData(statusResponse.sealed);
3125
+ return parseCompletedSealOutputResponse({
3126
+ value: statusResponse,
3127
+ thresholdNetworkUrl,
3128
+ requestId
3129
+ });
3000
3130
  }
3001
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
3131
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
3132
+ attemptIndex += 1;
3002
3133
  }
3003
3134
  throw new CofheError({
3004
3135
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
@@ -3009,9 +3140,21 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
3009
3140
  }
3010
3141
  });
3011
3142
  }
3012
- async function tnSealOutputV2(ctHash, chainId, permission, thresholdNetworkUrl) {
3013
- const requestId = await submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission);
3014
- return await pollSealOutputStatus(thresholdNetworkUrl, requestId);
3143
+ async function tnSealOutputV2(params) {
3144
+ const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
3145
+ const overallStartTime = Date.now();
3146
+ const submitResult = await submitSealOutputRequest(
3147
+ thresholdNetworkUrl,
3148
+ ctHash,
3149
+ chainId,
3150
+ permission,
3151
+ overallStartTime,
3152
+ onPoll
3153
+ );
3154
+ if (submitResult.kind === "completed") {
3155
+ return submitResult.sealed;
3156
+ }
3157
+ return await pollSealOutputStatus(thresholdNetworkUrl, submitResult.requestId, overallStartTime, onPoll);
3015
3158
  }
3016
3159
 
3017
3160
  // core/decrypt/decryptForViewBuilder.ts
@@ -3020,6 +3163,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3020
3163
  utype;
3021
3164
  permitHash;
3022
3165
  permit;
3166
+ pollCallback;
3023
3167
  constructor(params) {
3024
3168
  super({
3025
3169
  config: params.config,
@@ -3076,6 +3220,10 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3076
3220
  getAccount() {
3077
3221
  return this.account;
3078
3222
  }
3223
+ onPoll(callback) {
3224
+ this.pollCallback = callback;
3225
+ return this;
3226
+ }
3079
3227
  withPermit(permitOrPermitHash) {
3080
3228
  if (typeof permitOrPermitHash === "string") {
3081
3229
  this.permitHash = permitOrPermitHash;
@@ -3199,7 +3347,13 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3199
3347
  this.assertPublicClient();
3200
3348
  const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
3201
3349
  const permission = PermitUtils.getPermission(permit, true);
3202
- const sealed = await tnSealOutputV2(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
3350
+ const sealed = await tnSealOutputV2({
3351
+ ctHash: this.ctHash,
3352
+ chainId: this.chainId,
3353
+ permission,
3354
+ thresholdNetworkUrl,
3355
+ onPoll: this.pollCallback
3356
+ });
3203
3357
  return PermitUtils.unseal(permit, sealed);
3204
3358
  }
3205
3359
  /**
@@ -3227,7 +3381,6 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3227
3381
  this.validateUtypeOrThrow();
3228
3382
  const permit = await this.getResolvedPermit();
3229
3383
  PermitUtils.validate(permit);
3230
- PermitUtils.isValid(permit);
3231
3384
  const chainId = permit._signedDomain.chainId;
3232
3385
  let unsealed;
3233
3386
  if (chainId === hardhat2.id) {
@@ -3238,6 +3391,9 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3238
3391
  return convertViaUtype(this.utype, unsealed);
3239
3392
  }
3240
3393
  };
3394
+ var UINT_TYPE_MASK = 0x7fn;
3395
+ var TYPE_BYTE_OFFSET = 8n;
3396
+ var getEncryptionTypeFromCtHash = (ctHash) => Number(ctHash >> TYPE_BYTE_OFFSET & UINT_TYPE_MASK);
3241
3397
  async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
3242
3398
  let allowed;
3243
3399
  let error;
@@ -3275,7 +3431,13 @@ async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
3275
3431
  message: `mocks decryptForTx call failed: ACL Access Denied (NotAllowed)`
3276
3432
  });
3277
3433
  }
3278
- const packed = viem.encodePacked(["uint256", "uint256"], [BigInt(ctHash), decryptedValue]);
3434
+ const chainId = publicClient.chain?.id ?? await publicClient.getChainId();
3435
+ const normalizedCtHash = BigInt(ctHash);
3436
+ const encryptionType = getEncryptionTypeFromCtHash(normalizedCtHash);
3437
+ const packed = viem.encodePacked(
3438
+ ["uint256", "uint32", "uint64", "uint256"],
3439
+ [decryptedValue, encryptionType, BigInt(chainId), normalizedCtHash]
3440
+ );
3279
3441
  const messageHash = viem.keccak256(packed);
3280
3442
  const signature = await accounts.sign({
3281
3443
  hash: messageHash,
@@ -3288,7 +3450,7 @@ async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
3288
3450
  signature
3289
3451
  };
3290
3452
  }
3291
- function normalizeSignature(signature) {
3453
+ function normalizeTnSignature(signature) {
3292
3454
  if (typeof signature !== "string") {
3293
3455
  throw new CofheError({
3294
3456
  code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
@@ -3344,141 +3506,384 @@ function parseDecryptedBytesToBigInt(decrypted) {
3344
3506
  }
3345
3507
  return BigInt(`0x${hex}`);
3346
3508
  }
3347
- function assertTnDecryptResponse(value) {
3509
+
3510
+ // core/decrypt/tnDecryptV2.ts
3511
+ var POLL_INTERVAL_MS2 = 1e3;
3512
+ var POLL_MAX_INTERVAL_MS2 = 1e4;
3513
+ var DECRYPT_TIMEOUT_MS = 5 * 60 * 1e3;
3514
+ var SUBMIT_RETRY_INTERVAL_MS2 = 1e3;
3515
+ function assertDecryptSubmitResponseV2(value) {
3348
3516
  if (value == null || typeof value !== "object") {
3349
3517
  throw new CofheError({
3350
3518
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3351
- message: "decrypt response must be a JSON object",
3519
+ message: "decrypt submit response must be a JSON object",
3352
3520
  context: {
3353
3521
  value
3354
3522
  }
3355
3523
  });
3356
3524
  }
3357
3525
  const v = value;
3358
- const decrypted = v.decrypted;
3359
- const signature = v.signature;
3360
- const encryptionType = v.encryption_type;
3361
- const errorMessage = v.error_message;
3362
- if (!Array.isArray(decrypted)) {
3526
+ if (v.request_id !== null && typeof v.request_id !== "string") {
3363
3527
  throw new CofheError({
3364
- code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
3365
- message: "decrypt response missing <decrypted> byte array",
3366
- context: { decryptResponse: value }
3528
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3529
+ message: "decrypt submit response has invalid request_id",
3530
+ context: {
3531
+ value
3532
+ }
3367
3533
  });
3368
3534
  }
3369
- if (typeof signature !== "string") {
3535
+ return {
3536
+ request_id: v.request_id ?? null,
3537
+ status: typeof v.status === "string" ? v.status : void 0,
3538
+ is_succeed: typeof v.is_succeed === "boolean" ? v.is_succeed : void 0,
3539
+ decrypted: Array.isArray(v.decrypted) ? v.decrypted : void 0,
3540
+ signature: typeof v.signature === "string" ? v.signature : void 0,
3541
+ encryption_type: typeof v.encryption_type === "number" ? v.encryption_type : void 0,
3542
+ error_message: typeof v.error_message === "string" || v.error_message === null ? v.error_message : void 0,
3543
+ message: typeof v.message === "string" ? v.message : void 0
3544
+ };
3545
+ }
3546
+ function parseCompletedDecryptResponseV2(params) {
3547
+ const { value, thresholdNetworkUrl, requestId } = params;
3548
+ if (value.is_succeed === false) {
3549
+ const errorMessage = value.error_message || "Unknown error";
3370
3550
  throw new CofheError({
3371
- code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
3372
- message: "decrypt response missing <signature> string",
3373
- context: { decryptResponse: value }
3551
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3552
+ message: `decrypt request failed: ${errorMessage}`,
3553
+ context: {
3554
+ thresholdNetworkUrl,
3555
+ requestId,
3556
+ response: value
3557
+ }
3374
3558
  });
3375
3559
  }
3376
- if (typeof encryptionType !== "number") {
3560
+ if (value.error_message) {
3377
3561
  throw new CofheError({
3378
3562
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3379
- message: "decrypt response missing <encryption_type> number",
3380
- context: { decryptResponse: value }
3563
+ message: `decrypt request failed: ${value.error_message}`,
3564
+ context: {
3565
+ thresholdNetworkUrl,
3566
+ requestId,
3567
+ response: value
3568
+ }
3381
3569
  });
3382
3570
  }
3383
- if (!(typeof errorMessage === "string" || errorMessage === null)) {
3571
+ if (!Array.isArray(value.decrypted)) {
3384
3572
  throw new CofheError({
3385
- code: "DECRYPT_FAILED" /* DecryptFailed */,
3386
- message: "decrypt response field <error_message> must be string or null",
3387
- context: { decryptResponse: value }
3573
+ code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
3574
+ message: "decrypt completed but response missing <decrypted> byte array",
3575
+ context: {
3576
+ thresholdNetworkUrl,
3577
+ requestId,
3578
+ response: value
3579
+ }
3388
3580
  });
3389
3581
  }
3390
- return {
3391
- decrypted,
3392
- signature,
3393
- encryption_type: encryptionType,
3394
- error_message: errorMessage
3395
- };
3582
+ const decryptedValue = parseDecryptedBytesToBigInt(value.decrypted);
3583
+ const signature = normalizeTnSignature(value.signature);
3584
+ return { decryptedValue, signature };
3396
3585
  }
3397
- async function tnDecrypt(ctHash, chainId, permission, thresholdNetworkUrl) {
3398
- const body = {
3399
- ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
3400
- host_chain_id: chainId
3401
- };
3402
- if (permission) {
3403
- body.permit = permission;
3404
- }
3405
- let response;
3406
- try {
3407
- response = await fetch(`${thresholdNetworkUrl}/decrypt`, {
3408
- method: "POST",
3409
- headers: {
3410
- "Content-Type": "application/json"
3411
- },
3412
- body: JSON.stringify(body)
3586
+ function assertDecryptStatusResponseV2(value) {
3587
+ if (value == null || typeof value !== "object") {
3588
+ throw new CofheError({
3589
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3590
+ message: "decrypt status response must be a JSON object",
3591
+ context: {
3592
+ value
3593
+ }
3413
3594
  });
3414
- } catch (e) {
3595
+ }
3596
+ const v = value;
3597
+ const requestId = v.request_id;
3598
+ const status = v.status;
3599
+ const submittedAt = v.submitted_at;
3600
+ if (typeof requestId !== "string" || requestId.trim().length === 0) {
3415
3601
  throw new CofheError({
3416
3602
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3417
- message: `decrypt request failed`,
3418
- hint: "Ensure the threshold network URL is valid and reachable.",
3419
- cause: e instanceof Error ? e : void 0,
3603
+ message: "decrypt status response missing request_id",
3420
3604
  context: {
3421
- thresholdNetworkUrl,
3422
- body
3605
+ value
3423
3606
  }
3424
3607
  });
3425
3608
  }
3426
- const responseText = await response.text();
3427
- if (!response.ok) {
3428
- let errorMessage = response.statusText || `HTTP ${response.status}`;
3429
- try {
3430
- const errorBody = JSON.parse(responseText);
3431
- const maybeMessage = errorBody.error_message || errorBody.message;
3432
- if (typeof maybeMessage === "string" && maybeMessage.length > 0)
3433
- errorMessage = maybeMessage;
3434
- } catch {
3435
- const trimmed = responseText.trim();
3436
- if (trimmed.length > 0)
3437
- errorMessage = trimmed;
3438
- }
3609
+ if (status !== "PROCESSING" && status !== "COMPLETED") {
3439
3610
  throw new CofheError({
3440
3611
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3441
- message: `decrypt request failed: ${errorMessage}`,
3442
- hint: "Check the threshold network URL and request parameters.",
3612
+ message: "decrypt status response has invalid status",
3443
3613
  context: {
3444
- thresholdNetworkUrl,
3445
- status: response.status,
3446
- statusText: response.statusText,
3447
- body,
3448
- responseText
3614
+ value,
3615
+ status
3449
3616
  }
3450
3617
  });
3451
3618
  }
3452
- let rawJson;
3453
- try {
3454
- rawJson = JSON.parse(responseText);
3455
- } catch (e) {
3619
+ if (typeof submittedAt !== "string" || submittedAt.trim().length === 0) {
3456
3620
  throw new CofheError({
3457
3621
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3458
- message: `Failed to parse decrypt response`,
3459
- cause: e instanceof Error ? e : void 0,
3622
+ message: "decrypt status response missing submitted_at",
3460
3623
  context: {
3461
- thresholdNetworkUrl,
3462
- body,
3463
- responseText
3624
+ value
3464
3625
  }
3465
3626
  });
3466
3627
  }
3467
- const decryptResponse = assertTnDecryptResponse(rawJson);
3468
- if (decryptResponse.error_message) {
3628
+ return value;
3629
+ }
3630
+ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
3631
+ const body = {
3632
+ ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
3633
+ host_chain_id: chainId
3634
+ };
3635
+ if (permission) {
3636
+ body.permit = permission;
3637
+ }
3638
+ let attemptIndex = 0;
3639
+ for (; ; ) {
3640
+ let response;
3641
+ try {
3642
+ response = await fetch(`${thresholdNetworkUrl}/v2/decrypt`, {
3643
+ method: "POST",
3644
+ headers: {
3645
+ "Content-Type": "application/json"
3646
+ },
3647
+ body: JSON.stringify(body)
3648
+ });
3649
+ } catch (e) {
3650
+ throw new CofheError({
3651
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3652
+ message: `decrypt request failed`,
3653
+ hint: "Ensure the threshold network URL is valid and reachable.",
3654
+ cause: e instanceof Error ? e : void 0,
3655
+ context: {
3656
+ thresholdNetworkUrl,
3657
+ body,
3658
+ attemptIndex
3659
+ }
3660
+ });
3661
+ }
3662
+ if (!response.ok) {
3663
+ let errorMessage = `HTTP ${response.status}`;
3664
+ try {
3665
+ const errorBody = await response.json();
3666
+ const maybeMessage = errorBody.error_message || errorBody.message;
3667
+ if (typeof maybeMessage === "string" && maybeMessage.length > 0)
3668
+ errorMessage = maybeMessage;
3669
+ } catch {
3670
+ errorMessage = response.statusText || errorMessage;
3671
+ }
3672
+ throw new CofheError({
3673
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3674
+ message: `decrypt request failed: ${errorMessage}`,
3675
+ hint: "Check the threshold network URL and request parameters.",
3676
+ context: {
3677
+ thresholdNetworkUrl,
3678
+ status: response.status,
3679
+ statusText: response.statusText,
3680
+ body,
3681
+ attemptIndex
3682
+ }
3683
+ });
3684
+ }
3685
+ let submitResponse;
3686
+ if (response.status !== 204) {
3687
+ let rawJson;
3688
+ try {
3689
+ rawJson = await response.json();
3690
+ } catch (e) {
3691
+ throw new CofheError({
3692
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3693
+ message: `Failed to parse decrypt submit response`,
3694
+ cause: e instanceof Error ? e : void 0,
3695
+ context: {
3696
+ thresholdNetworkUrl,
3697
+ body,
3698
+ attemptIndex
3699
+ }
3700
+ });
3701
+ }
3702
+ submitResponse = assertDecryptSubmitResponseV2(rawJson);
3703
+ if (Array.isArray(submitResponse.decrypted) && typeof submitResponse.signature === "string") {
3704
+ return {
3705
+ kind: "completed",
3706
+ ...parseCompletedDecryptResponseV2({
3707
+ value: submitResponse,
3708
+ thresholdNetworkUrl,
3709
+ requestId: submitResponse.request_id
3710
+ })
3711
+ };
3712
+ }
3713
+ if (submitResponse.request_id) {
3714
+ return { kind: "request_id", requestId: submitResponse.request_id };
3715
+ }
3716
+ }
3717
+ if (response.status === 204) {
3718
+ const elapsedMs = Date.now() - overallStartTime;
3719
+ if (elapsedMs > DECRYPT_TIMEOUT_MS) {
3720
+ throw new CofheError({
3721
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3722
+ message: `decrypt submit retried without receiving request_id for ${DECRYPT_TIMEOUT_MS}ms`,
3723
+ hint: "The ciphertext may still be propagating. Try again later.",
3724
+ context: {
3725
+ thresholdNetworkUrl,
3726
+ body,
3727
+ attemptIndex,
3728
+ timeoutMs: DECRYPT_TIMEOUT_MS,
3729
+ submitResponse,
3730
+ status: response.status
3731
+ }
3732
+ });
3733
+ }
3734
+ onPoll?.({
3735
+ operation: "decrypt",
3736
+ requestId: "",
3737
+ attemptIndex,
3738
+ elapsedMs,
3739
+ intervalMs: SUBMIT_RETRY_INTERVAL_MS2,
3740
+ timeoutMs: DECRYPT_TIMEOUT_MS
3741
+ });
3742
+ await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS2));
3743
+ attemptIndex += 1;
3744
+ continue;
3745
+ }
3469
3746
  throw new CofheError({
3470
3747
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3471
- message: `decrypt request failed: ${decryptResponse.error_message}`,
3748
+ message: `decrypt submit response missing request_id`,
3472
3749
  context: {
3473
3750
  thresholdNetworkUrl,
3474
3751
  body,
3475
- decryptResponse
3752
+ submitResponse,
3753
+ attemptIndex
3476
3754
  }
3477
3755
  });
3478
3756
  }
3479
- const decryptedValue = parseDecryptedBytesToBigInt(decryptResponse.decrypted);
3480
- const signature = normalizeSignature(decryptResponse.signature);
3481
- return { decryptedValue, signature };
3757
+ }
3758
+ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
3759
+ let attemptIndex = 0;
3760
+ while (true) {
3761
+ const elapsedMs = Date.now() - overallStartTime;
3762
+ const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
3763
+ minIntervalMs: POLL_INTERVAL_MS2,
3764
+ maxIntervalMs: POLL_MAX_INTERVAL_MS2
3765
+ });
3766
+ onPoll?.({
3767
+ operation: "decrypt",
3768
+ requestId,
3769
+ attemptIndex,
3770
+ elapsedMs,
3771
+ intervalMs,
3772
+ timeoutMs: DECRYPT_TIMEOUT_MS
3773
+ });
3774
+ if (elapsedMs > DECRYPT_TIMEOUT_MS) {
3775
+ throw new CofheError({
3776
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3777
+ message: `decrypt polling timed out after ${DECRYPT_TIMEOUT_MS}ms`,
3778
+ hint: "The request may still be processing. Try again later.",
3779
+ context: {
3780
+ thresholdNetworkUrl,
3781
+ requestId,
3782
+ timeoutMs: DECRYPT_TIMEOUT_MS
3783
+ }
3784
+ });
3785
+ }
3786
+ let response;
3787
+ try {
3788
+ response = await fetch(`${thresholdNetworkUrl}/v2/decrypt/${requestId}`, {
3789
+ method: "GET",
3790
+ headers: {
3791
+ "Content-Type": "application/json"
3792
+ }
3793
+ });
3794
+ } catch (e) {
3795
+ throw new CofheError({
3796
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3797
+ message: `decrypt status poll failed`,
3798
+ hint: "Ensure the threshold network URL is valid and reachable.",
3799
+ cause: e instanceof Error ? e : void 0,
3800
+ context: {
3801
+ thresholdNetworkUrl,
3802
+ requestId
3803
+ }
3804
+ });
3805
+ }
3806
+ if (response.status === 404) {
3807
+ throw new CofheError({
3808
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3809
+ message: `decrypt request not found: ${requestId}`,
3810
+ hint: "The request may have expired or been invalid.",
3811
+ context: {
3812
+ thresholdNetworkUrl,
3813
+ requestId
3814
+ }
3815
+ });
3816
+ }
3817
+ if (!response.ok) {
3818
+ let errorMessage = `HTTP ${response.status}`;
3819
+ try {
3820
+ const errorBody = await response.json();
3821
+ const maybeMessage = errorBody.error_message || errorBody.message;
3822
+ if (typeof maybeMessage === "string" && maybeMessage.length > 0)
3823
+ errorMessage = maybeMessage;
3824
+ } catch {
3825
+ errorMessage = response.statusText || errorMessage;
3826
+ }
3827
+ throw new CofheError({
3828
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3829
+ message: `decrypt status poll failed: ${errorMessage}`,
3830
+ context: {
3831
+ thresholdNetworkUrl,
3832
+ requestId,
3833
+ status: response.status,
3834
+ statusText: response.statusText
3835
+ }
3836
+ });
3837
+ }
3838
+ let rawJson;
3839
+ try {
3840
+ rawJson = await response.json();
3841
+ } catch (e) {
3842
+ throw new CofheError({
3843
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3844
+ message: `Failed to parse decrypt status response`,
3845
+ cause: e instanceof Error ? e : void 0,
3846
+ context: {
3847
+ thresholdNetworkUrl,
3848
+ requestId
3849
+ }
3850
+ });
3851
+ }
3852
+ const statusResponse = assertDecryptStatusResponseV2(rawJson);
3853
+ if (statusResponse.status === "COMPLETED") {
3854
+ return parseCompletedDecryptResponseV2({
3855
+ value: statusResponse,
3856
+ thresholdNetworkUrl,
3857
+ requestId
3858
+ });
3859
+ }
3860
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
3861
+ attemptIndex += 1;
3862
+ }
3863
+ throw new CofheError({
3864
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3865
+ message: "Polling loop exited unexpectedly",
3866
+ context: {
3867
+ thresholdNetworkUrl,
3868
+ requestId
3869
+ }
3870
+ });
3871
+ }
3872
+ async function tnDecryptV2(params) {
3873
+ const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
3874
+ const overallStartTime = Date.now();
3875
+ const submitResult = await submitDecryptRequestV2(
3876
+ thresholdNetworkUrl,
3877
+ ctHash,
3878
+ chainId,
3879
+ permission,
3880
+ overallStartTime,
3881
+ onPoll
3882
+ );
3883
+ if (submitResult.kind === "completed") {
3884
+ return submitResult;
3885
+ }
3886
+ return await pollDecryptStatusV2(thresholdNetworkUrl, submitResult.requestId, overallStartTime, onPoll);
3482
3887
  }
3483
3888
 
3484
3889
  // core/decrypt/decryptForTxBuilder.ts
@@ -3487,6 +3892,7 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3487
3892
  permitHash;
3488
3893
  permit;
3489
3894
  permitSelection = "unset";
3895
+ pollCallback;
3490
3896
  constructor(params) {
3491
3897
  super({
3492
3898
  config: params.config,
@@ -3512,6 +3918,10 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3512
3918
  getAccount() {
3513
3919
  return this.account;
3514
3920
  }
3921
+ onPoll(callback) {
3922
+ this.pollCallback = callback;
3923
+ return this;
3924
+ }
3515
3925
  withPermit(permitOrPermitHash) {
3516
3926
  if (this.permitSelection === "with-permit") {
3517
3927
  throw new CofheError({
@@ -3639,7 +4049,13 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3639
4049
  this.assertPublicClient();
3640
4050
  const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
3641
4051
  const permission = permit ? PermitUtils.getPermission(permit, true) : null;
3642
- const { decryptedValue, signature } = await tnDecrypt(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
4052
+ const { decryptedValue, signature } = await tnDecryptV2({
4053
+ ctHash: this.ctHash,
4054
+ chainId: this.chainId,
4055
+ permission,
4056
+ thresholdNetworkUrl,
4057
+ onPoll: this.pollCallback
4058
+ });
3643
4059
  return {
3644
4060
  ctHash: this.ctHash,
3645
4061
  decryptedValue,
@@ -3657,7 +4073,6 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3657
4073
  const permit = await this.getResolvedPermit();
3658
4074
  if (permit !== null) {
3659
4075
  PermitUtils.validate(permit);
3660
- PermitUtils.isValid(permit);
3661
4076
  const chainId = permit._signedDomain.chainId;
3662
4077
  if (chainId === hardhat2.id) {
3663
4078
  return await this.mocksDecryptForTx(permit);
@@ -3678,6 +4093,35 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3678
4093
  }
3679
4094
  }
3680
4095
  };
4096
+ var decryptResultSignerAbi = viem.parseAbi(["function decryptResultSigner() view returns (address)"]);
4097
+ var UINT_TYPE_MASK2 = 0x7fn;
4098
+ var TYPE_BYTE_OFFSET2 = 8n;
4099
+ var getEncryptionTypeFromCtHash2 = (ctHash) => Number(ctHash >> TYPE_BYTE_OFFSET2 & UINT_TYPE_MASK2);
4100
+ var buildDecryptResultHash = (ctHash, cleartext, chainId) => {
4101
+ const encryptionType = getEncryptionTypeFromCtHash2(ctHash);
4102
+ return viem.keccak256(
4103
+ viem.encodePacked(["uint256", "uint32", "uint64", "uint256"], [cleartext, encryptionType, BigInt(chainId), ctHash])
4104
+ );
4105
+ };
4106
+ async function verifyDecryptResult(handle, cleartext, signature, publicClient) {
4107
+ const chainId = publicClient.chain?.id ?? await publicClient.getChainId();
4108
+ const expectedSigner = await publicClient.readContract({
4109
+ address: TASK_MANAGER_ADDRESS,
4110
+ abi: decryptResultSignerAbi,
4111
+ functionName: "decryptResultSigner",
4112
+ args: []
4113
+ });
4114
+ if (viem.isAddressEqual(expectedSigner, viem.zeroAddress))
4115
+ return true;
4116
+ const ctHash = BigInt(handle);
4117
+ const messageHash = buildDecryptResultHash(ctHash, cleartext, chainId);
4118
+ try {
4119
+ const recovered = await viem.recoverAddress({ hash: messageHash, signature });
4120
+ return viem.isAddressEqual(recovered, expectedSigner);
4121
+ } catch {
4122
+ return false;
4123
+ }
4124
+ }
3681
4125
 
3682
4126
  // core/client.ts
3683
4127
  var InitialConnectStore = {
@@ -3796,6 +4240,11 @@ function createCofheClientBase(opts) {
3796
4240
  requireConnected: _requireConnected
3797
4241
  });
3798
4242
  }
4243
+ function verifyDecryptResult2(handle, cleartext, signature) {
4244
+ _requireConnected();
4245
+ const { publicClient } = connectStore.getState();
4246
+ return verifyDecryptResult(handle, cleartext, signature, publicClient);
4247
+ }
3799
4248
  const _getChainIdAndAccount = (chainId, account) => {
3800
4249
  const state = connectStore.getState();
3801
4250
  const _chainId = chainId ?? state.chainId;
@@ -3847,19 +4296,19 @@ function createCofheClientBase(opts) {
3847
4296
  return permits.getOrCreateSharingPermit(publicClient, walletClient, options, _chainId, _account);
3848
4297
  },
3849
4298
  // Retrieval methods (auto-fill chainId/account)
3850
- getPermit: async (hash, chainId, account) => {
4299
+ getPermit: (hash, chainId, account) => {
3851
4300
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
3852
4301
  return permits.getPermit(_chainId, _account, hash);
3853
4302
  },
3854
- getPermits: async (chainId, account) => {
4303
+ getPermits: (chainId, account) => {
3855
4304
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
3856
4305
  return permits.getPermits(_chainId, _account);
3857
4306
  },
3858
- getActivePermit: async (chainId, account) => {
4307
+ getActivePermit: (chainId, account) => {
3859
4308
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
3860
4309
  return permits.getActivePermit(_chainId, _account);
3861
4310
  },
3862
- getActivePermitHash: async (chainId, account) => {
4311
+ getActivePermitHash: (chainId, account) => {
3863
4312
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
3864
4313
  return permits.getActivePermitHash(_chainId, _account);
3865
4314
  },
@@ -3878,6 +4327,7 @@ function createCofheClientBase(opts) {
3878
4327
  },
3879
4328
  // Utils (no context needed)
3880
4329
  getHash: permits.getHash,
4330
+ export: permits.export,
3881
4331
  serialize: permits.serialize,
3882
4332
  deserialize: permits.deserialize
3883
4333
  };
@@ -3906,6 +4356,7 @@ function createCofheClientBase(opts) {
3906
4356
  */
3907
4357
  decryptHandle: decryptForView,
3908
4358
  decryptForTx,
4359
+ verifyDecryptResult: verifyDecryptResult2,
3909
4360
  permits: clientPermits
3910
4361
  // Add SDK-specific methods below that require connection
3911
4362
  // Example:
@@ -3915,12 +4366,19 @@ function createCofheClientBase(opts) {
3915
4366
  // },
3916
4367
  };
3917
4368
  }
3918
- var createWebStorage = () => {
4369
+
4370
+ // web/const.ts
4371
+ var hasDOM = typeof globalThis?.document !== "undefined" && typeof globalThis?.window !== "undefined";
4372
+
4373
+ // web/storage.ts
4374
+ var createWebStorage = (opts = { enableLog: false }) => {
4375
+ if (!hasDOM)
4376
+ throw new Error("createWebStorage can only be used in a browser environment");
3919
4377
  const client = iframeSharedStorage.constructClient({
3920
4378
  iframe: {
3921
4379
  src: "https://iframe-shared-storage.vercel.app/hub.html",
3922
4380
  messagingOptions: {
3923
- enableLog: "both"
4381
+ enableLog: opts.enableLog ? "both" : void 0
3924
4382
  },
3925
4383
  iframeReadyTimeoutMs: 3e4,
3926
4384
  // if the iframe is not initied during this interval AND a reuqest is made, such request will throw an error
@@ -3943,6 +4401,16 @@ var createWebStorage = () => {
3943
4401
  }
3944
4402
  };
3945
4403
  };
4404
+ function createSsrStorage() {
4405
+ console.warn("using no-op server-side SSR storage");
4406
+ return {
4407
+ getItem: async () => null,
4408
+ setItem: async () => {
4409
+ },
4410
+ removeItem: async () => {
4411
+ }
4412
+ };
4413
+ }
3946
4414
 
3947
4415
  // web/workerManager.ts
3948
4416
  var ZkProveWorkerManager = class {
@@ -3968,7 +4436,7 @@ var ZkProveWorkerManager = class {
3968
4436
  return;
3969
4437
  }
3970
4438
  try {
3971
- this.worker = new Worker(new URL("./zkProve.worker.js", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.src || new URL('out.js', document.baseURI).href))), { type: "module" });
4439
+ this.worker = new Worker(new URL("./zkProve.worker.js", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('out.js', document.baseURI).href))), { type: "module" });
3972
4440
  } catch (error) {
3973
4441
  reject(new Error(`Failed to create worker: ${error}`));
3974
4442
  return;
@@ -4098,16 +4566,22 @@ var fromHexString2 = (hexString) => {
4098
4566
  return new Uint8Array();
4099
4567
  return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
4100
4568
  };
4569
+ var _deserializeTfhePublicKey = (buff) => {
4570
+ return init.TfheCompactPublicKey.safe_deserialize(fromHexString2(buff), TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT);
4571
+ };
4572
+ var _deserializeCompactPkeCrs = (buff) => {
4573
+ return init.CompactPkeCrs.safe_deserialize(fromHexString2(buff), TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT);
4574
+ };
4101
4575
  var tfhePublicKeyDeserializer = (buff) => {
4102
- init.TfheCompactPublicKey.deserialize(fromHexString2(buff));
4576
+ _deserializeTfhePublicKey(buff);
4103
4577
  };
4104
4578
  var compactPkeCrsDeserializer = (buff) => {
4105
- init.CompactPkeCrs.deserialize(fromHexString2(buff));
4579
+ _deserializeCompactPkeCrs(buff);
4106
4580
  };
4107
4581
  var zkBuilderAndCrsGenerator = (fhe, crs) => {
4108
- const fhePublicKey = init.TfheCompactPublicKey.deserialize(fromHexString2(fhe));
4582
+ const fhePublicKey = _deserializeTfhePublicKey(fhe);
4109
4583
  const zkBuilder = init.ProvenCompactCiphertextList.builder(fhePublicKey);
4110
- const zkCrs = init.CompactPkeCrs.deserialize(fromHexString2(crs));
4584
+ const zkCrs = _deserializeCompactPkeCrs(crs);
4111
4585
  return { zkBuilder, zkCrs };
4112
4586
  };
4113
4587
  async function zkProveWithWorker2(fheKeyHex, crsHex, items, metadata) {
@@ -4122,7 +4596,7 @@ function createCofheConfig(config) {
4122
4596
  return createCofheConfigBase({
4123
4597
  environment: "web",
4124
4598
  ...config,
4125
- fheKeyStorage: config.fheKeyStorage === null ? null : config.fheKeyStorage ?? createWebStorage()
4599
+ fheKeyStorage: config.fheKeyStorage === null ? null : config.fheKeyStorage ?? (hasDOM ? createWebStorage() : createSsrStorage())
4126
4600
  });
4127
4601
  }
4128
4602
  function createCofheClient(config) {
@@ -4152,4 +4626,6 @@ exports.areWorkersAvailable = areWorkersAvailable;
4152
4626
  exports.createCofheClient = createCofheClient;
4153
4627
  exports.createCofheClientWithCustomWorker = createCofheClientWithCustomWorker;
4154
4628
  exports.createCofheConfig = createCofheConfig;
4629
+ exports.createSsrStorage = createSsrStorage;
4630
+ exports.hasDOM = hasDOM;
4155
4631
  exports.terminateWorker = terminateWorker;