@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
@@ -1,6 +1,6 @@
1
- import { hardhat as hardhat$1 } from './chunk-TBLR7NNE.js';
2
- import { permitStore, PermitUtils } from './chunk-MRCKUMOS.js';
3
- import { TFHE_RS_KEY_VERSION, TASK_MANAGER_ADDRESS, TFHE_RS_ZK_MAX_BITS, TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT, MOCKS_THRESHOLD_NETWORK_ADDRESS, MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY, MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY, MOCKS_ZK_VERIFIER_ADDRESS } from './chunk-4FP4V35O.js';
1
+ import { hardhat as hardhat$1 } from './chunk-MTRAXQXC.js';
2
+ import { permitStore, PermitUtils } from './chunk-VB62WYPL.js';
3
+ import { TFHE_RS_KEY_VERSION, TASK_MANAGER_ADDRESS, TFHE_RS_ZK_MAX_BITS, TFHE_RS_SAFE_SERIALIZATION_SIZE_LIMIT, MOCKS_THRESHOLD_NETWORK_ADDRESS, MOCKS_DECRYPT_RESULT_SIGNER_PRIVATE_KEY, MOCKS_ZK_VERIFIER_SIGNER_PRIVATE_KEY, MOCKS_ZK_VERIFIER_ADDRESS } from './chunk-ESMZCFJY.js';
4
4
  import { createStore } from 'zustand/vanilla';
5
5
  import { parseAbi, isAddressEqual, zeroAddress, recoverAddress, encodePacked, keccak256, createWalletClient, http, getAddress, parseSignature, serializeSignature } from 'viem';
6
6
  import { hardhat } from 'viem/chains';
@@ -10,51 +10,51 @@ import { persist, createJSONStorage } from 'zustand/middleware';
10
10
  import { produce } from 'immer';
11
11
 
12
12
  // core/error.ts
