@cofhe/sdk 0.5.1 → 0.6.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 (47) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/adapters/test/ethers5.test.ts +1 -1
  3. package/adapters/test/ethers6.test.ts +1 -1
  4. package/adapters/test/wagmi.test.ts +1 -1
  5. package/chains/chains/hardhat.ts +3 -3
  6. package/core/consts.ts +0 -3
  7. package/core/decrypt/decryptForTxBuilder.ts +21 -0
  8. package/core/decrypt/decryptForViewBuilder.ts +19 -0
  9. package/core/decrypt/submitRetry.ts +126 -0
  10. package/core/decrypt/tnDecryptV2.ts +48 -53
  11. package/core/decrypt/tnSealOutputV2.ts +48 -54
  12. package/core/encrypt/cofheMocksZkVerifySign.ts +2 -2
  13. package/core/encrypt/encryptInputsBuilder.ts +46 -11
  14. package/core/encrypt/zkPackProveVerify.ts +3 -3
  15. package/core/index.ts +13 -1
  16. package/core/test/decryptBuilders.test.ts +28 -0
  17. package/core/test/encryptInputsBuilder.test.ts +35 -0
  18. package/core/test/pollCallbacks.test.ts +226 -0
  19. package/core/types.ts +65 -5
  20. package/dist/chains.cjs +3 -3
  21. package/dist/chains.js +1 -1
  22. package/dist/{chunk-4FP4V35O.js → chunk-ESMZCFJY.js} +1 -2
  23. package/dist/{chunk-TBLR7NNE.js → chunk-MTRAXQXC.js} +3 -3
  24. package/dist/{chunk-S7OKGLFD.js → chunk-PE5V5CCV.js} +288 -153
  25. package/dist/{chunk-MRCKUMOS.js → chunk-VB62WYPL.js} +1 -1
  26. package/dist/{clientTypes-BSbwairE.d.cts → clientTypes-BDy1qIBu.d.cts} +78 -11
  27. package/dist/{clientTypes-DDmcgZ0a.d.ts → clientTypes-CyUvRRzA.d.ts} +78 -11
  28. package/dist/core.cjs +288 -155
  29. package/dist/core.d.cts +3 -5
  30. package/dist/core.d.ts +3 -5
  31. package/dist/core.js +4 -4
  32. package/dist/node.cjs +243 -108
  33. package/dist/node.d.cts +1 -1
  34. package/dist/node.d.ts +1 -1
  35. package/dist/node.js +4 -4
  36. package/dist/permits.d.cts +10 -6
  37. package/dist/permits.d.ts +10 -6
  38. package/dist/permits.js +2 -2
  39. package/dist/web.cjs +243 -108
  40. package/dist/web.d.cts +1 -1
  41. package/dist/web.d.ts +1 -1
  42. package/dist/web.js +4 -4
  43. package/dist/zkProve.worker.js +1 -1
  44. package/package.json +2 -2
  45. package/permits/store.ts +1 -0
  46. package/web/test/ssr.test.ts +23 -0
  47. package/web/test/tfheinit.web.test.ts +81 -5
@@ -302,15 +302,17 @@ type PermitsStore = {
302
302
  activePermitHash: ChainRecord<AccountRecord<string | undefined>>;
303
303
  };
304
304
  declare const PERMIT_STORE_DEFAULTS: PermitsStore;
