@cofhe/sdk 0.0.0-alpha-20260409113701

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 (132) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/adapters/ethers5.test.ts +174 -0
  3. package/adapters/ethers5.ts +36 -0
  4. package/adapters/ethers6.test.ts +169 -0
  5. package/adapters/ethers6.ts +36 -0
  6. package/adapters/hardhat-node.ts +167 -0
  7. package/adapters/hardhat.hh2.test.ts +159 -0
  8. package/adapters/hardhat.ts +36 -0
  9. package/adapters/index.test.ts +20 -0
  10. package/adapters/index.ts +5 -0
  11. package/adapters/smartWallet.ts +99 -0
  12. package/adapters/test-utils.ts +53 -0
  13. package/adapters/types.ts +6 -0
  14. package/adapters/wagmi.test.ts +156 -0
  15. package/adapters/wagmi.ts +17 -0
  16. package/chains/chains/arbSepolia.ts +14 -0
  17. package/chains/chains/baseSepolia.ts +14 -0
  18. package/chains/chains/hardhat.ts +15 -0
  19. package/chains/chains/localcofhe.ts +14 -0
  20. package/chains/chains/sepolia.ts +14 -0
  21. package/chains/chains.test.ts +50 -0
  22. package/chains/defineChain.ts +18 -0
  23. package/chains/index.ts +35 -0
  24. package/chains/types.ts +32 -0
  25. package/core/baseBuilder.ts +119 -0
  26. package/core/client.test.ts +429 -0
  27. package/core/client.ts +341 -0
  28. package/core/clientTypes.ts +119 -0
  29. package/core/config.test.ts +242 -0
  30. package/core/config.ts +225 -0
  31. package/core/consts.ts +22 -0
  32. package/core/decrypt/MockThresholdNetworkAbi.ts +179 -0
  33. package/core/decrypt/cofheMocksDecryptForTx.ts +84 -0
  34. package/core/decrypt/cofheMocksDecryptForView.ts +48 -0
  35. package/core/decrypt/decryptForTxBuilder.ts +359 -0
  36. package/core/decrypt/decryptForViewBuilder.ts +332 -0
  37. package/core/decrypt/decryptUtils.ts +28 -0
  38. package/core/decrypt/pollCallbacks.test.ts +194 -0
  39. package/core/decrypt/polling.ts +14 -0
  40. package/core/decrypt/tnDecryptUtils.ts +65 -0
  41. package/core/decrypt/tnDecryptV1.ts +171 -0
  42. package/core/decrypt/tnDecryptV2.ts +365 -0
  43. package/core/decrypt/tnSealOutputV1.ts +59 -0
  44. package/core/decrypt/tnSealOutputV2.ts +324 -0
  45. package/core/decrypt/verifyDecryptResult.ts +52 -0
  46. package/core/encrypt/MockZkVerifierAbi.ts +106 -0
  47. package/core/encrypt/cofheMocksZkVerifySign.ts +281 -0
  48. package/core/encrypt/encryptInputsBuilder.test.ts +747 -0
  49. package/core/encrypt/encryptInputsBuilder.ts +583 -0
  50. package/core/encrypt/encryptUtils.ts +67 -0
  51. package/core/encrypt/zkPackProveVerify.ts +335 -0
  52. package/core/error.ts +168 -0
  53. package/core/fetchKeys.test.ts +195 -0
  54. package/core/fetchKeys.ts +144 -0
  55. package/core/index.ts +106 -0
  56. package/core/keyStore.test.ts +226 -0
  57. package/core/keyStore.ts +154 -0
  58. package/core/permits.test.ts +493 -0
  59. package/core/permits.ts +201 -0
  60. package/core/types.ts +419 -0
  61. package/core/utils.ts +130 -0
  62. package/dist/adapters.cjs +88 -0
  63. package/dist/adapters.d.cts +14576 -0
  64. package/dist/adapters.d.ts +14576 -0
  65. package/dist/adapters.js +83 -0
  66. package/dist/chains.cjs +111 -0
  67. package/dist/chains.d.cts +121 -0
  68. package/dist/chains.d.ts +121 -0
  69. package/dist/chains.js +1 -0
  70. package/dist/chunk-36FBWLUS.js +3310 -0
  71. package/dist/chunk-7HLGHV67.js +990 -0
  72. package/dist/chunk-TBLR7NNE.js +102 -0
  73. package/dist/clientTypes-AVSCBet7.d.cts +998 -0
  74. package/dist/clientTypes-flH1ju82.d.ts +998 -0
  75. package/dist/core.cjs +4362 -0
  76. package/dist/core.d.cts +138 -0
  77. package/dist/core.d.ts +138 -0
  78. package/dist/core.js +3 -0
  79. package/dist/node.cjs +4225 -0
  80. package/dist/node.d.cts +22 -0
  81. package/dist/node.d.ts +22 -0
  82. package/dist/node.js +91 -0
  83. package/dist/permit-jRirYqFt.d.cts +376 -0
  84. package/dist/permit-jRirYqFt.d.ts +376 -0
  85. package/dist/permits.cjs +1025 -0
  86. package/dist/permits.d.cts +353 -0
  87. package/dist/permits.d.ts +353 -0
  88. package/dist/permits.js +1 -0
  89. package/dist/types-YiAC4gig.d.cts +33 -0
  90. package/dist/types-YiAC4gig.d.ts +33 -0
  91. package/dist/web.cjs +4434 -0
  92. package/dist/web.d.cts +42 -0
  93. package/dist/web.d.ts +42 -0
  94. package/dist/web.js +256 -0
  95. package/dist/zkProve.worker.cjs +93 -0
  96. package/dist/zkProve.worker.d.cts +2 -0
  97. package/dist/zkProve.worker.d.ts +2 -0
  98. package/dist/zkProve.worker.js +91 -0
  99. package/node/client.test.ts +159 -0
  100. package/node/config.test.ts +68 -0
  101. package/node/encryptInputs.test.ts +155 -0
  102. package/node/index.ts +97 -0
  103. package/node/storage.ts +51 -0
  104. package/package.json +121 -0
  105. package/permits/index.ts +68 -0
  106. package/permits/localstorage.test.ts +113 -0
  107. package/permits/onchain-utils.ts +221 -0
  108. package/permits/permit.test.ts +534 -0
  109. package/permits/permit.ts +386 -0
  110. package/permits/sealing.test.ts +84 -0
  111. package/permits/sealing.ts +131 -0
  112. package/permits/signature.ts +79 -0
  113. package/permits/store.test.ts +88 -0
  114. package/permits/store.ts +156 -0
  115. package/permits/test-utils.ts +28 -0
  116. package/permits/types.ts +204 -0
  117. package/permits/utils.ts +58 -0
  118. package/permits/validation.test.ts +361 -0
  119. package/permits/validation.ts +327 -0
  120. package/web/client.web.test.ts +159 -0
  121. package/web/config.web.test.ts +69 -0
  122. package/web/const.ts +2 -0
  123. package/web/encryptInputs.web.test.ts +172 -0
  124. package/web/index.ts +166 -0
  125. package/web/storage.ts +49 -0
  126. package/web/worker.builder.web.test.ts +148 -0
  127. package/web/worker.config.web.test.ts +329 -0
  128. package/web/worker.output.web.test.ts +84 -0
  129. package/web/workerManager.test.ts +80 -0
  130. package/web/workerManager.ts +214 -0
  131. package/web/workerManager.web.test.ts +114 -0
  132. package/web/zkProve.worker.ts +133 -0
