@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,583 @@
1
+ import {
2
+ type ZkBuilderAndCrsGenerator,
3
+ type ZkProveWorkerFunction,
4
+ zkPack,
5
+ zkProve,
6
+ zkProveWithWorker,
7
+ zkVerify,
8
+ constructZkPoKMetadata,
9
+ } from './zkPackProveVerify.js';
10
+ import { CofheError, CofheErrorCode } from '../error.js';
11
+ import {
12
+ type EncryptStepCallbackFunction,
13
+ EncryptStep,
14
+ type EncryptableItem,
15
+ type EncryptedItemInput,
16
+ type EncryptedItemInputs,
17
+ type TfheInitializer,
18
+ type EncryptStepCallbackContext,
19
+ } from '../types.js';
20
+ import { cofheMocksCheckEncryptableBits, cofheMocksZkVerifySign } from './cofheMocksZkVerifySign.js';
21
+ import { hardhat } from 'viem/chains';
22
+ import { fetchKeys, type FheKeyDeserializer } from '../fetchKeys.js';
23
+ import { getZkVerifierUrlOrThrow } from '../config.js';
24
+ import { type WalletClient } from 'viem';
25
+ import { sleep } from '../utils.js';
26
+ import { BaseBuilder, type BaseBuilderParams } from '../baseBuilder.js';
27
+ import { type KeysStorage } from '../keyStore.js';
28
+
29
+ type EncryptInputsBuilderParams<T extends EncryptableItem[]> = BaseBuilderParams & {
30
+ inputs: [...T];
31
+ securityZone?: number;
32
+
33
+ zkvWalletClient?: WalletClient | undefined;
34
+
35
+ tfhePublicKeyDeserializer: FheKeyDeserializer | undefined;
36
+ compactPkeCrsDeserializer: FheKeyDeserializer | undefined;
37
+ zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator | undefined;
38
+ initTfhe: TfheInitializer | undefined;
39
+ zkProveWorkerFn: ZkProveWorkerFunction | undefined;
40
+
41
+ keysStorage: KeysStorage | undefined;
42
+ };
43
+
44
+ /**
45
+ * EncryptInputsBuilder exposes a builder pattern for encrypting inputs.
46
+ * account, securityZone, and chainId can be overridden in the builder.
47
+ * config, tfhePublicKeyDeserializer, compactPkeCrsDeserializer, and zkBuilderAndCrsGenerator are required to be set in the builder.
48
+ */
49
+
50
+ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuilder {
51
+ private securityZone: number;
52
+ private stepCallback?: EncryptStepCallbackFunction;
53
+ private inputItems: [...T];
54
+
55
+ private zkvWalletClient: WalletClient | undefined;
56
+
57
+ private tfhePublicKeyDeserializer: FheKeyDeserializer;
58
+ private compactPkeCrsDeserializer: FheKeyDeserializer;
59
+ private zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator;
60
+ private initTfhe: TfheInitializer | undefined;
61
+ private zkProveWorkerFn: ZkProveWorkerFunction | undefined;
62
+
63
+ private keysStorage: KeysStorage | undefined;
64
+
65
+ // Worker configuration (from config, overrideable)
66
+ private useWorker: boolean;
67
+
68
+ private stepTimestamps: Record<EncryptStep, number> = {
69
+ [EncryptStep.InitTfhe]: 0,
70
+ [EncryptStep.FetchKeys]: 0,
71
+ [EncryptStep.Pack]: 0,
72
+ [EncryptStep.Prove]: 0,
73
+ [EncryptStep.Verify]: 0,
74
+ };
75
+
76
+ constructor(params: EncryptInputsBuilderParams<T>) {
77
+ super({
78
+ config: params.config,
79
+ publicClient: params.publicClient,
80
+ walletClient: params.walletClient,
81
+ chainId: params.chainId,
82
+ account: params.account,
83
+ requireConnected: params.requireConnected,
84
+ });
85
+
86
+ this.inputItems = params.inputs;
87
+ this.securityZone = params.securityZone ?? 0;
88
+
89
+ this.zkvWalletClient = params.zkvWalletClient;
90
+
91
+ // Check that tfhePublicKeyDeserializer is provided
92
+ if (!params.tfhePublicKeyDeserializer) {
93
+ throw new CofheError({
94
+ code: CofheErrorCode.MissingTfhePublicKeyDeserializer,
95
+ message: 'EncryptInputsBuilder tfhePublicKeyDeserializer is undefined',
96
+ hint: 'Ensure client has been created with a tfhePublicKeyDeserializer.',
97
+ context: {
98
+ tfhePublicKeyDeserializer: params.tfhePublicKeyDeserializer,
99
+ },
100
+ });
101
+ }
102
+ this.tfhePublicKeyDeserializer = params.tfhePublicKeyDeserializer;
103
+
104
+ // Check that compactPkeCrsDeserializer is provided
105
+ if (!params.compactPkeCrsDeserializer) {
106
+ throw new CofheError({
107
+ code: CofheErrorCode.MissingCompactPkeCrsDeserializer,
108
+ message: 'EncryptInputsBuilder compactPkeCrsDeserializer is undefined',
109
+ hint: 'Ensure client has been created with a compactPkeCrsDeserializer.',
110
+ context: {
111
+ compactPkeCrsDeserializer: params.compactPkeCrsDeserializer,
112
+ },
113
+ });
114
+ }
115
+ this.compactPkeCrsDeserializer = params.compactPkeCrsDeserializer;
116
+
117
+ // Check that zkBuilderAndCrsGenerator is provided
118
+ if (!params.zkBuilderAndCrsGenerator) {
119
+ throw new CofheError({
120
+ code: CofheErrorCode.MissingZkBuilderAndCrsGenerator,
121
+ message: 'EncryptInputsBuilder zkBuilderAndCrsGenerator is undefined',
122
+ hint: 'Ensure client has been created with a zkBuilderAndCrsGenerator.',
123
+ context: {
124
+ zkBuilderAndCrsGenerator: params.zkBuilderAndCrsGenerator,
125
+ },
126
+ });
127
+ }
128
+ this.zkBuilderAndCrsGenerator = params.zkBuilderAndCrsGenerator;
129
+
130
+ // Optional tfhe initialization function, will be run if provided
131
+ this.initTfhe = params.initTfhe;
132
+
133
+ // Optional zkProve worker function, will be used on web if useWorkers is true and worker function is provided
134
+ this.zkProveWorkerFn = params.zkProveWorkerFn;
135
+
136
+ // Keys storage is used to store the FHE key and CRS
137
+ this.keysStorage = params.keysStorage;
138
+
139
+ // Initialize useWorker from config (can be overridden via setUseWorker) - default to true
140
+ this.useWorker = params.config?.useWorkers ?? true;
141
+ }
142
+
143
+ /**
144
+ * @param account - Account that will create the tx using the encrypted inputs.
145
+ *
146
+ * If not provided, the account will be fetched from the connected walletClient.
147
+ *
148
+ * Example:
149
+ * ```typescript
150
+ * const encrypted = await encryptInputs([Encryptable.uint128(10n)])
151
+ * .setAccount("0x123")
152
+ * .execute();
153
+ * ```
154
+ *
155
+ * @returns The chainable EncryptInputsBuilder instance.
156
+ */
157
+ setAccount(account: string): EncryptInputsBuilder<T> {
158
+ this.account = account;
159
+ return this;
160
+ }
161
+
162
+ getAccount(): string | undefined {
163
+ return this.account;
164
+ }
165
+
166
+ /**
167
+ * @param chainId - Chain that will consume the encrypted inputs.
168
+ *
169
+ * If not provided, the chainId will be fetched from the connected publicClient.
170
+ *
171
+ * Example:
172
+ * ```typescript
173
+ * const encrypted = await encryptInputs([Encryptable.uint128(10n)])
174
+ * .setChainId(11155111)
175
+ * .execute();
176
+ * ```
177
+ *
178
+ * @returns The chainable EncryptInputsBuilder instance.
179
+ */
180
+ setChainId(chainId: number): EncryptInputsBuilder<T> {
181
+ this.chainId = chainId;
182
+ return this;
183
+ }
184
+
185
+ getChainId(): number | undefined {
186
+ return this.chainId;
187
+ }
188
+
189
+ /**
190
+ * @param securityZone - Security zone to encrypt the inputs for.
191
+ *
192
+ * If not provided, the default securityZone 0 will be used.
193
+ *
194
+ * Example:
195
+ * ```typescript
196
+ * const encrypted = await encryptInputs([Encryptable.uint128(10n)])
197
+ * .setSecurityZone(1)
198
+ * .execute();
199
+ * ```
200
+ *
201
+ * @returns The chainable EncryptInputsBuilder instance.
202
+ */
203
+ setSecurityZone(securityZone: number): EncryptInputsBuilder<T> {
204
+ this.securityZone = securityZone;
205
+ return this;
206
+ }
207
+
208
+ getSecurityZone(): number {
209
+ return this.securityZone;
210
+ }
211
+
212
+ /**
213
+ * @param useWorker - Whether to use Web Workers for ZK proof generation.
214
+ *
215
+ * Overrides the config-level useWorkers setting for this specific encryption.
216
+ *
217
+ * Example:
218
+ * ```typescript
219
+ * const encrypted = await encryptInputs([Encryptable.uint128(10n)])
220
+ * .setUseWorker(false)
221
+ * .execute();
222
+ * ```
223
+ *
224
+ * @returns The chainable EncryptInputsBuilder instance.
225
+ */
226
+ setUseWorker(useWorker: boolean): EncryptInputsBuilder<T> {
227
+ this.useWorker = useWorker;
228
+ return this;
229
+ }
230
+
231
+ /**
232
+ * Gets the current worker configuration.
233
+ *
234
+ * @returns Whether Web Workers are enabled for this encryption.
235
+ *
236
+ * Example:
237
+ * ```typescript
238
+ * const builder = encryptInputs([Encryptable.uint128(10n)]);
239
+ * console.log(builder.getUseWorker()); // true (from config)
240
+ * builder.setUseWorker(false);
241
+ * console.log(builder.getUseWorker()); // false (overridden)
242
+ * ```
243
+ */
244
+ getUseWorker(): boolean {
245
+ return this.useWorker;
246
+ }
247
+
248
+ /**
249
+ * @param callback - Function to be called with the encryption step.
250
+ *
251
+ * Useful for debugging and tracking the progress of the encryption process.
252
+ * Useful for a UI element that shows the progress of the encryption process.
253
+ *
254
+ * Example:
255
+ * ```typescript
256
+ * const encrypted = await encryptInputs([Encryptable.uint128(10n)])
257
+ * .onStep((step: EncryptStep) => console.log(step))
258
+ * .execute();
259
+ * ```
260
+ *
261
+ * @returns The EncryptInputsBuilder instance.
262
+ */
263
+ onStep(callback: EncryptStepCallbackFunction): EncryptInputsBuilder<T> {
264
+ this.stepCallback = callback;
265
+ return this;
266
+ }
267
+
268
+ getStepCallback(): EncryptStepCallbackFunction | undefined {
269
+ return this.stepCallback;
270
+ }
271
+
272
+ /**
273
+ * Fires the step callback if set
274
+ */
275
+ private fireStepStart(
276
+ step: EncryptStep,
277
+ context: Omit<EncryptStepCallbackContext, 'isStart' | 'isEnd' | 'duration'> = {}
278
+ ) {
279
+ if (!this.stepCallback) return;
280
+ this.stepTimestamps[step] = Date.now();
281
+ this.stepCallback(step, { ...context, isStart: true, isEnd: false, duration: 0 });
282
+ }
283
+ private fireStepEnd(
284
+ step: EncryptStep,
285
+ context: Omit<EncryptStepCallbackContext, 'isStart' | 'isEnd' | 'duration'> = {}
286
+ ) {
287
+ if (!this.stepCallback) return;
288
+ const duration = Date.now() - this.stepTimestamps[step];
289
+ this.stepCallback(step, { ...context, isStart: false, isEnd: true, duration });
290
+ }
291
+
292
+ /**
293
+ * zkVerifierUrl is included in the chains exported from @cofhe/sdk/chains for use in CofheConfig.supportedChains
294
+ * Users should generally not set this manually.
295
+ */
296
+ private async getZkVerifierUrl(): Promise<string> {
297
+ this.assertChainId();
298
+ return getZkVerifierUrlOrThrow(this.config, this.chainId);
299
+ }
300
+
301
+ /**
302
+ * initTfhe is a platform-specific dependency injected into core/createCofheClientBase by web/createCofheClient and node/createCofheClient
303
+ * web/ uses zama "tfhe"
304
+ * node/ uses zama "node-tfhe"
305
+ * Users should not set this manually.
306
+ */
307
+ private async initTfheOrThrow(): Promise<boolean> {
308
+ if (!this.initTfhe) return false;
309
+
310
+ try {
311
+ return await this.initTfhe();
312
+ } catch (error) {
313
+ throw CofheError.fromError(error, {
314
+ code: CofheErrorCode.InitTfheFailed,
315
+ message: `Failed to initialize TFHE`,
316
+ context: {
317
+ initTfhe: this.initTfhe,
318
+ },
319
+ });
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Fetches the FHE key and CRS from the CoFHE API
325
+ * If the key/crs already exists in the store it is returned, else it is fetched, stored, and returned
326
+ */
327
+ private async fetchFheKeyAndCrs(): Promise<{
328
+ fheKey: string;
329
+ fheKeyFetchedFromCoFHE: boolean;
330
+ crs: string;
331
+ crsFetchedFromCoFHE: boolean;
332
+ }> {
333
+ this.assertChainId();
334
+ const securityZone = this.getSecurityZone();
335
+
336
+ try {
337
+ await this.keysStorage?.rehydrateKeysStore();
338
+ } catch (error) {
339
+ throw CofheError.fromError(error, {
340
+ code: CofheErrorCode.RehydrateKeysStoreFailed,
341
+ message: `Failed to rehydrate keys store`,
342
+ context: {
343
+ keysStorage: this.keysStorage,
344
+ },
345
+ });
346
+ }
347
+
348
+ let fheKey: string | undefined;
349
+ let fheKeyFetchedFromCoFHE: boolean = false;
350
+ let crs: string | undefined;
351
+ let crsFetchedFromCoFHE: boolean = false;
352
+
353
+ try {
354
+ [[fheKey, fheKeyFetchedFromCoFHE], [crs, crsFetchedFromCoFHE]] = await fetchKeys(
355
+ this.config,
356
+ this.chainId,
357
+ securityZone,
358
+ this.tfhePublicKeyDeserializer,
359
+ this.compactPkeCrsDeserializer,
360
+ this.keysStorage
361
+ );
362
+ } catch (error) {
363
+ throw CofheError.fromError(error, {
364
+ code: CofheErrorCode.FetchKeysFailed,
365
+ message: `Failed to fetch FHE key and CRS`,
366
+ context: {
367
+ config: this.config,
368
+ chainId: this.chainId,
369
+ securityZone,
370
+ compactPkeCrsDeserializer: this.compactPkeCrsDeserializer,
371
+ tfhePublicKeyDeserializer: this.tfhePublicKeyDeserializer,
372
+ },
373
+ });
374
+ }
375
+
376
+ if (!fheKey) {
377
+ throw new CofheError({
378
+ code: CofheErrorCode.MissingFheKey,
379
+ message: `FHE key not found`,
380
+ context: {
381
+ chainId: this.chainId,
382
+ securityZone,
383
+ },
384
+ });
385
+ }
386
+
387
+ if (!crs) {
388
+ throw new CofheError({
389
+ code: CofheErrorCode.MissingCrs,
390
+ message: `CRS not found for chainId <${this.chainId}>`,
391
+ context: {
392
+ chainId: this.chainId,
393
+ },
394
+ });
395
+ }
396
+
397
+ return { fheKey, fheKeyFetchedFromCoFHE, crs, crsFetchedFromCoFHE };
398
+ }
399
+
400
+ /**
401
+ * Resolves the encryptDelay config into an array of 5 per-step delays.
402
+ * A single number is broadcast to all steps; a tuple is used as-is.
403
+ */
404
+ private resolveEncryptDelays(): [number, number, number, number, number] {
405
+ const encryptDelay = this.config?.mocks?.encryptDelay ?? [100, 100, 100, 500, 500];
406
+ if (typeof encryptDelay === 'number') {
407
+ return [encryptDelay, encryptDelay, encryptDelay, encryptDelay, encryptDelay];
408
+ }
409
+ return encryptDelay;
410
+ }
411
+
412
+ /**
413
+ * @dev Encrypt against the cofheMocks instead of CoFHE
414
+ *
415
+ * In the cofheMocks, the MockZkVerifier contract is deployed on hardhat to a fixed address, this contract handles mocking the zk verifying.
416
+ * cofheMocksInsertPackedHashes - stores the ctHashes and their plaintext values for on-chain mocking of FHE operations.
417
+ * cofheMocksZkCreateProofSignatures - creates signatures to be included in the encrypted inputs. The signers address is known and verified in the mock contracts.
418
+ */
419
+ private async mocksExecute(): Promise<[...EncryptedItemInputs<T>]> {
420
+ this.assertAccount();
421
+ this.assertPublicClient();
422
+ this.assertWalletClient();
423
+
424
+ const [initTfheDelay, fetchKeysDelay, packDelay, proveDelay, verifyDelay] = this.resolveEncryptDelays();
425
+
426
+ this.fireStepStart(EncryptStep.InitTfhe);
427
+ await sleep(initTfheDelay);
428
+ this.fireStepEnd(EncryptStep.InitTfhe, {
429
+ tfheInitializationExecuted: false,
430
+ isMocks: true,
431
+ mockSleep: initTfheDelay,
432
+ });
433
+
434
+ this.fireStepStart(EncryptStep.FetchKeys);
435
+ await sleep(fetchKeysDelay);
436
+ this.fireStepEnd(EncryptStep.FetchKeys, {
437
+ fheKeyFetchedFromCoFHE: false,
438
+ crsFetchedFromCoFHE: false,
439
+ isMocks: true,
440
+ mockSleep: fetchKeysDelay,
441
+ });
442
+
443
+ this.fireStepStart(EncryptStep.Pack);
444
+ await cofheMocksCheckEncryptableBits(this.inputItems);
445
+ await sleep(packDelay);
446
+ this.fireStepEnd(EncryptStep.Pack, { isMocks: true, mockSleep: packDelay });
447
+
448
+ this.fireStepStart(EncryptStep.Prove);
449
+ await sleep(proveDelay);
450
+ this.fireStepEnd(EncryptStep.Prove, { isMocks: true, mockSleep: proveDelay });
451
+
452
+ this.fireStepStart(EncryptStep.Verify);
453
+ await sleep(verifyDelay);
454
+ const signedResults = await cofheMocksZkVerifySign(
455
+ this.inputItems,
456
+ this.account,
457
+ this.securityZone,
458
+ this.publicClient,
459
+ this.walletClient,
460
+ this.zkvWalletClient
461
+ );
462
+ const encryptedInputs: EncryptedItemInput[] = signedResults.map(({ ct_hash, signature }, index) => ({
463
+ ctHash: BigInt(ct_hash),
464
+ securityZone: this.securityZone,
465
+ utype: this.inputItems[index].utype,
466
+ signature,
467
+ }));
468
+ this.fireStepEnd(EncryptStep.Verify, { isMocks: true, mockSleep: verifyDelay });
469
+
470
+ return encryptedInputs as [...EncryptedItemInputs<T>];
471
+ }
472
+
473
+ /**
474
+ * In the production context, perform a true encryption with the CoFHE coprocessor.
475
+ */
476
+ private async productionExecute(): Promise<[...EncryptedItemInputs<T>]> {
477
+ this.assertAccount();
478
+ this.assertChainId();
479
+
480
+ this.fireStepStart(EncryptStep.InitTfhe);
481
+
482
+ // Deferred initialization of tfhe wasm until encrypt is called
483
+ // Returns true if tfhe was initialized, false if already initialized
484
+ const tfheInitializationExecuted = await this.initTfheOrThrow();
485
+
486
+ this.fireStepEnd(EncryptStep.InitTfhe, { tfheInitializationExecuted });
487
+
488
+ this.fireStepStart(EncryptStep.FetchKeys);
489
+
490
+ // Deferred fetching of fheKey and crs until encrypt is called
491
+ // if the key/crs is already in the store, it is not fetched from the CoFHE API
492
+ const { fheKey, fheKeyFetchedFromCoFHE, crs, crsFetchedFromCoFHE } = await this.fetchFheKeyAndCrs();
493
+
494
+ let { zkBuilder, zkCrs } = this.zkBuilderAndCrsGenerator(fheKey, crs);
495
+
496
+ this.fireStepEnd(EncryptStep.FetchKeys, { fheKeyFetchedFromCoFHE, crsFetchedFromCoFHE });
497
+
498
+ this.fireStepStart(EncryptStep.Pack);
499
+
500
+ zkBuilder = zkPack(this.inputItems, zkBuilder);
501
+
502
+ this.fireStepEnd(EncryptStep.Pack);
503
+
504
+ this.fireStepStart(EncryptStep.Prove);
505
+
506
+ // Construct metadata once (used by both worker and main thread paths)
507
+ const metadata = constructZkPoKMetadata(this.account, this.securityZone, this.chainId);
508
+
509
+ let proof: Uint8Array | null = null;
510
+ let usedWorker = false;
511
+ let workerFailedError: string | undefined;
512
+
513
+ // Decision logic: try worker if enabled and available, fallback to main thread
514
+ if (this.useWorker && this.zkProveWorkerFn) {
515
+ try {
516
+ // Call worker function directly (no packing needed, worker does it)
517
+ proof = await zkProveWithWorker(this.zkProveWorkerFn, fheKey, crs, this.inputItems, metadata);
518
+ usedWorker = true;
519
+ } catch (error) {
520
+ // Worker failed - capture error for debugging
521
+ workerFailedError = error instanceof Error ? error.message : String(error);
522
+ }
523
+ }
524
+
525
+ if (proof == null) {
526
+ // Use main thread directly (workers disabled or unavailable)
527
+ proof = await zkProve(zkBuilder, zkCrs, metadata);
528
+ usedWorker = false;
529
+ }
530
+
531
+ this.fireStepEnd(EncryptStep.Prove, {
532
+ useWorker: this.useWorker,
533
+ usedWorker,
534
+ workerFailedError,
535
+ });
536
+
537
+ this.fireStepStart(EncryptStep.Verify);
538
+
539
+ const zkVerifierUrl = await this.getZkVerifierUrl();
540
+
541
+ const verifyResults = await zkVerify(zkVerifierUrl, proof, this.account, this.securityZone, this.chainId);
542
+ // Add securityZone and utype to the verify results
543
+ const encryptedInputs: EncryptedItemInput[] = verifyResults.map(
544
+ ({ ct_hash, signature }: { ct_hash: string; signature: string }, index: number) => ({
545
+ ctHash: BigInt(ct_hash),
546
+ securityZone: this.securityZone,
547
+ utype: this.inputItems[index].utype,
548
+ signature,
549
+ })
550
+ );
551
+
552
+ this.fireStepEnd(EncryptStep.Verify);
553
+
554
+ return encryptedInputs as [...EncryptedItemInputs<T>];
555
+ }
556
+
557
+ /**
558
+ * Final step of the encryption process. MUST BE CALLED LAST IN THE CHAIN.
559
+ *
560
+ * This will:
561
+ * - Pack the encryptable items into a zk proof
562
+ * - Prove the zk proof
563
+ * - Verify the zk proof with CoFHE
564
+ * - Package and return the encrypted inputs
565
+ *
566
+ * Example:
567
+ * ```typescript
568
+ * const encrypted = await encryptInputs([Encryptable.uint128(10n)])
569
+ * .setAccount('0x123...890') // optional
570
+ * .setChainId(11155111) // optional
571
+ * .execute(); // execute
572
+ * ```
573
+ *
574
+ * @returns The encrypted inputs.
575
+ */
576
+ async execute(): Promise<[...EncryptedItemInputs<T>]> {
577
+ // On hardhat chain, interact with MockZkVerifier contract instead of CoFHE
578
+ if (this.chainId === hardhat.id) return this.mocksExecute();
579
+
580
+ // On other chains, interact with CoFHE coprocessor
581
+ return this.productionExecute();
582
+ }
583
+ }
@@ -0,0 +1,67 @@
1
+ /* eslint-disable no-redeclare */
2
+ import {
3
+ type EncryptableItem,
4
+ isEncryptableItem,
5
+ type EncryptedItemInput,
6
+ type EncryptedItemInputs,
7
+ } from '../types.js';
8
+
9
+ export function encryptExtract<T>(item: T): EncryptableItem[];
10
+ export function encryptExtract<T extends any[]>(item: [...T]): EncryptableItem[];
11
+ export function encryptExtract<T>(item: T) {
12
+ if (isEncryptableItem(item)) {
13
+ return item;
14
+ }
15
+
16
+ // Object | Array
17
+ if (typeof item === 'object' && item !== null) {
18
+ if (Array.isArray(item)) {
19
+ // Array - recurse
20
+ return item.flatMap((nestedItem) => encryptExtract(nestedItem));
21
+ } else {
22
+ // Object - recurse
23
+ return Object.values(item).flatMap((value) => encryptExtract(value));
24
+ }
25
+ }
26
+
27
+ return [];
28
+ }
29
+
30
+ export function encryptReplace<T>(
31
+ item: T,
32
+ encryptedItems: EncryptedItemInput[]
33
+ ): [EncryptedItemInputs<T>, EncryptedItemInput[]];
34
+ export function encryptReplace<T extends any[]>(
35
+ item: [...T],
36
+ encryptedItems: EncryptedItemInput[]
37
+ ): [...EncryptedItemInputs<T>, EncryptedItemInput[]];
38
+ export function encryptReplace<T>(item: T, encryptedItems: EncryptedItemInput[]) {
39
+ if (isEncryptableItem(item)) {
40
+ return [encryptedItems[0], encryptedItems.slice(1)];
41
+ }
42
+
43
+ // Object | Array
44
+ if (typeof item === 'object' && item !== null) {
45
+ if (Array.isArray(item)) {
46
+ // Array - recurse
47
+ return item.reduce<[any[], EncryptedItemInput[]]>(
48
+ ([acc, remaining], item) => {
49
+ const [newItem, newRemaining] = encryptReplace(item, remaining);
50
+ return [[...acc, newItem], newRemaining];
51
+ },
52
+ [[], encryptedItems]
53
+ );
54
+ } else {
55
+ // Object - recurse
56
+ return Object.entries(item).reduce<[Record<string, any>, EncryptedItemInput[]]>(
57
+ ([acc, remaining], [key, value]) => {
58
+ const [newValue, newRemaining] = encryptReplace(value, remaining);
59
+ return [{ ...acc, [key]: newValue }, newRemaining];
60
+ },
61
+ [{}, encryptedItems]
62
+ );
63
+ }
64
+ }
65
+
66
+ return [item, encryptedItems];
67
+ }