305
- declare const _permitStore: Omit<zustand_vanilla.StoreApi<PermitsStore>, "persist"> & {
305
+ declare const _permitStore: Omit<zustand_vanilla.StoreApi<PermitsStore>, "setState" | "persist"> & {
306
+ setState(partial: PermitsStore | Partial<PermitsStore> | ((state: PermitsStore) => PermitsStore | Partial<PermitsStore>), replace?: false | undefined): unknown;
307
+ setState(state: PermitsStore | ((state: PermitsStore) => PermitsStore), replace: true): unknown;
306
308
  persist: {
307
- setOptions: (options: Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore>>) => void;
309
+ setOptions: (options: Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore, unknown>>) => void;
308
310
  clearStorage: () => void;
309
311
  rehydrate: () => Promise<void> | void;
310
312
  hasHydrated: () => boolean;
311
313
  onHydrate: (fn: (state: PermitsStore) => void) => () => void;
312
314
  onFinishHydration: (fn: (state: PermitsStore) => void) => () => void;
313
- getOptions: () => Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore>>;
315
+ getOptions: () => Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore, unknown>>;
314
316
  };
315
317
  };
316
318
  declare const clearStaleStore: () => void;
@@ -324,15 +326,17 @@ declare const setActivePermitHash: (chainId: number, account: string, hash: stri
324
326
  declare const removeActivePermitHash: (chainId: number, account: string) => void;
325
327
  declare const resetStore: () => void;
326
328
  declare const permitStore: {
327
- store: Omit<zustand_vanilla.StoreApi<PermitsStore>, "persist"> & {
329
+ store: Omit<zustand_vanilla.StoreApi<PermitsStore>, "setState" | "persist"> & {
330
+ setState(partial: PermitsStore | Partial<PermitsStore> | ((state: PermitsStore) => PermitsStore | Partial<PermitsStore>), replace?: false | undefined): unknown;
331
+ setState(state: PermitsStore | ((state: PermitsStore) => PermitsStore), replace: true): unknown;
328
332
  persist: {
329
- setOptions: (options: Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore>>) => void;
333
+ setOptions: (options: Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore, unknown>>) => void;
330
334
  clearStorage: () => void;
331
335
  rehydrate: () => Promise<void> | void;
332
336
  hasHydrated: () => boolean;
333
337
  onHydrate: (fn: (state: PermitsStore) => void) => () => void;
334
338
  onFinishHydration: (fn: (state: PermitsStore) => void) => () => void;
335
- getOptions: () => Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore>>;
339
+ getOptions: () => Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore, unknown>>;
336
340
  };
337
341
  };
338
342
  getPermit: (chainId: number | undefined, account: string | undefined, hash: string | undefined) => Permit | undefined;
package/dist/permits.d.ts CHANGED
@@ -302,15 +302,17 @@ type PermitsStore = {
302
302
  activePermitHash: ChainRecord<AccountRecord<string | undefined>>;
303
303
  };
304
304
  declare const PERMIT_STORE_DEFAULTS: PermitsStore;
305
- declare const _permitStore: Omit<zustand_vanilla.StoreApi<PermitsStore>, "persist"> & {
305
+ declare const _permitStore: Omit<zustand_vanilla.StoreApi<PermitsStore>, "setState" | "persist"> & {
306
+ setState(partial: PermitsStore | Partial<PermitsStore> | ((state: PermitsStore) => PermitsStore | Partial<PermitsStore>), replace?: false | undefined): unknown;
307
+ setState(state: PermitsStore | ((state: PermitsStore) => PermitsStore), replace: true): unknown;
306
308
  persist: {
307
- setOptions: (options: Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore>>) => void;
309
+ setOptions: (options: Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore, unknown>>) => void;
308
310
  clearStorage: () => void;
309
311
  rehydrate: () => Promise<void> | void;
310
312
  hasHydrated: () => boolean;
311
313
  onHydrate: (fn: (state: PermitsStore) => void) => () => void;
312
314
  onFinishHydration: (fn: (state: PermitsStore) => void) => () => void;
313
- getOptions: () => Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore>>;
315
+ getOptions: () => Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore, unknown>>;
314
316
  };
315
317
  };
316
318
  declare const clearStaleStore: () => void;
@@ -324,15 +326,17 @@ declare const setActivePermitHash: (chainId: number, account: string, hash: stri
324
326
  declare const removeActivePermitHash: (chainId: number, account: string) => void;
325
327
  declare const resetStore: () => void;
326
328
  declare const permitStore: {
327
- store: Omit<zustand_vanilla.StoreApi<PermitsStore>, "persist"> & {
329
+ store: Omit<zustand_vanilla.StoreApi<PermitsStore>, "setState" | "persist"> & {
330
+ setState(partial: PermitsStore | Partial<PermitsStore> | ((state: PermitsStore) => PermitsStore | Partial<PermitsStore>), replace?: false | undefined): unknown;
331
+ setState(state: PermitsStore | ((state: PermitsStore) => PermitsStore), replace: true): unknown;
328
332
  persist: {
329
- setOptions: (options: Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore>>) => void;
333
+ setOptions: (options: Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore, unknown>>) => void;
330
334
  clearStorage: () => void;
331
335
  rehydrate: () => Promise<void> | void;
332
336
  hasHydrated: () => boolean;
333
337
  onHydrate: (fn: (state: PermitsStore) => void) => () => void;
334
338
  onFinishHydration: (fn: (state: PermitsStore) => void) => () => void;
335
- getOptions: () => Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore>>;
339
+ getOptions: () => Partial<zustand_middleware.PersistOptions<PermitsStore, PermitsStore, unknown>>;
336
340
  };
337
341
  };
338
342
  getPermit: (chainId: number | undefined, account: string | undefined, hash: string | undefined) => Permit | undefined;
package/dist/permits.js CHANGED
@@ -1,2 +1,2 @@
1
- export { GenerateSealingKey, ImportPermitOptionsValidator, ImportPermitValidator, PERMIT_STORE_DEFAULTS, PermitUtils, SealingKey, SelfPermitOptionsValidator, SelfPermitValidator, SharingPermitOptionsValidator, SharingPermitValidator, SignatureTypes, SignatureUtils, ValidationUtils, _permitStore, addressNotZeroSchema, addressSchema, bytesNotEmptySchema, bytesSchema, clearStaleStore, getActivePermit, getActivePermitHash, getPermit, getPermits, getSignatureTypesAndMessage, permitStore, removeActivePermitHash, removePermit, resetStore, setActivePermitHash, setPermit, validateImportPermit, validateImportPermitOptions, validateSelfPermit, validateSelfPermitOptions, validateSharingPermit, validateSharingPermitOptions } from './chunk-MRCKUMOS.js';
2
- import './chunk-4FP4V35O.js';
1
+ export { GenerateSealingKey, ImportPermitOptionsValidator, ImportPermitValidator, PERMIT_STORE_DEFAULTS, PermitUtils, SealingKey, SelfPermitOptionsValidator, SelfPermitValidator, SharingPermitOptionsValidator, SharingPermitValidator, SignatureTypes, SignatureUtils, ValidationUtils, _permitStore, addressNotZeroSchema, addressSchema, bytesNotEmptySchema, bytesSchema, clearStaleStore, getActivePermit, getActivePermitHash, getPermit, getPermits, getSignatureTypesAndMessage, permitStore, removeActivePermitHash, removePermit, resetStore, setActivePermitHash, setPermit, validateImportPermit, validateImportPermitOptions, validateSelfPermit, validateSelfPermitOptions, validateSharingPermit, validateSharingPermitOptions } from './chunk-VB62WYPL.js';
2
+ import './chunk-ESMZCFJY.js';
package/dist/web.cjs CHANGED
@@ -393,7 +393,7 @@ var zkVerify = async (verifierUrl, serializedBytes, address, securityZone, chain
393
393
  }
394
394
  };
395
395
  var concatSigRecid = (signature, recid) => {
396
- return signature + (recid + 27).toString(16).padStart(2, "0");
396
+ return `${signature}${(recid + 27).toString(16).padStart(2, "0")}`;
397
397
  };
398
398
 
399
399
  // core/encrypt/MockZkVerifierAbi.ts
@@ -725,9 +725,9 @@ var hardhat2 = defineChain({
725
725
  name: "Hardhat",
726
726
  network: "localhost",
727
727
  // These are unused in the mock environment
728
- coFheUrl: "http://127.0.0.1:8448",
729
- verifierUrl: "http://127.0.0.1:3001",
730
- thresholdNetworkUrl: "http://127.0.0.1:3000",
728
+ coFheUrl: "http://ignored-in-mock-environment",
729
+ verifierUrl: "http://ignored-in-mock-environment",
730
+ thresholdNetworkUrl: "http://ignored-in-mock-environment",
731
731
  environment: "MOCK"
732
732
  });
733
733
  var CofheConfigSchema = zod.z.object({
@@ -1113,6 +1113,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
1113
1113
  securityZone;
1114
1114
  stepCallback;
1115
1115
  inputItems;
1116
+ hpp = false;
1116
1117
  zkvWalletClient;
1117
1118
  tfhePublicKeyDeserializer;
1118
1119
  compactPkeCrsDeserializer;
@@ -1242,6 +1243,20 @@ var EncryptInputsBuilder = class extends BaseBuilder {
1242
1243
  getSecurityZone() {
1243
1244
  return this.securityZone;
1244
1245
  }
1246
+ /**
1247
+ * Example:
1248
+ * ```typescript
1249
+ * const encrypted = await encryptInputs([Encryptable.uint128(10n)])
1250
+ * .asHashPlusProof()
1251
+ * .execute();
1252
+ * ```
1253
+ *
1254
+ * @returns Chainable EncryptInputsBuilder instance that will return a HashPlusProofResult instead of an array of EncryptedItemInputs.
1255
+ */
1256
+ asHashPlusProof() {
1257
+ this.hpp = true;
1258
+ return this;
1259
+ }
1245
1260
  /**
1246
1261
  * @param useWorker - Whether to use Web Workers for ZK proof generation.
1247
1262
  *
@@ -1523,6 +1538,15 @@ var EncryptInputsBuilder = class extends BaseBuilder {
1523
1538
  this.fireStepEnd("verify" /* Verify */);
1524
1539
  return encryptedInputs;
1525
1540
  }
1541
+ structsToHashPlusProof(inItems) {
1542
+ let hashes = [];
1543
+ let proof = "";
1544
+ for (const item of inItems) {
1545
+ hashes.push("0x" + item.ctHash.toString(16).padStart(64, "0"));
1546
+ proof += item.signature;
1547
+ }
1548
+ return [...hashes, proof];
1549
+ }
1526
1550
  /**
1527
1551
  * Final step of the encryption process. MUST BE CALLED LAST IN THE CHAIN.
1528
1552
  *
@@ -1543,9 +1567,14 @@ var EncryptInputsBuilder = class extends BaseBuilder {
1543
1567
  * @returns The encrypted inputs.
1544
1568
  */
1545
1569
  async execute() {
1570
+ let items;
1546
1571
  if (this.chainId === chains.hardhat.id)
1547
- return this.mocksExecute();
1548
- return this.productionExecute();
1572
+ items = await this.mocksExecute();
1573
+ else
1574
+ items = await this.productionExecute();
1575
+ if (this.hpp)
1576
+ return this.structsToHashPlusProof(items);
1577
+ return items;
1549
1578
  }
1550
1579
  };
1551
1580
 
@@ -2844,6 +2873,96 @@ function computeMinuteRampPollIntervalMs(elapsedMs, params) {
2844
2873
  return Math.min(params.maxIntervalMs, Math.max(params.minIntervalMs, intervalMs));
2845
2874
  }
2846
2875
 
2876
+ // core/decrypt/submitRetry.ts
2877
+ var DEFAULT_404_RETRY_TIMEOUT_MS = 1e4;
2878
+ function isRetryableSubmitStatus(status) {
2879
+ return status === 204 || status === 404;
2880
+ }
2881
+ function normalize404RetryTimeoutMs(params) {
2882
+ const { timeoutMs, operationLabel, errorCode } = params;
2883
+ if (timeoutMs === void 0)
2884
+ return DEFAULT_404_RETRY_TIMEOUT_MS;
2885
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
2886
+ throw new CofheError({
2887
+ code: errorCode,
2888
+ message: `${operationLabel} submit 404 retry timeout must be a finite number greater than or equal to 0`,
2889
+ context: {
2890
+ timeoutMs
2891
+ }
2892
+ });
2893
+ }
2894
+ return timeoutMs;
2895
+ }
2896
+ async function classifySubmitResponse(params) {
2897
+ const { response, extractErrorMessage } = params;
2898
+ if (isRetryableSubmitStatus(response.status)) {
2899
+ return { kind: "retryable", status: response.status };
2900
+ }
2901
+ if (response.ok) {
2902
+ return { kind: "parse-json" };
2903
+ }
2904
+ let errorMessage = `HTTP ${response.status}`;
2905
+ try {
2906
+ const errorBody = await response.json();
2907
+ const maybeErrorMessage = extractErrorMessage?.(errorBody);
2908
+ if (typeof maybeErrorMessage === "string" && maybeErrorMessage.length > 0) {
2909
+ errorMessage = maybeErrorMessage;
2910
+ } else if (errorBody && typeof errorBody === "object") {
2911
+ const defaultMessage = errorBody.error_message;
2912
+ const fallbackMessage = errorBody.message;
2913
+ if (typeof defaultMessage === "string" && defaultMessage.length > 0) {
2914
+ errorMessage = defaultMessage;
2915
+ } else if (typeof fallbackMessage === "string" && fallbackMessage.length > 0) {
2916
+ errorMessage = fallbackMessage;
2917
+ }
2918
+ }
2919
+ } catch {
2920
+ errorMessage = response.statusText || errorMessage;
2921
+ }
2922
+ return { kind: "fatal-http", errorMessage };
2923
+ }
2924
+ function throwIfSubmitRetryTimedOut(params) {
2925
+ const {
2926
+ operationLabel,
2927
+ errorCode,
2928
+ status,
2929
+ elapsedMs,
2930
+ retry404TimeoutMs,
2931
+ overallTimeoutMs,
2932
+ thresholdNetworkUrl,
2933
+ body,
2934
+ attemptIndex
2935
+ } = params;
2936
+ if (status === 404 && elapsedMs > retry404TimeoutMs) {
2937
+ throw new CofheError({
2938
+ code: errorCode,
2939
+ message: `${operationLabel} submit retried 404 responses without receiving request_id for ${retry404TimeoutMs}ms`,
2940
+ hint: "The ciphertext may not be indexed yet. Increase set404RetryTimeout(...) if the backend is slow to index ciphertexts.",
2941
+ context: {
2942
+ thresholdNetworkUrl,
2943
+ body,
2944
+ attemptIndex,
2945
+ timeoutMs: retry404TimeoutMs,
2946
+ status
2947
+ }
2948
+ });
2949
+ }
2950
+ if (elapsedMs > overallTimeoutMs) {
2951
+ throw new CofheError({
2952
+ code: errorCode,
2953
+ message: `${operationLabel} submit retried without receiving request_id for ${overallTimeoutMs}ms`,
2954
+ hint: "The ciphertext may still be propagating. Try again later.",
2955
+ context: {
2956
+ thresholdNetworkUrl,
2957
+ body,
2958
+ attemptIndex,
2959
+ timeoutMs: overallTimeoutMs,
2960
+ status
2961
+ }
2962
+ });
2963
+ }
2964
+ }
2965
+
2847
2966
  // core/decrypt/tnSealOutputV2.ts
2848
2967
  var POLL_INTERVAL_MS = 1e3;
2849
2968
  var POLL_MAX_INTERVAL_MS = 1e4;
@@ -2905,7 +3024,7 @@ function parseCompletedSealOutputResponse(params) {
2905
3024
  }
2906
3025
  return convertSealedData(sealed);
2907
3026
  }
2908
- async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
3027
+ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, retry404TimeoutMs, onPoll) {
2909
3028
  const body = {
2910
3029
  ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
2911
3030
  host_chain_id: chainId,
@@ -2935,17 +3054,11 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
2935
3054
  }
2936
3055
  });
2937
3056
  }
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
- }
3057
+ const responseClassification = await classifySubmitResponse({ response });
3058
+ if (responseClassification.kind === "fatal-http") {
2946
3059
  throw new CofheError({
2947
3060
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2948
- message: `sealOutput request failed: ${errorMessage}`,
3061
+ message: `sealOutput request failed: ${responseClassification.errorMessage}`,
2949
3062
  hint: "Check the threshold network URL and request parameters.",
2950
3063
  context: {
2951
3064
  thresholdNetworkUrl,
@@ -2956,8 +3069,8 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
2956
3069
  }
2957
3070
  });
2958
3071
  }
2959
- let submitResponse;
2960
- if (response.status !== 204) {
3072
+ if (responseClassification.kind === "parse-json") {
3073
+ let submitResponse;
2961
3074
  try {
2962
3075
  submitResponse = await response.json();
2963
3076
  } catch (e) {
@@ -2985,46 +3098,39 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
2985
3098
  if (submitResponse.request_id) {
2986
3099
  return { kind: "request_id", requestId: submitResponse.request_id };
2987
3100
  }
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
3101
+ throw new CofheError({
3102
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
3103
+ message: `sealOutput submit response missing request_id`,
3104
+ context: {
3105
+ thresholdNetworkUrl,
3106
+ body,
3107
+ submitResponse,
3108
+ attemptIndex
3109
+ }
3013
3110
  });
3014
- await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS));
3015
- attemptIndex += 1;
3016
- continue;
3017
3111
  }
3018
- throw new CofheError({
3019
- code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
3020
- message: `sealOutput submit response missing request_id`,
3021
- context: {
3022
- thresholdNetworkUrl,
3023
- body,
3024
- submitResponse,
3025
- attemptIndex
3026
- }
3112
+ const elapsedMs = Date.now() - overallStartTime;
3113
+ throwIfSubmitRetryTimedOut({
3114
+ operationLabel: "sealOutput",
3115
+ errorCode: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
3116
+ status: responseClassification.status,
3117
+ elapsedMs,
3118
+ retry404TimeoutMs,
3119
+ overallTimeoutMs: SEAL_OUTPUT_TIMEOUT_MS,
3120
+ thresholdNetworkUrl,
3121
+ body,
3122
+ attemptIndex
3027
3123
  });
3124
+ onPoll?.({
3125
+ operation: "sealoutput",
3126
+ requestId: "",
3127
+ attemptIndex,
3128
+ elapsedMs,
3129
+ intervalMs: SUBMIT_RETRY_INTERVAL_MS,
3130
+ timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
3131
+ });
3132
+ await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS));
3133
+ attemptIndex += 1;
3028
3134
  }
