@cofhe/sdk 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) 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 +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 +604 -256
  56. package/dist/web.d.cts +8 -4
  57. package/dist/web.d.ts +8 -4
  58. package/dist/web.js +49 -14
  59. package/dist/zkProve.worker.cjs +72 -64
  60. package/dist/zkProve.worker.js +71 -64
  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 +40 -11
  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 +94 -84
  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
@@ -9,31 +9,11 @@ var middleware = require('zustand/middleware');
9
9
  var immer = require('immer');
10
10
  var nacl = require('tweetnacl');
11
11
  var iframeSharedStorage = require('iframe-shared-storage');
12
- var init = require('tfhe');
13
12
 
14
13
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
15
14
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
16
15
 
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);
36
- var init__default = /*#__PURE__*/_interopDefault(init);
16
+ var nacl__default = /*#__PURE__*/_interopDefault(nacl);
37
17
 
38
18
  // core/client.ts
39
19
 
@@ -126,6 +106,16 @@ var bigintSafeJsonStringify = (value) => {
126
106
  };
127
107
  var isCofheError = (error) => error instanceof CofheError;
128
108
 
109
+ // core/consts.ts
110
+ var TASK_MANAGER_ADDRESS = "0xeA30c4B8b44078Bbf8a6ef5b9f1eC1626C7848D9";
111
+ var MOCKS_ZK_VERIFIER_ADDRESS = "0x0000000000000000000000000000000000005001";
112
+ var MOCKS_THRESHOLD_NETWORK_ADDRESS = "0x0000000000000000000000000000000000005002";
113
+ var MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY = "0x6C8D7F768A6BB4AAFE85E8A2F5A9680355239C7E14646ED62B044E39DE154512";
114
+ var MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
115
+ var TFHE_RS_ZK_MAX_BITS = 2048;
116
+ var TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT = BigInt(1 << 30);
117
+ var TFHE_RS_KEY_VERSION = 2;
118
+
129
119
  // core/types.ts
