@cofhe/sdk 0.4.0 → 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 (95) hide show
  1. package/CHANGELOG.md +32 -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 +11 -1
  9. package/core/clientTypes.ts +3 -1
  10. package/core/consts.ts +9 -0
  11. package/core/decrypt/cofheMocksDecryptForTx.ts +14 -3
  12. package/core/decrypt/decryptForTxBuilder.ts +16 -2
  13. package/core/decrypt/decryptForViewBuilder.ts +14 -7
  14. package/core/decrypt/polling.ts +14 -0
  15. package/core/decrypt/tnDecryptV2.ts +250 -110
  16. package/core/decrypt/tnSealOutputV2.ts +245 -104
  17. package/core/decrypt/verifyDecryptResult.ts +65 -0
  18. package/core/encrypt/cofheMocksZkVerifySign.ts +6 -6
  19. package/core/encrypt/zkPackProveVerify.ts +10 -19
  20. package/core/fetchKeys.ts +0 -2
  21. package/core/index.ts +9 -1
  22. package/core/keyStore.ts +5 -2
  23. package/core/permits.ts +5 -0
  24. package/core/{client.test.ts → test/client.test.ts} +7 -7
  25. package/core/{config.test.ts → test/config.test.ts} +1 -1
  26. package/core/test/decrypt.test.ts +252 -0
  27. package/core/test/decryptBuilders.test.ts +390 -0
  28. package/core/{encrypt → test}/encryptInputsBuilder.test.ts +61 -6
  29. package/core/{fetchKeys.test.ts → test/fetchKeys.test.ts} +3 -3
  30. package/core/{keyStore.test.ts → test/keyStore.test.ts} +5 -3
  31. package/core/{permits.test.ts → test/permits.test.ts} +42 -1
  32. package/core/test/pollCallbacks.test.ts +563 -0
  33. package/core/types.ts +13 -0
  34. package/dist/chains.d.cts +2 -2
  35. package/dist/chains.d.ts +2 -2
  36. package/dist/chunk-4FP4V35O.js +13 -0
  37. package/dist/{chunk-NWDKXBIP.js → chunk-MRCKUMOS.js} +62 -22
  38. package/dist/{chunk-MXND5SVN.js → chunk-S7OKGLFD.js} +485 -207
  39. package/dist/{clientTypes-kkrRdawm.d.ts → clientTypes-BSbwairE.d.cts} +23 -6
  40. package/dist/{clientTypes-ACVWbrXL.d.cts → clientTypes-DDmcgZ0a.d.ts} +23 -6
  41. package/dist/core.cjs +561 -244
  42. package/dist/core.d.cts +24 -6
  43. package/dist/core.d.ts +24 -6
  44. package/dist/core.js +3 -2
  45. package/dist/node.cjs +566 -246
  46. package/dist/node.d.cts +3 -3
  47. package/dist/node.d.ts +3 -3
  48. package/dist/node.js +14 -7
  49. package/dist/{permit-MZ502UBl.d.cts → permit-DnVMDT5h.d.cts} +34 -4
  50. package/dist/{permit-MZ502UBl.d.ts → permit-DnVMDT5h.d.ts} +34 -4
  51. package/dist/permits.cjs +66 -29
  52. package/dist/permits.d.cts +18 -13
  53. package/dist/permits.d.ts +18 -13
  54. package/dist/permits.js +2 -1
  55. package/dist/web.cjs +588 -251
  56. package/dist/web.d.cts +8 -4
  57. package/dist/web.d.ts +8 -4
  58. package/dist/web.js +34 -11
  59. package/dist/zkProve.worker.cjs +6 -3
  60. package/dist/zkProve.worker.js +5 -3
  61. package/node/index.ts +13 -4
  62. package/node/test/client.test.ts +25 -0
  63. package/node/test/config.test.ts +16 -0
  64. package/node/test/inherited.test.ts +244 -0
  65. package/node/test/tfheinit.test.ts +56 -0
  66. package/package.json +24 -22
  67. package/permits/permit.ts +31 -5
  68. package/permits/sealing.ts +1 -1
  69. package/permits/{localstorage.test.ts → test/localstorage.test.ts} +2 -2
  70. package/permits/{permit.test.ts → test/permit.test.ts} +35 -1
  71. package/permits/{sealing.test.ts → test/sealing.test.ts} +1 -1
  72. package/permits/{store.test.ts → test/store.test.ts} +2 -2
  73. package/permits/{validation.test.ts → test/validation.test.ts} +82 -6
  74. package/permits/types.ts +1 -1
  75. package/permits/validation.ts +42 -2
  76. package/web/const.ts +2 -0
  77. package/web/index.ts +20 -6
  78. package/web/storage.ts +18 -3
  79. package/web/{client.web.test.ts → test/client.web.test.ts} +13 -1
  80. package/web/test/config.web.test.ts +16 -0
  81. package/web/test/inherited.web.test.ts +245 -0
  82. package/web/test/tfheinit.web.test.ts +62 -0
  83. package/web/{worker.config.web.test.ts → test/worker.config.web.test.ts} +1 -1
  84. package/web/{worker.output.web.test.ts → test/worker.output.web.test.ts} +1 -1
  85. package/web/{workerManager.test.ts → test/workerManager.test.ts} +1 -1
  86. package/web/{workerManager.web.test.ts → test/workerManager.web.test.ts} +1 -1
  87. package/web/zkProve.worker.ts +4 -3
  88. package/node/client.test.ts +0 -147
  89. package/node/config.test.ts +0 -68
  90. package/node/encryptInputs.test.ts +0 -155
  91. package/web/config.web.test.ts +0 -69
  92. package/web/encryptInputs.web.test.ts +0 -172
  93. package/web/worker.builder.web.test.ts +0 -148
  94. /package/dist/{types-YiAC4gig.d.cts → types-C07FK-cL.d.cts} +0 -0
  95. /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