@@ -0,0 +1,365 @@
1
+ import { type Permission } from '@/permits';
2
+
3
+ import { CofheError, CofheErrorCode } from '../error';
4
+ import { type DecryptPollCallbackFunction } from '../types';
5
+ import { normalizeTnSignature, parseDecryptedBytesToBigInt } from './tnDecryptUtils';
6
+ import { computeMinuteRampPollIntervalMs } from './polling.js';
7
+
8
+ // Polling configuration
9
+ const POLL_INTERVAL_MS = 1000; // 1 second
10
+ const POLL_MAX_INTERVAL_MS = 10_000; // 10 seconds
11
+ const POLL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
12
+
13
+ type DecryptSubmitResponseV2 = {
14
+ request_id: string;
15
+ };
16
+
17
+ type DecryptStatusResponseV2 = {
18
+ request_id: string;
19
+ status: 'PROCESSING' | 'COMPLETED';
20
+ submitted_at: string;
21
+ completed_at?: string;
22
+ is_succeed?: boolean;
23
+ decrypted?: number[];
24
+ signature?: string;
25
+ encryption_type?: number;
26
+ error_message?: string | null;
27
+ };
28
+
29
+ function assertDecryptSubmitResponseV2(value: unknown): DecryptSubmitResponseV2 {
30
+ if (value == null || typeof value !== 'object') {
31
+ throw new CofheError({
32
+ code: CofheErrorCode.DecryptFailed,
33
+ message: 'decrypt submit response must be a JSON object',
34
+ context: {
35
+ value,
36
+ },
37
+ });
38
+ }
39
+
40
+ const v = value as Record<string, unknown>;
41
+ if (typeof v.request_id !== 'string' || v.request_id.trim().length === 0) {
42
+ throw new CofheError({
43
+ code: CofheErrorCode.DecryptFailed,
44
+ message: 'decrypt submit response missing request_id',
45
+ context: {
46
+ value,
47
+ },
48
+ });
49
+ }
50
+
51
+ return { request_id: v.request_id };
52
+ }
53
+
54
+ function assertDecryptStatusResponseV2(value: unknown): DecryptStatusResponseV2 {
55
+ if (value == null || typeof value !== 'object') {
56
+ throw new CofheError({
57
+ code: CofheErrorCode.DecryptFailed,
58
+ message: 'decrypt status response must be a JSON object',
59
+ context: {
60
+ value,
61
+ },
62
+ });
63
+ }
64
+
65
+ const v = value as Record<string, unknown>;
66
+
67
+ const requestId = v.request_id;
68
+ const status = v.status;
69
+ const submittedAt = v.submitted_at;
70
+
71
+ if (typeof requestId !== 'string' || requestId.trim().length === 0) {
72
+ throw new CofheError({
73
+ code: CofheErrorCode.DecryptFailed,
74
+ message: 'decrypt status response missing request_id',
75
+ context: {
76
+ value,
77
+ },
78
+ });
79
+ }
80
+
81
+ if (status !== 'PROCESSING' && status !== 'COMPLETED') {
82
+ throw new CofheError({
83
+ code: CofheErrorCode.DecryptFailed,
84
+ message: 'decrypt status response has invalid status',
85
+ context: {
86
+ value,
87
+ status,
88
+ },
89
+ });
90
+ }
91
+
92
+ if (typeof submittedAt !== 'string' || submittedAt.trim().length === 0) {
93
+ throw new CofheError({
94
+ code: CofheErrorCode.DecryptFailed,
95
+ message: 'decrypt status response missing submitted_at',
96
+ context: {
97
+ value,
98
+ },
99
+ });
100
+ }
101
+
102
+ return value as DecryptStatusResponseV2;
103
+ }
104
+
105
+ async function submitDecryptRequestV2(
106
+ thresholdNetworkUrl: string,
107
+ ctHash: bigint | string,
108
+ chainId: number,
109
+ permission: Permission | null
110
+ ): Promise<string> {
111
+ const body: {
112
+ ct_tempkey: string;
113
+ host_chain_id: number;
114
+ permit?: Permission;
115
+ } = {
116
+ ct_tempkey: BigInt(ctHash).toString(16).padStart(64, '0'),
117
+ host_chain_id: chainId,
118
+ };
119
+
120
+ if (permission) {
121
+ body.permit = permission;
122
+ }
123
+
124
+ let response: Response;
125
+ try {
126
+ response = await fetch(`${thresholdNetworkUrl}/v2/decrypt`, {
127
+ method: 'POST',
128
+ headers: {
129
+ 'Content-Type': 'application/json',
130
+ },
131
+ body: JSON.stringify(body),
132
+ });
133
+ } catch (e) {
134
+ throw new CofheError({
135
+ code: CofheErrorCode.DecryptFailed,
136
+ message: `decrypt request failed`,
137
+ hint: 'Ensure the threshold network URL is valid and reachable.',
138
+ cause: e instanceof Error ? e : undefined,
139
+ context: {
140
+ thresholdNetworkUrl,
141
+ body,
142
+ },
143
+ });
144
+ }
145
+
146
+ if (!response.ok) {
147
+ let errorMessage = `HTTP ${response.status}`;
148
+ try {
149
+ const errorBody = (await response.json()) as Record<string, unknown>;
150
+ const maybeMessage = (errorBody.error_message || errorBody.message) as unknown;
151
+ if (typeof maybeMessage === 'string' && maybeMessage.length > 0) errorMessage = maybeMessage;
152
+ } catch {
153
+ errorMessage = response.statusText || errorMessage;
154
+ }
155
+
156
+ throw new CofheError({
157
+ code: CofheErrorCode.DecryptFailed,
158
+ message: `decrypt request failed: ${errorMessage}`,
159
+ hint: 'Check the threshold network URL and request parameters.',
160
+ context: {
161
+ thresholdNetworkUrl,
162
+ status: response.status,
163
+ statusText: response.statusText,
164
+ body,
165
+ },
166
+ });
167
+ }
168
+
169
+ let rawJson: unknown;
170
+ try {
171
+ rawJson = (await response.json()) as unknown;
172
+ } catch (e) {
173
+ throw new CofheError({
174
+ code: CofheErrorCode.DecryptFailed,
175
+ message: `Failed to parse decrypt submit response`,
176
+ cause: e instanceof Error ? e : undefined,
177
+ context: {
178
+ thresholdNetworkUrl,
179
+ body,
180
+ },
181
+ });
182
+ }
183
+
184
+ const submitResponse = assertDecryptSubmitResponseV2(rawJson);
185
+ return submitResponse.request_id;
186
+ }
187
+
188
+ async function pollDecryptStatusV2(
189
+ thresholdNetworkUrl: string,
190
+ requestId: string,
191
+ onPoll?: DecryptPollCallbackFunction
192
+ ): Promise<{ decryptedValue: bigint; signature: `0x${string}` }> {
193
+ const startTime = Date.now();
194
+ let attemptIndex = 0;
195
+ let completed = false;
196
+
197
+ while (!completed) {
198
+ const elapsedMs = Date.now() - startTime;
199
+ const intervalMs = computeMinuteRampPollIntervalMs(elapsedMs, {
200
+ minIntervalMs: POLL_INTERVAL_MS,
201
+ maxIntervalMs: POLL_MAX_INTERVAL_MS,
202
+ });
203
+ onPoll?.({
204
+ operation: 'decrypt',
205
+ requestId,
206
+ attemptIndex,
207
+ elapsedMs,
208
+ intervalMs,
209
+ timeoutMs: POLL_TIMEOUT_MS,
210
+ });
211
+
212
+ if (elapsedMs > POLL_TIMEOUT_MS) {
213
+ throw new CofheError({
214
+ code: CofheErrorCode.DecryptFailed,
215
+ message: `decrypt polling timed out after ${POLL_TIMEOUT_MS}ms`,
216
+ hint: 'The request may still be processing. Try again later.',
217
+ context: {
218
+ thresholdNetworkUrl,
219
+ requestId,
220
+ timeoutMs: POLL_TIMEOUT_MS,
221
+ },
222
+ });
223
+ }
224
+
225
+ let response: Response;
226
+ try {
227
+ response = await fetch(`${thresholdNetworkUrl}/v2/decrypt/${requestId}`, {
228
+ method: 'GET',
229
+ headers: {
230
+ 'Content-Type': 'application/json',
231
+ },
232
+ });
233
+ } catch (e) {
234
+ throw new CofheError({
235
+ code: CofheErrorCode.DecryptFailed,
236
+ message: `decrypt status poll failed`,
237
+ hint: 'Ensure the threshold network URL is valid and reachable.',
238
+ cause: e instanceof Error ? e : undefined,
239
+ context: {
240
+ thresholdNetworkUrl,
241
+ requestId,
242
+ },
243
+ });
244
+ }
245
+
246
+ if (response.status === 404) {
247
+ throw new CofheError({
248
+ code: CofheErrorCode.DecryptFailed,
249
+ message: `decrypt request not found: ${requestId}`,
250
+ hint: 'The request may have expired or been invalid.',
251
+ context: {
252
+ thresholdNetworkUrl,
253
+ requestId,
254
+ },
255
+ });
256
+ }
257
+
258
+ if (!response.ok) {
259
+ let errorMessage = `HTTP ${response.status}`;
260
+ try {
261
+ const errorBody = (await response.json()) as Record<string, unknown>;
262
+ const maybeMessage = (errorBody.error_message || errorBody.message) as unknown;
263
+ if (typeof maybeMessage === 'string' && maybeMessage.length > 0) errorMessage = maybeMessage;
264
+ } catch {
265
+ errorMessage = response.statusText || errorMessage;
266
+ }
267
+
268
+ throw new CofheError({
269
+ code: CofheErrorCode.DecryptFailed,
270
+ message: `decrypt status poll failed: ${errorMessage}`,
271
+ context: {
272
+ thresholdNetworkUrl,
273
+ requestId,
274
+ status: response.status,
275
+ statusText: response.statusText,
276
+ },
277
+ });
278
+ }
279
+
280
+ let rawJson: unknown;
281
+ try {
282
+ rawJson = (await response.json()) as unknown;
283
+ } catch (e) {
284
+ throw new CofheError({
285
+ code: CofheErrorCode.DecryptFailed,
286
+ message: `Failed to parse decrypt status response`,
287
+ cause: e instanceof Error ? e : undefined,
288
+ context: {
289
+ thresholdNetworkUrl,
290
+ requestId,
291
+ },
292
+ });
293
+ }
294
+
295
+ const statusResponse = assertDecryptStatusResponseV2(rawJson);
296
+
297
+ if (statusResponse.status === 'COMPLETED') {
298
+ if (statusResponse.is_succeed === false) {
299
+ const errorMessage = statusResponse.error_message || 'Unknown error';
300
+ throw new CofheError({
301
+ code: CofheErrorCode.DecryptFailed,
302
+ message: `decrypt request failed: ${errorMessage}`,
303
+ context: {
304
+ thresholdNetworkUrl,
305
+ requestId,
306
+ statusResponse,
307
+ },
308
+ });
309
+ }
310
+
311
+ if (statusResponse.error_message) {
312
+ throw new CofheError({
313
+ code: CofheErrorCode.DecryptFailed,
314
+ message: `decrypt request failed: ${statusResponse.error_message}`,
315
+ context: {
316
+ thresholdNetworkUrl,
317
+ requestId,
318
+ statusResponse,
319
+ },
320
+ });
321
+ }
322
+
323
+ if (!Array.isArray(statusResponse.decrypted)) {
324
+ throw new CofheError({
325
+ code: CofheErrorCode.DecryptReturnedNull,
326
+ message: 'decrypt completed but response missing <decrypted> byte array',
327
+ context: {
328
+ thresholdNetworkUrl,
329
+ requestId,
330
+ statusResponse,
331
+ },
332
+ });
333
+ }
334
+
335
+ const decryptedValue = parseDecryptedBytesToBigInt(statusResponse.decrypted);
336
+ const signature = normalizeTnSignature(statusResponse.signature);
337
+ return { decryptedValue, signature };
338
+ }
339
+
340
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
341
+ attemptIndex += 1;
342
+ }
343
+
344
+ // This should never be reached, but keeps TS and linters happy.
345
+ throw new CofheError({
346
+ code: CofheErrorCode.DecryptFailed,
347
+ message: 'Polling loop exited unexpectedly',
348
+ context: {
349
+ thresholdNetworkUrl,
350
+ requestId,
351
+ },
352
+ });
353
+ }
354
+
355
+ export async function tnDecryptV2(params: {
356
+ ctHash: bigint | string;
357
+ chainId: number;
358
+ permission: Permission | null;
359
+ thresholdNetworkUrl: string;
360
+ onPoll?: DecryptPollCallbackFunction;
361
+ }): Promise<{ decryptedValue: bigint; signature: `0x${string}` }> {
362
+ const { thresholdNetworkUrl, ctHash, chainId, permission, onPoll } = params;
363
+ const requestId = await submitDecryptRequestV2(thresholdNetworkUrl, ctHash, chainId, permission);
364
+ return await pollDecryptStatusV2(thresholdNetworkUrl, requestId, onPoll);
365
+ }
@@ -0,0 +1,59 @@
1
+ import { type Permission, type EthEncryptedData } from '@/permits';
2
+
3
+ import { CofheError, CofheErrorCode } from '../error.js';
4
+
5
+ export async function tnSealOutputV1(
6
+ ctHash: bigint,
7
+ chainId: number,
8
+ permission: Permission,
9
+ thresholdNetworkUrl: string
10
+ ): Promise<EthEncryptedData> {
11
+ let sealed: EthEncryptedData | undefined;
12
+ let errorMessage: string | undefined;
13
+ let sealOutputResult: { sealed: EthEncryptedData; error_message: string } | undefined;
14
+
15
+ const body = {
16
+ ct_tempkey: ctHash.toString(16).padStart(64, '0'),
17
+ host_chain_id: chainId,
18
+ permit: permission,
19
+ };
20
+
21
+ try {
22
+ const sealOutputRes = await fetch(`${thresholdNetworkUrl}/sealoutput`, {
23
+ method: 'POST',
24
+ headers: {
25
+ 'Content-Type': 'application/json',
26
+ },
27
+ body: JSON.stringify(body),
28
+ });
29
+
30
+ sealOutputResult = (await sealOutputRes.json()) as { sealed: EthEncryptedData; error_message: string };
31
+ sealed = sealOutputResult.sealed;
32
+ errorMessage = sealOutputResult.error_message;
33
+ } catch (e) {
34
+ throw new CofheError({
35
+ code: CofheErrorCode.SealOutputFailed,
36
+ message: `sealOutput request failed`,
37
+ hint: 'Ensure the threshold network URL is valid.',
38
+ cause: e instanceof Error ? e : undefined,
39
+ context: {
40
+ thresholdNetworkUrl,
41
+ body,
42
+ },
43
+ });
44
+ }
45
+
46
+ if (sealed == null) {
47
+ throw new CofheError({
48
+ code: CofheErrorCode.SealOutputReturnedNull,
49
+ message: `sealOutput request returned no data | Caused by: ${errorMessage}`,
50
+ context: {
51
+ thresholdNetworkUrl,
52
+ body,
53
+ sealOutputResult,
54
+ },
55
+ });
56
+ }
57
+
58
+ return sealed;
59
+ }