130
120
  var FheUintUTypes = [
131
121
  2 /* Uint8 */,
@@ -248,7 +238,6 @@ var MAX_UINT32 = 4294967295n;
248
238
  var MAX_UINT64 = 18446744073709551615n;
249
239
  var MAX_UINT128 = 340282366920938463463374607431768211455n;
250
240
  var MAX_UINT160 = 1461501637330902918203684832716283019655932542975n;
251
- var MAX_ENCRYPTABLE_BITS = 2048;
252
241
  var zkPack = (items, builder) => {
253
242
  let totalBits = 0;
254
243
  for (const item of items) {
@@ -312,14 +301,14 @@ var zkPack = (items, builder) => {
312
301
  }
313
302
  }
314
303
  }
315
- if (totalBits > MAX_ENCRYPTABLE_BITS) {
304
+ if (totalBits > TFHE_RS_ZK_MAX_BITS) {
316
305
  throw new CofheError({
317
306
  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}`,
307
+ message: `Total bits ${totalBits} exceeds ${TFHE_RS_ZK_MAX_BITS}`,
308
+ hint: `Ensure that the total bits of the items to encrypt does not exceed ${TFHE_RS_ZK_MAX_BITS}`,
320
309
  context: {
321
310
  totalBits,
322
- maxBits: MAX_ENCRYPTABLE_BITS,
311
+ maxBits: TFHE_RS_ZK_MAX_BITS,
323
312
  items
324
313
  }
325
314
  });
@@ -338,7 +327,7 @@ var zkProve = async (builder, crs, metadata) => {
338
327
  1
339
328
  // ZkComputeLoad.Verify
340
329
  );
341
- resolve(compactList.serialize());
330
+ resolve(compactList.safe_serialize(TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT));
342
331
  }, 0);
343
332
  });
344
333
  };
@@ -514,15 +503,6 @@ var MockZkVerifierAbi = [
514
503
  },
515
504
  { type: "error", name: "InvalidInputs", inputs: [] }
516
505
  ];
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
506
  function createMockZkVerifierSigner() {
527
507
  return viem.createWalletClient({
528
508
  chain: chains.hardhat,
@@ -564,14 +544,14 @@ async function cofheMocksCheckEncryptableBits(items) {
564
544
  }
565
545
  }
566
546
  }
567
- if (totalBits > MAX_ENCRYPTABLE_BITS) {
547
+ if (totalBits > TFHE_RS_ZK_MAX_BITS) {
568
548
  throw new CofheError({
569
549
  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}`,
550
+ message: `Total bits ${totalBits} exceeds ${TFHE_RS_ZK_MAX_BITS}`,
551
+ hint: `Ensure that the total bits of the items to encrypt does not exceed ${TFHE_RS_ZK_MAX_BITS}`,
572
552
  context: {
573
553
  totalBits,
574
- maxBits: MAX_ENCRYPTABLE_BITS,
554
+ maxBits: TFHE_RS_ZK_MAX_BITS,
575
555
  items
576
556
  }
577
557
  });
@@ -842,6 +822,7 @@ function getThresholdNetworkUrlOrThrow(config, chainId) {
842
822
  }
843
823
  return url;
844
824
  }
825
+ var KEYSTORE_NAME = `cofhesdk-keys-v${TFHE_RS_KEY_VERSION}`;
845
826
  function isValidPersistedState(state) {
846
827
  if (state && typeof state === "object") {
847
828
  if ("fhe" in state && "crs" in state) {
@@ -896,7 +877,7 @@ function createKeysStore(storage) {
896
877
  };
897
878
  const clearKeysStorage = async () => {
898
879
  if (storage) {
899
- await storage.removeItem("cofhesdk-keys");
880
+ await storage.removeItem(KEYSTORE_NAME);
900
881
  }
901
882
  };
902
883
  const rehydrateKeysStore = async () => {
@@ -926,7 +907,7 @@ function createStoreWithPersit(storage) {
926
907
  if (_error)
927
908
  throw new Error(`onRehydrateStorage: Error rehydrating keys store: ${_error}`);
928
909
  },
929
- name: "cofhesdk-keys",
910
+ name: KEYSTORE_NAME,
930
911
  storage: middleware.createJSONStorage(() => storage),
931
912
  merge: (persistedState, currentState) => {
932
913
  const persisted = isValidPersistedState(persistedState) ? persistedState : DEFAULT_KEYS_STORE;
@@ -1651,7 +1632,7 @@ var SealingKey = class _SealingKey {
1651
1632
  const ephemPublicKey = parsedData.public_key instanceof Uint8Array ? parsedData.public_key : new Uint8Array(parsedData.public_key);
1652
1633
  const dataToDecrypt = parsedData.data instanceof Uint8Array ? parsedData.data : new Uint8Array(parsedData.data);
1653
1634
  const privateKeyBytes = fromHexString(this.privateKey);
1654
- const decryptedMessage = nacl__namespace.box.open(dataToDecrypt, nonce, ephemPublicKey, privateKeyBytes);
1635
+ const decryptedMessage = nacl__default.default.box.open(dataToDecrypt, nonce, ephemPublicKey, privateKeyBytes);
1655
1636
  if (!decryptedMessage) {
1656
1637
  throw new Error("Failed to decrypt message");
1657
1638
  }
@@ -1684,9 +1665,9 @@ var SealingKey = class _SealingKey {
1684
1665
  static seal = (value, publicKey) => {
1685
1666
  isString(publicKey);
1686
1667
  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);
1668
+ const ephemeralKeyPair = nacl__default.default.box.keyPair();
1669
+ const nonce = nacl__default.default.randomBytes(nacl__default.default.box.nonceLength);
1670
+ const encryptedMessage = nacl__default.default.box(toBeArray(value), nonce, fromHexString(publicKey), ephemeralKeyPair.secretKey);
1690
1671
  return {
1691
1672
  data: encryptedMessage,
1692
1673
  public_key: ephemeralKeyPair.publicKey,
@@ -1695,7 +1676,7 @@ var SealingKey = class _SealingKey {
1695
1676
  };
1696
1677
  };
1697
1678
  var GenerateSealingKey = () => {
1698
- const sodiumKeypair = nacl__namespace.box.keyPair();
1679
+ const sodiumKeypair = nacl__default.default.box.keyPair();
1699
1680
  return new SealingKey(toHexString2(sodiumKeypair.secretKey), toHexString2(sodiumKeypair.publicKey));
1700
1681
  };
1701
1682
  var SerializedSealingPair = zod.z.object({
@@ -1853,9 +1834,9 @@ var ValidationUtils = {
1853
1834
  return false;
1854
1835
  },
1855
1836
  /**
1856
- * Overall validity checker of a permit
1837
+ * Checks that a permit is signed and not expired.
1857
1838
  */
1858
- isValid: (permit) => {
1839
+ isSignedAndNotExpired: (permit) => {
1859
1840
  if (ValidationUtils.isExpired(permit)) {
1860
1841
  return { valid: false, error: "expired" };
1861
1842
  }
@@ -1863,6 +1844,34 @@ var ValidationUtils = {
1863
1844
  return { valid: false, error: "not-signed" };
1864
1845
  }
1865
1846
  return { valid: true, error: null };
1847
+ },
1848
+ /**
1849
+ * Asserts that a permit is signed and not expired.
1850
+ *
1851
+ * Throws `Error` with message:
1852
+ * - `Permit is expired`
1853
+ * - `Permit is not signed`
1854
+ */
1855
+ assertSignedAndNotExpired: (permit) => {
1856
+ const result = ValidationUtils.isSignedAndNotExpired(permit);
1857
+ if (result.valid)
1858
+ return;
1859
+ if (result.error === "expired") {
1860
+ throw new Error("Permit is expired");
1861
+ }
1862
+ if (result.error === "not-signed") {
1863
+ throw new Error("Permit is not signed");
1864
+ }
1865
+ throw new Error("Permit is invalid");
1866
+ },
1867
+ isValid: (permit) => {
1868
+ const schema = permit.type === "self" ? SelfPermitValidator : permit.type === "sharing" ? SharingPermitValidator : permit.type === "recipient" ? ImportPermitValidator : null;
1869
+ if (schema == null)
1870
+ return { valid: false, error: "invalid-schema" };
1871
+ const schemaResult = schema.safeParse(permit);
1872
+ if (!schemaResult.success)
1873
+ return { valid: false, error: "invalid-schema" };
1874
+ return ValidationUtils.isSignedAndNotExpired(permit);
1866
1875
  }
1867
1876
  };
1868
1877
 
@@ -2244,9 +2253,9 @@ var PermitUtils = {
2244
2253
  };
2245
2254
  },
2246
2255
  /**
2247
- * Validate a permit
2256
+ * Validate a permit (schema-level validation)
2248
2257
  */
2249
- validate: (permit) => {
2258
+ validateSchema: (permit) => {
2250
2259
  if (permit.type === "self") {
2251
2260
  return validateSelfPermit(permit);
2252
2261
  } else if (permit.type === "sharing") {
@@ -2257,12 +2266,27 @@ var PermitUtils = {
2257
2266
  throw new Error("Invalid permit type");
2258
2267
  }
2259
2268
  },
2269
+ /**
2270
+ * Validate a permit (holistic validation).
2271
+ *
2272
+ * This validates:
2273
+ * - Permit schema (shape + invariants)
2274
+ * - Permit is signed
2275
+ * - Permit is not expired
2276
+ *
2277
+ * For schema-only validation, use `validateSchema(permit)`.
2278
+ */
2279
+ validate: (permit) => {
2280
+ const validated = PermitUtils.validateSchema(permit);
2281
+ ValidationUtils.assertSignedAndNotExpired(validated);
2282
+ return validated;
2283
+ },
2260
2284
  /**
2261
2285
  * Get the permission object from a permit (for use in contracts)
2262
2286
  */
2263
2287
  getPermission: (permit, skipValidation = false) => {
2264
2288
  if (!skipValidation) {
2265
- PermitUtils.validate(permit);
2289
+ PermitUtils.validateSchema(permit);
2266
2290
  }
2267
2291
  return {
2268
2292
  issuer: permit.issuer,
@@ -2328,8 +2352,17 @@ var PermitUtils = {
2328
2352
  return ValidationUtils.isSigned(permit);
2329
2353
  },
2330
2354
  /**
2331
- * Check if permit is valid
2355
+ * Check if permit is signed and not expired
2356
+ */
2357
+ isSignedAndNotExpired: (permit) => {
2358
+ return ValidationUtils.isSignedAndNotExpired(permit);
2359
+ },
2360
+ /**
2361
+ * Assert that permit is signed and not expired
2332
2362
  */
2363
+ assertSignedAndNotExpired: (permit) => {
2364
+ return ValidationUtils.assertSignedAndNotExpired(permit);
2365
+ },
2333
2366
  isValid: (permit) => {
2334
2367
  return ValidationUtils.isValid(permit);
2335
2368
  },
@@ -2507,6 +2540,9 @@ var importShared = async (options, publicClient, walletClient) => {
2507
2540
  var getHash = (permit) => {
2508
2541
  return PermitUtils.getHash(permit);
2509
2542
  };
2543
+ var exportShared = (permit) => {
2544
+ return PermitUtils.export(permit);
2545
+ };
2510
2546
  var serialize = (permit) => {
2511
2547
  return PermitUtils.serialize(permit);
2512
2548
  };
@@ -2557,6 +2593,7 @@ var permits = {
2557
2593
  getOrCreateSelfPermit,
2558
2594
  getOrCreateSharingPermit,
2559
2595
  getHash,
2596
+ export: exportShared,
2560
2597
  serialize,
2561
2598
  deserialize,
2562
2599
  getPermit: getPermit2,
@@ -2799,9 +2836,19 @@ async function cofheMocksDecryptForView(ctHash, utype, permit, publicClient) {
2799
2836
  return unsealed;
2800
2837
  }
2801
2838
 
2839
+ // core/decrypt/polling.ts
2840
+ function computeMinuteRampPollIntervalMs(elapsedMs, params) {
2841
+ const elapsedSeconds = Math.floor(elapsedMs / 1e3);
2842
+ const intervalSeconds = 1 + Math.floor(elapsedSeconds / 60);
2843
+ const intervalMs = intervalSeconds * 1e3;
2844
+ return Math.min(params.maxIntervalMs, Math.max(params.minIntervalMs, intervalMs));
2845
+ }
2846
+
2802
2847
  // core/decrypt/tnSealOutputV2.ts
2803
2848
  var POLL_INTERVAL_MS = 1e3;
2804
- var POLL_TIMEOUT_MS = 5 * 60 * 1e3;
2849
+ var POLL_MAX_INTERVAL_MS = 1e4;
2850
+ var SEAL_OUTPUT_TIMEOUT_MS = 5 * 60 * 1e3;
2851
+ var SUBMIT_RETRY_INTERVAL_MS = 1e3;
2805
2852
  function numberArrayToUint8Array(arr) {
2806
2853
  return new Uint8Array(arr);
2807
2854
  }
@@ -2818,93 +2865,193 @@ function convertSealedData(sealed) {
2818
2865
  nonce: numberArrayToUint8Array(sealed.nonce)
2819
2866
  };
2820
2867
  }
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
- });
2868
+ function getSealedDataFromSubmitResponse(value) {
2869
+ if (value.sealed)
2870
+ return value.sealed;
2871
+ if (Array.isArray(value.sealed_data) && Array.isArray(value.ephemeral_public_key) && Array.isArray(value.nonce)) {
2872
+ return {
2873
+ data: value.sealed_data,
2874
+ public_key: value.ephemeral_public_key,
2875
+ nonce: value.nonce
2876
+ };
2847
2877
  }
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
- }
2878
+ return void 0;
2879
+ }
2880
+ function parseCompletedSealOutputResponse(params) {
2881
+ const { value, thresholdNetworkUrl, requestId } = params;
2882
+ if (value.is_succeed === false) {
2883
+ const errorMessage = value.error_message || "Unknown error";
2856
2884
  throw new CofheError({
2857
2885
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2858
2886
  message: `sealOutput request failed: ${errorMessage}`,
2859
- hint: "Check the threshold network URL and request parameters.",
2860
2887
  context: {
2861
2888
  thresholdNetworkUrl,
2862
- status: response.status,
2863
- statusText: response.statusText,
2864
- body
2889
+ requestId,
2890
+ response: value
2865
2891
  }
2866
2892
  });
2867
2893
  }
2868
- let submitResponse;
2869
- try {
2870
- submitResponse = await response.json();
2871
- } catch (e) {
2894
+ const sealed = "sealed" in value ? value.sealed : getSealedDataFromSubmitResponse(value);
2895
+ if (!sealed) {
2872
2896
  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,
2897
+ code: "SEAL_OUTPUT_RETURNED_NULL" /* SealOutputReturnedNull */,
2898
+ message: `sealOutput request completed but returned no sealed data`,
2876
2899
  context: {
2877
2900
  thresholdNetworkUrl,
2878
- body
2901
+ requestId,
2902
+ response: value
2879
2903
  }
2880
2904
  });
2881
2905
  }
2882
- if (!submitResponse.request_id) {
2906
+ return convertSealedData(sealed);
2907
+ }
2908
+ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
2909
+ const body = {
2910
+ ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
2911
+ host_chain_id: chainId,
2912
+ permit: permission
2913
+ };
2914
+ let attemptIndex = 0;
2915
+ for (; ; ) {
2916
+ let response;
2917
+ try {
2918
+ response = await fetch(`${thresholdNetworkUrl}/v2/sealoutput`, {
2919
+ method: "POST",
2920
+ headers: {
2921
+ "Content-Type": "application/json"
2922
+ },
2923
+ body: JSON.stringify(body)
2924
+ });
2925
+ } catch (e) {
2926
+ throw new CofheError({
2927
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2928
+ message: `sealOutput request failed`,
2929
+ hint: "Ensure the threshold network URL is valid and reachable.",
2930
+ cause: e instanceof Error ? e : void 0,
2931
+ context: {
2932
+ thresholdNetworkUrl,
2933
+ body,
2934
+ attemptIndex
2935
+ }
2936
+ });
2937
+ }
2938
+ if (!response.ok) {
2939
+ let errorMessage = `HTTP ${response.status}`;
2940
+ try {
2941
+ const errorBody = await response.json();
2942
+ errorMessage = errorBody.error_message || errorBody.message || errorMessage;
2943
+ } catch {
2944
+ errorMessage = response.statusText || errorMessage;
2945
+ }
2946
+ throw new CofheError({
2947
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2948
+ message: `sealOutput request failed: ${errorMessage}`,
2949
+ hint: "Check the threshold network URL and request parameters.",
2950
+ context: {
2951
+ thresholdNetworkUrl,
2952
+ status: response.status,
2953
+ statusText: response.statusText,
2954
+ body,
2955
+ attemptIndex
2956
+ }
2957
+ });
2958
+ }
2959
+ let submitResponse;
2960
+ if (response.status !== 204) {
2961
+ try {
2962
+ submitResponse = await response.json();
2963
+ } catch (e) {
2964
+ throw new CofheError({
2965
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2966
+ message: `Failed to parse sealOutput submit response`,
2967
+ cause: e instanceof Error ? e : void 0,
2968
+ context: {
2969
+ thresholdNetworkUrl,
2970
+ body,
2971
+ attemptIndex
2972
+ }
2973
+ });
2974
+ }
2975
+ if (getSealedDataFromSubmitResponse(submitResponse)) {
2976
+ return {
2977
+ kind: "completed",
2978
+ sealed: parseCompletedSealOutputResponse({
2979
+ value: submitResponse,
2980
+ thresholdNetworkUrl,
2981
+ requestId: submitResponse.request_id
2982
+ })
2983
+ };
2984
+ }
2985
+ if (submitResponse.request_id) {
2986
+ return { kind: "request_id", requestId: submitResponse.request_id };
2987
+ }
2988
+ }
2989
+ if (response.status === 204) {
2990
+ const elapsedMs = Date.now() - overallStartTime;
2991
+ if (elapsedMs > SEAL_OUTPUT_TIMEOUT_MS) {
2992
+ throw new CofheError({
2993
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2994
+ message: `sealOutput submit retried without receiving request_id for ${SEAL_OUTPUT_TIMEOUT_MS}ms`,
2995
+ hint: "The ciphertext may still be propagating. Try again later.",
2996
+ context: {
2997
+ thresholdNetworkUrl,
2998
+ body,
2999
+ attemptIndex,
3000
+ timeoutMs: SEAL_OUTPUT_TIMEOUT_MS,
3001
+ submitResponse,
3002
+ status: response.status
3003
+ }
3004
+ });
3005
+ }
3006
+ onPoll?.({
3007
+ operation: "sealoutput",
3008
+ requestId: "",
3009
+ attemptIndex,
3010
+ elapsedMs,
3011
+ intervalMs: SUBMIT_RETRY_INTERVAL_MS,
3012
+ timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
3013
+ });
3014
+ await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS));
3015
+ attemptIndex += 1;
3016
+ continue;
3017
+ }
2883
3018
  throw new CofheError({
2884
3019
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2885
3020
  message: `sealOutput submit response missing request_id`,
2886
3021
  context: {
2887
3022
  thresholdNetworkUrl,
2888
3023
  body,
2889
- submitResponse
3024
+ submitResponse,
3025
+ attemptIndex
2890
3026
  }
2891
3027
  });
2892
3028
  }
2893
- return submitResponse.request_id;
2894
3029
  }
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) {
3030
+ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
3031
+ let attemptIndex = 0;
3032
+ while (true) {
3033
+ const elapsedMs = Date.now() - overallStartTime;
3034
+ const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
3035
+ minIntervalMs: POLL_INTERVAL_MS,
3036
+ maxIntervalMs: POLL_MAX_INTERVAL_MS
3037
+ });
3038
+ onPoll?.({
3039
+ operation: "sealoutput",
3040
+ requestId,
3041
+ attemptIndex,
3042
+ elapsedMs,
3043
+ intervalMs,
3044
+ timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
3045
+ });
3046
+ if (elapsedMs > SEAL_OUTPUT_TIMEOUT_MS) {
2900
3047
  throw new CofheError({
2901
3048
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2902
- message: `sealOutput polling timed out after ${POLL_TIMEOUT_MS}ms`,
3049
+ message: `sealOutput polling timed out after ${SEAL_OUTPUT_TIMEOUT_MS}ms`,
2903
3050
  hint: "The request may still be processing. Try again later.",
2904
3051
  context: {
2905
3052
  thresholdNetworkUrl,
2906
3053
  requestId,
2907
- timeoutMs: POLL_TIMEOUT_MS
3054
+ timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
2908
3055
  }
2909
3056
  });
2910
3057
  }
@@ -2973,32 +3120,14 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
2973
3120
  });
2974
3121
  }
2975
3122
  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);
3123
+ return parseCompletedSealOutputResponse({
3124
+ value: statusResponse,
3125
+ thresholdNetworkUrl,
3126
+ requestId
3127
+ });
3000
3128
  }
3001
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
3129
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
3130
+ attemptIndex += 1;
3002
3131
  }
3003
3132
  throw new CofheError({
3004
3133
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
@@ -3009,9 +3138,21 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId) {
3009
3138
  }
3010
3139
  });