13
- var CofheErrorCode = /* @__PURE__ */ ((CofheErrorCode2) => {
14
- CofheErrorCode2["InternalError"] = "INTERNAL_ERROR";
15
- CofheErrorCode2["UnknownEnvironment"] = "UNKNOWN_ENVIRONMENT";
16
- CofheErrorCode2["InitTfheFailed"] = "INIT_TFHE_FAILED";
17
- CofheErrorCode2["InitViemFailed"] = "INIT_VIEM_FAILED";
18
- CofheErrorCode2["InitEthersFailed"] = "INIT_ETHERS_FAILED";
19
- CofheErrorCode2["NotConnected"] = "NOT_CONNECTED";
20
- CofheErrorCode2["MissingPublicClient"] = "MISSING_PUBLIC_CLIENT";
21
- CofheErrorCode2["MissingWalletClient"] = "MISSING_WALLET_CLIENT";
22
- CofheErrorCode2["MissingProviderParam"] = "MISSING_PROVIDER_PARAM";
23
- CofheErrorCode2["EmptySecurityZonesParam"] = "EMPTY_SECURITY_ZONES_PARAM";
24
- CofheErrorCode2["InvalidPermitData"] = "INVALID_PERMIT_DATA";
25
- CofheErrorCode2["InvalidPermitDomain"] = "INVALID_PERMIT_DOMAIN";
26
- CofheErrorCode2["PermitNotFound"] = "PERMIT_NOT_FOUND";
27
- CofheErrorCode2["CannotRemoveLastPermit"] = "CANNOT_REMOVE_LAST_PERMIT";
28
- CofheErrorCode2["AccountUninitialized"] = "ACCOUNT_UNINITIALIZED";
29
- CofheErrorCode2["ChainIdUninitialized"] = "CHAIN_ID_UNINITIALIZED";
30
- CofheErrorCode2["SealOutputFailed"] = "SEAL_OUTPUT_FAILED";
31
- CofheErrorCode2["SealOutputReturnedNull"] = "SEAL_OUTPUT_RETURNED_NULL";
32
- CofheErrorCode2["InvalidUtype"] = "INVALID_UTYPE";
33
- CofheErrorCode2["DecryptFailed"] = "DECRYPT_FAILED";
34
- CofheErrorCode2["DecryptReturnedNull"] = "DECRYPT_RETURNED_NULL";
35
- CofheErrorCode2["ZkMocksInsertCtHashesFailed"] = "ZK_MOCKS_INSERT_CT_HASHES_FAILED";
36
- CofheErrorCode2["ZkMocksCalcCtHashesFailed"] = "ZK_MOCKS_CALC_CT_HASHES_FAILED";
37
- CofheErrorCode2["ZkMocksVerifySignFailed"] = "ZK_MOCKS_VERIFY_SIGN_FAILED";
38
- CofheErrorCode2["ZkMocksCreateProofSignatureFailed"] = "ZK_MOCKS_CREATE_PROOF_SIGNATURE_FAILED";
39
- CofheErrorCode2["ZkVerifyFailed"] = "ZK_VERIFY_FAILED";
40
- CofheErrorCode2["ZkPackFailed"] = "ZK_PACK_FAILED";
41
- CofheErrorCode2["ZkProveFailed"] = "ZK_PROVE_FAILED";
42
- CofheErrorCode2["EncryptRemainingInItems"] = "ENCRYPT_REMAINING_IN_ITEMS";
43
- CofheErrorCode2["ZkUninitialized"] = "ZK_UNINITIALIZED";
44
- CofheErrorCode2["ZkVerifierUrlUninitialized"] = "ZK_VERIFIER_URL_UNINITIALIZED";
45
- CofheErrorCode2["ThresholdNetworkUrlUninitialized"] = "THRESHOLD_NETWORK_URL_UNINITIALIZED";
46
- CofheErrorCode2["MissingConfig"] = "MISSING_CONFIG";
47
- CofheErrorCode2["UnsupportedChain"] = "UNSUPPORTED_CHAIN";
48
- CofheErrorCode2["MissingZkBuilderAndCrsGenerator"] = "MISSING_ZK_BUILDER_AND_CRS_GENERATOR";
49
- CofheErrorCode2["MissingTfhePublicKeyDeserializer"] = "MISSING_TFHE_PUBLIC_KEY_DESERIALIZER";
50
- CofheErrorCode2["MissingCompactPkeCrsDeserializer"] = "MISSING_COMPACT_PKE_CRS_DESERIALIZER";
51
- CofheErrorCode2["MissingFheKey"] = "MISSING_FHE_KEY";
52
- CofheErrorCode2["MissingCrs"] = "MISSING_CRS";
53
- CofheErrorCode2["FetchKeysFailed"] = "FETCH_KEYS_FAILED";
54
- CofheErrorCode2["PublicWalletGetChainIdFailed"] = "PUBLIC_WALLET_GET_CHAIN_ID_FAILED";
55
- CofheErrorCode2["PublicWalletGetAddressesFailed"] = "PUBLIC_WALLET_GET_ADDRESSES_FAILED";
56
- CofheErrorCode2["RehydrateKeysStoreFailed"] = "REHYDRATE_KEYS_STORE_FAILED";
57
- return CofheErrorCode2;
13
+ var CofheErrorCode = /* @__PURE__ */ ((CofheErrorCode3) => {
14
+ CofheErrorCode3["InternalError"] = "INTERNAL_ERROR";
15
+ CofheErrorCode3["UnknownEnvironment"] = "UNKNOWN_ENVIRONMENT";
16
+ CofheErrorCode3["InitTfheFailed"] = "INIT_TFHE_FAILED";
17
+ CofheErrorCode3["InitViemFailed"] = "INIT_VIEM_FAILED";
18
+ CofheErrorCode3["InitEthersFailed"] = "INIT_ETHERS_FAILED";
19
+ CofheErrorCode3["NotConnected"] = "NOT_CONNECTED";
20
+ CofheErrorCode3["MissingPublicClient"] = "MISSING_PUBLIC_CLIENT";
21
+ CofheErrorCode3["MissingWalletClient"] = "MISSING_WALLET_CLIENT";
22
+ CofheErrorCode3["MissingProviderParam"] = "MISSING_PROVIDER_PARAM";
23
+ CofheErrorCode3["EmptySecurityZonesParam"] = "EMPTY_SECURITY_ZONES_PARAM";
24
+ CofheErrorCode3["InvalidPermitData"] = "INVALID_PERMIT_DATA";
25
+ CofheErrorCode3["InvalidPermitDomain"] = "INVALID_PERMIT_DOMAIN";
26
+ CofheErrorCode3["PermitNotFound"] = "PERMIT_NOT_FOUND";
27
+ CofheErrorCode3["CannotRemoveLastPermit"] = "CANNOT_REMOVE_LAST_PERMIT";
28
+ CofheErrorCode3["AccountUninitialized"] = "ACCOUNT_UNINITIALIZED";
29
+ CofheErrorCode3["ChainIdUninitialized"] = "CHAIN_ID_UNINITIALIZED";
30
+ CofheErrorCode3["SealOutputFailed"] = "SEAL_OUTPUT_FAILED";
31
+ CofheErrorCode3["SealOutputReturnedNull"] = "SEAL_OUTPUT_RETURNED_NULL";
32
+ CofheErrorCode3["InvalidUtype"] = "INVALID_UTYPE";
33
+ CofheErrorCode3["DecryptFailed"] = "DECRYPT_FAILED";
34
+ CofheErrorCode3["DecryptReturnedNull"] = "DECRYPT_RETURNED_NULL";
35
+ CofheErrorCode3["ZkMocksInsertCtHashesFailed"] = "ZK_MOCKS_INSERT_CT_HASHES_FAILED";
36
+ CofheErrorCode3["ZkMocksCalcCtHashesFailed"] = "ZK_MOCKS_CALC_CT_HASHES_FAILED";
37
+ CofheErrorCode3["ZkMocksVerifySignFailed"] = "ZK_MOCKS_VERIFY_SIGN_FAILED";
38
+ CofheErrorCode3["ZkMocksCreateProofSignatureFailed"] = "ZK_MOCKS_CREATE_PROOF_SIGNATURE_FAILED";
39
+ CofheErrorCode3["ZkVerifyFailed"] = "ZK_VERIFY_FAILED";
40
+ CofheErrorCode3["ZkPackFailed"] = "ZK_PACK_FAILED";
41
+ CofheErrorCode3["ZkProveFailed"] = "ZK_PROVE_FAILED";
42
+ CofheErrorCode3["EncryptRemainingInItems"] = "ENCRYPT_REMAINING_IN_ITEMS";
43
+ CofheErrorCode3["ZkUninitialized"] = "ZK_UNINITIALIZED";
44
+ CofheErrorCode3["ZkVerifierUrlUninitialized"] = "ZK_VERIFIER_URL_UNINITIALIZED";
45
+ CofheErrorCode3["ThresholdNetworkUrlUninitialized"] = "THRESHOLD_NETWORK_URL_UNINITIALIZED";
46
+ CofheErrorCode3["MissingConfig"] = "MISSING_CONFIG";
47
+ CofheErrorCode3["UnsupportedChain"] = "UNSUPPORTED_CHAIN";
48
+ CofheErrorCode3["MissingZkBuilderAndCrsGenerator"] = "MISSING_ZK_BUILDER_AND_CRS_GENERATOR";
49
+ CofheErrorCode3["MissingTfhePublicKeyDeserializer"] = "MISSING_TFHE_PUBLIC_KEY_DESERIALIZER";
50
+ CofheErrorCode3["MissingCompactPkeCrsDeserializer"] = "MISSING_COMPACT_PKE_CRS_DESERIALIZER";
51
+ CofheErrorCode3["MissingFheKey"] = "MISSING_FHE_KEY";
52
+ CofheErrorCode3["MissingCrs"] = "MISSING_CRS";
53
+ CofheErrorCode3["FetchKeysFailed"] = "FETCH_KEYS_FAILED";
54
+ CofheErrorCode3["PublicWalletGetChainIdFailed"] = "PUBLIC_WALLET_GET_CHAIN_ID_FAILED";
55
+ CofheErrorCode3["PublicWalletGetAddressesFailed"] = "PUBLIC_WALLET_GET_ADDRESSES_FAILED";
56
+ CofheErrorCode3["RehydrateKeysStoreFailed"] = "REHYDRATE_KEYS_STORE_FAILED";
57
+ return CofheErrorCode3;
58
58
  })(CofheErrorCode || {});
59
59
  var CofheError = class _CofheError extends Error {
60
60
  code;
@@ -528,7 +528,7 @@ var zkVerify = async (verifierUrl, serializedBytes, address, securityZone, chain
528
528
  }
529
529
  };
530
530
  var concatSigRecid = (signature, recid) => {
531
- return signature + (recid + 27).toString(16).padStart(2, "0");
531
+ return `${signature}${(recid + 27).toString(16).padStart(2, "0")}`;
532
532
  };
533
533
 
534
534
  // core/encrypt/MockZkVerifierAbi.ts
@@ -1215,6 +1215,7 @@ var EncryptInputsBuilder = class extends BaseBuilder {
1215
1215
  securityZone;
1216
1216
  stepCallback;
1217
1217
  inputItems;
1218
+ hpp = false;
1218
1219
  zkvWalletClient;
1219
1220
  tfhePublicKeyDeserializer;
1220
1221
  compactPkeCrsDeserializer;
@@ -1344,6 +1345,20 @@ var EncryptInputsBuilder = class extends BaseBuilder {
1344
1345
  getSecurityZone() {
1345
1346
  return this.securityZone;
1346
1347
  }
1348
+ /**
1349
+ * Example:
1350
+ * ```typescript
1351
+ * const encrypted = await encryptInputs([Encryptable.uint128(10n)])
1352
+ * .asHashPlusProof()
1353
+ * .execute();
1354
+ * ```
1355
+ *
1356
+ * @returns Chainable EncryptInputsBuilder instance that will return a HashPlusProofResult instead of an array of EncryptedItemInputs.
1357
+ */
1358
+ asHashPlusProof() {
1359
+ this.hpp = true;
1360
+ return this;
1361
+ }
1347
1362
  /**
1348
1363
  * @param useWorker - Whether to use Web Workers for ZK proof generation.
1349
1364
  *
@@ -1625,6 +1640,15 @@ var EncryptInputsBuilder = class extends BaseBuilder {
1625
1640
  this.fireStepEnd("verify" /* Verify */);
1626
1641
  return encryptedInputs;
1627
1642
  }
1643
+ structsToHashPlusProof(inItems) {
1644
+ let hashes = [];
1645
+ let proof = "";
1646
+ for (const item of inItems) {
1647
+ hashes.push("0x" + item.ctHash.toString(16).padStart(64, "0"));
1648
+ proof += item.signature;
1649
+ }
1650
+ return [...hashes, proof];
1651
+ }
1628
1652
  /**
1629
1653
  * Final step of the encryption process. MUST BE CALLED LAST IN THE CHAIN.
1630
1654
  *
@@ -1645,9 +1669,14 @@ var EncryptInputsBuilder = class extends BaseBuilder {
1645
1669
  * @returns The encrypted inputs.
1646
1670
  */
1647
1671
  async execute() {
1672
+ let items;
1648
1673
  if (this.chainId === hardhat.id)
1649
- return this.mocksExecute();
1650
- return this.productionExecute();
1674
+ items = await this.mocksExecute();
1675
+ else
1676
+ items = await this.productionExecute();
1677
+ if (this.hpp)
1678
+ return this.structsToHashPlusProof(items);
1679
+ return items;
1651
1680
  }
1652
1681
  };
1653
1682
  var storeActivePermit = async (permit, publicClient, walletClient) => {
@@ -1977,6 +2006,96 @@ function computeMinuteRampPollIntervalMs(elapsedMs, params) {
1977
2006
  return Math.min(params.maxIntervalMs, Math.max(params.minIntervalMs, intervalMs));
1978
2007
  }
1979
2008
 
2009
+ // core/decrypt/submitRetry.ts
2010
+ var DEFAULT_404_RETRY_TIMEOUT_MS = 1e4;
2011
+ function isRetryableSubmitStatus(status) {
2012
+ return status === 204 || status === 404;
2013
+ }
2014
+ function normalize404RetryTimeoutMs(params) {
2015
+ const { timeoutMs, operationLabel, errorCode } = params;
2016
+ if (timeoutMs === void 0)
2017
+ return DEFAULT_404_RETRY_TIMEOUT_MS;
2018
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
2019
+ throw new CofheError({
2020
+ code: errorCode,
2021
+ message: `${operationLabel} submit 404 retry timeout must be a finite number greater than or equal to 0`,
2022
+ context: {
2023
+ timeoutMs
2024
+ }
2025
+ });
2026
+ }
2027
+ return timeoutMs;
2028
+ }
2029
+ async function classifySubmitResponse(params) {
2030
+ const { response, extractErrorMessage } = params;
2031
+ if (isRetryableSubmitStatus(response.status)) {
2032
+ return { kind: "retryable", status: response.status };
2033
+ }
2034
+ if (response.ok) {
2035
+ return { kind: "parse-json" };
2036
+ }
2037
+ let errorMessage = `HTTP ${response.status}`;
2038
+ try {
2039
+ const errorBody = await response.json();
2040
+ const maybeErrorMessage = extractErrorMessage?.(errorBody);
2041
+ if (typeof maybeErrorMessage === "string" && maybeErrorMessage.length > 0) {
2042
+ errorMessage = maybeErrorMessage;
2043
+ } else if (errorBody && typeof errorBody === "object") {
2044
+ const defaultMessage = errorBody.error_message;
2045
+ const fallbackMessage = errorBody.message;
2046
+ if (typeof defaultMessage === "string" && defaultMessage.length > 0) {
2047
+ errorMessage = defaultMessage;
2048
+ } else if (typeof fallbackMessage === "string" && fallbackMessage.length > 0) {
2049
+ errorMessage = fallbackMessage;
2050
+ }
2051
+ }
2052
+ } catch {
2053
+ errorMessage = response.statusText || errorMessage;
2054
+ }
2055
+ return { kind: "fatal-http", errorMessage };
2056
+ }
2057
+ function throwIfSubmitRetryTimedOut(params) {
2058
+ const {
2059
+ operationLabel,
2060
+ errorCode,
2061
+ status,
2062
+ elapsedMs,
2063
+ retry404TimeoutMs,
2064
+ overallTimeoutMs,
2065
+ thresholdNetworkUrl,
2066
+ body,
2067
+ attemptIndex
2068
+ } = params;
2069
+ if (status === 404 && elapsedMs > retry404TimeoutMs) {
2070
+ throw new CofheError({
2071
+ code: errorCode,
2072
+ message: `${operationLabel} submit retried 404 responses without receiving request_id for ${retry404TimeoutMs}ms`,
2073
+ hint: "The ciphertext may not be indexed yet. Increase set404RetryTimeout(...) if the backend is slow to index ciphertexts.",
2074
+ context: {
2075
+ thresholdNetworkUrl,
2076
+ body,
2077
+ attemptIndex,
2078
+ timeoutMs: retry404TimeoutMs,
2079
+ status
2080
+ }
2081
+ });
2082
+ }
2083
+ if (elapsedMs > overallTimeoutMs) {
2084
+ throw new CofheError({
2085
+ code: errorCode,
2086
+ message: `${operationLabel} submit retried without receiving request_id for ${overallTimeoutMs}ms`,
2087
+ hint: "The ciphertext may still be propagating. Try again later.",
2088
+ context: {
2089
+ thresholdNetworkUrl,
2090
+ body,
2091
+ attemptIndex,
2092
+ timeoutMs: overallTimeoutMs,
2093
+ status
2094
+ }
2095
+ });
2096
+ }
2097
+ }
2098
+
1980
2099
  // core/decrypt/tnSealOutputV2.ts
1981
2100
  var POLL_INTERVAL_MS = 1e3;
1982
2101
  var POLL_MAX_INTERVAL_MS = 1e4;
@@ -2038,7 +2157,7 @@ function parseCompletedSealOutputResponse(params) {
2038
2157
  }
2039
2158
  return convertSealedData(sealed);
2040
2159
  }
