@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
package/core/client.ts CHANGED
@@ -1,16 +1,16 @@
1
- import type { CreateSelfPermitOptions, CreateSharingPermitOptions, ImportSharedPermitOptions } from '@/permits';
1
+ import type { CreateSelfACPOptions, CreateSharingACPOptions, ImportSharedACPOptions } from '@/acps';
2
2
 
3
3
  import { createStore } from 'zustand/vanilla';
4
4
  import { type Hex, type PublicClient, type WalletClient } from 'viem';
5
5
  import { CofheError, CofheErrorCode } from './error.js';
6
- import { EncryptInputsBuilder } from './encrypt/encryptInputsBuilder.js';
6
+ import { EncryptInputsBuilder, type EncryptInputsBuilderUnset } from './encrypt/encryptInputsBuilder.js';
7
7
  import { createKeysStore } from './keyStore.js';
8
- import { permits } from './permits.js';
8
+ import { acps } from './acps.js';
9
9
  import { DecryptForViewBuilder } from './decrypt/decryptForViewBuilder.js';
10
10
  import { DecryptForTxBuilder, type DecryptForTxBuilderUnset } from './decrypt/decryptForTxBuilder.js';
11
11
  import { verifyDecryptResult as verifyDecryptResultStandalone } from './decrypt/verifyDecryptResult.js';
12
12
  import { getPublicClientChainID, getWalletClientAccount } from './utils.js';
13
- import type { CofheClientConnectionState, CofheClientParams, CofheClient, CofheClientPermits } from './clientTypes.js';
13
+ import type { CofheClientConnectionState, CofheClientParams, CofheClient, CofheClientACPs } from './clientTypes.js';
14
14
  import type { EncryptableItem, FheTypes } from './types.js';
15
15
  import type { CofheConfig } from './config.js';
16
16
 
@@ -48,7 +48,23 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
48
48
  connectStore.setState((state) => ({ ...state, ...partial }));
49
49
  };
50
50
 
