@cofhe/sdk 0.1.0 → 0.2.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 (121) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/adapters/ethers5.test.ts +174 -0
  3. package/adapters/ethers5.ts +36 -0
  4. package/adapters/ethers6.test.ts +169 -0
  5. package/adapters/ethers6.ts +36 -0
  6. package/adapters/hardhat-node.ts +167 -0
  7. package/adapters/hardhat.hh2.test.ts +159 -0
  8. package/adapters/hardhat.ts +36 -0
  9. package/adapters/index.test.ts +20 -0
  10. package/adapters/index.ts +5 -0
  11. package/adapters/smartWallet.ts +99 -0
  12. package/adapters/test-utils.ts +53 -0
  13. package/adapters/types.ts +6 -0
  14. package/adapters/wagmi.test.ts +156 -0
  15. package/adapters/wagmi.ts +17 -0
  16. package/chains/chains/arbSepolia.ts +14 -0
  17. package/chains/chains/baseSepolia.ts +14 -0
  18. package/chains/chains/hardhat.ts +15 -0
  19. package/chains/chains/localcofhe.ts +14 -0
  20. package/chains/chains/sepolia.ts +14 -0
  21. package/chains/chains.test.ts +50 -0
  22. package/chains/defineChain.ts +18 -0
  23. package/chains/index.ts +35 -0
  24. package/chains/types.ts +32 -0
  25. package/core/baseBuilder.ts +119 -0
  26. package/core/client.test.ts +315 -0
  27. package/core/client.ts +292 -0
  28. package/core/clientTypes.ts +108 -0
  29. package/core/config.test.ts +235 -0
  30. package/core/config.ts +220 -0
  31. package/core/decrypt/MockQueryDecrypterAbi.ts +129 -0
  32. package/core/decrypt/cofheMocksSealOutput.ts +57 -0
  33. package/core/decrypt/decryptHandleBuilder.ts +287 -0
  34. package/core/decrypt/decryptUtils.ts +28 -0
  35. package/core/decrypt/tnSealOutputV1.ts +59 -0
  36. package/core/decrypt/tnSealOutputV2.ts +298 -0
  37. package/core/encrypt/MockZkVerifierAbi.ts +106 -0
  38. package/core/encrypt/cofheMocksZkVerifySign.ts +284 -0
  39. package/core/encrypt/encryptInputsBuilder.test.ts +751 -0
  40. package/core/encrypt/encryptInputsBuilder.ts +560 -0
  41. package/core/encrypt/encryptUtils.ts +67 -0
  42. package/core/encrypt/zkPackProveVerify.ts +335 -0
  43. package/core/error.ts +168 -0
  44. package/core/fetchKeys.test.ts +195 -0
  45. package/core/fetchKeys.ts +144 -0
  46. package/core/index.ts +89 -0
  47. package/core/keyStore.test.ts +226 -0
  48. package/core/keyStore.ts +154 -0
  49. package/core/permits.test.ts +494 -0
  50. package/core/permits.ts +200 -0
  51. package/core/types.ts +398 -0
  52. package/core/utils.ts +130 -0
  53. package/dist/adapters.cjs +88 -0
  54. package/dist/adapters.d.cts +14576 -0
  55. package/dist/adapters.d.ts +14576 -0
  56. package/dist/adapters.js +83 -0
  57. package/dist/chains.cjs +114 -0
  58. package/dist/chains.d.cts +121 -0
  59. package/dist/chains.d.ts +121 -0
  60. package/dist/chains.js +1 -0
  61. package/dist/chunk-UGBVZNRT.js +818 -0
  62. package/dist/chunk-WEAZ25JO.js +105 -0
  63. package/dist/chunk-WGCRJCBR.js +2523 -0
  64. package/dist/clientTypes-5_1nwtUe.d.cts +914 -0
  65. package/dist/clientTypes-Es7fyi65.d.ts +914 -0
  66. package/dist/core.cjs +3414 -0
  67. package/dist/core.d.cts +111 -0
  68. package/dist/core.d.ts +111 -0
  69. package/dist/core.js +3 -0
  70. package/dist/node.cjs +3286 -0
  71. package/dist/node.d.cts +22 -0
  72. package/dist/node.d.ts +22 -0
  73. package/dist/node.js +91 -0
  74. package/dist/permit-fUSe6KKq.d.cts +349 -0
  75. package/dist/permit-fUSe6KKq.d.ts +349 -0
  76. package/dist/permits.cjs +871 -0
  77. package/dist/permits.d.cts +1045 -0
  78. package/dist/permits.d.ts +1045 -0
  79. package/dist/permits.js +1 -0
  80. package/dist/types-KImPrEIe.d.cts +48 -0
  81. package/dist/types-KImPrEIe.d.ts +48 -0
  82. package/dist/web.cjs +3478 -0
  83. package/dist/web.d.cts +38 -0
  84. package/dist/web.d.ts +38 -0
  85. package/dist/web.js +240 -0
  86. package/dist/zkProve.worker.cjs +93 -0
  87. package/dist/zkProve.worker.d.cts +2 -0
  88. package/dist/zkProve.worker.d.ts +2 -0
  89. package/dist/zkProve.worker.js +91 -0
  90. package/node/client.test.ts +147 -0
  91. package/node/config.test.ts +68 -0
  92. package/node/encryptInputs.test.ts +155 -0
  93. package/node/index.ts +97 -0
  94. package/node/storage.ts +51 -0
  95. package/package.json +27 -15
  96. package/permits/index.ts +68 -0
  97. package/permits/localstorage.test.ts +117 -0
  98. package/permits/permit.test.ts +477 -0
  99. package/permits/permit.ts +405 -0
  100. package/permits/sealing.test.ts +84 -0
  101. package/permits/sealing.ts +131 -0
  102. package/permits/signature.ts +79 -0
  103. package/permits/store.test.ts +128 -0
  104. package/permits/store.ts +166 -0
  105. package/permits/test-utils.ts +20 -0
  106. package/permits/types.ts +191 -0
  107. package/permits/utils.ts +62 -0
  108. package/permits/validation.test.ts +288 -0
  109. package/permits/validation.ts +369 -0
  110. package/web/client.web.test.ts +147 -0
  111. package/web/config.web.test.ts +69 -0
  112. package/web/encryptInputs.web.test.ts +172 -0
  113. package/web/index.ts +161 -0
  114. package/web/storage.ts +34 -0
  115. package/web/worker.builder.web.test.ts +148 -0
  116. package/web/worker.config.web.test.ts +329 -0
  117. package/web/worker.output.web.test.ts +84 -0
  118. package/web/workerManager.test.ts +80 -0
  119. package/web/workerManager.ts +214 -0
  120. package/web/workerManager.web.test.ts +114 -0
  121. package/web/zkProve.worker.ts +133 -0
