@cofhe/sdk 0.6.0 → 0.7.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 (114) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/acps/acp.ts +411 -0
  3. package/acps/index.ts +70 -0
  4. package/{permits → acps}/onchain-utils.ts +49 -24
  5. package/acps/sealing.ts +90 -0
  6. package/acps/signature.ts +89 -0
  7. package/acps/store.ts +172 -0
  8. package/acps/test/acp.test.ts +615 -0
  9. package/acps/test/localstorage.test.ts +105 -0
  10. package/acps/test/sealing.test.ts +77 -0
  11. package/acps/test/store.test.ts +88 -0
  12. package/acps/test/validation.test.ts +361 -0
  13. package/acps/test-utils.ts +32 -0
  14. package/acps/types.ts +252 -0
  15. package/acps/validation.ts +392 -0
  16. package/adapters/test/ethers5.test.ts +4 -1
  17. package/adapters/test/ethers6.test.ts +4 -1
  18. package/adapters/test/wagmi.test.ts +5 -2
  19. package/chains/chains/stagingCofhe.ts +21 -0
  20. package/chains/index.ts +3 -1
  21. package/chains/test/chains.test.ts +2 -1
  22. package/core/acps.ts +625 -0
  23. package/core/client.ts +136 -39
  24. package/core/clientTypes.ts +52 -41
  25. package/core/config.ts +66 -5
  26. package/core/debug.ts +72 -0
  27. package/core/decrypt/MockThresholdNetworkAbi.ts +20 -11
  28. package/core/decrypt/apiError.ts +104 -0
  29. package/core/decrypt/cofheMocksDecryptForTx.ts +11 -11
  30. package/core/decrypt/cofheMocksDecryptForView.ts +7 -7
  31. package/core/decrypt/decryptForTxBuilder.ts +102 -102
  32. package/core/decrypt/decryptForViewBuilder.ts +90 -90
  33. package/core/decrypt/submitRetry.ts +38 -30
  34. package/core/decrypt/tnDecryptV1.ts +5 -5
  35. package/core/decrypt/tnDecryptV2.ts +45 -34
  36. package/core/decrypt/tnSealOutputV1.ts +4 -3
  37. package/core/decrypt/tnSealOutputV2.ts +44 -31
  38. package/core/encrypt/cofheMocksZkVerifySign.ts +59 -74
  39. package/core/encrypt/encryptInputsBuilder.ts +86 -52
  40. package/core/encrypt/zkPackProveVerify.ts +25 -18
  41. package/core/error.ts +34 -6
  42. package/core/index.ts +14 -14
  43. package/core/test/acpDefaults.test.ts +52 -0
  44. package/core/test/acps.test.ts +596 -0
  45. package/core/test/apiError.test.ts +130 -0
  46. package/core/test/client.test.ts +22 -19
  47. package/core/test/config.test.ts +25 -5
  48. package/core/test/decrypt.test.ts +40 -35
  49. package/core/test/decryptBuilders.test.ts +68 -68
  50. package/core/test/decryptErrorCodes.test.ts +217 -0
  51. package/core/test/encryptInputsBuilder.test.ts +72 -41
  52. package/core/test/pollCallbacks.test.ts +89 -18
  53. package/core/test/stagingRedirect.ts +18 -0
  54. package/core/test/submitRetry.test.ts +182 -0
  55. package/core/types.ts +9 -69
  56. package/dist/acp-Wi6isVQI.d.cts +407 -0
  57. package/dist/acp-Wi6isVQI.d.ts +407 -0
  58. package/dist/acps.cjs +1081 -0
  59. package/dist/acps.d.cts +480 -0
  60. package/dist/acps.d.ts +480 -0
  61. package/dist/acps.js +2 -0
  62. package/dist/chains.cjs +14 -1
  63. package/dist/chains.d.cts +30 -1
  64. package/dist/chains.d.ts +30 -1
  65. package/dist/chains.js +1 -1
  66. package/dist/{chunk-PE5V5CCV.js → chunk-43USWPEH.js} +1000 -604
  67. package/dist/{chunk-MTRAXQXC.js → chunk-N6IDQRRU.js} +14 -2
  68. package/dist/chunk-Q7CBWGQX.js +1029 -0
  69. package/dist/{clientTypes-CyUvRRzA.d.ts → clientTypes-BN3nbzYM.d.ts} +342 -165
  70. package/dist/{clientTypes-BDy1qIBu.d.cts → clientTypes-CYZjFznO.d.cts} +342 -165
  71. package/dist/core.cjs +1431 -1026
  72. package/dist/core.d.cts +55 -10
  73. package/dist/core.d.ts +55 -10
  74. package/dist/core.js +3 -3
  75. package/dist/node.cjs +1356 -976
  76. package/dist/node.d.cts +2 -2
  77. package/dist/node.d.ts +2 -2
  78. package/dist/node.js +3 -3
  79. package/dist/web.cjs +1356 -976
  80. package/dist/web.d.cts +2 -2
  81. package/dist/web.d.ts +2 -2
  82. package/dist/web.js +3 -3
  83. package/node/test/inherited.test.ts +75 -65
  84. package/node/test/tfheinit.test.ts +23 -8
  85. package/package.json +6 -6
  86. package/web/test/client.web.test.ts +5 -1
  87. package/web/test/inherited.web.test.ts +75 -65
  88. package/web/test/tfheinit.web.test.ts +14 -5
  89. package/web/test/worker.config.web.test.ts +38 -23
  90. package/web/test/worker.output.web.test.ts +25 -24
  91. package/core/encrypt/encryptUtils.ts +0 -67
  92. package/core/permits.ts +0 -206
  93. package/core/test/permits.test.ts +0 -534
  94. package/dist/chunk-VB62WYPL.js +0 -978
  95. package/dist/permit-DnVMDT5h.d.cts +0 -376
  96. package/dist/permit-DnVMDT5h.d.ts +0 -376
  97. package/dist/permits.cjs +0 -1026
  98. package/dist/permits.d.cts +0 -353
  99. package/dist/permits.d.ts +0 -353
  100. package/dist/permits.js +0 -2
  101. package/permits/index.ts +0 -68
  102. package/permits/permit.ts +0 -385
  103. package/permits/sealing.ts +0 -131
  104. package/permits/signature.ts +0 -79
  105. package/permits/store.ts +0 -157
  106. package/permits/test/localstorage.test.ts +0 -113
  107. package/permits/test/permit.test.ts +0 -557
  108. package/permits/test/sealing.test.ts +0 -84
  109. package/permits/test/store.test.ts +0 -88
  110. package/permits/test/validation.test.ts +0 -361
  111. package/permits/test-utils.ts +0 -28
  112. package/permits/types.ts +0 -204
  113. package/permits/validation.ts +0 -327
  114. /package/{permits → acps}/utils.ts +0 -0
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  type ZkBuilderAndCrsGenerator,
3
3
  type ZkProveWorkerFunction,