3029
3135
  }
3030
3136
  async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
@@ -3139,7 +3245,12 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
3139
3245
  });
3140
3246
  }
3141
3247
  async function tnSealOutputV2(params) {
3142
- const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
3248
+ const { thresholdNetworkUrl, ctHash, chainId, permission, retry404TimeoutMs, onPoll } = params;
3249
+ const normalized404RetryTimeoutMs = normalize404RetryTimeoutMs({
3250
+ timeoutMs: retry404TimeoutMs,
3251
+ operationLabel: "sealOutput",
3252
+ errorCode: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */
3253
+ });
3143
3254
  const overallStartTime = Date.now();
3144
3255
  const submitResult = await submitSealOutputRequest(
3145
3256
  thresholdNetworkUrl,
@@ -3147,6 +3258,7 @@ async function tnSealOutputV2(params) {
3147
3258
  chainId,
3148
3259
  permission,
3149
3260
  overallStartTime,
3261
+ normalized404RetryTimeoutMs,
3150
3262
  onPoll
3151
3263
  );
3152
3264
  if (submitResult.kind === "completed") {
@@ -3156,12 +3268,14 @@ async function tnSealOutputV2(params) {
3156
3268
  }
3157
3269
 
3158
3270
  // core/decrypt/decryptForViewBuilder.ts
3271
+ var DEFAULT_404_RETRY_TIMEOUT_MS2 = 1e4;
3159
3272
  var DecryptForViewBuilder = class extends BaseBuilder {
3160
3273
  ctHash;
3161
3274
  utype;
3162
3275
  permitHash;
3163
3276
  permit;
3164
3277
  pollCallback;
3278
+ retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS2;
3165
3279
  constructor(params) {
3166
3280
  super({
3167
3281
  config: params.config,
@@ -3222,6 +3336,19 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3222
3336
  this.pollCallback = callback;
3223
3337
  return this;
3224
3338
  }
3339
+ set404RetryTimeout(timeoutMs) {
3340
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
3341
+ throw new CofheError({
3342
+ code: "INTERNAL_ERROR" /* InternalError */,
3343
+ message: "decryptForView: set404RetryTimeout(timeoutMs) expects a finite number greater than or equal to 0.",
3344
+ context: {
3345
+ timeoutMs
3346
+ }
3347
+ });
3348
+ }
3349
+ this.retry404TimeoutMs = timeoutMs;
3350
+ return this;
3351
+ }
3225
3352
  withPermit(permitOrPermitHash) {
3226
3353
  if (typeof permitOrPermitHash === "string") {
3227
3354
  this.permitHash = permitOrPermitHash;
@@ -3350,6 +3477,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
3350
3477
  chainId: this.chainId,
3351
3478
  permission,
3352
3479
  thresholdNetworkUrl,
3480
+ retry404TimeoutMs: this.retry404TimeoutMs,
3353
3481
  onPoll: this.pollCallback
3354
3482
  });
3355
3483
  return PermitUtils.unseal(permit, sealed);
@@ -3625,7 +3753,7 @@ function assertDecryptStatusResponseV2(value) {
3625
3753
  }
3626
3754
  return value;
3627
3755
  }
3628
- async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
3756
+ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, retry404TimeoutMs, onPoll) {
3629
3757
  const body = {
3630
3758
  ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
3631
3759
  host_chain_id: chainId
@@ -3657,19 +3785,11 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
3657
3785
  }
3658
3786
  });
3659
3787
  }
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;
3669
- }
3788
+ const responseClassification = await classifySubmitResponse({ response });
3789
+ if (responseClassification.kind === "fatal-http") {
3670
3790
  throw new CofheError({
3671
3791
  code: "DECRYPT_FAILED" /* DecryptFailed */,
3672
- message: `decrypt request failed: ${errorMessage}`,
3792
+ message: `decrypt request failed: ${responseClassification.errorMessage}`,
3673
3793
  hint: "Check the threshold network URL and request parameters.",
3674
3794
  context: {
3675
3795
  thresholdNetworkUrl,
@@ -3680,8 +3800,8 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
3680
3800
  }
3681
3801
  });
3682
3802
  }