package/core/client.ts ADDED
@@ -0,0 +1,292 @@
1
+ import type { CreateSelfPermitOptions, CreateSharingPermitOptions, ImportSharedPermitOptions } from '@/permits';
2
+
3
+ import { createStore } from 'zustand/vanilla';
4
+ import { type PublicClient, type WalletClient } from 'viem';
5
+ import { CofhesdkError, CofhesdkErrorCode } from './error.js';
6
+ import { EncryptInputsBuilder } from './encrypt/encryptInputsBuilder.js';
7
+ import { createKeysStore } from './keyStore.js';
8
+ import { permits } from './permits.js';
9
+ import { DecryptHandlesBuilder } from './decrypt/decryptHandleBuilder.js';
10
+ import { getPublicClientChainID, getWalletClientAccount } from './utils.js';
11
+ import type {
12
+ CofhesdkClientConnectionState,
13
+ CofhesdkClientParams,
14
+ CofhesdkClient,
15
+ CofhesdkClientPermits,
16
+ } from './clientTypes.js';
17
+ import type { EncryptableItem, FheTypes } from './types.js';
18
+ import type { CofhesdkConfig } from './config.js';
19
+
20
+ export const InitialConnectStore: CofhesdkClientConnectionState = {
21
+ connected: false,
22
+ connecting: false,
23
+ connectError: undefined,
24
+ chainId: undefined,
25
+ account: undefined,
26
+ publicClient: undefined,
27
+ walletClient: undefined,
28
+ };
29
+
30
+ /**
31
+ * Creates a CoFHE SDK client instance (base implementation)
32
+ * @param {CofhesdkClientParams} opts - Initialization options including config and platform-specific serializers
33
+ * @returns {CofhesdkClient} - The CoFHE SDK client instance
34
+ */
35
+ export function createCofhesdkClientBase<TConfig extends CofhesdkConfig>(
36
+ opts: CofhesdkClientParams<TConfig>
37
+ ): CofhesdkClient<TConfig> {
38
+ // Create keysStorage instance using configured storage
39
+ const keysStorage = createKeysStore(opts.config.fheKeyStorage);
40
+
41
+ // Zustand store for reactive state management
42
+
43
+ const connectStore = createStore<CofhesdkClientConnectionState>(() => InitialConnectStore);
44
+
45
+ // Helper to update state
46
+ const updateConnectState = (partial: Partial<CofhesdkClientConnectionState>) => {
47
+ connectStore.setState((state) => ({ ...state, ...partial }));
48
+ };
49
+
50
+ // Called before any operation, throws of connection not yet established
51
+ const _requireConnected = () => {
52
+ const state = connectStore.getState();
53
+ const notConnected =
54
+ !state.connected || !state.account || !state.chainId || !state.publicClient || !state.walletClient;
55
+ if (notConnected) {
56
+ throw new CofhesdkError({
57
+ code: CofhesdkErrorCode.NotConnected,
58
+ message: 'Client must be connected, account and chainId must be initialized',
59
+ hint: 'Ensure client.connect() has been called and awaited.',
60
+ context: {
61
+ connected: state.connected,
62
+ account: state.account,
63
+ chainId: state.chainId,
64
+ publicClient: state.publicClient,
65
+ walletClient: state.walletClient,
66
+ },
67
+ });
68
+ }
69
+ };
70
+
71
+ // LIFECYCLE
72
+
73
+ async function connect(publicClient: PublicClient, walletClient: WalletClient) {
74
+ const state = connectStore.getState();
75
+
76
+ // Exit if already connected and clients are the same
77
+ if (state.connected && state.publicClient === publicClient && state.walletClient === walletClient) return;
78
+
79
+ // Set connecting state
80
+ updateConnectState({
81
+ ...InitialConnectStore,
82
+ connecting: true,
83
+ });
84
+
85
+ // Fetch chainId and account
86
+ try {
87
+ const chainId = await getPublicClientChainID(publicClient);
88
+ const account = await getWalletClientAccount(walletClient);
89
+ updateConnectState({
90
+ connected: true,
91
+ connecting: false,
92
+ connectError: undefined,
93
+ chainId,
94
+ account,
95
+ publicClient,
96
+ walletClient,
97
+ });
98
+ } catch (e) {
99
+ updateConnectState({
100
+ ...InitialConnectStore,
101
+ connectError: e,
102
+ });
103
+ throw e;
104
+ }
105
+ }
106
+
107
+ // CLIENT OPERATIONS
108
+
109
+ function encryptInputs<T extends EncryptableItem[]>(inputs: [...T]): EncryptInputsBuilder<[...T]> {
110
+ const state = connectStore.getState();
111
+
112
+ return new EncryptInputsBuilder({
113
+ inputs,
114
+ account: state.account ?? undefined,
115
+ chainId: state.chainId ?? undefined,
116
+
117
+ config: opts.config,
118
+ publicClient: state.publicClient ?? undefined,
119
+ walletClient: state.walletClient ?? undefined,
120
+ zkvWalletClient: opts.config._internal?.zkvWalletClient,
121
+
122
+ tfhePublicKeyDeserializer: opts.tfhePublicKeyDeserializer,
123
+ compactPkeCrsDeserializer: opts.compactPkeCrsDeserializer,
124
+ zkBuilderAndCrsGenerator: opts.zkBuilderAndCrsGenerator,
125
+ initTfhe: opts.initTfhe,
126
+ zkProveWorkerFn: opts.zkProveWorkerFn,
127
+
128
+ keysStorage,
129
+
130
+ requireConnected: _requireConnected,
131
+ });
132
+ }
133
+
134
+ function decryptHandle<U extends FheTypes>(ctHash: bigint, utype: U): DecryptHandlesBuilder<U> {
135
+ const state = connectStore.getState();
136
+
137
+ return new DecryptHandlesBuilder({
138
+ ctHash,
139
+ utype,
140
+ chainId: state.chainId ?? undefined,
141
+ account: state.account ?? undefined,
142
+
143
+ config: opts.config,
144
+ publicClient: state.publicClient ?? undefined,
145
+ walletClient: state.walletClient ?? undefined,
146
+
147
+ requireConnected: _requireConnected,
148
+ });
149
+ }
150
+
151
+ // PERMITS - Context-aware wrapper
152
+
153
+ const _getChainIdAndAccount = (chainId?: number, account?: string) => {
154
+ const state = connectStore.getState();
155
+ const _chainId = chainId ?? state.chainId;
156
+ const _account = account ?? state.account;
157
+
158
+ if (_chainId == null || _account == null) {
159
+ throw new CofhesdkError({
160
+ code: CofhesdkErrorCode.NotConnected,
161
+ message: 'ChainId or account not available.',
162
+ hint: 'Ensure client.connect() has been called, or provide chainId and account explicitly.',
163
+ context: {
164
+ chainId: _chainId,
165
+ account: _account,
166
+ },
167
+ });
168
+ }
169
+
170
+ return { chainId: _chainId, account: _account };
171
+ };
172
+
173
+ const clientPermits: CofhesdkClientPermits = {
174
+ // Pass through store access
175
+ getSnapshot: permits.getSnapshot,
176
+ subscribe: permits.subscribe,
177
+
178
+ // Creation methods (require connection)
179
+ createSelf: async (
180
+ options: CreateSelfPermitOptions,
181
+ clients?: { publicClient: PublicClient; walletClient: WalletClient }
182
+ ) => {
183
+ _requireConnected();
184
+ const { publicClient, walletClient } = clients ?? connectStore.getState();
185
+ return permits.createSelf(options, publicClient!, walletClient!);
186
+ },
187
+
188
+ createSharing: async (
189
+ options: CreateSharingPermitOptions,
190
+ clients?: { publicClient: PublicClient; walletClient: WalletClient }
191
+ ) => {
192
+ _requireConnected();
193
+ const { publicClient, walletClient } = clients ?? connectStore.getState();
194
+ return permits.createSharing(options, publicClient!, walletClient!);
195
+ },
196
+
197
+ importShared: async (
198
+ options: ImportSharedPermitOptions | string,
199
+ clients?: { publicClient: PublicClient; walletClient: WalletClient }
200
+ ) => {
201
+ _requireConnected();
202
+ const { publicClient, walletClient } = clients ?? connectStore.getState();
203
+ return permits.importShared(options, publicClient!, walletClient!);
204
+ },
205
+
206
+ // Get or create methods (require connection)
207
+ getOrCreateSelfPermit: async (chainId?: number, account?: string, options?: CreateSelfPermitOptions) => {
208
+ _requireConnected();
209
+ const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
210
+ const { publicClient, walletClient } = connectStore.getState();
211
+ return permits.getOrCreateSelfPermit(publicClient!, walletClient!, _chainId, _account, options);
212
+ },
213
+
214
+ getOrCreateSharingPermit: async (options: CreateSharingPermitOptions, chainId?: number, account?: string) => {
215
+ _requireConnected();
216
+ const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
217
+ const { publicClient, walletClient } = connectStore.getState();
218
+ return permits.getOrCreateSharingPermit(publicClient!, walletClient!, options, _chainId, _account);
219
+ },
220
+
221
+ // Retrieval methods (auto-fill chainId/account)
222
+ getPermit: async (hash: string, chainId?: number, account?: string) => {
223
+ const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
224
+ return permits.getPermit(_chainId, _account, hash);
225
+ },
226
+
227
+ getPermits: async (chainId?: number, account?: string) => {
228
+ const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
229
+ return permits.getPermits(_chainId, _account);
230
+ },
231
+
232
+ getActivePermit: async (chainId?: number, account?: string) => {
233
+ const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
234
+ return permits.getActivePermit(_chainId, _account);
235
+ },
236
+
237
+ getActivePermitHash: async (chainId?: number, account?: string) => {
238
+ const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
239
+ return permits.getActivePermitHash(_chainId, _account);
240
+ },
241
+
242
+ // Mutation methods (auto-fill chainId/account)
243
+ selectActivePermit: (hash: string, chainId?: number, account?: string) => {
244
+ const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
245
+ return permits.selectActivePermit(_chainId, _account, hash);
246
+ },
247
+
248
+ removePermit: async (hash: string, chainId?: number, account?: string, force?: boolean) => {
249
+ const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
250
+ return permits.removePermit(_chainId, _account, hash, force);
251
+ },
252
+
253
+ removeActivePermit: async (chainId?: number, account?: string) => {
254
+ const { chainId: _chainId, account: _account } = _getChainIdAndAccount(chainId, account);
255
+ return permits.removeActivePermit(_chainId, _account);
256
+ },
257
+
258
+ // Utils (no context needed)
259
+ getHash: permits.getHash,
260
+ serialize: permits.serialize,
261
+ deserialize: permits.deserialize,
262
+ };
263
+
264
+ return {
265
+ // Zustand reactive accessors (don't export store directly to prevent mutation)
266
+ getSnapshot: connectStore.getState,
267
+ subscribe: connectStore.subscribe,
268
+
269
+ // flags (read-only: reflect snapshot)
270
+ get connected() {
271
+ return connectStore.getState().connected;
272
+ },
273
+ get connecting() {
274
+ return connectStore.getState().connecting;
275
+ },
276
+
277
+ // config & platform-specific (read-only)
278
+ config: opts.config,
279
+
280
+ connect,
281
+ encryptInputs,
282
+ decryptHandle,
283
+ permits: clientPermits,
284
+
285
+ // Add SDK-specific methods below that require connection
286
+ // Example:
287
+ // async encryptData(data: unknown) {
288
+ // requireConnected();
289
+ // // Use state.publicClient and state.walletClient for implementation
290
+ // },
291
+ };
292
+ }
@@ -0,0 +1,108 @@
1
+ // TODO: Extract client types to its own file, keep this one as primitives
2
+ import { type PublicClient, type WalletClient } from 'viem';
3
+ import { type CofhesdkConfig, type CofhesdkEnvironment } from './config.js';
4
+ import { type DecryptHandlesBuilder } from './decrypt/decryptHandleBuilder.js';
5
+ import { type EncryptInputsBuilder } from './encrypt/encryptInputsBuilder.js';
6
+ import { type ZkBuilderAndCrsGenerator, type ZkProveWorkerFunction } from './encrypt/zkPackProveVerify.js';
7
+ import { type FheKeyDeserializer } from './fetchKeys.js';
8
+ import { permits } from './permits.js';
9
+ import type { EncryptableItem, FheTypes, TfheInitializer } from './types.js';
10
+ import type { PermitUtils } from 'permits/permit.js';
11
+ import type {
12
+ CreateSelfPermitOptions,
13
+ Permit,
14
+ CreateSharingPermitOptions,
15
+ ImportSharedPermitOptions,
16
+ SharingPermit,
17
+ RecipientPermit,
18
+ SelfPermit,
19
+ } from 'permits/types.js';
20
+
21
+ // CLIENT
22
+
23
+ export type CofhesdkClient<TConfig extends CofhesdkConfig = CofhesdkConfig> = {
24
+ // --- state access ---
25
+ getSnapshot(): CofhesdkClientConnectionState;
26
+ subscribe(listener: Listener): () => void;
27
+
28
+ // --- convenience flags (read-only) ---
29
+ readonly connected: boolean;
30
+ readonly connecting: boolean;
31
+
32
+ // --- config & platform-specific ---
33
+ readonly config: TConfig;
34
+
35
+ connect(publicClient: PublicClient, walletClient: WalletClient): Promise<void>;
36
+ /**
37
+ * Types docstring
38
+ */
39
+ encryptInputs<T extends EncryptableItem[]>(inputs: [...T]): EncryptInputsBuilder<[...T]>;
40
+ decryptHandle<U extends FheTypes>(ctHash: bigint, utype: U): DecryptHandlesBuilder<U>;
41
+ permits: CofhesdkClientPermits;
42
+ };
43
+
44
+ export type CofhesdkClientConnectionState = {
45
+ connected: boolean;
46
+ connecting: boolean;
47
+ connectError: unknown | undefined;
48
+ chainId: number | undefined;
49
+ account: `0x${string}` | undefined;
50
+ publicClient: PublicClient | undefined;
51
+ walletClient: WalletClient | undefined;
52
+ };
53
+
54
+ type Listener = (snapshot: CofhesdkClientConnectionState) => void;
55
+
56
+ export type CofhesdkClientPermitsClients = {
57
+ publicClient: PublicClient;
58
+ walletClient: WalletClient;
59
+ };
60
+
61
+ export type CofhesdkClientPermits = {
62
+ getSnapshot: typeof permits.getSnapshot;
63
+ subscribe: typeof permits.subscribe;
64
+
65
+ // Creation methods (require connection, no params)
66
+ createSelf: (options: CreateSelfPermitOptions, clients?: CofhesdkClientPermitsClients) => Promise<SelfPermit>;
67
+ createSharing: (
68
+ options: CreateSharingPermitOptions,
69
+ clients?: CofhesdkClientPermitsClients
70
+ ) => Promise<SharingPermit>;
71
+ importShared: (
72
+ options: ImportSharedPermitOptions | string,
73
+ clients?: CofhesdkClientPermitsClients
74
+ ) => Promise<RecipientPermit>;
75
+
76
+ // Retrieval methods (chainId/account optional)
77
+ getPermit: (hash: string, chainId?: number, account?: string) => Promise<Permit | undefined>;
78
+ getPermits: (chainId?: number, account?: string) => Promise<Record<string, Permit>>;
79
+ getActivePermit: (chainId?: number, account?: string) => Promise<Permit | undefined>;
80
+ getActivePermitHash: (chainId?: number, account?: string) => Promise<string | undefined>;
81
+
82
+ // Get or create methods (get active or create new, chainId/account optional)
83
+ getOrCreateSelfPermit: (chainId?: number, account?: string, options?: CreateSelfPermitOptions) => Promise<Permit>;
84
+ getOrCreateSharingPermit: (
85
+ options: CreateSharingPermitOptions,
86
+ chainId?: number,
87
+ account?: string
88
+ ) => Promise<Permit>;
89
+
90
+ // Mutation methods (chainId/account optional)
91
+ selectActivePermit: (hash: string, chainId?: number, account?: string) => void;
92
+ removePermit: (hash: string, chainId?: number, account?: string, force?: boolean) => void;
93
+ removeActivePermit: (chainId?: number, account?: string) => void;
94
+
95
+ // Utils
96
+ getHash: typeof PermitUtils.getHash;
97
+ serialize: typeof PermitUtils.serialize;
98
+ deserialize: typeof PermitUtils.deserialize;
99
+ };
100
+
101
+ export type CofhesdkClientParams<TConfig extends CofhesdkConfig> = {
102
+ config: TConfig;
103
+ zkBuilderAndCrsGenerator: ZkBuilderAndCrsGenerator;
104
+ tfhePublicKeyDeserializer: FheKeyDeserializer;
105
+ compactPkeCrsDeserializer: FheKeyDeserializer;
106
+ initTfhe: TfheInitializer;
107
+ zkProveWorkerFn?: ZkProveWorkerFunction;
108
+ };
@@ -0,0 +1,235 @@
1
+ import { sepolia, hardhat } from '@/chains';
2
+
3
+ import { describe, it, expect, vi } from 'vitest';
4
+ import {
5
+ createCofhesdkConfigBase,
6
+ getCofhesdkConfigItem,
7
+ type CofhesdkInputConfig,
8
+ getSupportedChainOrThrow,
9
+ getCoFheUrlOrThrow,
10
+ getZkVerifierUrlOrThrow,
11
+ getThresholdNetworkUrlOrThrow,
12
+ } from './config.js';
13
+
14
+ describe('createCofhesdkConfigBase', () => {
15
+ const validBaseConfig: CofhesdkInputConfig = {
16
+ supportedChains: [],
17
+ };
18
+
19
+ const setNestedValue = (obj: any, path: string, value: any): void => {
20
+ const keys = path.split('.');
21
+ const lastKey = keys.pop()!;
22
+ const target = keys.reduce((acc, key) => {
23
+ if (!acc[key]) acc[key] = {};
24
+ return acc[key];
25
+ }, obj);
26
+ target[lastKey] = value;
27
+ };
28
+
29
+ const getNestedValue = (obj: any, path: string): any => {
30
+ return path.split('.').reduce((acc, key) => acc?.[key], obj);
31
+ };
32
+
33
+ const expectInvalidConfigItem = (path: string, value: any, log = false): void => {
34
+ const config = { ...validBaseConfig };
35
+ setNestedValue(config, path, value);
36
+ if (log) {
37
+ console.log('expect config invalid', path, value, config);
38
+ try {
39
+ createCofhesdkConfigBase(config as CofhesdkInputConfig);
40
+ } catch (e) {
41
+ console.log('expect config invalid', path, value, config, e);
42
+ }
43
+ }
44
+ expect(() => createCofhesdkConfigBase(config as CofhesdkInputConfig)).toThrow('Invalid cofhesdk configuration:');
45
+ };
46
+
47
+ const expectValidConfigItem = (path: string, value: any, expectedValue: any): void => {
48
+ const config = { ...validBaseConfig };
49
+ setNestedValue(config, path, value);
50
+ const result = createCofhesdkConfigBase(config);
51
+ expect(getNestedValue(result, path)).toEqual(expectedValue);
52
+ };
53
+
54
+ it('environment', () => {
55
+ expectInvalidConfigItem('environment', 'not-a-valid-environment');
56
+ expectInvalidConfigItem('environment', 123);
57
+ expectInvalidConfigItem('environment', {});
58
+
59
+ expectValidConfigItem('environment', 'node', 'node');
60
+ expectValidConfigItem('environment', 'hardhat', 'hardhat');
61
+ expectValidConfigItem('environment', 'web', 'web');
62
+ expectValidConfigItem('environment', 'react', 'react');
63
+ });
64
+
65
+ it('supportedChains', () => {
66
+ expectInvalidConfigItem('supportedChains', {});
67
+ expectInvalidConfigItem('supportedChains', 'not-an-array');
68
+ expectInvalidConfigItem('supportedChains', null);
69
+ expectInvalidConfigItem('supportedChains', undefined);
70
+
71
+ expectValidConfigItem('supportedChains', [sepolia], [sepolia]);
72
+ expectValidConfigItem('supportedChains', [sepolia, hardhat], [sepolia, hardhat]);
73
+ });
74
+
75
+ it('permitGeneration', () => {
76
+ expectInvalidConfigItem('permitGeneration', 'not-a-boolean');
77
+ expectInvalidConfigItem('permitGeneration', null);
78
+
79
+ expectValidConfigItem('permitGeneration', 'ON_CONNECT', 'ON_CONNECT');
80
+ expectValidConfigItem('permitGeneration', 'ON_DECRYPT_HANDLES', 'ON_DECRYPT_HANDLES');
81
+ expectValidConfigItem('permitGeneration', 'MANUAL', 'MANUAL');
82
+ expectValidConfigItem('permitGeneration', undefined, 'ON_CONNECT');
83
+ });
84
+
85
+ it('defaultPermitExpiration', () => {
86
+ expectInvalidConfigItem('defaultPermitExpiration', 'not-a-number');
87
+ expectInvalidConfigItem('defaultPermitExpiration', null);
88
+
89
+ expectValidConfigItem('defaultPermitExpiration', 5, 5);
90
+ expectValidConfigItem('defaultPermitExpiration', undefined, 60 * 60 * 24 * 30);
91
+ });
92
+
93
+ it('fheKeyStorage', async () => {
94
+ expectInvalidConfigItem('fheKeyStorage', 'not-an-object');
95
+
96
+ expectValidConfigItem('fheKeyStorage', undefined, null);
97
+ expectValidConfigItem('fheKeyStorage', null, null);
98
+
99
+ let getItemCalled = false;
100
+ let setItemCalled = false;
101
+ let removeItemCalled = false;
102
+
103
+ const fakeStorage = {
104
+ getItem: (name: string) => {
105
+ getItemCalled = true;
106
+ return Promise.resolve(null);
107
+ },
108
+ setItem: (name: string, value: any) => {
109
+ setItemCalled = true;
110
+ return Promise.resolve();
111
+ },
112
+ removeItem: (name: string) => {
113
+ removeItemCalled = true;
114
+ return Promise.resolve();
115
+ },
116
+ };
117
+
118
+ const config = { ...validBaseConfig, fheKeyStorage: fakeStorage };
119
+ const result = createCofhesdkConfigBase(config);
120
+
121
+ expect(result.fheKeyStorage).not.toBeNull();
122
+ await result.fheKeyStorage!.getItem('test');
123
+ await result.fheKeyStorage!.setItem('test', 'test');
124
+ await result.fheKeyStorage!.removeItem('test');
125
+
126
+ expect(getItemCalled).toBe(true);
127
+ expect(setItemCalled).toBe(true);
128
+ expect(removeItemCalled).toBe(true);
129
+ });
130
+
131
+ it('mocks', () => {
132
+ expectInvalidConfigItem('mocks', 'not-an-object');
133
+ expectInvalidConfigItem('mocks', null);
134
+
135
+ expectValidConfigItem('mocks', { sealOutputDelay: 1000 }, { sealOutputDelay: 1000 });
136
+ expectValidConfigItem('mocks', undefined, { sealOutputDelay: 0 });
137
+ });
138
+
139
+ it('mocks.sealOutputDelay', () => {
140
+ expectInvalidConfigItem('mocks.sealOutputDelay', 'not-a-number');
141
+ expectInvalidConfigItem('mocks.sealOutputDelay', null);
142
+
143
+ expectValidConfigItem('mocks.sealOutputDelay', undefined, 0);
144
+ expectValidConfigItem('mocks.sealOutputDelay', 1000, 1000);
145
+ });
146
+
147
+ it('useWorkers', () => {
148
+ expectInvalidConfigItem('useWorkers', 'not-a-boolean');
149
+ expectInvalidConfigItem('useWorkers', null);
150
+ expectInvalidConfigItem('useWorkers', 123);
151
+ expectInvalidConfigItem('useWorkers', {});
152
+
153
+ expectValidConfigItem('useWorkers', true, true);
154
+ expectValidConfigItem('useWorkers', false, false);
155
+ expectValidConfigItem('useWorkers', undefined, true); // defaults to true
156
+ });
157
+
158
+ it('should get config item', () => {
159
+ const config: CofhesdkInputConfig = {
160
+ supportedChains: [sepolia],
161
+ };
162
+
163
+ const result = createCofhesdkConfigBase(config);
164
+
165
+ const supportedChains = getCofhesdkConfigItem(result, 'supportedChains');
166
+ expect(supportedChains).toEqual(config.supportedChains);
167
+ });
168
+ });
169
+
170
+ describe('Config helper functions', () => {
171
+ const config = createCofhesdkConfigBase({
172
+ supportedChains: [sepolia, hardhat],
173
+ });
174
+
175
+ describe('getSupportedChainOrThrow', () => {
176
+ it('should return chain when found', () => {
177
+ expect(getSupportedChainOrThrow(config, sepolia.id)).toEqual(sepolia);
178
+ });
179
+
180
+ it('should throw UnsupportedChain error when not found', () => {
181
+ expect(() => getSupportedChainOrThrow(config, 999999)).toThrow();
182
+ });
183
+ });
184
+
185
+ describe('getCoFheUrlOrThrow', () => {
186
+ it('should return coFheUrl', () => {
187
+ expect(getCoFheUrlOrThrow(config, sepolia.id)).toBe(sepolia.coFheUrl);
188
+ });
189
+
190
+ it('should throw when chain not found', () => {
191
+ expect(() => getCoFheUrlOrThrow(config, 999999)).toThrow();
192
+ });
193
+
194
+ it('should throw MissingConfig when url not set', () => {
195
+ const configWithoutUrl = createCofhesdkConfigBase({
196
+ supportedChains: [{ ...sepolia, coFheUrl: undefined } as any],
197
+ });
198
+ expect(() => getCoFheUrlOrThrow(configWithoutUrl, sepolia.id)).toThrow();
199
+ });
200
+ });
201
+
202
+ describe('getZkVerifierUrlOrThrow', () => {
203
+ it('should return verifierUrl', () => {
204
+ expect(getZkVerifierUrlOrThrow(config, sepolia.id)).toBe(sepolia.verifierUrl);
205
+ });
206
+
207
+ it('should throw when chain not found', () => {
208
+ expect(() => getZkVerifierUrlOrThrow(config, 999999)).toThrow();
209
+ });
210
+
211
+ it('should throw ZkVerifierUrlUninitialized when url not set', () => {
212
+ const configWithoutUrl = createCofhesdkConfigBase({
213
+ supportedChains: [{ ...sepolia, verifierUrl: undefined } as any],
214
+ });
215
+ expect(() => getZkVerifierUrlOrThrow(configWithoutUrl, sepolia.id)).toThrow();
216
+ });
217
+ });
218
+
219
+ describe('getThresholdNetworkUrlOrThrow', () => {
220
+ it('should return thresholdNetworkUrl', () => {
221
+ expect(getThresholdNetworkUrlOrThrow(config, sepolia.id)).toBe(sepolia.thresholdNetworkUrl);
222
+ });
223
+
224
+ it('should throw when chain not found', () => {
225
+ expect(() => getThresholdNetworkUrlOrThrow(config, 999999)).toThrow();
226
+ });
227
+
228
+ it('should throw ThresholdNetworkUrlUninitialized when url not set', () => {
229
+ const configWithoutUrl = createCofhesdkConfigBase({
230
+ supportedChains: [{ ...sepolia, thresholdNetworkUrl: undefined } as any],
231
+ });
232
+ expect(() => getThresholdNetworkUrlOrThrow(configWithoutUrl, sepolia.id)).toThrow();
233
+ });
234
+ });
235
+ });