2332
2358
  */
2359
+ isSignedAndNotExpired: (permit) => {
2360
+ return ValidationUtils.isSignedAndNotExpired(permit);
2361
+ },
2362
+ /**
2363
+ * Assert that permit is signed and not expired
2364
+ */
2365
+ assertSignedAndNotExpired: (permit) => {
2366
+ return ValidationUtils.assertSignedAndNotExpired(permit);
2367
+ },
2333
2368
  isValid: (permit) => {
2334
2369
  return ValidationUtils.isValid(permit);
2335
2370
  },
@@ -2507,6 +2542,9 @@ 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
  };
@@ -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,
@@ -3347,7 +3509,9 @@ function parseDecryptedBytesToBigInt(decrypted) {
3347
3509
 
3348
3510
  // core/decrypt/tnDecryptV2.ts
3349
3511
  var POLL_INTERVAL_MS2 = 1e3;
3350
- var POLL_TIMEOUT_MS2 = 5 * 60 * 1e3;
3512
+ var POLL_MAX_INTERVAL_MS2 = 1e4;
3513
+ var DECRYPT_TIMEOUT_MS = 5 * 60 * 1e3;
3514
+ var SUBMIT_RETRY_INTERVAL_MS2 = 1e3;
3351
3515
  function assertDecryptSubmitResponseV2(value) {
3352
3516
  if (value == null || typeof value !== "object") {
3353
3517
  throw new CofheError({
@@ -3359,16 +3523,65 @@ function assertDecryptSubmitResponseV2(value) {
3359
3523
  });
3360
3524
  }
3361
3525
  const v = value;
3362
- if (typeof v.request_id !== "string" || v.request_id.trim().length === 0) {
3526
+ if (v.request_id !== null && typeof v.request_id !== "string") {
3363
3527
  throw new CofheError({
3364
3528
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3365
- message: "decrypt submit response missing request_id",
3529
+ message: "decrypt submit response has invalid request_id",
3366
3530
  context: {
3367
3531
  value
3368
3532
  }
3369
3533
  });
3370
3534
  }
3371
- return { request_id: v.request_id };
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";
3550
+ throw new CofheError({
3551
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3552
+ message: `decrypt request failed: ${errorMessage}`,
3553
+ context: {
3554
+ thresholdNetworkUrl,
3555
+ requestId,
3556
+ response: value
3557
+ }
3558
+ });
3559
+ }
3560
+ if (value.error_message) {
3561
+ throw new CofheError({
3562
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3563
+ message: `decrypt request failed: ${value.error_message}`,
3564
+ context: {
3565
+ thresholdNetworkUrl,
3566
+ requestId,
3567
+ response: value
3568
+ }
3569
+ });
3570
+ }
3571
+ if (!Array.isArray(value.decrypted)) {
3572
+ throw new CofheError({
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
+ }
3580
+ });
3581
+ }
3582
+ const decryptedValue = parseDecryptedBytesToBigInt(value.decrypted);
3583
+ const signature = normalizeTnSignature(value.signature);
3584
+ return { decryptedValue, signature };
3372
3585
  }
3373
3586
  function assertDecryptStatusResponseV2(value) {
3374
3587
  if (value == null || typeof value !== "object") {
@@ -3414,7 +3627,7 @@ function assertDecryptStatusResponseV2(value) {
3414
3627
  }
3415
3628
  return value;
3416
3629
  }
3417
- async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission) {
3630
+ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
3418
3631
  const body = {
3419
3632
  ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
3420
3633
  host_chain_id: chainId
@@ -3422,79 +3635,151 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
3422
3635
  if (permission) {
3423
3636
  body.permit = permission;
3424
3637
  }
3425
- let response;
3426
- try {
3427
- response = await fetch(`${thresholdNetworkUrl}/v2/decrypt`, {
3428
- method: "POST",
3429
- headers: {
3430
- "Content-Type": "application/json"
3431
- },
3432
- body: JSON.stringify(body)
3433
- });
3434
- } catch (e) {
3435
- throw new CofheError({
3436
- code: "DECRYPT_FAILED" /* DecryptFailed */,
3437
- message: `decrypt request failed`,
3438
- hint: "Ensure the threshold network URL is valid and reachable.",
3439
- cause: e instanceof Error ? e : void 0,
3440
- context: {
3441
- thresholdNetworkUrl,
3442
- body
3443
- }
3444
- });
3445
- }
3446
- if (!response.ok) {
3447
- let errorMessage = `HTTP ${response.status}`;
3638
+ let attemptIndex = 0;
3639
+ for (; ; ) {
3640
+ let response;
3448
3641
  try {
3449
- const errorBody = await response.json();
3450
- const maybeMessage = errorBody.error_message || errorBody.message;
3451
- if (typeof maybeMessage === "string" && maybeMessage.length > 0)
3452
- errorMessage = maybeMessage;
3453
- } catch {
3454
- errorMessage = response.statusText || errorMessage;
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
+ });
3455
3661
  }
3456
- throw new CofheError({
3457
- code: "DECRYPT_FAILED" /* DecryptFailed */,
3458
- message: `decrypt request failed: ${errorMessage}`,
3459
- hint: "Check the threshold network URL and request parameters.",
3460
- context: {
3461
- thresholdNetworkUrl,
3462
- status: response.status,
3463
- statusText: response.statusText,
3464
- body
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;
3465
3671
  }
3466
- });
3467
- }
3468
- let rawJson;
3469
- try {
3470
- rawJson = await response.json();
3471
- } catch (e) {
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
+ }
3472
3746
  throw new CofheError({
3473
3747
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3474
- message: `Failed to parse decrypt submit response`,
3475
- cause: e instanceof Error ? e : void 0,
3748
+ message: `decrypt submit response missing request_id`,
3476
3749
  context: {
3477
3750
  thresholdNetworkUrl,
3478
- body
3751
+ body,
3752
+ submitResponse,
3753
+ attemptIndex
3479
3754
  }
3480
3755
  });
3481
3756
  }
3482
- const submitResponse = assertDecryptSubmitResponseV2(rawJson);
3483
- return submitResponse.request_id;
3484
3757
  }