3683
- let submitResponse;
3684
- if (response.status !== 204) {
3803
+ if (responseClassification.kind === "parse-json") {
3804
+ let submitResponse;
3685
3805
  let rawJson;
3686
3806
  try {
3687
3807
  rawJson = await response.json();
@@ -3711,46 +3831,39 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
3711
3831
  if (submitResponse.request_id) {
3712
3832
  return { kind: "request_id", requestId: submitResponse.request_id };
3713
3833
  }
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
3834
+ throw new CofheError({
3835
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
3836
+ message: `decrypt submit response missing request_id`,
3837
+ context: {
3838
+ thresholdNetworkUrl,
3839
+ body,
3840
+ submitResponse,
3841
+ attemptIndex
3842
+ }
3739
3843
  });
3740
- await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS2));
3741
- attemptIndex += 1;
3742
- continue;
3743
3844
  }
3744
- throw new CofheError({
3745
- code: "DECRYPT_FAILED" /* DecryptFailed */,
3746
- message: `decrypt submit response missing request_id`,
3747
- context: {
3748
- thresholdNetworkUrl,
3749
- body,
3750
- submitResponse,
3751
- attemptIndex
3752
- }
3845
+ const elapsedMs = Date.now() - overallStartTime;
3846
+ throwIfSubmitRetryTimedOut({
3847
+ operationLabel: "decrypt",
3848
+ errorCode: "DECRYPT_FAILED" /* DecryptFailed */,
3849
+ status: responseClassification.status,
3850
+ elapsedMs,
3851
+ retry404TimeoutMs,
3852
+ overallTimeoutMs: DECRYPT_TIMEOUT_MS,
3853
+ thresholdNetworkUrl,
3854
+ body,
3855
+ attemptIndex
3753
3856
  });
3857
+ onPoll?.({
3858
+ operation: "decrypt",
3859
+ requestId: "",
3860
+ attemptIndex,
3861
+ elapsedMs,
3862
+ intervalMs: SUBMIT_RETRY_INTERVAL_MS2,
3863
+ timeoutMs: DECRYPT_TIMEOUT_MS
3864
+ });
3865
+ await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS2));
3866
+ attemptIndex += 1;
3754
3867
  }