2041
- async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
2160
+ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, retry404TimeoutMs, onPoll) {
2042
2161
  const body = {
2043
2162
  ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
2044
2163
  host_chain_id: chainId,
@@ -2068,17 +2187,11 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
2068
2187
  }
2069
2188
  });
2070
2189
  }
2071
- if (!response.ok) {
2072
- let errorMessage = `HTTP ${response.status}`;
2073
- try {
2074
- const errorBody = await response.json();
2075
- errorMessage = errorBody.error_message || errorBody.message || errorMessage;
2076
- } catch {
2077
- errorMessage = response.statusText || errorMessage;
2078
- }
2190
+ const responseClassification = await classifySubmitResponse({ response });
2191
+ if (responseClassification.kind === "fatal-http") {
2079
2192
  throw new CofheError({
2080
2193
  code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2081
- message: `sealOutput request failed: ${errorMessage}`,
2194
+ message: `sealOutput request failed: ${responseClassification.errorMessage}`,
2082
2195
  hint: "Check the threshold network URL and request parameters.",
2083
2196
  context: {
2084
2197
  thresholdNetworkUrl,
@@ -2089,8 +2202,8 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
2089
2202
  }
2090
2203
  });
2091
2204
  }
2092
- let submitResponse;
2093
- if (response.status !== 204) {
2205
+ if (responseClassification.kind === "parse-json") {
2206
+ let submitResponse;
2094
2207
  try {
2095
2208
  submitResponse = await response.json();
2096
2209
  } catch (e) {
@@ -2118,46 +2231,39 @@ async function submitSealOutputRequest(thresholdNetworkUrl, ctHash, chainId, per
2118
2231
  if (submitResponse.request_id) {
2119
2232
  return { kind: "request_id", requestId: submitResponse.request_id };
2120
2233
  }
2121
- }
2122
- if (response.status === 204) {
2123
- const elapsedMs = Date.now() - overallStartTime;
2124
- if (elapsedMs > SEAL_OUTPUT_TIMEOUT_MS) {
2125
- throw new CofheError({
2126
- code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2127
- message: `sealOutput submit retried without receiving request_id for ${SEAL_OUTPUT_TIMEOUT_MS}ms`,
2128
- hint: "The ciphertext may still be propagating. Try again later.",
2129
- context: {
2130
- thresholdNetworkUrl,
2131
- body,
2132
- attemptIndex,
2133
- timeoutMs: SEAL_OUTPUT_TIMEOUT_MS,
2134
- submitResponse,
2135
- status: response.status
2136
- }
2137
- });
2138
- }
2139
- onPoll?.({
2140
- operation: "sealoutput",
2141
- requestId: "",
2142
- attemptIndex,
2143
- elapsedMs,
2144
- intervalMs: SUBMIT_RETRY_INTERVAL_MS,
2145
- timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
2234
+ throw new CofheError({
2235
+ code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2236
+ message: `sealOutput submit response missing request_id`,
2237
+ context: {
2238
+ thresholdNetworkUrl,
2239
+ body,
2240
+ submitResponse,
2241
+ attemptIndex
2242
+ }
2146
2243
  });
2147
- await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS));
2148
- attemptIndex += 1;
2149
- continue;
2150
2244
  }
2151
- throw new CofheError({
2152
- code: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2153
- message: `sealOutput submit response missing request_id`,
2154
- context: {
2155
- thresholdNetworkUrl,
2156
- body,
2157
- submitResponse,
2158
- attemptIndex
2159
- }
2245
+ const elapsedMs = Date.now() - overallStartTime;
2246
+ throwIfSubmitRetryTimedOut({
2247
+ operationLabel: "sealOutput",
2248
+ errorCode: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */,
2249
+ status: responseClassification.status,
2250
+ elapsedMs,
2251
+ retry404TimeoutMs,
2252
+ overallTimeoutMs: SEAL_OUTPUT_TIMEOUT_MS,
2253
+ thresholdNetworkUrl,
2254
+ body,
2255
+ attemptIndex
2160
2256
  });
2257
+ onPoll?.({
2258
+ operation: "sealoutput",
2259
+ requestId: "",
2260
+ attemptIndex,
2261
+ elapsedMs,
2262
+ intervalMs: SUBMIT_RETRY_INTERVAL_MS,
2263
+ timeoutMs: SEAL_OUTPUT_TIMEOUT_MS
2264
+ });
2265
+ await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS));
2266
+ attemptIndex += 1;
2161
2267
  }
2162
2268
  }
2163
2269
  async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
@@ -2272,7 +2378,12 @@ async function pollSealOutputStatus(thresholdNetworkUrl, requestId, overallStart
2272
2378
  });
2273
2379
  }