3485
- async function pollDecryptStatusV2(thresholdNetworkUrl, requestId) {
3486
- const startTime = Date.now();
3487
- let completed = false;
3488
- while (!completed) {
3489
- if (Date.now() - startTime > POLL_TIMEOUT_MS2) {
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) {
3490
3775
  throw new CofheError({
3491
3776
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3492
- message: `decrypt polling timed out after ${POLL_TIMEOUT_MS2}ms`,
3777
+ message: `decrypt polling timed out after ${DECRYPT_TIMEOUT_MS}ms`,
3493
3778
  hint: "The request may still be processing. Try again later.",
3494
3779
  context: {
3495
3780
  thresholdNetworkUrl,
3496
3781
  requestId,
3497
- timeoutMs: POLL_TIMEOUT_MS2
3782
+ timeoutMs: DECRYPT_TIMEOUT_MS
3498
3783
  }
3499
3784
  });
3500
3785
  }
@@ -3566,45 +3851,14 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId) {
3566
3851
  }
3567
3852
  const statusResponse = assertDecryptStatusResponseV2(rawJson);
3568
3853
  if (statusResponse.status === "COMPLETED") {
3569
- if (statusResponse.is_succeed === false) {
3570
- const errorMessage = statusResponse.error_message || "Unknown error";
3571
- throw new CofheError({
3572
- code: "DECRYPT_FAILED" /* DecryptFailed */,
3573
- message: `decrypt request failed: ${errorMessage}`,
3574
- context: {
3575
- thresholdNetworkUrl,
3576
- requestId,
3577
- statusResponse
3578
- }
3579
- });
3580
- }
3581
- if (statusResponse.error_message) {
3582
- throw new CofheError({
3583
- code: "DECRYPT_FAILED" /* DecryptFailed */,
3584
- message: `decrypt request failed: ${statusResponse.error_message}`,
3585
- context: {
3586
- thresholdNetworkUrl,
3587
- requestId,
3588
- statusResponse
3589
- }
3590
- });
3591
- }
3592
- if (!Array.isArray(statusResponse.decrypted)) {
3593
- throw new CofheError({
3594
- code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
3595
- message: "decrypt completed but response missing <decrypted> byte array",
3596
- context: {
3597
- thresholdNetworkUrl,
3598
- requestId,
3599
- statusResponse
3600
- }
3601
- });
3602
- }
3603
- const decryptedValue = parseDecryptedBytesToBigInt(statusResponse.decrypted);
3604
- const signature = normalizeTnSignature(statusResponse.signature);
3605
- return { decryptedValue, signature };
3854
+ return parseCompletedDecryptResponseV2({
3855
+ value: statusResponse,
3856
+ thresholdNetworkUrl,
3857
+ requestId
3858
+ });
3606
3859
  }
3607
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS2));
3860
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
3861
+ attemptIndex += 1;
3608
3862
  }