3755
3868
  }
3756
3869
  async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
@@ -3868,7 +3981,12 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
3868
3981
  });
3869
3982
  }
3870
3983
  async function tnDecryptV2(params) {
3871
- const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
3984
+ const { thresholdNetworkUrl, ctHash, chainId, permission, retry404TimeoutMs, onPoll } = params;
3985
+ const normalized404RetryTimeoutMs = normalize404RetryTimeoutMs({
3986
+ timeoutMs: retry404TimeoutMs,
3987
+ operationLabel: "decrypt",
3988
+ errorCode: "DECRYPT_FAILED" /* DecryptFailed */
3989
+ });
3872
3990
  const overallStartTime = Date.now();
3873
3991
  const submitResult = await submitDecryptRequestV2(
3874
3992
  thresholdNetworkUrl,
@@ -3876,6 +3994,7 @@ async function tnDecryptV2(params) {
3876
3994
  chainId,
3877
3995
  permission,
3878
3996
  overallStartTime,
3997
+ normalized404RetryTimeoutMs,
3879
3998
  onPoll
3880
3999
  );
3881
4000
  if (submitResult.kind === "completed") {
@@ -3885,12 +4004,14 @@ async function tnDecryptV2(params) {
3885
4004
  }
3886
4005
 
3887
4006
  // core/decrypt/decryptForTxBuilder.ts
4007
+ var DEFAULT_404_RETRY_TIMEOUT_MS3 = 1e4;
3888
4008
  var DecryptForTxBuilder = class extends BaseBuilder {
3889
4009
  ctHash;
3890
4010
  permitHash;
3891
4011
  permit;
3892
4012
  permitSelection = "unset";
3893
4013
  pollCallback;
4014
+ retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS3;
3894
4015
  constructor(params) {
3895
4016
  super({
3896
4017
  config: params.config,
@@ -3920,6 +4041,19 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3920
4041
  this.pollCallback = callback;
3921
4042
  return this;
3922
4043
  }
4044
+ set404RetryTimeout(timeoutMs) {
4045
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
4046
+ throw new CofheError({
4047
+ code: "INTERNAL_ERROR" /* InternalError */,
4048
+ message: "decryptForTx: set404RetryTimeout(timeoutMs) expects a finite number greater than or equal to 0.",
4049
+ context: {
4050
+ timeoutMs
4051
+ }
4052
+ });
4053
+ }
4054
+ this.retry404TimeoutMs = timeoutMs;
4055
+ return this;
4056
+ }
3923
4057
  withPermit(permitOrPermitHash) {
3924
4058
  if (this.permitSelection === "with-permit") {
3925
4059
  throw new CofheError({
@@ -4052,6 +4186,7 @@ var DecryptForTxBuilder = class extends BaseBuilder {
4052
4186
  chainId: this.chainId,
4053
4187
  permission,
4054
4188
  thresholdNetworkUrl,
4189
+ retry404TimeoutMs: this.retry404TimeoutMs,
4055
4190
  onPoll: this.pollCallback
4056
4191
  });
4057
4192
  return {
package/dist/web.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-BSbwairE.cjs';
1
+ import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-BDy1qIBu.cjs';
2
2
  import 'viem';
3
3
  import './types-C07FK-cL.cjs';
4
4
  import 'zod';
package/dist/web.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-DDmcgZ0a.js';
1
+ import { I as IStorage, C as CofheInputConfig, a as CofheConfig, b as CofheClient, E as EncryptableItem } from './clientTypes-CyUvRRzA.js';
2
2
  import 'viem';
3
3
  import './types-C07FK-cL.js';
4
4
  import 'zod';
package/dist/web.js CHANGED
@@ -1,7 +1,7 @@
1
- import { createCofheConfigBase, createCofheClientBase, fheTypeToString } from './chunk-S7OKGLFD.js';
2
- import './chunk-TBLR7NNE.js';
3
- import './chunk-MRCKUMOS.js';
4
- import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-4FP4V35O.js';
1
+ import { createCofheConfigBase, createCofheClientBase, fheTypeToString } from './chunk-PE5V5CCV.js';
2
+ import './chunk-MTRAXQXC.js';
3
+ import './chunk-VB62WYPL.js';
4
+ import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-ESMZCFJY.js';
5
5
  import { constructClient } from 'iframe-shared-storage';
6
6
 
7
7
  // web/const.ts
@@ -1,4 +1,4 @@
1
- import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-4FP4V35O.js';
1
+ import { TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT } from './chunk-ESMZCFJY.js';
2
2
 
3
3
  // web/zkProve.worker.ts
4
4
  var tfheModule = null;