2274
2380
  async function tnSealOutputV2(params) {
2275
- const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
2381
+ const { thresholdNetworkUrl, ctHash, chainId, permission, retry404TimeoutMs, onPoll } = params;
2382
+ const normalized404RetryTimeoutMs = normalize404RetryTimeoutMs({
2383
+ timeoutMs: retry404TimeoutMs,
2384
+ operationLabel: "sealOutput",
2385
+ errorCode: "SEAL_OUTPUT_FAILED" /* SealOutputFailed */
2386
+ });
2276
2387
  const overallStartTime = Date.now();
2277
2388
  const submitResult = await submitSealOutputRequest(
2278
2389
  thresholdNetworkUrl,
@@ -2280,6 +2391,7 @@ async function tnSealOutputV2(params) {
2280
2391
  chainId,
2281
2392
  permission,
2282
2393
  overallStartTime,
2394
+ normalized404RetryTimeoutMs,
2283
2395
  onPoll
2284
2396
  );
2285
2397
  if (submitResult.kind === "completed") {
@@ -2289,12 +2401,14 @@ async function tnSealOutputV2(params) {
2289
2401
  }
2290
2402
 
2291
2403
  // core/decrypt/decryptForViewBuilder.ts
2404
+ var DEFAULT_404_RETRY_TIMEOUT_MS2 = 1e4;
2292
2405
  var DecryptForViewBuilder = class extends BaseBuilder {
2293
2406
  ctHash;
2294
2407
  utype;
2295
2408
  permitHash;
2296
2409
  permit;
2297
2410
  pollCallback;
2411
+ retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS2;
2298
2412
  constructor(params) {
2299
2413
  super({
2300
2414
  config: params.config,
@@ -2355,6 +2469,19 @@ var DecryptForViewBuilder = class extends BaseBuilder {
2355
2469
  this.pollCallback = callback;
2356
2470
  return this;
2357
2471
  }
2472
+ set404RetryTimeout(timeoutMs) {
2473
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
2474
+ throw new CofheError({
2475
+ code: "INTERNAL_ERROR" /* InternalError */,
2476
+ message: "decryptForView: set404RetryTimeout(timeoutMs) expects a finite number greater than or equal to 0.",
2477
+ context: {
2478
+ timeoutMs
2479
+ }
2480
+ });
2481
+ }
2482
+ this.retry404TimeoutMs = timeoutMs;
2483
+ return this;
2484
+ }
2358
2485
  withPermit(permitOrPermitHash) {
2359
2486
  if (typeof permitOrPermitHash === "string") {
2360
2487
  this.permitHash = permitOrPermitHash;
@@ -2483,6 +2610,7 @@ var DecryptForViewBuilder = class extends BaseBuilder {
2483
2610
  chainId: this.chainId,
2484
2611
  permission,
2485
2612
  thresholdNetworkUrl,
2613
+ retry404TimeoutMs: this.retry404TimeoutMs,
2486
2614
  onPoll: this.pollCallback
2487
2615
  });
2488
2616
  return PermitUtils.unseal(permit, sealed);
@@ -2758,7 +2886,7 @@ function assertDecryptStatusResponseV2(value) {
2758
2886
  }
2759
2887
  return value;
2760
2888
  }
2761
- async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, onPoll) {
2889
+ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission, overallStartTime, retry404TimeoutMs, onPoll) {
2762
2890
  const body = {
2763
2891
  ct_tempkey: BigInt(ctHash).toString(16).padStart(64, "0"),
2764
2892
  host_chain_id: chainId
@@ -2790,19 +2918,11 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
2790
2918
  }
2791
2919
  });
2792
2920
  }
2793
- if (!response.ok) {
2794
- let errorMessage = `HTTP ${response.status}`;
2795
- try {
2796
- const errorBody = await response.json();
2797
- const maybeMessage = errorBody.error_message || errorBody.message;
2798
- if (typeof maybeMessage === "string" && maybeMessage.length > 0)
2799
- errorMessage = maybeMessage;
2800
- } catch {
2801
- errorMessage = response.statusText || errorMessage;
2802
- }
2921
+ const responseClassification = await classifySubmitResponse({ response });
2922
+ if (responseClassification.kind === "fatal-http") {
2803
2923
  throw new CofheError({
2804
2924
  code: "DECRYPT_FAILED" /* DecryptFailed */,
2805
- message: `decrypt request failed: ${errorMessage}`,
2925
+ message: `decrypt request failed: ${responseClassification.errorMessage}`,
2806
2926
  hint: "Check the threshold network URL and request parameters.",
2807
2927
  context: {
2808
2928
  thresholdNetworkUrl,
@@ -2813,8 +2933,8 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
2813
2933
  }
2814
2934
  });
2815
2935
  }
2816
- let submitResponse;
2817
- if (response.status !== 204) {
2936
+ if (responseClassification.kind === "parse-json") {
2937
+ let submitResponse;
2818
2938
  let rawJson;
2819
2939
  try {
2820
2940
  rawJson = await response.json();
@@ -2844,46 +2964,39 @@ async function submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, perm
2844
2964
  if (submitResponse.request_id) {
2845
2965
  return { kind: "request_id", requestId: submitResponse.request_id };
2846
2966
  }
2847
- }
2848
- if (response.status === 204) {
2849
- const elapsedMs = Date.now() - overallStartTime;
2850
- if (elapsedMs > DECRYPT_TIMEOUT_MS) {
2851
- throw new CofheError({
2852
- code: "DECRYPT_FAILED" /* DecryptFailed */,
2853
- message: `decrypt submit retried without receiving request_id for ${DECRYPT_TIMEOUT_MS}ms`,
2854
- hint: "The ciphertext may still be propagating. Try again later.",
2855
- context: {
2856
- thresholdNetworkUrl,
2857
- body,
2858
- attemptIndex,
2859
- timeoutMs: DECRYPT_TIMEOUT_MS,
2860
- submitResponse,
2861
- status: response.status
2862
- }
2863
- });
2864
- }
2865
- onPoll?.({
2866
- operation: "decrypt",
2867
- requestId: "",
2868
- attemptIndex,
2869
- elapsedMs,
2870
- intervalMs: SUBMIT_RETRY_INTERVAL_MS2,
2871
- timeoutMs: DECRYPT_TIMEOUT_MS
2967
+ throw new CofheError({
2968
+ code: "DECRYPT_FAILED" /* DecryptFailed */,
2969
+ message: `decrypt submit response missing request_id`,
2970
+ context: {
2971
+ thresholdNetworkUrl,
2972
+ body,
2973
+ submitResponse,
2974
+ attemptIndex
2975
+ }
2872
2976
  });
2873
- await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS2));
2874
- attemptIndex += 1;
2875
- continue;
2876
2977
  }
2877
- throw new CofheError({
2878
- code: "DECRYPT_FAILED" /* DecryptFailed */,
2879
- message: `decrypt submit response missing request_id`,
2880
- context: {
2881
- thresholdNetworkUrl,
2882
- body,
2883
- submitResponse,
2884
- attemptIndex
2885
- }
2978
+ const elapsedMs = Date.now() - overallStartTime;
2979
+ throwIfSubmitRetryTimedOut({
2980
+ operationLabel: "decrypt",
2981
+ errorCode: "DECRYPT_FAILED" /* DecryptFailed */,
2982
+ status: responseClassification.status,
2983
+ elapsedMs,
2984
+ retry404TimeoutMs,
2985
+ overallTimeoutMs: DECRYPT_TIMEOUT_MS,
2986
+ thresholdNetworkUrl,
2987
+ body,
2988
+ attemptIndex
2886
2989
  });
2990
+ onPoll?.({
2991
+ operation: "decrypt",
2992
+ requestId: "",
2993
+ attemptIndex,
2994
+ elapsedMs,
2995
+ intervalMs: SUBMIT_RETRY_INTERVAL_MS2,
2996
+ timeoutMs: DECRYPT_TIMEOUT_MS
2997
+ });
2998
+ await new Promise((resolve) => setTimeout(resolve, SUBMIT_RETRY_INTERVAL_MS2));
2999
+ attemptIndex += 1;
2887
3000
  }
2888
3001
  }
2889
3002
  async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartTime, onPoll) {
@@ -3001,7 +3114,12 @@ async function pollDecryptStatusV2(thresholdNetworkUrl, requestId, overallStartT
3001
3114
  });
3002
3115
  }
3003
3116
  async function tnDecryptV2(params) {
3004
- const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
3117
+ const { thresholdNetworkUrl, ctHash, chainId, permission, retry404TimeoutMs, onPoll } = params;
3118
+ const normalized404RetryTimeoutMs = normalize404RetryTimeoutMs({
3119
+ timeoutMs: retry404TimeoutMs,
3120
+ operationLabel: "decrypt",
3121
+ errorCode: "DECRYPT_FAILED" /* DecryptFailed */
3122
+ });
3005
3123
  const overallStartTime = Date.now();
3006
3124
  const submitResult = await submitDecryptRequestV2(
3007
3125
  thresholdNetworkUrl,
@@ -3009,6 +3127,7 @@ async function tnDecryptV2(params) {
3009
3127
  chainId,
3010
3128
  permission,
3011
3129
  overallStartTime,
3130
+ normalized404RetryTimeoutMs,
3012
3131
  onPoll
3013
3132
  );
3014
3133
  if (submitResult.kind === "completed") {
@@ -3018,12 +3137,14 @@ async function tnDecryptV2(params) {
3018
3137
  }
3019
3138
 
3020
3139
  // core/decrypt/decryptForTxBuilder.ts
3140
+ var DEFAULT_404_RETRY_TIMEOUT_MS3 = 1e4;
3021
3141
  var DecryptForTxBuilder = class extends BaseBuilder {
3022
3142
  ctHash;
3023
3143
  permitHash;
3024
3144
  permit;
3025
3145
  permitSelection = "unset";
3026
3146
  pollCallback;
3147
+ retry404TimeoutMs = DEFAULT_404_RETRY_TIMEOUT_MS3;
3027
3148
  constructor(params) {
3028
3149
  super({
3029
3150
  config: params.config,
@@ -3053,6 +3174,19 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3053
3174
  this.pollCallback = callback;
3054
3175
  return this;
3055
3176
  }
3177
+ set404RetryTimeout(timeoutMs) {
3178
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
3179
+ throw new CofheError({
3180
+ code: "INTERNAL_ERROR" /* InternalError */,
3181
+ message: "decryptForTx: set404RetryTimeout(timeoutMs) expects a finite number greater than or equal to 0.",
3182
+ context: {
3183
+ timeoutMs
3184
+ }
3185
+ });
3186
+ }
3187
+ this.retry404TimeoutMs = timeoutMs;
3188
+ return this;
3189
+ }
3056
3190
  withPermit(permitOrPermitHash) {
3057
3191
  if (this.permitSelection === "with-permit") {
3058
3192
  throw new CofheError({
@@ -3185,6 +3319,7 @@ var DecryptForTxBuilder = class extends BaseBuilder {
3185
3319
  chainId: this.chainId,
3186
3320
  permission,
3187
3321
  thresholdNetworkUrl,
3322
+ retry404TimeoutMs: this.retry404TimeoutMs,
3188
3323
  onPoll: this.pollCallback
3189
3324
  });
3190
3325
  return {
@@ -1,4 +1,4 @@
1
- import { TASK_MANAGER_ADDRESS } from './chunk-4FP4V35O.js';
1
+ import { TASK_MANAGER_ADDRESS } from './chunk-ESMZCFJY.js';
2
2
  import { isAddress, getAddress, zeroAddress, isHex, keccak256, toHex, BaseError, ContractFunctionRevertedError, decodeErrorResult, parseAbi } from 'viem';
3
3
  import nacl from 'tweetnacl';
4
4
  import { z } from 'zod';