4
+ type VerifyBatchResult,
4
5
  zkPack,
5
6
  zkProve,
6
7
  zkProveWithWorker,
7
- zkVerify,
8
+ zkVerifyBatch,
8
9
  constructZkPoKMetadata,
9
10
  } from './zkPackProveVerify.js';
10
11
  import { CofheError, CofheErrorCode } from '../error.js';
@@ -12,8 +13,6 @@ import {
12
13
  type EncryptStepCallbackFunction,
13
14
  EncryptStep,
14
15
  type EncryptableItem,
15
- type EncryptedItemInput,
16
- type EncryptedItemInputs,
17
16
  type TfheInitializer,
18
17
  type EncryptStepCallbackContext,
19
18
  type HashPlusProofResult,
@@ -48,11 +47,20 @@ type EncryptInputsBuilderParams<T extends EncryptableItem[]> = BaseBuilderParams
48
47
  * config, tfhePublicKeyDeserializer, compactPkeCrsDeserializer, and zkBuilderAndCrsGenerator are required to be set in the builder.
49
48
  */
50
49
 
51
- export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boolean = false> extends BaseBuilder {
50
+ /**
51
+ * The builder as returned by `client.encryptInputs(...)`, before a consuming contract has been set.
52
+ *
53
+ * `execute()` is intentionally absent: the verifier binds the consuming contract into the signed
54
+ * digest, so no batch can be produced until the caller commits to the contract that will consume it.
55
+ * `setConsumingContract(...)` returns the full {@link EncryptInputsBuilder}, unlocking `execute()`.
56
+ */
57
+ export type EncryptInputsBuilderUnset<T extends EncryptableItem[]> = Omit<EncryptInputsBuilder<T>, 'execute'>;
58
+
59
+ export class EncryptInputsBuilder<T extends EncryptableItem[]> extends BaseBuilder {
52
60
  private securityZone: number;
61
+ protected consumingContract: string | undefined;
53
62
  private stepCallback?: EncryptStepCallbackFunction;
54
63
  private inputItems: [...T];
55
- private hpp: boolean = false;
56
64
 
57
65
  private zkvWalletClient: WalletClient | undefined;
58
66
 
@@ -156,7 +164,9 @@ export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boole
156
164
  *
157
165
  * @returns The chainable EncryptInputsBuilder instance.
158
166
  */
159
- setAccount(account: string): EncryptInputsBuilder<T, HPP> {
167
+ setAccount(this: EncryptInputsBuilder<T>, account: string): EncryptInputsBuilder<T>;
168
+ setAccount(this: EncryptInputsBuilderUnset<T>, account: string): EncryptInputsBuilderUnset<T>;
169
+ setAccount(account: string): EncryptInputsBuilder<T> {
160
170
  this.account = account;
161
171
  return this;
162
172
  }
@@ -179,7 +189,9 @@ export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boole
179
189
  *
180
190
  * @returns The chainable EncryptInputsBuilder instance.
181
191
  */
182
- setChainId(chainId: number): EncryptInputsBuilder<T, HPP> {
192
+ setChainId(this: EncryptInputsBuilder<T>, chainId: number): EncryptInputsBuilder<T>;
193
+ setChainId(this: EncryptInputsBuilderUnset<T>, chainId: number): EncryptInputsBuilderUnset<T>;
194
+ setChainId(chainId: number): EncryptInputsBuilder<T> {
183
195
  this.chainId = chainId;
184
196
  return this;
185
197
  }
@@ -202,7 +214,9 @@ export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boole
202
214
  *
203
215
  * @returns The chainable EncryptInputsBuilder instance.
204
216
  */
205
- setSecurityZone(securityZone: number): EncryptInputsBuilder<T, HPP> {
217
+ setSecurityZone(this: EncryptInputsBuilder<T>, securityZone: number): EncryptInputsBuilder<T>;
218
+ setSecurityZone(this: EncryptInputsBuilderUnset<T>, securityZone: number): EncryptInputsBuilderUnset<T>;
219
+ setSecurityZone(securityZone: number): EncryptInputsBuilder<T> {
206
220
  this.securityZone = securityZone;
207
221
  return this;
208
222
  }
@@ -212,18 +226,44 @@ export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boole
212
226
  }
213
227
 
214
228
  /**
229
+ * @param address - The contract that will consume the resulting hashes+signature (i.e. the
230
+ * contract that will call `FHE.asEuint*`/`FHE.asEuint*s` with them).
231
+ *
232
+ * Required before `execute()` - the verifier binds this address into the signed digest, so a
233
+ * batch signed for one contract cannot be replayed into another.
234
+ *
215
235
  * Example:
216
236
  * ```typescript
217
237
  * const encrypted = await encryptInputs([Encryptable.uint128(10n)])
218
- * .asHashPlusProof()
238
+ * .setConsumingContract("0x123...890")
219
239
  * .execute();
220
240
  * ```
221
241
  *
222
- * @returns Chainable EncryptInputsBuilder instance that will return a HashPlusProofResult instead of an array of EncryptedItemInputs.
242
+ * @returns The chainable EncryptInputsBuilder instance.
223
243
  */
224
- asHashPlusProof(): EncryptInputsBuilder<T, true> {
225
- this.hpp = true;
226
- return this as unknown as EncryptInputsBuilder<T, true>;
244
+ setConsumingContract(address: string): EncryptInputsBuilder<T> {
245
+ this.consumingContract = address;
246
+ return this;
247
+ }
248
+
249
+ getConsumingContract(): string | undefined {
250
+ return this.consumingContract;
251
+ }
252
+
253
+ /**
254
+ * Asserts that this.consumingContract is populated
255
+ * @throws {CofheError} If consumingContract is not set
256
+ */
257
+ private assertConsumingContract(): asserts this is this & { consumingContract: string } {
258
+ if (this.consumingContract) return;
259
+ throw new CofheError({
260
+ code: CofheErrorCode.ConsumingContractUninitialized,
261
+ message: 'Consuming contract is not set',
262
+ hint: 'Use setConsumingContract(...) to set the contract that will consume the encrypted inputs.',
263
+ context: {
264
+ consumingContract: this.consumingContract,
265
+ },
266
+ });
227
267
  }
228
268
 
229
269
  /**
@@ -240,7 +280,9 @@ export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boole
240
280
  *
241
281
  * @returns The chainable EncryptInputsBuilder instance.
242
282
  */
243
- setUseWorker(useWorker: boolean): EncryptInputsBuilder<T, HPP> {
283
+ setUseWorker(this: EncryptInputsBuilder<T>, useWorker: boolean): EncryptInputsBuilder<T>;
284
+ setUseWorker(this: EncryptInputsBuilderUnset<T>, useWorker: boolean): EncryptInputsBuilderUnset<T>;
285
+ setUseWorker(useWorker: boolean): EncryptInputsBuilder<T> {
244
286
  this.useWorker = useWorker;
245
287
  return this;
246
288
  }
@@ -277,7 +319,9 @@ export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boole
277
319
  *
278
320
  * @returns The EncryptInputsBuilder instance.
279
321
  */
280
- onStep(callback: EncryptStepCallbackFunction): EncryptInputsBuilder<T, HPP> {
322
+ onStep(this: EncryptInputsBuilder<T>, callback: EncryptStepCallbackFunction): EncryptInputsBuilder<T>;
323
+ onStep(this: EncryptInputsBuilderUnset<T>, callback: EncryptStepCallbackFunction): EncryptInputsBuilderUnset<T>;
324
+ onStep(callback: EncryptStepCallbackFunction): EncryptInputsBuilder<T> {
281
325
  this.stepCallback = callback;
282
326
  return this;
283
327
  }
@@ -433,10 +477,11 @@ export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boole
433
477
  * cofheMocksInsertPackedHashes - stores the ctHashes and their plaintext values for on-chain mocking of FHE operations.
434
478
  * cofheMocksZkCreateProofSignatures - creates signatures to be included in the encrypted inputs. The signers address is known and verified in the mock contracts.
435
479
  */
436
- private async mocksExecute(): Promise<[...EncryptedItemInputs<T>]> {
480
+ private async mocksExecute(): Promise<VerifyBatchResult> {
437
481
  this.assertAccount();
438
482
  this.assertPublicClient();
439
483
  this.assertWalletClient();
484
+ this.assertConsumingContract();
440
485
 
441
486
  const [initTfheDelay, fetchKeysDelay, packDelay, proveDelay, verifyDelay] = this.resolveEncryptDelays();
442
487
 
@@ -468,31 +513,27 @@ export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boole
468
513
 
469
514
  this.fireStepStart(EncryptStep.Verify);
470
515
  await sleep(verifyDelay);
471
- const signedResults = await cofheMocksZkVerifySign(
516
+ const result = await cofheMocksZkVerifySign(
472
517
  this.inputItems,
473
518
  this.account,
474
519
  this.securityZone,
520
+ this.consumingContract,
475
521
  this.publicClient,
476
522
  this.walletClient,
477
523
  this.zkvWalletClient
478
524
  );
479
- const encryptedInputs: EncryptedItemInput[] = signedResults.map(({ ct_hash, signature }, index) => ({
480
- ctHash: BigInt(ct_hash),
481
- securityZone: this.securityZone,
482
- utype: this.inputItems[index].utype,
483
- signature,
484
- }));
485
525
  this.fireStepEnd(EncryptStep.Verify, { isMocks: true, mockSleep: verifyDelay });
486
526
 
487
- return encryptedInputs as [...EncryptedItemInputs<T>];
527
+ return result;
488
528
  }
489
529
 
490
530
  /**
491
531
  * In the production context, perform a true encryption with the CoFHE coprocessor.
492
532
  */
493
- private async productionExecute(): Promise<[...EncryptedItemInputs<T>]> {
533
+ private async productionExecute(): Promise<VerifyBatchResult> {
494
534
  this.assertAccount();
495
535
  this.assertChainId();
536
+ this.assertConsumingContract();
496
537
 
497
538
  this.fireStepStart(EncryptStep.InitTfhe);
498
539
 
@@ -555,32 +596,28 @@ export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boole
555
596
 
556
597
  const zkVerifierUrl = await this.getZkVerifierUrl();
557
598
 
558
- const verifyResults = await zkVerify(zkVerifierUrl, proof, this.account, this.securityZone, this.chainId);
559
- // Add securityZone and utype to the verify results
560
- const encryptedInputs: EncryptedItemInput[] = verifyResults.map(
561
- ({ ct_hash, signature }: { ct_hash: string; signature: `0x${string}` }, index: number) => ({
562
- ctHash: BigInt(ct_hash),
563
- securityZone: this.securityZone,
564
- utype: this.inputItems[index].utype,
565
- signature,
566
- })
599
+ const result = await zkVerifyBatch(
600
+ zkVerifierUrl,
601
+ proof,
602
+ this.account,
603
+ this.securityZone,
604
+ this.chainId,
605
+ this.consumingContract
567
606
  );
568
607
 
569
608
  this.fireStepEnd(EncryptStep.Verify);
570
609
 
571
- return encryptedInputs as [...EncryptedItemInputs<T>];
610
+ return result;
572
611
  }
573
612
 
574
- private structsToHashPlusProof(inItems: [...EncryptedItemInputs<T>]): HashPlusProofResult<T> {
575
- let hashes: string[] = [];
576
- let proof: string = '';
577
-
578
- for (const item of inItems) {
579
- hashes.push('0x' + item.ctHash.toString(16).padStart(64, '0'));
580
- proof += item.signature;
581
- }
613
+ /**
614
+ * Converts a batch-verified result (per-item ctHash/ctType + one shared signature) into the
615
+ * tuple returned by `execute()`: per-item hashes in input order, followed by the single signature.
616
+ */
617
+ private buildBatchResult(result: VerifyBatchResult): HashPlusProofResult<T> {
618
+ const hashes = result.outputs.map((output) => '0x' + BigInt(output.ct_hash).toString(16).padStart(64, '0'));
582
619
 
583
- return [...hashes, proof] as unknown as HashPlusProofResult<T>;
620
+ return [...hashes, result.signature] as unknown as HashPlusProofResult<T>;
584
621
  }
585
622
 
586
623
  /**
@@ -602,17 +639,14 @@ export class EncryptInputsBuilder<T extends EncryptableItem[], HPP extends boole
602
639
  *
603
640
  * @returns The encrypted inputs.
604
641
  */
605
- async execute(): Promise<HPP extends true ? HashPlusProofResult<T> : [...EncryptedItemInputs<T>]> {
606
- type Result = HPP extends true ? HashPlusProofResult<T> : [...EncryptedItemInputs<T>];
607
-
608
- let items: [...EncryptedItemInputs<T>];
642
+ async execute(): Promise<HashPlusProofResult<T>> {
643
+ let result: VerifyBatchResult;
609
644
 
610
645
  // On hardhat chain, interact with MockZkVerifier contract instead of CoFHE
611
- if (this.chainId === hardhat.id) items = await this.mocksExecute();
646
+ if (this.chainId === hardhat.id) result = await this.mocksExecute();
612
647
  // On other chains, interact with CoFHE coprocessor
613
- else items = await this.productionExecute();
648
+ else result = await this.productionExecute();
614
649
 
615
- if (this.hpp) return this.structsToHashPlusProof(items) as unknown as Result;
616
- return items as unknown as Result;
650
+ return this.buildBatchResult(result);
617
651
  }
618
652
  }
@@ -41,14 +41,19 @@ export interface ZkProveWorkerResponse {
41
41
  error?: string;
42
42
  }
43
43
 
44
- export type VerifyResultRaw = {
44
+ export type VerifyBatchOutputRaw = {
45
45
  ct_hash: string;
46
+ ct_type: number;
47
+ };
48
+
49
+ export type VerifyBatchResultRaw = {
50
+ ciphertexts: VerifyBatchOutputRaw[];
46
51
  signature: string;
47
52
  recid: number;
48
53
  };
49
54
 
50
- export type VerifyResult = {
51
- ct_hash: string;
55
+ export type VerifyBatchResult = {
56
+ outputs: VerifyBatchOutputRaw[];
52
57
  signature: `0x${string}`;
53
58
  };
54
59
 
@@ -256,13 +261,14 @@ export const constructZkPoKMetadata = (accountAddr: string, securityZone: number
256
261
  return metadata;
257
262
  };
258
263
 
259
- export const zkVerify = async (
264
+ export const zkVerifyBatch = async (
260
265
  verifierUrl: string,
261
266
  serializedBytes: Uint8Array,
262
267
  address: string,
263
268
  securityZone: number,
264
- chainId: number
265
- ): Promise<VerifyResult[]> => {
269
+ chainId: number,
270
+ consumingContract: string
271
+ ): Promise<VerifyBatchResult> => {
266
272
  // Convert bytearray to hex string
267
273
  const packed_list = toHexString(serializedBytes);
268
274
 
@@ -274,13 +280,14 @@ export const zkVerify = async (
274
280
  account_addr: address,
275
281
  security_zone: sz_byte[0],
276
282
  chain_id: chainId,
283
+ contract_address: consumingContract,
277
284
  };
278
285
 
279
286
  const body = JSON.stringify(payload);
280
287
 
281
288
  // Send request to verification server
282
289
  try {
283
- const response = await fetch(`${verifierUrl}/verify`, {
290
+ const response = await fetch(`${verifierUrl}/verifyBatch`, {
284
291
  method: 'POST',
285
292
  headers: {
286
293
  'Content-Type': 'application/json',
@@ -293,34 +300,34 @@ export const zkVerify = async (
293
300
  const errorBody = await response.text();
294
301
  throw new CofheError({
295
302
  code: CofheErrorCode.ZkVerifyFailed,
296
- message: `HTTP error! ZK proof verification failed - ${errorBody}`,
303
+ message: `HTTP error! ZK batch proof verification failed - ${errorBody}`,
297
304
  });
298
305
  }
299
306
 
300
- const json = (await response.json()) as { status: string; data: VerifyResultRaw[]; error: string };
307
+ const json = (await response.json()) as { status: string; data: VerifyBatchResultRaw; error: string };
301
308
 
302
309
  if (json.status !== 'success') {
303
310
  throw new CofheError({
304
311
  code: CofheErrorCode.ZkVerifyFailed,
305
- message: `ZK proof verification response malformed - ${json.error}`,
312
+ message: `ZK batch proof verification response malformed - ${json.error}`,
306
313
  });
307
314
  }
308
315
 
309
- return json.data.map(({ ct_hash, signature, recid }) => {
310
- return {
311
- ct_hash,
312
- signature: concatSigRecid(signature, recid),
313
- };
314
- });
316
+ const { ciphertexts, signature, recid } = json.data;
317
+
318
+ return {
319
+ outputs: ciphertexts,
320
+ signature: concatSigRecid(signature, recid),
321
+ };
315
322
  } catch (e) {
316
323
  throw new CofheError({
317
324
  code: CofheErrorCode.ZkVerifyFailed,
318
- message: `ZK proof verification failed`,
325
+ message: `ZK batch proof verification failed`,
319
326
  cause: e instanceof Error ? e : undefined,
320
327
  });
321
328
  }
322
329
  };
323
330
 
324
- const concatSigRecid = (signature: string, recid: number): `0x${string}` => {
331
+ export const concatSigRecid = (signature: string, recid: number): `0x${string}` => {
325
332
  return `${signature}${(recid + 27).toString(16).padStart(2, '0')}` as `0x${string}`;
326
333
  };
package/core/error.ts CHANGED
@@ -9,12 +9,13 @@ export enum CofheErrorCode {
9
9
  MissingWalletClient = 'MISSING_WALLET_CLIENT',
10
10
  MissingProviderParam = 'MISSING_PROVIDER_PARAM',
11
11
  EmptySecurityZonesParam = 'EMPTY_SECURITY_ZONES_PARAM',
12
- InvalidPermitData = 'INVALID_PERMIT_DATA',
13
- InvalidPermitDomain = 'INVALID_PERMIT_DOMAIN',
14
- PermitNotFound = 'PERMIT_NOT_FOUND',
15
- CannotRemoveLastPermit = 'CANNOT_REMOVE_LAST_PERMIT',
12
+ InvalidACPData = 'INVALID_ACP_DATA',
13
+ InvalidACPDomain = 'INVALID_ACP_DOMAIN',
14
+ ACPNotFound = 'ACP_NOT_FOUND',
15
+ CannotRemoveLastACP = 'CANNOT_REMOVE_LAST_ACP',
16
16
  AccountUninitialized = 'ACCOUNT_UNINITIALIZED',
17
17
  ChainIdUninitialized = 'CHAIN_ID_UNINITIALIZED',
18
+ ConsumingContractUninitialized = 'CONSUMING_CONTRACT_UNINITIALIZED',
18
19
  SealOutputFailed = 'SEAL_OUTPUT_FAILED',
19
20
  SealOutputReturnedNull = 'SEAL_OUTPUT_RETURNED_NULL',
20
21
  InvalidUtype = 'INVALID_UTYPE',
@@ -42,6 +43,26 @@ export enum CofheErrorCode {
42
43
  PublicWalletGetChainIdFailed = 'PUBLIC_WALLET_GET_CHAIN_ID_FAILED',
43
44
  PublicWalletGetAddressesFailed = 'PUBLIC_WALLET_GET_ADDRESSES_FAILED',
44
45
  RehydrateKeysStoreFailed = 'REHYDRATE_KEYS_STORE_FAILED',
46
+
47
+ // Threshold-network stable error codes (decrypt/sealoutput), see API-RESPONSES.md
48
+ BadRequest = 'BAD_REQUEST',
49
+ UnknownChain = 'UNKNOWN_CHAIN',
50
+ ACPMalformed = 'ACP_MALFORMED',
51
+ ACPDenied = 'ACP_DENIED',
52
+ ACPExpired = 'ACP_EXPIRED',
53
+ ACPInvalid = 'ACP_INVALID',
54
+ ACPRevoked = 'ACP_REVOKED',
55
+ NotPubliclyAllowed = 'NOT_PUBLICLY_ALLOWED',
56
+ CtNotFound = 'CT_NOT_FOUND',
57
+ UnsupportedSecurityZone = 'UNSUPPORTED_SECURITY_ZONE',
58
+ UnsupportedType = 'UNSUPPORTED_TYPE',
59
+ SigningFailed = 'SIGNING_FAILED',
60
+ CtSourceError = 'CT_SOURCE_ERROR',
61
+ ACPVerifierError = 'ACP_VERIFIER_ERROR',
62
+ CtSourceTimeout = 'CT_SOURCE_TIMEOUT',
63
+ ACPVerifierTimeout = 'ACP_VERIFIER_TIMEOUT',
64
+ ACPRequired = 'ACP_REQUIRED',
65
+ SealFailed = 'SEAL_FAILED',
45
66
  }
46
67
 
47
68
  export type CofheErrorParams = {
@@ -50,6 +71,8 @@ export type CofheErrorParams = {
50
71
  cause?: Error;
51
72
  hint?: string;
52
73
  context?: Record<string, unknown>;
74
+ /** Raw backend `error` string, when this error originated from a threshold-network response. */
75
+ apiErrorCode?: string;
53
76
  };
54
77
 
55
78
  /**
@@ -68,8 +91,9 @@ export class CofheError extends Error {
68
91
  public readonly cause?: Error;
69
92
  public readonly hint?: string;
70
93
  public readonly context?: Record<string, unknown>;
94
+ public readonly apiErrorCode?: string;
71
95
 
72
- constructor({ code, message, cause, hint, context }: CofheErrorParams) {
96
+ constructor({ code, message, cause, hint, context, apiErrorCode }: CofheErrorParams) {
73
97
  // If there's a cause, append its message to provide full context
74
98
  const fullMessage = cause ? `${message} | Caused by: ${cause.message}` : message;
75
99
 
@@ -79,6 +103,7 @@ export class CofheError extends Error {
79
103
  this.cause = cause;
80
104
  this.hint = hint;
81
105
  this.context = context;
106
+ this.apiErrorCode = apiErrorCode;
82
107
 
83
108
  // Maintains proper stack trace for where our error was thrown (only available on V8)
84
109
  if (Error.captureStackTrace) {
@@ -101,6 +126,7 @@ export class CofheError extends Error {
101
126
  message: wrapperError?.message ?? 'An internal error occurred',
102
127
  hint: wrapperError?.hint,
103
128
  context: wrapperError?.context,
129
+ apiErrorCode: wrapperError?.apiErrorCode,
104
130
  cause: cause,
105
131
  });
106
132
  }
@@ -115,6 +141,7 @@ export class CofheError extends Error {
115
141
  message: this.message,
116
142
  hint: this.hint,
117
143
  context: this.context,
144
+ apiErrorCode: this.apiErrorCode,
118
145
  cause: this.cause
119
146
  ? {
120
147
  name: this.cause.name,
@@ -130,7 +157,8 @@ export class CofheError extends Error {
130
157
  * Returns a human-readable string representation of the error
131
158
  */
132
159
  toString(): string {
133
- const parts = [`${this.name} [${this.code}]: ${this.message}`];
160
+ const codeSuffix = this.apiErrorCode ? ` (api: ${this.apiErrorCode})` : '';
161
+ const parts = [`${this.name} [${this.code}]${codeSuffix}: ${this.message}`];
134
162
 
135
163
  if (this.hint) {
136
164
  parts.push(`Hint: ${this.hint}`);
package/core/index.ts CHANGED
@@ -1,8 +1,18 @@
1
1
  // Client (base implementations)
2
2
  export { createCofheClientBase, InitialConnectStore as CONNECT_STORE_DEFAULTS } from './client.js';
3
3
 
4
+ // Debug interceptors (neutral, opt-in network hooks for fault injection / logging)
5
+ export { setCofheDebugInterceptors, getCofheDebugInterceptors, cofheFetch } from './debug.js';
6
+ export type {
7
+ CofheDebugInterceptors,
8
+ CofheOnRequest,
9
+ CofheOnResponse,
10
+ CofheRequestContext,
11
+ CofheRequestOverride,
12
+ } from './debug.js';
13
+
4
14
  // Configuration (base implementations)
5
- export { createCofheConfigBase, getCofheConfigItem } from './config.js';
15
+ export { createCofheConfigBase, getCofheConfigItem, assertNoRenamedConfigKeys } from './config.js';
6
16
  export type { CofheConfig, CofheInputConfig, CofheInternalConfig } from './config.js';
7
17
 
8
18
  // Types
@@ -11,7 +21,7 @@ export type {
11
21
  CofheClient as CofheClient,
12
22
  CofheClientParams as CofheClientParams,
13
23
  CofheClientConnectionState as CofheClientConnectionState,
14
- CofheClientPermits as CofheClientPermits,
24
+ CofheClientACPs as CofheClientACPs,
15
25
  } from './clientTypes.js';
16
26
 
17
27
  export type {
@@ -30,18 +40,8 @@ export type {
30
40
  EncryptableAddress,
31
41
  // Encrypted types
32
42
  EncryptedNumber,
33
- EncryptedItemInput,
34
- EncryptedBoolInput,
35
- EncryptedUint8Input,
36
- EncryptedUint16Input,
37
- EncryptedUint32Input,
38
- EncryptedUint64Input,
39
- EncryptedUint128Input,
40
- EncryptedAddressInput,
41
- EncryptedItemInputs,
42
- EncryptableToEncryptedItemInputMap,
43
43
  FheTypeValue,
44
- // Hash-plus-proof external types
44
+ // Batch-verified external hash + signature types
45
45
  ExternalBoolHash,
46
46
  ExternalUint8Hash,
47
47
  ExternalUint16Hash,
@@ -71,7 +71,6 @@ export {
71
71
  isEncryptableItem,
72
72
  EncryptStep,
73
73
  isLastEncryptionStep,
74
- assertCorrectEncryptedItemInput,
75
74
  } from './types.js';
76
75
 
77
76
  // Error handling
@@ -88,6 +87,7 @@ export type { KeysStorage, KeysStore } from './keyStore.js';
88
87
 
89
88
  // Builders (exported via client, but can be imported directly for typing)
90
89
  export { EncryptInputsBuilder } from './encrypt/encryptInputsBuilder.js';
90
+ export type { EncryptInputsBuilderUnset } from './encrypt/encryptInputsBuilder.js';
91
91
  export { DecryptForViewBuilder } from './decrypt/decryptForViewBuilder.js';
92
92
  export { DecryptForTxBuilder } from './decrypt/decryptForTxBuilder.js';
93
93
  export type { DecryptForTxResult } from './decrypt/decryptForTxBuilder.js';
@@ -0,0 +1,52 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { acps } from '../acps.js';
3
+
4
+ type ScopeOpts = {
5
+ revokerData?: number;
6
+ revokerContract?: string;
7
+ scope?: number;
8
+ contracts?: string[];
9
+ handles?: bigint[];
10
+ };
11
+ const opts = (o: ScopeOpts): ScopeOpts => o;
12
+
13
+ const CHAIN = 31337;
14
+ const REVOKER = '0x00000000000000000000000000000000000000aa' as `0x${string}`;
15
+ const CONTRACT_A = '0x00000000000000000000000000000000000000bb' as `0x${string}`;
16
+
17
+ const acpConfig = {
18
+ defaultRevoker: { [CHAIN]: REVOKER },
19
+ defaultContractScopes: { [CHAIN]: [CONTRACT_A] },
20
+ };
21
+
22
+ describe('applyACPDefaults', () => {
23
+ it('injects default revoker (contract + creation timestamp) when no revoker options given', () => {
24
+ const before = Math.round(Date.now() / 1000);
25
+ const result = acps.applyACPDefaults(opts({}), acpConfig, CHAIN);
26
+ expect(result.revokerContract).toBe(REVOKER);
27
+ expect(result.revokerData).toBeGreaterThanOrEqual(before - 60); // 60s clock-skew backdating
28
+ expect(result.revokerData).toBeLessThanOrEqual(Math.round(Date.now() / 1000) - 59);
29
+ });
30
+
31
+ it('does not override an explicit revoker pair', () => {
32
+ const result = acps.applyACPDefaults({ revokerData: 42, revokerContract: CONTRACT_A }, acpConfig, CHAIN);
33
+ expect(result.revokerData).toBe(42);
34
+ expect(result.revokerContract).toBe(CONTRACT_A);
35
+ });
36
+
37
+ it('injects default contract scopes when no scope options given', () => {
38
+ const result = acps.applyACPDefaults(opts({}), acpConfig, CHAIN);
39
+ expect(result.contracts).toEqual([CONTRACT_A]);
40
+ });
41
+
42
+ it('does not inject scopes when the caller provides any scope option (incl. explicit scope)', () => {
43
+ expect(acps.applyACPDefaults(opts({ scope: 0 }), acpConfig, CHAIN).contracts).toBeUndefined();
44
+ expect(acps.applyACPDefaults(opts({ handles: [1n] }), acpConfig, CHAIN).contracts).toBeUndefined();
45
+ expect(acps.applyACPDefaults(opts({ contracts: [] }), acpConfig, CHAIN).contracts).toEqual([]);
46
+ });
47
+
48
+ it('no-op for chains without defaults and for missing config', () => {
49
+ expect(acps.applyACPDefaults(opts({}), acpConfig, 999)).toEqual({});
50
+ expect(acps.applyACPDefaults(opts({}), undefined, CHAIN)).toEqual({});
51
+ });
52
+ });