3609
3863
  throw new CofheError({
3610
3864
  code: "DECRYPT_FAILED" /* DecryptFailed */,
@@ -3615,9 +3869,21 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId) {
3615
3869
  }
3616
3870
  });
3617
3871
  }
3618
- async function tnDecryptV2(ctHash, chainId, permission, thresholdNetworkUrl) {
3619
- const requestId = await submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission);
3620
- return await pollDecryptStatusV2(thresholdNetworkUrl, requestId);
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);
3621
3887
  }
3622
3888
 
3623
3889
  // core/decrypt/decryptForTxBuilder.ts
@@ -3626,6 +3892,7 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3626
3892
  permitHash;
3627
3893
  permit;
3628
3894
  permitSelection = "unset";
3895
+ pollCallback;
3629
3896
  constructor(params) {
3630
3897
  super({
3631
3898
  config: params.config,
@@ -3651,6 +3918,10 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3651
3918
  getAccount() {
3652
3919
  return this.account;
3653
3920
  }
3921
+ onPoll(callback) {
3922
+ this.pollCallback = callback;
3923
+ return this;
3924
+ }
3654
3925
  withPermit(permitOrPermitHash) {
3655
3926
  if (this.permitSelection === "with-permit") {
3656
3927
  throw new CofheError({
@@ -3778,7 +4049,13 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3778
4049
  this.assertPublicClient();
3779
4050
  const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
3780
4051
  const permission = permit ? PermitUtils.getPermission(permit, true) : null;
3781
- const { decryptedValue, signature } = await tnDecryptV2(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
+ });
3782
4059
  return {
3783
4060
  ctHash: this.ctHash,
3784
4061
  decryptedValue,
@@ -3796,7 +4073,6 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3796
4073
  const permit = await this.getResolvedPermit();
3797
4074
  if (permit !== null) {
3798
4075
  PermitUtils.validate(permit);
3799
- PermitUtils.isValid(permit);
3800
4076
  const chainId = permit._signedDomain.chainId;
3801
4077
  if (chainId === hardhat2.id) {
3802
4078
  return await this.mocksDecryptForTx(permit);
@@ -3817,6 +4093,35 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3817
4093
  }
3818
4094
  }
3819
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
+ }
3820
4125
 
3821
4126
  // core/client.ts
3822
4127
  var InitialConnectStore = {
@@ -3935,6 +4240,11 @@ function createCofheClientBase(opts) {
3935
4240
  requireConnected: _requireConnected
3936
4241
  });
3937
4242
  }
4243
+ function verifyDecryptResult2(handle, cleartext, signature) {
4244
+ _requireConnected();
4245
+ const { publicClient } = connectStore.getState();
4246
+ return verifyDecryptResult(handle, cleartext, signature, publicClient);
4247
+ }
3938
4248
  const _getChainIdAndAccount = (chainId, account) => {
3939
4249
  const state = connectStore.getState();
3940
4250
  const _chainId = chainId ?? state.chainId;
@@ -4017,6 +4327,7 @@ function createCofheClientBase(opts) {
4017
4327
  },
4018
4328
  // Utils (no context needed)
4019
4329
  getHash: permits.getHash,
4330
+ export: permits.export,
4020
4331
  serialize: permits.serialize,
4021
4332
  deserialize: permits.deserialize
4022
4333
  };
@@ -4045,6 +4356,7 @@ function createCofheClientBase(opts) {
4045
4356
  */
4046
4357
  decryptHandle: decryptForView,
4047
4358
  decryptForTx,
4359
+ verifyDecryptResult: verifyDecryptResult2,
4048
4360
  permits: clientPermits
4049
4361
  // Add SDK-specific methods below that require connection
4050
4362
  // Example:
@@ -4054,12 +4366,19 @@ function createCofheClientBase(opts) {
4054
4366
  // },
4055
4367
  };
4056
4368
  }
4057
- 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");
4058
4377
  const client = iframeSharedStorage.constructClient({
4059
4378
  iframe: {
4060
4379
  src: "https://iframe-shared-storage.vercel.app/hub.html",
4061
4380
  messagingOptions: {
4062
- enableLog: "both"
4381
+ enableLog: opts.enableLog ? "both" : void 0
4063
4382
  },
4064
4383
  iframeReadyTimeoutMs: 3e4,
4065
4384
  // if the iframe is not initied during this interval AND a reuqest is made, such request will throw an error
@@ -4082,6 +4401,16 @@ var createWebStorage = () => {
4082
4401
  }
4083
4402
  };
4084
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
+ }
4085
4414
 
4086
4415
  // web/workerManager.ts
4087
4416
  var ZkProveWorkerManager = class {
@@ -4107,7 +4436,7 @@ var ZkProveWorkerManager = class {
4107
4436
  return;
4108
4437
  }
4109
4438
  try {
4110
- 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" });
4111
4440
  } catch (error) {
4112
4441
  reject(new Error(`Failed to create worker: ${error}`));
4113
4442
  return;
@@ -4237,16 +4566,22 @@ var fromHexString2 = (hexString) => {
4237
4566
  return new Uint8Array();
4238
4567
  return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
4239
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
+ };
4240
4575
  var tfhePublicKeyDeserializer = (buff) => {
4241
- init.TfheCompactPublicKey.deserialize(fromHexString2(buff));
4576
+ _deserializeTfhePublicKey(buff);
4242
4577
  };
4243
4578
  var compactPkeCrsDeserializer = (buff) => {
4244
- init.CompactPkeCrs.deserialize(fromHexString2(buff));
4579
+ _deserializeCompactPkeCrs(buff);
4245
4580
  };
4246
4581
  var zkBuilderAndCrsGenerator = (fhe, crs) => {
4247
- const fhePublicKey = init.TfheCompactPublicKey.deserialize(fromHexString2(fhe));
4582
+ const fhePublicKey = _deserializeTfhePublicKey(fhe);
4248
4583
  const zkBuilder = init.ProvenCompactCiphertextList.builder(fhePublicKey);
4249
- const zkCrs = init.CompactPkeCrs.deserialize(fromHexString2(crs));
4584
+ const zkCrs = _deserializeCompactPkeCrs(crs);
4250
4585
  return { zkBuilder, zkCrs };
4251
4586
  };
4252
4587
  async function zkProveWithWorker2(fheKeyHex, crsHex, items, metadata) {
@@ -4261,7 +4596,7 @@ function createCofheConfig(config) {
4261
4596
  return createCofheConfigBase({
4262
4597
  environment: "web",
4263
4598
  ...config,
4264
- fheKeyStorage: config.fheKeyStorage === null ? null : config.fheKeyStorage ?? createWebStorage()
4599
+ fheKeyStorage: config.fheKeyStorage === null ? null : config.fheKeyStorage ?? (hasDOM ? createWebStorage() : createSsrStorage())
4265
4600
  });
4266
4601
  }
4267
4602
  function createCofheClient(config) {
@@ -4291,4 +4626,6 @@ exports.areWorkersAvailable = areWorkersAvailable;
4291
4626
  exports.createCofheClient = createCofheClient;
4292
4627
  exports.createCofheClientWithCustomWorker = createCofheClientWithCustomWorker;
4293
4628
  exports.createCofheConfig = createCofheConfig;
4629
+ exports.createSsrStorage = createSsrStorage;
4630
+ exports.hasDOM = hasDOM;
4294
4631
  exports.terminateWorker = terminateWorker;