@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.
- package/CHANGELOG.md +49 -0
- package/acps/acp.ts +411 -0
- package/acps/index.ts +70 -0
- package/{permits → acps}/onchain-utils.ts +49 -24
- package/acps/sealing.ts +90 -0
- package/acps/signature.ts +89 -0
- package/acps/store.ts +172 -0
- package/acps/test/acp.test.ts +615 -0
- package/acps/test/localstorage.test.ts +105 -0
- package/acps/test/sealing.test.ts +77 -0
- package/acps/test/store.test.ts +88 -0
- package/acps/test/validation.test.ts +361 -0
- package/acps/test-utils.ts +32 -0
- package/acps/types.ts +252 -0
- package/acps/validation.ts +392 -0
- package/adapters/test/ethers5.test.ts +4 -1
- package/adapters/test/ethers6.test.ts +4 -1
- package/adapters/test/wagmi.test.ts +5 -2
- package/chains/chains/stagingCofhe.ts +21 -0
- package/chains/index.ts +3 -1
- package/chains/test/chains.test.ts +2 -1
- package/core/acps.ts +625 -0
- package/core/client.ts +136 -39
- package/core/clientTypes.ts +52 -41
- package/core/config.ts +66 -5
- package/core/debug.ts +72 -0
- package/core/decrypt/MockThresholdNetworkAbi.ts +20 -11
- package/core/decrypt/apiError.ts +104 -0
- package/core/decrypt/cofheMocksDecryptForTx.ts +11 -11
- package/core/decrypt/cofheMocksDecryptForView.ts +7 -7
- package/core/decrypt/decryptForTxBuilder.ts +102 -102
- package/core/decrypt/decryptForViewBuilder.ts +90 -90
- package/core/decrypt/submitRetry.ts +38 -30
- package/core/decrypt/tnDecryptV1.ts +5 -5
- package/core/decrypt/tnDecryptV2.ts +45 -34
- package/core/decrypt/tnSealOutputV1.ts +4 -3
- package/core/decrypt/tnSealOutputV2.ts +44 -31
- package/core/encrypt/cofheMocksZkVerifySign.ts +59 -74
- package/core/encrypt/encryptInputsBuilder.ts +86 -52
- package/core/encrypt/zkPackProveVerify.ts +25 -18
- package/core/error.ts +34 -6
- package/core/index.ts +14 -14
- package/core/test/acpDefaults.test.ts +52 -0
- package/core/test/acps.test.ts +596 -0
- package/core/test/apiError.test.ts +130 -0
- package/core/test/client.test.ts +22 -19
- package/core/test/config.test.ts +25 -5
- package/core/test/decrypt.test.ts +40 -35
- package/core/test/decryptBuilders.test.ts +68 -68
- package/core/test/decryptErrorCodes.test.ts +217 -0
- package/core/test/encryptInputsBuilder.test.ts +72 -41
- package/core/test/pollCallbacks.test.ts +89 -18
- package/core/test/stagingRedirect.ts +18 -0
- package/core/test/submitRetry.test.ts +182 -0
- package/core/types.ts +9 -69
- package/dist/acp-Wi6isVQI.d.cts +407 -0
- package/dist/acp-Wi6isVQI.d.ts +407 -0
- package/dist/acps.cjs +1081 -0
- package/dist/acps.d.cts +480 -0
- package/dist/acps.d.ts +480 -0
- package/dist/acps.js +2 -0
- package/dist/chains.cjs +14 -1
- package/dist/chains.d.cts +30 -1
- package/dist/chains.d.ts +30 -1
- package/dist/chains.js +1 -1
- package/dist/{chunk-PE5V5CCV.js → chunk-43USWPEH.js} +1000 -604
- package/dist/{chunk-MTRAXQXC.js → chunk-N6IDQRRU.js} +14 -2
- package/dist/chunk-Q7CBWGQX.js +1029 -0
- package/dist/{clientTypes-CyUvRRzA.d.ts → clientTypes-BN3nbzYM.d.ts} +342 -165
- package/dist/{clientTypes-BDy1qIBu.d.cts → clientTypes-CYZjFznO.d.cts} +342 -165
- package/dist/core.cjs +1431 -1026
- package/dist/core.d.cts +55 -10
- package/dist/core.d.ts +55 -10
- package/dist/core.js +3 -3
- package/dist/node.cjs +1356 -976
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +3 -3
- package/dist/web.cjs +1356 -976
- package/dist/web.d.cts +2 -2
- package/dist/web.d.ts +2 -2
- package/dist/web.js +3 -3
- package/node/test/inherited.test.ts +75 -65
- package/node/test/tfheinit.test.ts +23 -8
- package/package.json +6 -6
- package/web/test/client.web.test.ts +5 -1
- package/web/test/inherited.web.test.ts +75 -65
- package/web/test/tfheinit.web.test.ts +14 -5
- package/web/test/worker.config.web.test.ts +38 -23
- package/web/test/worker.output.web.test.ts +25 -24
- package/core/encrypt/encryptUtils.ts +0 -67
- package/core/permits.ts +0 -206
- package/core/test/permits.test.ts +0 -534
- package/dist/chunk-VB62WYPL.js +0 -978
- package/dist/permit-DnVMDT5h.d.cts +0 -376
- package/dist/permit-DnVMDT5h.d.ts +0 -376
- package/dist/permits.cjs +0 -1026
- package/dist/permits.d.cts +0 -353
- package/dist/permits.d.ts +0 -353
- package/dist/permits.js +0 -2
- package/permits/index.ts +0 -68
- package/permits/permit.ts +0 -385
- package/permits/sealing.ts +0 -131
- package/permits/signature.ts +0 -79
- package/permits/store.ts +0 -157
- package/permits/test/localstorage.test.ts +0 -113
- package/permits/test/permit.test.ts +0 -557
- package/permits/test/sealing.test.ts +0 -84
- package/permits/test/store.test.ts +0 -88
- package/permits/test/validation.test.ts +0 -361
- package/permits/test-utils.ts +0 -28
- package/permits/types.ts +0 -204
- package/permits/validation.ts +0 -327
- /package/{permits → acps}/utils.ts +0 -0
package/core/client.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type {
|
|
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 {
|
|
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,
|
|
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
|
-
//
|
|
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]):
|
|
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
|
-
//
|
|
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
|
|
228
|
+
const clientACPs: CofheClientACPs = {
|
|
213
229
|
// Pass through store access
|
|
214
|
-
getSnapshot:
|
|
215
|
-
subscribe:
|
|
230
|
+
getSnapshot: acps.getSnapshot,
|
|
231
|
+
subscribe: acps.subscribe,
|
|
216
232
|
|
|
217
233
|
// Creation methods (require connection)
|
|
218
234
|
createSelf: async (
|
|
219
|
-
options:
|
|
235
|
+
options: CreateSelfACPOptions,
|
|
220
236
|
clients?: { publicClient: PublicClient; walletClient: WalletClient }
|
|
221
237
|
) => {
|
|
222
238
|
_requireConnected();
|
|
223
239
|
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
224
|
-
|
|
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:
|
|
249
|
+
options: CreateSharingACPOptions,
|
|
229
250
|
clients?: { publicClient: PublicClient; walletClient: WalletClient }
|
|
230
251
|
) => {
|
|
231
252
|
_requireConnected();
|
|
232
253
|
const { publicClient, walletClient } = clients ?? connectStore.getState();
|
|
233
|
-
|
|
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:
|
|
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
|
|
268
|
+
return acps.importShared(options, publicClient!, walletClient!);
|
|
243
269
|
},
|
|
244
270
|
|
|
245
271
|
// Get or create methods (require connection)
|
|
246
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
358
|
+
getACP: (hash: string, chainId?: number, account?: string) => {
|
|
262
359
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
263
|
-
return
|
|
360
|
+
return acps.getACP(_chainId, _account, hash);
|
|
264
361
|
},
|
|
265
362
|
|
|
266
|
-
|
|
363
|
+
getACPs: (chainId?: number, account?: string) => {
|
|
267
364
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
268
|
-
return
|
|
365
|
+
return acps.getACPs(_chainId, _account);
|
|
269
366
|
},
|
|
270
367
|
|
|
271
|
-
|
|
368
|
+
getActiveACP: (chainId?: number, account?: string) => {
|
|
272
369
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
273
|
-
return
|
|
370
|
+
return acps.getActiveACP(_chainId, _account);
|
|
274
371
|
},
|
|
275
372
|
|
|
276
|
-
|
|
373
|
+
getActiveACPHash: (chainId?: number, account?: string) => {
|
|
277
374
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
278
|
-
return
|
|
375
|
+
return acps.getActiveACPHash(_chainId, _account);
|
|
279
376
|
},
|
|
280
377
|
|
|
281
378
|
// Mutation methods (auto-fill chainId/account)
|
|
282
|
-
|
|
379
|
+
selectActiveACP: (hash: string, chainId?: number, account?: string) => {
|
|
283
380
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
284
|
-
return
|
|
381
|
+
return acps.selectActiveACP(_chainId, _account, hash);
|
|
285
382
|
},
|
|
286
383
|
|
|
287
|
-
|
|
384
|
+
removeACP: async (hash: string, chainId?: number, account?: string) => {
|
|
288
385
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
289
|
-
return
|
|
386
|
+
return acps.removeACP(_chainId, _account, hash);
|
|
290
387
|
},
|
|
291
388
|
|
|
292
|
-
|
|
389
|
+
removeActiveACP: async (chainId?: number, account?: string) => {
|
|
293
390
|
const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
|
|
294
|
-
return
|
|
391
|
+
return acps.removeActiveACP(_chainId, _account);
|
|
295
392
|
},
|
|
296
393
|
|
|
297
394
|
// Utils (no context needed)
|
|
298
|
-
getHash:
|
|
299
|
-
export:
|
|
300
|
-
serialize:
|
|
301
|
-
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
|
-
|
|
430
|
+
acp: clientACPs,
|
|
334
431
|
|
|
335
432
|
// Add SDK-specific methods below that require connection
|
|
336
433
|
// Example:
|
package/core/clientTypes.ts
CHANGED
|
@@ -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
|
|
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 {
|
|
9
|
+
import { acps } from './acps.js';
|
|
10
10
|
import type { EncryptableItem, FheTypes, TfheInitializer } from './types.js';
|
|
11
|
-
import type {
|
|
11
|
+
import type { ACPUtils } from 'acps/acp.js';
|
|
12
12
|
import type {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
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]):
|
|
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
|
-
|
|
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
|
|
72
|
+
export type CofheClientACPsClients = {
|
|
71
73
|
publicClient: PublicClient;
|
|
72
74
|
walletClient: WalletClient;
|
|
73
75
|
};
|
|
74
76
|
|
|
75
|
-
export type
|
|
76
|
-
getSnapshot: typeof
|
|
77
|
-
subscribe: typeof
|
|
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:
|
|
81
|
-
createSharing: (options:
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
108
|
-
export: typeof
|
|
109
|
-
serialize: typeof
|
|
110
|
-
deserialize: typeof
|
|
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
|
|
19
|
-
|
|
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
|
|
64
|
-
|
|
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
|
-
|
|
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
|
+
}
|