51
- // Called before any operation, throws of connection not yet established
51
+ // Share registry resolution: explicit `acp.sharingRegistry` config wins,
52
+ // otherwise the address the chain's ACL serves.
53
+ const _resolveSharingRegistry = async (publicClient: PublicClient, chainId: number): Promise<`0x${string}`> => {
54
+ const configured = opts.config.acp?.sharingRegistry?.[chainId];
55
+ if (configured != null) return configured;
56
+ const registry = (await acps.getAclServedAddresses(publicClient, chainId)).shareRegistry;
57
+ if (registry == null) {
58
+ throw new CofheError({
59
+ code: CofheErrorCode.MissingConfig,
60
+ message: `No ACP share registry available for chainId <${chainId}>`,
61
+ hint: 'The ACL on this chain does not serve a share registry address. Set `acp.sharingRegistry` in the cofhe config to use on-chain sharing.',
62
+ context: { chainId },
63
+ });
64
+ }
65
+ return registry;
66
+ };
67
+
52
68
  const _requireConnected = () => {
53
69
  const state = connectStore.getState();
54
70
  const notConnected =
@@ -122,7 +138,7 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
122
138
 
123
139
  // CLIENT OPERATIONS
124
140
 
125
- function encryptInputs<T extends EncryptableItem[]>(inputs: [...T]): EncryptInputsBuilder<[...T]> {
141
+ function encryptInputs<T extends EncryptableItem[]>(inputs: [...T]): EncryptInputsBuilderUnset<[...T]> {
126
142
  const state = connectStore.getState();
127
143
 
128
144
  return new EncryptInputsBuilder({
@@ -187,7 +203,7 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
187
203
  return verifyDecryptResultStandalone(handle, cleartext, signature, publicClient!);
188
204
  }
189
205
 
190
- // PERMITS - Context-aware wrapper
206
+ // ACPS - Context-aware wrapper
191
207
 
192
208
  const _getChainIdAndAccount = (chainId?: number, account?: string) => {
193
209
  const state = connectStore.getState();
@@ -209,96 +225,177 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
209
225
  return { chainId: _chainId, account: _account };
210
226
  };
211
227
 
212
- const clientPermits: CofheClientPermits = {
228
+ const clientACPs: CofheClientACPs = {
213
229
  // Pass through store access
214
- getSnapshot: permits.getSnapshot,
215
- subscribe: permits.subscribe,
230
+ getSnapshot: acps.getSnapshot,
231
+ subscribe: acps.subscribe,
216
232
 
217
233
  // Creation methods (require connection)
218
234
  createSelf: async (
219
- options: CreateSelfPermitOptions,
235
+ options: CreateSelfACPOptions,
220
236
  clients?: { publicClient: PublicClient; walletClient: WalletClient }
221
237
  ) => {
222
238
  _requireConnected();
223
239
  const { publicClient, walletClient } = clients ?? connectStore.getState();
224
- return permits.createSelf(options, publicClient!, walletClient!);
240
+ const chainId = await publicClient!.getChainId();
241
+ return acps.createSelf(
242
+ await acps.applyACPDefaultsFromChain(options, opts.config.acp, publicClient!, chainId),
243
+ publicClient!,
244
+ walletClient!
245
+ );
225
246
  },
226
247
 
227
248
  createSharing: async (
228
- options: CreateSharingPermitOptions,
249
+ options: CreateSharingACPOptions,
229
250
  clients?: { publicClient: PublicClient; walletClient: WalletClient }
230
251
  ) => {
231
252
  _requireConnected();
232
253
  const { publicClient, walletClient } = clients ?? connectStore.getState();
233
- return permits.createSharing(options, publicClient!, walletClient!);
254
+ const chainId = await publicClient!.getChainId();
255
+ return acps.createSharing(
256
+ await acps.applyACPDefaultsFromChain(options, opts.config.acp, publicClient!, chainId),
257
+ publicClient!,
258
+ walletClient!
259
+ );
234
260
  },
235
261
 
236
262
  importShared: async (
237
- options: ImportSharedPermitOptions | string,
263
+ options: ImportSharedACPOptions | string,
238
264
  clients?: { publicClient: PublicClient; walletClient: WalletClient }
239
265
  ) => {
240
266
  _requireConnected();
241
267
  const { publicClient, walletClient } = clients ?? connectStore.getState();
242
- return permits.importShared(options, publicClient!, walletClient!);
268
+ return acps.importShared(options, publicClient!, walletClient!);
243
269
  },
244
270
 
245
271
  // Get or create methods (require connection)
246
- getOrCreateSelfPermit: async (chainId?: number, account?: string, options?: CreateSelfPermitOptions) => {
272
+ getOrCreateSelfACP: async (chainId?: number, account?: string, options?: CreateSelfACPOptions) => {
247
273
  _requireConnected();
248
274
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
249
275
  const { publicClient, walletClient } = connectStore.getState();
250
- return permits.getOrCreateSelfPermit(publicClient!, walletClient!, _chainId, _account, options);
276
+ const optionsWithDefaults = await acps.applyACPDefaultsFromChain(
277
+ options ?? { issuer: _account, name: 'Autogenerated Self ACP' },
278
+ opts.config.acp,
279
+ publicClient!,
280
+ _chainId
281
+ );
282
+ return acps.getOrCreateSelfACP(publicClient!, walletClient!, _chainId, _account, optionsWithDefaults);
251
283
  },
252
284
 
253
- getOrCreateSharingPermit: async (options: CreateSharingPermitOptions, chainId?: number, account?: string) => {
285
+ getOrCreateSharingACP: async (options: CreateSharingACPOptions, chainId?: number, account?: string) => {
254
286
  _requireConnected();
255
287
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
256
288
  const { publicClient, walletClient } = connectStore.getState();
257
- return permits.getOrCreateSharingPermit(publicClient!, walletClient!, options, _chainId, _account);
289
+ return acps.getOrCreateSharingACP(
290
+ publicClient!,
291
+ walletClient!,
292
+ await acps.applyACPDefaultsFromChain(options, opts.config.acp, publicClient!, _chainId),
293
+ _chainId,
294
+ _account
295
+ );
296
+ },
297
+
298
+ // Revocation (require connection)
299
+ revokeACP: async (acp) => {
300
+ _requireConnected();
301
+ const { walletClient } = connectStore.getState();
302
+ return acps.revokeACP(acp, walletClient!);
303
+ },
304
+
305
+ revokeAllACPs: async (revokerContract?: `0x${string}`) => {
306
+ _requireConnected();
307
+ const { publicClient, walletClient } = connectStore.getState();
308
+ return acps.revokeAllACPs(walletClient!, publicClient!, revokerContract);
309
+ },
310
+
311
+ isACPRevoked: async (acp) => {
312
+ _requireConnected();
313
+ const { publicClient } = connectStore.getState();
314
+ return acps.isACPRevoked(acp, publicClient!);
315
+ },
316
+
317
+ // On-chain sharing (require connection + config.acp.sharingRegistry)
318
+ shareOnChain: async (acp) => {
319
+ _requireConnected();
320
+ const { publicClient, walletClient } = connectStore.getState();
321
+ const chainId = await publicClient!.getChainId();
322
+ const sharingRegistry = await _resolveSharingRegistry(publicClient!, chainId);
323
+ return acps.shareOnChain(acp, walletClient!, sharingRegistry);
324
+ },
325
+
326
+ getIncomingShares: async () => {
327
+ _requireConnected();
328
+ const { publicClient, walletClient } = connectStore.getState();
329
+ const chainId = await publicClient!.getChainId();
330
+ const account = walletClient!.account!.address;
331
+ const sharingRegistry = await _resolveSharingRegistry(publicClient!, chainId);
332
+ return acps.getIncomingShares(publicClient!, sharingRegistry, account);
333
+ },
334
+
335
+ importFromChain: async (share) => {
336
+ _requireConnected();
337
+ const { publicClient, walletClient } = connectStore.getState();
338
+ return acps.importFromChain(share, publicClient!, walletClient!);
339
+ },
340
+
341
+ dismissShare: async (shareId) => {
342
+ _requireConnected();
343
+ const { publicClient, walletClient } = connectStore.getState();
344
+ const chainId = await publicClient!.getChainId();
345
+ const sharingRegistry = await _resolveSharingRegistry(publicClient!, chainId);
346
+ return acps.removeShareOnChain(shareId, walletClient!, sharingRegistry);
347
+ },
348
+
349
+ cancelShare: async (shareId) => {
350
+ _requireConnected();
351
+ const { publicClient, walletClient } = connectStore.getState();
352
+ const chainId = await publicClient!.getChainId();
353
+ const sharingRegistry = await _resolveSharingRegistry(publicClient!, chainId);
354
+ return acps.removeShareOnChain(shareId, walletClient!, sharingRegistry);
258
355
  },
259
356
 
260
357
  // Retrieval methods (auto-fill chainId/account)
261
- getPermit: (hash: string, chainId?: number, account?: string) => {
358
+ getACP: (hash: string, chainId?: number, account?: string) => {
262
359
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
263
- return permits.getPermit(_chainId, _account, hash);
360
+ return acps.getACP(_chainId, _account, hash);
264
361
  },
265
362
 
266
- getPermits: (chainId?: number, account?: string) => {
363
+ getACPs: (chainId?: number, account?: string) => {
267
364
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
268
- return permits.getPermits(_chainId, _account);
365
+ return acps.getACPs(_chainId, _account);
269
366
  },
270
367
 
271
- getActivePermit: (chainId?: number, account?: string) => {
368
+ getActiveACP: (chainId?: number, account?: string) => {
272
369
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
273
- return permits.getActivePermit(_chainId, _account);
370
+ return acps.getActiveACP(_chainId, _account);
274
371
  },
275
372
 
276
- getActivePermitHash: (chainId?: number, account?: string) => {
373
+ getActiveACPHash: (chainId?: number, account?: string) => {
277
374
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
278
- return permits.getActivePermitHash(_chainId, _account);
375
+ return acps.getActiveACPHash(_chainId, _account);
279
376
  },
280
377
 
281
378
  // Mutation methods (auto-fill chainId/account)
282
- selectActivePermit: (hash: string, chainId?: number, account?: string) => {
379
+ selectActiveACP: (hash: string, chainId?: number, account?: string) => {
283
380
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
284
- return permits.selectActivePermit(_chainId, _account, hash);
381
+ return acps.selectActiveACP(_chainId, _account, hash);
285
382
  },
286
383
 
287
- removePermit: async (hash: string, chainId?: number, account?: string) => {
384
+ removeACP: async (hash: string, chainId?: number, account?: string) => {
288
385
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
289
- return permits.removePermit(_chainId, _account, hash);
386
+ return acps.removeACP(_chainId, _account, hash);
290
387
  },
291
388
 
292
- removeActivePermit: async (chainId?: number, account?: string) => {
389
+ removeActiveACP: async (chainId?: number, account?: string) => {
293
390
  const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
294
- return permits.removeActivePermit(_chainId, _account);
391
+ return acps.removeActiveACP(_chainId, _account);
295
392
  },
296
393
 
297
394
  // Utils (no context needed)
298
- getHash: permits.getHash,
299
- export: permits.export,
300
- serialize: permits.serialize,
301
- deserialize: permits.deserialize,
395
+ getHash: acps.getHash,
396
+ export: acps.export,
397
+ serialize: acps.serialize,
398
+ deserialize: acps.deserialize,
302
399
  };
303
400
 
304
401
  return {
@@ -330,7 +427,7 @@ export function createCofheClientBase<TConfig extends CofheConfig>(
330
427
  decryptHandle: decryptForView,
331
428
  decryptForTx,
332
429
  verifyDecryptResult,
333
- permits: clientPermits,
430
+ acp: clientACPs,
334
431
 
335
432
  // Add SDK-specific methods below that require connection
336
433
  // Example:
@@ -3,21 +3,22 @@ import { type Hex, type PublicClient, type WalletClient } from 'viem';
3
3
  import { type CofheConfig } from './config.js';
4
4
  import { type DecryptForViewBuilder } from './decrypt/decryptForViewBuilder.js';
5
5
  import { type DecryptForTxBuilderUnset } from './decrypt/decryptForTxBuilder.js';
6
- import { type EncryptInputsBuilder } from './encrypt/encryptInputsBuilder.js';
6
+ import { type EncryptInputsBuilderUnset } from './encrypt/encryptInputsBuilder.js';
7
7
  import { type ZkBuilderAndCrsGenerator, type ZkProveWorkerFunction } from './encrypt/zkPackProveVerify.js';
8
8
  import { type FheKeyDeserializer } from './fetchKeys.js';
9
- import { permits } from './permits.js';
9
+ import { acps } from './acps.js';
10
10
  import type { EncryptableItem, FheTypes, TfheInitializer } from './types.js';
11
- import type { PermitUtils } from 'permits/permit.js';
11
+ import type { ACPUtils } from 'acps/acp.js';
12
12
  import type {
13
- CreateSelfPermitOptions,
14
- Permit,
15
- CreateSharingPermitOptions,
16
- ImportSharedPermitOptions,
17
- SharingPermit,
18
- RecipientPermit,
19
- SelfPermit,
20
- } from 'permits/types.js';
13
+ CreateSelfACPOptions,
14
+ ACP,
15
+ CreateSharingACPOptions,
16
+ ImportSharedACPOptions,
17
+ SharingACP,
18
+ RecipientACP,
19
+ SelfACP,
20
+ IncomingShare,
21
+ } from 'acps/types.js';
21
22
 
22
23
  // CLIENT
23
24
 
@@ -38,13 +39,13 @@ export type CofheClient<TConfig extends CofheConfig = CofheConfig> = {
38
39
  /**
39
40
  * Clears the current connection state (account/chainId/clients) and marks the client as disconnected.
40
41
  *
41
- * This does not delete persisted permits or stored FHE keys; it only resets the in-memory connection.
42
+ * This does not delete persisted acps or stored FHE keys; it only resets the in-memory connection.
42
43
  */
43
44
  disconnect(): void;
44
45
  /**
45
46
  * Types docstring
46
47
  */
47
- encryptInputs<T extends EncryptableItem[]>(inputs: [...T]): EncryptInputsBuilder<[...T]>;
48
+ encryptInputs<T extends EncryptableItem[]>(inputs: [...T]): EncryptInputsBuilderUnset<[...T]>;
48
49
  /**
49
50
  * @deprecated Use `decryptForView` instead. Kept for backward compatibility.
50
51
  */
@@ -52,7 +53,8 @@ export type CofheClient<TConfig extends CofheConfig = CofheConfig> = {
52
53
  decryptForView<U extends FheTypes>(ctHash: bigint | string, utype: U): DecryptForViewBuilder<U>;
53
54
  decryptForTx(ctHash: bigint | string): DecryptForTxBuilderUnset;
54
55
  verifyDecryptResult(handle: bigint | string, cleartext: bigint, signature: Hex): Promise<boolean>;
55
- permits: CofheClientPermits;
56
+ /** ACP (Access Control Permission) management — create, share, revoke, select. */
57
+ acp: CofheClientACPs;
56
58
  };
57
59
 
58
60
  export type CofheClientConnectionState = {
@@ -67,47 +69,56 @@ export type CofheClientConnectionState = {
67
69
 
68
70
  type Listener = (snapshot: CofheClientConnectionState) => void;
69
71
 
70
- export type CofheClientPermitsClients = {
72
+ export type CofheClientACPsClients = {
71
73
  publicClient: PublicClient;
72
74
  walletClient: WalletClient;
73
75
  };
74
76
 
75
- export type CofheClientPermits = {
76
- getSnapshot: typeof permits.getSnapshot;
77
- subscribe: typeof permits.subscribe;
77
+ export type CofheClientACPs = {
78
+ getSnapshot: typeof acps.getSnapshot;
79
+ subscribe: typeof acps.subscribe;
78
80
 
79
81
  // Creation methods (require connection, no params)
80
- createSelf: (options: CreateSelfPermitOptions, clients?: CofheClientPermitsClients) => Promise<SelfPermit>;
81
- createSharing: (options: CreateSharingPermitOptions, clients?: CofheClientPermitsClients) => Promise<SharingPermit>;
82
- importShared: (
83
- options: ImportSharedPermitOptions | string,
84
- clients?: CofheClientPermitsClients
85
- ) => Promise<RecipientPermit>;
82
+ createSelf: (options: CreateSelfACPOptions, clients?: CofheClientACPsClients) => Promise<SelfACP>;
83
+ createSharing: (options: CreateSharingACPOptions, clients?: CofheClientACPsClients) => Promise<SharingACP>;
84
+ importShared: (options: ImportSharedACPOptions | string, clients?: CofheClientACPsClients) => Promise<RecipientACP>;
86
85
 
87
86
  // Retrieval methods (chainId/account optional)
88
- getPermit: (hash: string, chainId?: number, account?: string) => Permit | undefined;
89
- getPermits: (chainId?: number, account?: string) => Record<string, Permit>;
90
- getActivePermit: (chainId?: number, account?: string) => Permit | undefined;
91
- getActivePermitHash: (chainId?: number, account?: string) => string | undefined;
87
+ getACP: (hash: string, chainId?: number, account?: string) => ACP | undefined;
88
+ getACPs: (chainId?: number, account?: string) => Record<string, ACP>;
89
+ getActiveACP: (chainId?: number, account?: string) => ACP | undefined;
90
+ getActiveACPHash: (chainId?: number, account?: string) => string | undefined;
92
91
 
93
92
  // Get or create methods (get active or create new, chainId/account optional)
94
- getOrCreateSelfPermit: (chainId?: number, account?: string, options?: CreateSelfPermitOptions) => Promise<Permit>;
95
- getOrCreateSharingPermit: (
96
- options: CreateSharingPermitOptions,
97
- chainId?: number,
98
- account?: string
99
- ) => Promise<Permit>;
93
+ getOrCreateSelfACP: (chainId?: number, account?: string, options?: CreateSelfACPOptions) => Promise<ACP>;
94
+ getOrCreateSharingACP: (options: CreateSharingACPOptions, chainId?: number, account?: string) => Promise<ACP>;
100
95
 
101
96
  // Mutation methods (chainId/account optional)
102
- selectActivePermit: (hash: string, chainId?: number, account?: string) => void;
103
- removePermit: (hash: string, chainId?: number, account?: string) => void;
104
- removeActivePermit: (chainId?: number, account?: string) => void;
97
+ selectActiveACP: (hash: string, chainId?: number, account?: string) => void;
98
+ removeACP: (hash: string, chainId?: number, account?: string) => void;
99
+ removeActiveACP: (chainId?: number, account?: string) => void;
100
+
101
+ // Revocation (on-chain, require connection)
102
+ revokeACP: (acp: ACP) => Promise<`0x${string}`>;
103
+ revokeAllACPs: (revokerContract?: `0x${string}`) => Promise<`0x${string}`>;
104
+ isACPRevoked: (acp: ACP) => Promise<boolean>;
105
+
106
+ /** Post a signed sharing ACP to the on-chain share registry (ACL-served, or config `acp.sharingRegistry`). */
107
+ shareOnChain: (acp: ACP) => Promise<{ txHash: `0x${string}`; shareId: `0x${string}` }>;
108
+ /** Importable shares addressed to the connected account (unexpired, not revoked). */
109
+ getIncomingShares: () => Promise<IncomingShare[]>;
110
+ /** Import a share read from the registry: sign as recipient, store and activate. */
111
+ importFromChain: (share: IncomingShare) => Promise<RecipientACP>;
112
+ /** Recipient-side: remove a share from the registry (after import, or to decline). */
113
+ dismissShare: (shareId: `0x${string}`) => Promise<`0x${string}`>;
114
+ /** Issuer-side: retract a pending share from the registry. */
115
+ cancelShare: (shareId: `0x${string}`) => Promise<`0x${string}`>;
105
116
 
106
117
  // Utils
107
- getHash: typeof PermitUtils.getHash;
108
- export: typeof PermitUtils.export;
109
- serialize: typeof PermitUtils.serialize;
110
- deserialize: typeof PermitUtils.deserialize;
118
+ getHash: typeof ACPUtils.getHash;
119
+ export: typeof ACPUtils.export;
120
+ serialize: typeof ACPUtils.serialize;
121
+ deserialize: typeof ACPUtils.deserialize;
111
122
  };
112
123
 
113
124
  export type CofheClientParams<TConfig extends CofheConfig> = {
package/core/config.ts CHANGED
@@ -15,8 +15,8 @@ export type CofheConfig = {
15
15
  environment: 'node' | 'hardhat' | 'web' | 'react';
16
16
  /** List of supported chains */
17
17
  supportedChains: CofheChain[];
18
- /** Default permit expiration in seconds, default is 30 days */
19
- defaultPermitExpiration: number;
18
+ /** Default acp expiration in seconds, default is 30 days */
19
+ defaultACPExpiration: number;
20
20
  /**
21
21
  * Storage scheme for the fetched fhe keys
22
22
  * FHE keys are large, and caching prevents re-fetching them on each encryptInputs call
@@ -29,6 +29,32 @@ export type CofheConfig = {
29
29
  * Default: true
30
30
  */
31
31
  useWorkers: boolean;
32
+ /** ACP acp defaults, applied when creating acps (user options always win) */
33
+ acp: {
34
+ /**
35
+ * Default revoker contract per chainId — an explicit override; when unset,
36
+ * the address served by the chain's ACL (`defaultRevokerContract()`) is
37
+ * used. When available, created acps are revocable by default:
38
+ * revokerContract = this address and revokerData = acp creation
39
+ * timestamp (interpreted by the default timestamp revoker; enables
40
+ * revokeSingle + O(1) revokeAll).
41
+ */
42
+ defaultRevoker?: Record<number, `0x${string}`>;
43
+ /**
44
+ * Default contract scopes per chainId, injected into created acps'
45
+ * `contracts` when the caller doesn't provide scope options.
46
+ * Note: injecting scopes makes created acps non-global by default.
47
+ */
48
+ defaultContractScopes?: Record<number, `0x${string}`[]>;
49
+ /**
50
+ * ACPShareRegistry address per chainId — the on-chain hand-off for sharing
51
+ * ACPs. An explicit override; when unset, the address served by the
52
+ * chain's ACL (`shareRegistry()`) is used. client.acp.shareOnChain /
53
+ * getIncomingShares / importFromChain throw MissingConfig when neither
54
+ * names a registry.
55
+ */
56
+ sharingRegistry?: Record<number, `0x${string}`>;
57
+ };
32
58
  /** Mocks configs */
33
59
  mocks: {
34
60
  /**
@@ -60,8 +86,8 @@ export const CofheConfigSchema = z.object({
60
86
  environment: z.enum(['node', 'hardhat', 'web', 'react']).optional().default('node'),
61
87
  /** List of supported chain configurations */
62
88
  supportedChains: z.array(z.custom<CofheChain>()),
63
- /** Default permit expiration in seconds, default is 30 days */
64
- defaultPermitExpiration: z
89
+ /** Default acp expiration in seconds, default is 30 days */
90
+ defaultACPExpiration: z
65
91
  .number()
66
92
  .optional()
67
93
  .default(60 * 60 * 24 * 30),
@@ -82,6 +108,15 @@ export const CofheConfigSchema = z.object({
82
108
  .default(null),
83
109
  /** Whether to use Web Workers for ZK proof generation (web platform only) */
84
110
  useWorkers: z.boolean().optional().default(true),
111
+ /** ACP acp defaults */
112
+ acp: z
113
+ .object({
114
+ defaultRevoker: z.custom<Record<number, `0x${string}`>>().optional(),
115
+ defaultContractScopes: z.custom<Record<number, `0x${string}`[]>>().optional(),
116
+ sharingRegistry: z.custom<Record<number, `0x${string}`>>().optional(),
117
+ })
118
+ .optional()
119
+ .default({}),
85
120
  /** Mocks configs */
86
121
  mocks: z
87
122
  .object({
@@ -106,6 +141,28 @@ export const CofheConfigSchema = z.object({
106
141
  */
107
142
  export type CofheInputConfig = z.input<typeof CofheConfigSchema>;
108
143
 
144
+ /**
145
+ * Config keys renamed in the ACP migration, mapped to their replacements.
146
+ *
147
+ * Unknown keys are rejected rather than stripped: silently dropping `defaultPermitExpiration` would
148
+ * leave the caller on the default expiration with no error, no warning, and no compiler complaint.
149
+ */
150
+ export const RENAMED_COFHE_CONFIG_KEYS: Record<string, string> = {
151
+ defaultPermitExpiration: 'defaultACPExpiration',
152
+ };
153
+
154
+ /**
155
+ * Throws a migration-shaped error if any pre-ACP config key is present.
156
+ * @throws {Error} Naming each stale key and its replacement.
157
+ */
158
+ export function assertNoRenamedConfigKeys(config: object, renamedKeys: Record<string, string>, label: string): void {
159
+ const stale = Object.keys(config).filter((key) => key in renamedKeys);
160
+ if (stale.length === 0) return;
161
+
162
+ const renames = stale.map((key) => `\`${key}\` is now \`${renamedKeys[key]}\``).join('; ');
163
+ throw new Error(`Invalid ${label}: ${renames}. See the v0.7.0 migration guide.`);
164
+ }
165
+
109
166
  /**
110
167
  * Creates and validates a cofhe configuration (base implementation)
111
168
  * @param config - The configuration object to validate
@@ -113,7 +170,11 @@ export type CofheInputConfig = z.input<typeof CofheConfigSchema>;
113
170
  * @throws {Error} If the configuration is invalid
114
171
  */
115
172
  export function createCofheConfigBase(config: CofheInputConfig): CofheConfig {
116
- const result = CofheConfigSchema.safeParse(config);
173
+ assertNoRenamedConfigKeys(config, RENAMED_COFHE_CONFIG_KEYS, 'cofhe configuration');
174
+
175
+ // .strict(): reject unknown keys instead of stripping them - a stale or typo'd key is an error,
176
+ // not a silent no-op that leaves the caller on a default they didn't ask for.
177
+ const result = CofheConfigSchema.strict().safeParse(config);
117
178
 
118
179
  if (!result.success) {
119
180
  throw new Error(`Invalid cofhe configuration: ${z.prettifyError(result.error)}`, { cause: result.error });
package/core/debug.ts ADDED
@@ -0,0 +1,72 @@
1
+ // Neutral, opt-in debug interceptors around every low-level SDK network request.
2
+ // Purpose-agnostic: a registered handler can observe, rewrite the URL, mutate the
3
+ // request body/init, replace the response, or throw — enabling fault-injection,
4
+ // logging, latency simulation, endpoint redirection, etc. without any
5
+ // scenario-specific code in the SDK.
6
+ //
7
+ // Nothing is active unless an interceptor is registered (setCofheDebugInterceptors),
8
+ // so this is a no-op in production by default.
9
+
10
+ export interface CofheRequestContext {
11
+ /** Coarse label for the call site, e.g. 'decrypt' | 'sealoutput' | 'fetchKeys'. */
12
+ op: string;
13
+ }
14
+
15
+ export interface CofheRequestOverride {
16
+ url?: string;
17
+ init?: RequestInit;
18
+ }
19
+
20
+ export type CofheOnRequest = (
21
+ url: string,
22
+ init: RequestInit | undefined,
23
+ ctx: CofheRequestContext
24
+ ) => CofheRequestOverride | void | Promise<CofheRequestOverride | void>;
25
+
26
+ export type CofheOnResponse = (
27
+ response: Response,
28
+ ctx: CofheRequestContext
29
+ ) => Response | void | Promise<Response | void>;
30
+
31
+ export interface CofheDebugInterceptors {
32
+ onRequest?: CofheOnRequest;
33
+ onResponse?: CofheOnResponse;
34
+ }
35
+
36
+ const interceptors: CofheDebugInterceptors = {};
37
+
38
+ /** Register (or clear, with null) the debug interceptors. */
39
+ export function setCofheDebugInterceptors(next: CofheDebugInterceptors | null): void {
40
+ interceptors.onRequest = next?.onRequest;
41
+ interceptors.onResponse = next?.onResponse;
42
+ }
43
+
44
+ export function getCofheDebugInterceptors(): CofheDebugInterceptors {
45
+ return interceptors;
46
+ }
47
+
48
+ /**
49
+ * fetch() wrapper used by all low-level SDK network ops. Applies the registered
50
+ * debug interceptors when present; otherwise behaves exactly like fetch().
51
+ */
52
+ export async function cofheFetch(
53
+ url: string,
54
+ init?: RequestInit,
55
+ ctx: CofheRequestContext = { op: 'request' }
56
+ ): Promise<Response> {
57
+ let finalUrl = url;
58
+ let finalInit = init;
59
+ if (interceptors.onRequest) {
60
+ const override = await interceptors.onRequest(finalUrl, finalInit, ctx);
61
+ if (override) {
62
+ if (override.url !== undefined) finalUrl = override.url;
63
+ if (override.init !== undefined) finalInit = override.init;
64
+ }
65
+ }
66
+ let response = await fetch(finalUrl, finalInit);
67
+ if (interceptors.onResponse) {
68
+ const replaced = await interceptors.onResponse(response, ctx);
69
+ if (replaced) response = replaced;
70
+ }
71
+ return response;
72
+ }