3011
3140
  }
3012
- async function tnSealOutputV2(ctHash, chainId, permission, thresholdNetworkUrl) {
3013
- const requestId = await submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission);
3014
- return await pollSealOutputStatus(thresholdNetworkUrl, requestId);
3141
+ async function tnSealOutputV2(params) {
3142
+ const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
3143
+ const overallStartTime = Date.now();
3144
+ const submitResult = await submitSealOutputRequest(
3145
+ thresholdNetworkUrl,
3146
+ ctHash,
3147
+ chainId,
3148
+ permission,
3149
+ overallStartTime,
3150
+ onPoll
3151
+ );
3152
+ if (submitResult.kind === "completed") {
3153
+ return submitResult.sealed;
3154
+ }
3155
+ return await pollSealOutputStatus(thresholdNetworkUrl, submitResult.requestId, overallStartTime, onPoll);
3015
3156
  }
3016
3157
 
3017
3158
  // core/decrypt/decryptForViewBuilder.ts
@@ -3020,6 +3161,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3020
3161
  utype;
3021
3162
  permitHash;
3022
3163
  permit;
3164
+ pollCallback;
3023
3165
  constructor(params) {
3024
3166
  super({
3025
3167
  config: params.config,
@@ -3076,6 +3218,10 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3076
3218
  getAccount() {
3077
3219
  return this.account;
3078
3220
  }
3221
+ onPoll(callback) {
3222
+ this.pollCallback = callback;
3223
+ return this;
3224
+ }
3079
3225
  withPermit(permitOrPermitHash) {
3080
3226
  if (typeof permitOrPermitHash === "string") {
3081
3227
  this.permitHash = permitOrPermitHash;
@@ -3199,7 +3345,13 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3199
3345
  this.assertPublicClient();
3200
3346
  const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
3201
3347
  const permission = PermitUtils.getPermission(permit, true);
3202
- const sealed = await tnSealOutputV2(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
3348
+ const sealed = await tnSealOutputV2({
3349
+ ctHash: this.ctHash,
3350
+ chainId: this.chainId,
3351
+ permission,
3352
+ thresholdNetworkUrl,
3353
+ onPoll: this.pollCallback
3354
+ });
3203
3355
  return PermitUtils.unseal(permit, sealed);
3204
3356
  }
3205
3357
  /**
@@ -3227,7 +3379,6 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3227
3379
  this.validateUtypeOrThrow();
3228
3380
  const permit = await this.getResolvedPermit();
3229
3381
  PermitUtils.validate(permit);
3230
- PermitUtils.isValid(permit);
3231
3382
  const chainId = permit._signedDomain.chainId;
3232
3383
  let unsealed;
3233
3384
  if (chainId === hardhat2.id) {
@@ -3238,6 +3389,9 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3238
3389
  return convertViaUtype(this.utype, unsealed);
3239
3390
  }
3240
3391
  };
3392
+ var UINT_TYPE_MASK = 0x7fn;
3393
+ var TYPE_BYTE_OFFSET = 8n;
3394
+ var getEncryptionTypeFromCtHash = (ctHash) => Number(ctHash >> TYPE_BYTE_OFFSET & UINT_TYPE_MASK);
3241
3395
  async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
3242
3396
  let allowed;
3243
3397
  let error;
@@ -3275,7 +3429,13 @@ async function cofheMocksDecryptForTx(ctHash, utype, permit, publicClient) {
3275
3429
  message: `mocks decryptForTx call failed: ACL Access Denied (NotAllowed)`
3276
3430
  });
3277
3431
  }
3278
- const packed = viem.encodePacked(["uint256", "uint256"], [BigInt(ctHash), decryptedValue]);
3432
+ const chainId = publicClient.chain?.id ?? await publicClient.getChainId();
3433
+ const normalizedCtHash = BigInt(ctHash);
3434
+ const encryptionType = getEncryptionTypeFromCtHash(normalizedCtHash);
3435
+ const packed = viem.encodePacked(
3436
+ ["uint256", "uint32", "uint64", "uint256"],
3437
+ [decryptedValue, encryptionType, BigInt(chainId), normalizedCtHash]
3438
+ );
3279
3439
  const messageHash = viem.keccak256(packed);
3280
3440
  const signature = await accounts.sign({
3281
3441
  hash: messageHash,
@@ -3347,7 +3507,9 @@ function parseDecryptedBytesToBigInt(decrypted) {
3347
3507
 
3348
3508
  // core/decrypt/tnDecryptV2.ts
3349
3509
  var POLL_INTERVAL_MS2 = 1e3;
3350
- var POLL_TIMEOUT_MS2 = 5 * 60 * 1e3;
3510
+ var POLL_MAX_INTERVAL_MS2 = 1e4;
3511
+ var DECRYPT_TIMEOUT_MS = 5 * 60 * 1e3;
3512
+ var SUBMIT_RETRY_INTERVAL_MS2 = 1e3;
3351
3513
  function assertDecryptSubmitResponseV2(value) {
3352
3514
  if (value == null || typeof value !== "object") {
3353
3515
  throw new CofheError({
@@ -3359,16 +3521,65 @@ function assertDecryptSubmitResponseV2(value) {
3359
3521
  });
3360
3522
  }
3361
3523
  const v = value;
3362
- if (typeof v.request_id !== "string" || v.request_id.trim().length === 0) {
3524
+ if (v.request_id !== null && typeof v.request_id !== "string") {
3363
3525
  throw new CofheError({
3364
3526
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3365
- message: "decrypt submit response missing request_id",
3527
+ message: "decrypt submit response has invalid request_id",
3366
3528
  context: {
3367
3529
  value
3368
3530
  }
3369
3531
  });
3370
3532
  }
3371
- return { request_id: v.request_id };
3533
+ return {
3534
+ request_id: v.request_id ?? null,
3535
+ status: typeof v.status === "string" ? v.status : void 0,
3536
+ is_succeed: typeof v.is_succeed === "boolean" ? v.is_succeed : void 0,
3537
+ decrypted: Array.isArray(v.decrypted) ? v.decrypted : void 0,
3538
+ signature: typeof v.signature === "string" ? v.signature : void 0,
3539
+ encryption_type: typeof v.encryption_type === "number" ? v.encryption_type : void 0,
3540
+ error_message: typeof v.error_message === "string" || v.error_message === null ? v.error_message : void 0,
3541
+ message: typeof v.message === "string" ? v.message : void 0
3542
+ };
3543
+ }
3544
+ function parseCompletedDecryptResponseV2(params) {
3545
+ const { value, thresholdNetworkUrl, requestId } = params;
3546
+ if (value.is_succeed === false) {
3547
+ const errorMessage = value.error_message || "Unknown error";
3548
+ throw new CofheError({
3549
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3550
+ message: `decrypt request failed: ${errorMessage}`,
3551
+ context: {
3552
+ thresholdNetworkUrl,
3553
+ requestId,
3554
+ response: value
3555
+ }
3556
+ });
3557
+ }
3558
+ if (value.error_message) {
3559
+ throw new CofheError({
3560
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3561
+ message: `decrypt request failed: ${value.error_message}`,
3562
+ context: {
3563
+ thresholdNetworkUrl,
3564
+ requestId,
3565
+ response: value
3566
+ }
3567
+ });
3568
+ }
3569
+ if (!Array.isArray(value.decrypted)) {
3570
+ throw new CofheError({
3571
+ code: "DECRYPT_RETURNED_NULL" /* DecryptReturnedNull */,
3572
+ message: "decrypt completed but response missing <decrypted> byte array",
3573
+ context: {
3574
+ thresholdNetworkUrl,
3575
+ requestId,
3576
+ response: value
3577
+ }
3578
+ });
3579
+ }
3580
+ const decryptedValue = parseDecryptedBytesToBigInt(value.decrypted);
3581
+ const signature = normalizeTnSignature(value.signature);
3582
+ return { decryptedValue, signature };
3372
3583
  }
3373
3584
  function assertDecryptStatusResponseV2(value) {
3374
3585
  if (value == null || typeof value !== "object") {
@@ -3414,7 +3625,7 @@ function assertDecryptStatusResponseV2(value) {
3414
3625
  }
3415
3626
  return value;
3416
3627
  }
3417
- async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission) {
3628
+ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
3418
3629
  const body = {
3419
3630
  ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
3420
3631
  host_chain_id: chainId
@@ -3422,79 +3633,151 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
3422
3633
  if (permission) {
3423
3634
  body.permit = permission;
3424
3635
  }
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}`;
3636
+ let attemptIndex = 0;
3637
+ for (; ; ) {
3638
+ let response;
3448
3639
  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;
3640
+ response = await fetch(`${thresholdNetworkUrl}/v2/decrypt`, {
3641
+ method: "POST",
3642
+ headers: {
3643
+ "Content-Type": "application/json"
3644
+ },
3645
+ body: JSON.stringify(body)
3646
+ });
3647
+ } catch (e) {
3648
+ throw new CofheError({
3649
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3650
+ message: `decrypt request failed`,
3651
+ hint: "Ensure the threshold network URL is valid and reachable.",
3652
+ cause: e instanceof Error ? e : void 0,
3653
+ context: {
3654
+ thresholdNetworkUrl,
3655
+ body,
3656
+ attemptIndex
3657
+ }
3658
+ });
3455
3659
  }
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
3660
+ if (!response.ok) {
3661
+ let errorMessage = `HTTP ${response.status}`;
3662
+ try {
3663
+ const errorBody = await response.json();
3664
+ const maybeMessage = errorBody.error_message || errorBody.message;
3665
+ if (typeof maybeMessage === "string" && maybeMessage.length > 0)
3666
+ errorMessage = maybeMessage;
3667
+ } catch {
3668
+ errorMessage = response.statusText || errorMessage;
3465
3669
  }
3466
- });
3467
- }
3468
- let rawJson;
3469
- try {
3470
- rawJson = await response.json();
3471
- } catch (e) {
3670
+ throw new CofheError({
3671
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3672
+ message: `decrypt request failed: ${errorMessage}`,
3673
+ hint: "Check the threshold network URL and request parameters.",
3674
+ context: {
3675
+ thresholdNetworkUrl,
3676
+ status: response.status,
3677
+ statusText: response.statusText,
3678
+ body,
3679
+ attemptIndex
3680
+ }
3681
+ });
3682
+ }
3683
+ let submitResponse;
3684
+ if (response.status !== 204) {
3685
+ let rawJson;
3686
+ try {
3687
+ rawJson = await response.json();
3688
+ } catch (e) {
3689
+ throw new CofheError({
3690
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3691
+ message: `Failed to parse decrypt submit response`,
3692
+ cause: e instanceof Error ? e : void 0,
3693
+ context: {
3694
+ thresholdNetworkUrl,
3695
+ body,
3696
+ attemptIndex
3697
+ }
3698
+ });
3699
+ }
3700
+ submitResponse = assertDecryptSubmitResponseV2(rawJson);
3701
+ if (Array.isArray(submitResponse.decrypted) && typeof submitResponse.signature === "string") {
3702
+ return {
3703
+ kind: "completed",
3704
+ ...parseCompletedDecryptResponseV2({
3705
+ value: submitResponse,
3706
+ thresholdNetworkUrl,
3707
+ requestId: submitResponse.request_id
3708
+ })
3709
+ };
3710
+ }
3711
+ if (submitResponse.request_id) {
3712
+ return { kind: "request_id", requestId: submitResponse.request_id };
3713
+ }
3714
+ }
3715
+ if (response.status === 204) {
3716
+ const elapsedMs = Date.now() - overallStartTime;
3717
+ if (elapsedMs > DECRYPT_TIMEOUT_MS) {
3718
+ throw new CofheError({
3719
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3720
+ message: `decrypt submit retried without receiving request_id for ${DECRYPT_TIMEOUT_MS}ms`,
3721
+ hint: "The ciphertext may still be propagating. Try again later.",
3722
+ context: {
3723
+ thresholdNetworkUrl,
3724
+ body,
3725
+ attemptIndex,
3726
+ timeoutMs: DECRYPT_TIMEOUT_MS,
3727
+ submitResponse,
3728
+ status: response.status
3729
+ }
3730
+ });
3731
+ }
3732
+ onPoll?.({
3733
+ operation: "decrypt",
3734
+ requestId: "",
3735
+ attemptIndex,
3736
+ elapsedMs,
3737
+ intervalMs: SUBMIT_RETRY_INTERVAL_MS2,
3738
+ timeoutMs: DECRYPT_TIMEOUT_MS
3739
+ });
3740
+ await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS2));
3741
+ attemptIndex += 1;
3742
+ continue;
3743
+ }
3472
3744
  throw new CofheError({
3473
3745
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3474
- message: `Failed to parse decrypt submit response`,
3475
- cause: e instanceof Error ? e : void 0,
3746
+ message: `decrypt submit response missing request_id`,
3476
3747
  context: {
3477
3748
  thresholdNetworkUrl,
3478
- body
3749
+ body,
3750
+ submitResponse,
3751
+ attemptIndex
3479
3752
  }
3480
3753
  });
3481
3754
  }
3482
- const submitResponse = assertDecryptSubmitResponseV2(rawJson);
3483
- return submitResponse.request_id;
3484
3755
  }
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) {
3756
+ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
3757
+ let attemptIndex = 0;
3758
+ while (true) {
3759
+ const elapsedMs = Date.now() - overallStartTime;
3760
+ const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
3761
+ minIntervalMs: POLL_INTERVAL_MS2,
3762
+ maxIntervalMs: POLL_MAX_INTERVAL_MS2
3763
+ });
3764
+ onPoll?.({
3765
+ operation: "decrypt",
3766
+ requestId,
3767
+ attemptIndex,
3768
+ elapsedMs,
3769
+ intervalMs,
3770
+ timeoutMs: DECRYPT_TIMEOUT_MS
3771
+ });
3772
+ if (elapsedMs > DECRYPT_TIMEOUT_MS) {
3490
3773
  throw new CofheError({
3491
3774
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3492
- message: `decrypt polling timed out after ${POLL_TIMEOUT_MS2}ms`,
3775
+ message: `decrypt polling timed out after ${DECRYPT_TIMEOUT_MS}ms`,
3493
3776
  hint: "The request may still be processing. Try again later.",
3494
3777
  context: {
3495
3778
  thresholdNetworkUrl,
3496
3779
  requestId,
3497
- timeoutMs: POLL_TIMEOUT_MS2
3780
+ timeoutMs: DECRYPT_TIMEOUT_MS
3498
3781
  }
3499
3782
  });
3500
3783
  }
@@ -3566,45 +3849,14 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId) {
3566
3849
  }
3567
3850
  const statusResponse = assertDecryptStatusResponseV2(rawJson);
3568
3851
  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 };
3852
+ return parseCompletedDecryptResponseV2({
3853
+ value: statusResponse,
3854
+ thresholdNetworkUrl,
3855
+ requestId
3856
+ });
3606
3857
  }
3607
- await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS2));
3858
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
3859
+ attemptIndex += 1;
3608
3860
  }
3609
3861
  throw new CofheError({
3610
3862
  code: "DECRYPT_FAILED" /* DecryptFailed */,
@@ -3615,9 +3867,21 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId) {
3615
3867
  }
3616
3868
  });
3617
3869
  }
3618
- async function tnDecryptV2(ctHash, chainId, permission, thresholdNetworkUrl) {
3619
- const requestId = await submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission);
3620
- return await pollDecryptStatusV2(thresholdNetworkUrl, requestId);
3870
+ async function tnDecryptV2(params) {
3871
+ const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
3872
+ const overallStartTime = Date.now();
3873
+ const submitResult = await submitDecryptRequestV2(
3874
+ thresholdNetworkUrl,
3875
+ ctHash,
3876
+ chainId,
3877
+ permission,
3878
+ overallStartTime,
3879
+ onPoll
3880
+ );
3881
+ if (submitResult.kind === "completed") {
3882
+ return submitResult;
3883
+ }
3884
+ return await pollDecryptStatusV2(thresholdNetworkUrl, submitResult.requestId, overallStartTime, onPoll);
3621
3885
  }
3622
3886
 
3623
3887
  // core/decrypt/decryptForTxBuilder.ts
@@ -3626,6 +3890,7 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3626
3890
  permitHash;
3627
3891
  permit;
3628
3892
  permitSelection = "unset";
3893
+ pollCallback;
3629
3894
  constructor(params) {
3630
3895
  super({
3631
3896
  config: params.config,
@@ -3651,6 +3916,10 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3651
3916
  getAccount() {
3652
3917
  return this.account;
3653
3918
  }
3919
+ onPoll(callback) {
3920
+ this.pollCallback = callback;
3921
+ return this;
3922
+ }
3654
3923
  withPermit(permitOrPermitHash) {
3655
3924
  if (this.permitSelection === "with-permit") {
3656
3925
  throw new CofheError({
@@ -3778,7 +4047,13 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3778
4047
  this.assertPublicClient();
3779
4048
  const thresholdNetworkUrl = await this.getThresholdNetworkUrl();
3780
4049
  const permission = permit ? PermitUtils.getPermission(permit, true) : null;
3781
- const { decryptedValue, signature } = await tnDecryptV2(this.ctHash, this.chainId, permission, thresholdNetworkUrl);
4050
+ const { decryptedValue, signature } = await tnDecryptV2({
4051
+ ctHash: this.ctHash,
4052
+ chainId: this.chainId,
4053
+ permission,
4054
+ thresholdNetworkUrl,
4055
+ onPoll: this.pollCallback
4056
+ });
3782
4057
  return {
3783
4058
  ctHash: this.ctHash,
3784
4059
  decryptedValue,
@@ -3796,7 +4071,6 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3796
4071
  const permit = await this.getResolvedPermit();
3797
4072
  if (permit !== null) {
3798
4073
  PermitUtils.validate(permit);
3799
- PermitUtils.isValid(permit);
3800
4074
  const chainId = permit._signedDomain.chainId;
3801
4075
  if (chainId === hardhat2.id) {
3802
4076
  return await this.mocksDecryptForTx(permit);
@@ -3817,6 +4091,35 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3817
4091
  }
3818
4092
  }
3819
4093
  };
4094
+ var decryptResultSignerAbi = viem.parseAbi(["function decryptResultSigner() view returns (address)"]);
4095
+ var UINT_TYPE_MASK2 = 0x7fn;
4096
+ var TYPE_BYTE_OFFSET2 = 8n;
4097
+ var getEncryptionTypeFromCtHash2 = (ctHash) => Number(ctHash >> TYPE_BYTE_OFFSET2 & UINT_TYPE_MASK2);
4098
+ var buildDecryptResultHash = (ctHash, cleartext, chainId) => {
4099
+ const encryptionType = getEncryptionTypeFromCtHash2(ctHash);
4100
+ return viem.keccak256(
4101
+ viem.encodePacked(["uint256", "uint32", "uint64", "uint256"], [cleartext, encryptionType, BigInt(chainId), ctHash])
4102
+ );
4103
+ };
4104
+ async function verifyDecryptResult(handle, cleartext, signature, publicClient) {
4105
+ const chainId = publicClient.chain?.id ?? await publicClient.getChainId();
4106
+ const expectedSigner = await publicClient.readContract({
4107
+ address: TASK_MANAGER_ADDRESS,
4108
+ abi: decryptResultSignerAbi,
4109
+ functionName: "decryptResultSigner",
4110
+ args: []
4111
+ });
4112
+ if (viem.isAddressEqual(expectedSigner, viem.zeroAddress))
4113
+ return true;
4114
+ const ctHash = BigInt(handle);
4115
+ const messageHash = buildDecryptResultHash(ctHash, cleartext, chainId);
4116
+ try {
4117
+ const recovered = await viem.recoverAddress({ hash: messageHash, signature });
4118
+ return viem.isAddressEqual(recovered, expectedSigner);
4119
+ } catch {
4120
+ return false;
4121
+ }
4122
+ }
3820
4123
 
3821
4124
  // core/client.ts
3822
4125
  var InitialConnectStore = {
@@ -3935,6 +4238,11 @@ function createCofheClientBase(opts) {
3935
4238
  requireConnected: _requireConnected
3936
4239
  });
3937
4240
  }
4241
+ function verifyDecryptResult2(handle, cleartext, signature) {
4242
+ _requireConnected();
4243
+ const { publicClient } = connectStore.getState();
4244
+ return verifyDecryptResult(handle, cleartext, signature, publicClient);
4245
+ }
3938
4246
  const _getChainIdAndAccount = (chainId, account) => {
3939
4247
  const state = connectStore.getState();
3940
4248
  const _chainId = chainId ?? state.chainId;
@@ -4017,6 +4325,7 @@ function createCofheClientBase(opts) {
4017
4325
  },
4018
4326
  // Utils (no context needed)
4019
4327
  getHash: permits.getHash,
4328
+ export: permits.export,
4020
4329
  serialize: permits.serialize,
4021
4330
  deserialize: permits.deserialize
4022
4331
  };
@@ -4045,6 +4354,7 @@ function createCofheClientBase(opts) {
4045
4354
  */
4046
4355
  decryptHandle: decryptForView,
4047
4356
  decryptForTx,
4357
+ verifyDecryptResult: verifyDecryptResult2,
4048
4358
  permits: clientPermits
4049
4359
  // Add SDK-specific methods below that require connection
4050
4360
  // Example:
@@ -4054,12 +4364,19 @@ function createCofheClientBase(opts) {
4054
4364
  // },
4055
4365
  };
4056
4366
  }
4057
- var createWebStorage = () => {
4367
+
4368
+ // web/const.ts
4369
+ var hasDOM = typeof globalThis?.document !== "undefined" && typeof globalThis?.window !== "undefined";
4370
+
4371
+ // web/storage.ts
4372
+ var createWebStorage = (opts = { enableLog: false }) => {
4373
+ if (!hasDOM)
4374
+ throw new Error("createWebStorage can only be used in a browser environment");
4058
4375
  const client = iframeSharedStorage.constructClient({
4059
4376
  iframe: {
4060
4377
  src: "https://iframe-shared-storage.vercel.app/hub.html",
4061
4378
  messagingOptions: {
4062
- enableLog: "both"
4379
+ enableLog: opts.enableLog ? "both" : void 0
4063
4380
  },
4064
4381
  iframeReadyTimeoutMs: 3e4,
4065
4382
  // if the iframe is not initied during this interval AND a reuqest is made, such request will throw an error
@@ -4082,6 +4399,16 @@ var createWebStorage = () => {
4082
4399
  }
4083
4400
  };
4084
4401
  };
4402
+ function createSsrStorage() {
4403
+ console.warn("using no-op server-side SSR storage");
4404
+ return {
4405
+ getItem: async () => null,
4406
+ setItem: async () => {
4407
+ },
4408
+ removeItem: async () => {
4409
+ }
4410
+ };
4411
+ }
4085
4412
 
4086
4413
  // web/workerManager.ts
4087
4414
  var ZkProveWorkerManager = class {
@@ -4107,7 +4434,7 @@ var ZkProveWorkerManager = class {
4107
4434
  return;
4108
4435
  }
4109
4436
  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" });
4437
+ 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
4438
  } catch (error) {
4112
4439
  reject(new Error(`Failed to create worker: ${error}`));
4113
4440
  return;
@@ -4221,15 +4548,25 @@ function terminateWorker() {
4221
4548
  function areWorkersAvailable() {
4222
4549
  return typeof Worker !== "undefined";
4223
4550
  }
4551
+
4552
+ // web/index.ts
4553
+ var tfheModule = null;
4224
4554
  var tfheInitialized = false;
4225
4555
  async function initTfhe() {
4226
4556
  if (tfheInitialized)
4227
4557
  return false;
4228
- await init__default.default();
4229
- await init.init_panic_hook();
4558
+ tfheModule = await import('tfhe');
4559
+ await tfheModule.default();
4560
+ await tfheModule.init_panic_hook();
4230
4561
  tfheInitialized = true;
4231
4562
  return true;
4232
4563
  }
4564
+ function requireTfhe() {
4565
+ if (!tfheModule) {
4566
+ throw new Error("TFHE not initialized \u2014 call initTfhe() (or any client method that triggers it) first");
4567
+ }
4568
+ return tfheModule;
4569
+ }
4233
4570
  var fromHexString2 = (hexString) => {
4234
4571
  const cleanString = hexString.length % 2 === 1 ? `0${hexString}` : hexString;
4235
4572
  const arr = cleanString.replace(/^0x/, "").match(/.{1,2}/g);
@@ -4237,16 +4574,25 @@ var fromHexString2 = (hexString) => {
4237
4574
  return new Uint8Array();
4238
4575
  return new Uint8Array(arr.map((byte) => parseInt(byte, 16)));
4239
4576
  };
4577
+ var _deserializeTfhePublicKey = (buff) => {
4578
+ return requireTfhe().TfheCompactPublicKey.safe_deserialize(
4579
+ fromHexString2(buff),
4580
+ TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT
4581
+ );
4582
+ };
4583
+ var _deserializeCompactPkeCrs = (buff) => {
4584
+ return requireTfhe().CompactPkeCrs.safe_deserialize(fromHexString2(buff), TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT);
4585
+ };
4240
4586
  var tfhePublicKeyDeserializer = (buff) => {
4241
- init.TfheCompactPublicKey.deserialize(fromHexString2(buff));
4587
+ _deserializeTfhePublicKey(buff);
4242
4588
  };
4243
4589
  var compactPkeCrsDeserializer = (buff) => {
4244
- init.CompactPkeCrs.deserialize(fromHexString2(buff));
4590
+ _deserializeCompactPkeCrs(buff);
4245
4591
  };
4246
4592
  var zkBuilderAndCrsGenerator = (fhe, crs) => {
4247
- const fhePublicKey = init.TfheCompactPublicKey.deserialize(fromHexString2(fhe));
4248
- const zkBuilder = init.ProvenCompactCiphertextList.builder(fhePublicKey);
4249
- const zkCrs = init.CompactPkeCrs.deserialize(fromHexString2(crs));
4593
+ const fhePublicKey = _deserializeTfhePublicKey(fhe);
4594
+ const zkBuilder = requireTfhe().ProvenCompactCiphertextList.builder(fhePublicKey);
4595
+ const zkCrs = _deserializeCompactPkeCrs(crs);
4250
4596
  return { zkBuilder, zkCrs };
4251
4597
  };
4252
4598
  async function zkProveWithWorker2(fheKeyHex, crsHex, items, metadata) {
@@ -4261,7 +4607,7 @@ function createCofheConfig(config) {
4261
4607
  return createCofheConfigBase({
4262
4608
  environment: "web",
4263
4609
  ...config,
4264
- fheKeyStorage: config.fheKeyStorage === null ? null : config.fheKeyStorage ?? createWebStorage()
4610
+ fheKeyStorage: config.fheKeyStorage === null ? null : config.fheKeyStorage ?? (hasDOM ? createWebStorage() : createSsrStorage())
4265
4611
  });
4266
4612
  }
4267
4613
  function createCofheClient(config) {
@@ -4291,4 +4637,6 @@ exports.areWorkersAvailable = areWorkersAvailable;
4291
4637
  exports.createCofheClient = createCofheClient;
4292
4638
  exports.createCofheClientWithCustomWorker = createCofheClientWithCustomWorker;
4293
4639
  exports.createCofheConfig = createCofheConfig;
4640
+ exports.createSsrStorage = createSsrStorage;
4641
+ exports.hasDOM = hasDOM;
4294
4642
  exports.terminateWorker = terminateWorker;