@cofhe/hardhat-plugin 0.0.0-alpha-20260409113701

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts ADDED
@@ -0,0 +1,475 @@
1
+ /* eslint-disable no-empty-pattern */
2
+ /* eslint-disable turbo/no-undeclared-env-vars */
3
+ import chalk from 'chalk';
4
+ import { type PublicClient, type WalletClient } from 'viem';
5
+ import { extendConfig, extendEnvironment, task, types } from 'hardhat/config';
6
+ import { TASK_TEST, TASK_NODE } from 'hardhat/builtin-tasks/task-names';
7
+ import { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';
8
+ import {
9
+ type CofheClient,
10
+ type CofheConfig,
11
+ type CofheInputConfig,
12
+ MOCKS_ZK_VERIFIER_SIGNER_ADDRESS,
13
+ } from '@cofhe/sdk';
14
+ import { createCofheClient, createCofheConfig } from '@cofhe/sdk/node';
15
+ import { HardhatSignerAdapter } from '@cofhe/sdk/adapters';
16
+
17
+ import { localcofheFundAccount } from './fund.js';
18
+ import { TASK_COFHE_MOCKS_DEPLOY, TASK_COFHE_MOCKS_SET_LOG_OPS, TASK_COFHE_USE_FAUCET } from './consts.js';
19
+ import { deployMocks, type DeployMocksArgs, type LogMocksDeploy } from './deploy.js';
20
+ import { mock_setLoggingEnabled, mock_withLogs } from './logging.js';
21
+ import { getFixedMockContract, mock_expectPlaintext } from './utils.js';
22
+ import { mock_getPlaintext } from './utils.js';
23
+ import { hardhat } from '@cofhe/sdk/chains';
24
+ import {
25
+ MockACLArtifact,
26
+ MockThresholdNetworkArtifact,
27
+ MockTaskManagerArtifact,
28
+ MockZkVerifierArtifact,
29
+ TestBedArtifact,
30
+ type MockACL,
31
+ type MockTaskManager,
32
+ type MockThresholdNetwork,
33
+ type MockZkVerifier,
34
+ type TestBed,
35
+ } from '@cofhe/mock-contracts';
36
+
37
+ /**
38
+ * Configuration interface for the CoFHE Hardhat plugin.
39
+ * Allows users to configure mock logging and gas warning settings.
40
+ */
41
+ declare module 'hardhat/types/config' {
42
+ interface HardhatUserConfig {
43
+ cofhe?: {
44
+ /** Whether to log mock operations (default: true) */
45
+ logMocks?: boolean;
46
+ /** Whether to show gas usage warnings for mock operations (default: true) */
47
+ gasWarning?: boolean;
48
+ /**
49
+ * Controls deploy-mocks console output.
50
+ * - `''` — silent, no output
51
+ * - `'v'` — single summary line (default)
52
+ * - `'vv'` — full per-contract deployment logs
53
+ */
54
+ mocksDeployVerbosity?: LogMocksDeploy;
55
+ };
56
+ }
57
+
58
+ interface HardhatConfig {
59
+ cofhe: {
60
+ /** Whether to log mock operations (default: true) */
61
+ logMocks: boolean;
62
+ /** Whether to show gas usage warnings for mock operations (default: true) */
63
+ gasWarning: boolean;
64
+ mocksDeployVerbosity: LogMocksDeploy;
65
+ };
66
+ }
67
+ }
68
+
69
+ extendConfig((config, userConfig) => {
70
+ // Allow users to override the localcofhe network config
71
+ if (userConfig.networks && userConfig.networks.localcofhe) {
72
+ return;
73
+ }
74
+
75
+ // Default config
76
+ config.networks.localcofhe = {
77
+ gas: 'auto',
78
+ gasMultiplier: 1.2,
79
+ gasPrice: 'auto',
80
+ timeout: 10_000,
81
+ httpHeaders: {},
82
+ url: 'http://127.0.0.1:42069',
83
+ accounts: [
84
+ '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
85
+ '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d',
86
+ '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a',
87
+ '0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6',
88
+ ],
89
+ };
90
+
91
+ // Only add Sepolia config if user hasn't defined it
92
+ if (!userConfig.networks?.['eth-sepolia']) {
93
+ config.networks['eth-sepolia'] = {
94
+ url: process.env.SEPOLIA_RPC_URL ?? 'https://ethereum-sepolia.publicnode.com',
95
+ accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
96
+ chainId: 11155111,
97
+ gas: 'auto',
98
+ gasMultiplier: 1.2,
99
+ gasPrice: 'auto',
100
+ timeout: 60_000,
101
+ httpHeaders: {},
102
+ };
103
+ }
104
+
105
+ // Only add Arbitrum Sepolia config if user hasn't defined it
106
+ if (!userConfig.networks?.['arb-sepolia']) {
107
+ config.networks['arb-sepolia'] = {
108
+ url: process.env.ARBITRUM_SEPOLIA_RPC_URL ?? 'https://sepolia-rollup.arbitrum.io/rpc',
109
+ accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
110
+ chainId: 421614,
111
+ gas: 'auto',
112
+ gasMultiplier: 1.2,
113
+ gasPrice: 'auto',
114
+ timeout: 60_000,
115
+ httpHeaders: {},
116
+ };
117
+ }
118
+
119
+ // Add cofhe config
120
+ config.cofhe = {
121
+ logMocks: userConfig.cofhe?.logMocks ?? false,
122
+ gasWarning: userConfig.cofhe?.gasWarning ?? false,
123
+ mocksDeployVerbosity: (userConfig.cofhe?.mocksDeployVerbosity ?? 'v') as LogMocksDeploy,
124
+ };
125
+ });
126
+
127
+ type UseFaucetArgs = {
128
+ address?: string;
129
+ };
130
+
131
+ task(TASK_COFHE_USE_FAUCET, 'Fund an account from the funder')
132
+ .addOptionalParam('address', 'Address to fund', undefined, types.string)
133
+ .setAction(async ({ address }: UseFaucetArgs, hre) => {
134
+ const { network } = hre;
135
+ const { name: networkName } = network;
136
+
137
+ if (networkName !== 'localcofhe') {
138
+ console.info(chalk.yellow(`Programmatic faucet only supported for localcofhe`));
139
+ return;
140
+ }
141
+
142
+ if (!address) {
143
+ console.info(chalk.red(`Failed to get address to fund`));
144
+ return;
145
+ }
146
+
147
+ console.info(chalk.green(`Getting funds from faucet for ${address}`));
148
+
149
+ try {
150
+ await localcofheFundAccount(hre, address);
151
+ } catch (e) {
152
+ console.info(chalk.red(`failed to get funds from localcofhe for ${address}: ${e}`));
153
+ }
154
+ });
155
+
156
+ // DEPLOY TASKS
157
+
158
+ task(TASK_COFHE_MOCKS_DEPLOY, 'Deploys the mock contracts on the Hardhat network')
159
+ .addOptionalParam('deployTestBed', 'Whether to deploy the test bed', true, types.boolean)
160
+ .setAction(async ({ deployTestBed }: DeployMocksArgs, hre) => {
161
+ await deployMocks(hre, {
162
+ deployTestBed: deployTestBed ?? true,
163
+ gasWarning: hre.config.cofhe.gasWarning ?? true,
164
+ mocksDeployVerbosity: hre.config.cofhe.mocksDeployVerbosity,
165
+ });
166
+ });
167
+
168
+ // Hardhat plugin auto-deploys mocks for every hardhat test run by overriding TASK_TEST and calling deployMocks(...) before runSuper()
169
+ task(TASK_TEST, 'Deploy mock contracts on hardhat').setAction(async ({}, hre, runSuper) => {
170
+ const skipAutoDeploy = (() => {
171
+ const raw = process.env.COFHE_SKIP_MOCKS_DEPLOY ?? '';
172
+ const normalized = raw.trim().toLowerCase();
173
+ return normalized === '1' || normalized === 'true' || normalized === 'yes';
174
+ })();
175
+
176
+ if (!skipAutoDeploy) {
177
+ await deployMocks(hre, {
178
+ deployTestBed: true,
179
+ gasWarning: hre.config.cofhe.gasWarning ?? true,
180
+ mocksDeployVerbosity: hre.config.cofhe.mocksDeployVerbosity,
181
+ });
182
+ }
183
+ return runSuper();
184
+ });
185
+
186
+ task(TASK_NODE, 'Deploy mock contracts on hardhat').setAction(async ({}, hre, runSuper) => {
187
+ const skipAutoDeploy = (() => {
188
+ const raw = process.env.COFHE_SKIP_MOCKS_DEPLOY ?? '';
189
+ const normalized = raw.trim().toLowerCase();
190
+ return normalized === '1' || normalized === 'true' || normalized === 'yes';
191
+ })();
192
+
193
+ if (!skipAutoDeploy) {
194
+ await deployMocks(hre, {
195
+ deployTestBed: true,
196
+ gasWarning: hre.config.cofhe.gasWarning ?? true,
197
+ mocksDeployVerbosity: hre.config.cofhe.mocksDeployVerbosity,
198
+ });
199
+ }
200
+ return runSuper();
201
+ });
202
+
203
+ // SET LOG OPS
204
+
205
+ task(TASK_COFHE_MOCKS_SET_LOG_OPS, 'Set logging for the Mock CoFHE contracts')
206
+ .addParam('enable', 'Whether to enable logging', false, types.boolean)
207
+ .setAction(async ({ enable }, hre) => {
208
+ await mock_setLoggingEnabled(hre, enable);
209
+ });
210
+
211
+ // MOCK UTILS
212
+
213
+ export * from './consts.js';
214
+ export * from './utils.js';
215
+ export * from './fund.js';
216
+ export * from './logging.js';
217
+ export * from './deploy.js';
218
+
219
+ /**
220
+ * Runtime environment extensions for the CoFHE Hardhat plugin.
221
+ * Provides access to CoFHE initialization, environment checks, and mock utilities.
222
+ */
223
+ declare module 'hardhat/types/runtime' {
224
+ export interface HardhatRuntimeEnvironment {
225
+ cofhe: {
226
+ /**
227
+ * Create a CoFHE configuration for use with hre.cofhe.createClient(...)
228
+ * @param {CofheInputConfig} config - The CoFHE input configuration
229
+ * @returns {CofheConfig} The CoFHE configuration
230
+ */
231
+ createConfig: (config: CofheInputConfig) => Promise<CofheConfig>;
232
+ /**
233
+ * Create a CoFHE client instance
234
+ * @param {CofheConfig} config - The CoFHE configuration (use createCofheConfig to create with Node.js defaults)
235
+ * @returns {Promise<CofheClient>} The CoFHE client instance
236
+ */
237
+ createClient: (config: CofheConfig) => CofheClient;
238
+ /**
239
+ * Create viem clients from a Hardhat ethers signer, to be used with `cofheClient.connect(...)`
240
+ * @param {HardhatEthersSigner} signer - The Hardhat ethers signer to use
241
+ * @returns {Promise<{ publicClient: PublicClient; walletClient: WalletClient }>} The viem clients
242
+ */
243
+ hardhatSignerAdapter: (
244
+ signer: HardhatEthersSigner
245
+ ) => Promise<{ publicClient: PublicClient; walletClient: WalletClient }>;
246
+ /**
247
+ * Connect a CoFHE client with a Hardhat ethers signer
248
+ * @param {CofheClient} client - The CoFHE client to connect
249
+ * @param {HardhatEthersSigner} signer - The Hardhat ethers signer to use
250
+ * @returns {Promise<void>}
251
+ */
252
+ connectWithHardhatSigner: (client: CofheClient, signer: HardhatEthersSigner) => Promise<void>;
253
+ /**
254
+ * Create and connect to a batteries included client.
255
+ * Also generates a self-usage a permit for the signer.
256
+ * If customization is needed, use createCofheClient and connectWithHardhatSigner.
257
+ * @param {HardhatEthersSigner} signer - The Hardhat ethers signer to use (optional - defaults to first signer)
258
+ * @returns {Promise<CofheClient>} The CoFHE client instance
259
+ */
260
+ createClientWithBatteries: (signer?: HardhatEthersSigner) => Promise<CofheClient>;
261
+
262
+ mocks: {
263
+ /**
264
+ * **[MOCKS ONLY]**
265
+ *
266
+ * Execute a block of code with cofhe mock contracts logging enabled.
267
+ *
268
+ * _(If logging only a function, we recommend passing the function name as the closureName (ex "counter.increment()"))_
269
+ *
270
+ * Example usage:
271
+ *
272
+ * ```ts
273
+ * await hre.cofhe.mocks.withLogs("counter.increment()", async () => {
274
+ * await counter.increment();
275
+ * });
276
+ * ```
277
+ *
278
+ * Expected output:
279
+ * ```
280
+ * ┌──────────────────┬──────────────────────────────────────────────────
281
+ * │ [COFHE-MOCKS] │ "counter.increment()" logs:
282
+ * ├──────────────────┴──────────────────────────────────────────────────
283
+ * ├ FHE.add | euint32(4473..3424)[0] + euint32(1157..3648)[1] => euint32(1106..1872)[1]
284
+ * ├ FHE.allowThis | euint32(1106..1872)[1] -> 0x663f..6602
285
+ * ├ FHE.allow | euint32(1106..1872)[1] -> 0x3c44..93bc
286
+ * └─────────────────────────────────────────────────────────────────────
287
+ * ```
288
+ * @param {string} closureName - Name of the code block to log within
289
+ * @param {() => Promise<void>} closure - The async function to execute
290
+ */
291
+ withLogs: (closureName: string, closure: () => Promise<void>) => Promise<void>;
292
+
293
+ /**
294
+ * **[MOCKS ONLY]**
295
+ *
296
+ * Enable logging from cofhe mock contracts
297
+ * @param {string} closureName - Optional name of the code block to enable logging for
298
+ */
299
+ enableLogs: (closureName?: string) => Promise<void>;
300
+
301
+ /**
302
+ * **[MOCKS ONLY]**
303
+ *
304
+ * Disable logging from cofhe mock contracts
305
+ */
306
+ disableLogs: () => Promise<void>;
307
+
308
+ /**
309
+ * **[MOCKS ONLY]**
310
+ *
311
+ * Deploy the cofhe mock contracts (normally this is done automatically)
312
+ * @param {DeployMocksArgs} options - Deployment options
313
+ */
314
+ deployMocks: (options?: DeployMocksArgs) => Promise<void>;
315
+
316
+ /**
317
+ * **[MOCKS ONLY]**
318
+ *
319
+ * Get the plaintext value for a ciphertext hash
320
+ * @param {bigint | string} ctHash - The ciphertext hash to look up
321
+ * @returns {Promise<bigint>} The plaintext value
322
+ */
323
+ getPlaintext: (ctHash: bigint | string) => Promise<bigint>;
324
+
325
+ /**
326
+ * **[MOCKS ONLY]**
327
+ *
328
+ * Assert that a ciphertext hash represents an expected plaintext value
329
+ * @param {bigint | string} ctHash - The ciphertext hash to check
330
+ * @param {bigint} expectedValue - The expected plaintext value
331
+ */
332
+ expectPlaintext: (ctHash: bigint | string, expectedValue: bigint) => Promise<void>;
333
+
334
+ /**
335
+ * Get the MockTaskManager contract (typed via typechain)
336
+ * @returns {Promise<MockTaskManager>} The MockTaskManager contract
337
+ */
338
+ getMockTaskManager: () => Promise<MockTaskManager>;
339
+
340
+ /**
341
+ * Get the MockACL contract (typed via typechain)
342
+ * @returns {Promise<MockACL>} The MockACL contract
343
+ */
344
+ getMockACL: () => Promise<MockACL>;
345
+
346
+ /**
347
+ * Get the MockThresholdNetwork contract (typed via typechain)
348
+ * @returns {Promise<MockThresholdNetwork>} The MockThresholdNetwork contract
349
+ */
350
+ getMockThresholdNetwork: () => Promise<MockThresholdNetwork>;
351
+
352
+ /**
353
+ * Get the MockZkVerifier contract (typed via typechain)
354
+ * @returns {Promise<MockZkVerifier>} The MockZkVerifier contract
355
+ */
356
+ getMockZkVerifier: () => Promise<MockZkVerifier>;
357
+
358
+ /**
359
+ * Get the TestBed contract (typed via typechain)
360
+ * @returns {Promise<TestBed>} The TestBed contract
361
+ */
362
+ getTestBed: () => Promise<TestBed>;
363
+ };
364
+ };
365
+ }
366
+ }
367
+
368
+ /**
369
+ * Builds the mocks config for the hardhat plugin.
370
+ * Defaults `encryptDelay` to `0` so tests run without artificial wait times,
371
+ * unless the user has explicitly provided a value.
372
+ */
373
+ export function buildHardhatPluginMocksConfig(
374
+ mocksConfig: CofheInputConfig['mocks']
375
+ ): NonNullable<CofheInputConfig['mocks']> {
376
+ return {
377
+ ...mocksConfig,
378
+ encryptDelay: mocksConfig?.encryptDelay ?? 0,
379
+ };
380
+ }
381
+
382
+ extendEnvironment((hre) => {
383
+ hre.cofhe = {
384
+ createConfig: async (config: CofheInputConfig) => {
385
+ // Create zkv wallet client
386
+ // This wallet interacts with the MockZkVerifier contract so that the user's connected wallet doesn't have to
387
+ const zkvHhSigner = await hre.ethers.getImpersonatedSigner(MOCKS_ZK_VERIFIER_SIGNER_ADDRESS);
388
+ const { walletClient: zkvWalletClient } = await HardhatSignerAdapter(zkvHhSigner);
389
+
390
+ // Inject zkv wallet client into config
391
+ // Set encryptDelay to 0 on hardhat to avoid waiting for delays during tests
392
+ const configWithZkvWalletClient = {
393
+ environment: 'hardhat' as const,
394
+ ...config,
395
+ mocks: buildHardhatPluginMocksConfig(config.mocks),
396
+ _internal: {
397
+ ...config._internal,
398
+ zkvWalletClient,
399
+ },
400
+ };
401
+
402
+ return createCofheConfig(configWithZkvWalletClient);
403
+ },
404
+ createClient: (config: CofheConfig) => {
405
+ return createCofheClient(config);
406
+ },
407
+ hardhatSignerAdapter: async (signer: HardhatEthersSigner) => {
408
+ return HardhatSignerAdapter(signer);
409
+ },
410
+ connectWithHardhatSigner: async (client: CofheClient, signer: HardhatEthersSigner) => {
411
+ const { publicClient, walletClient } = await HardhatSignerAdapter(signer);
412
+ return client.connect(publicClient, walletClient);
413
+ },
414
+ createClientWithBatteries: async (signer?: HardhatEthersSigner) => {
415
+ // Get signer if not provided
416
+ if (!signer) {
417
+ [signer] = await hre.ethers.getSigners();
418
+ }
419
+
420
+ // Create config
421
+ const config = await hre.cofhe.createConfig({
422
+ environment: 'hardhat',
423
+ supportedChains: [hardhat],
424
+ });
425
+
426
+ // Create client
427
+ const client = hre.cofhe.createClient(config);
428
+
429
+ // Connect client
430
+ await hre.cofhe.connectWithHardhatSigner(client, signer);
431
+
432
+ // Create self-usage permit
433
+ await client.permits.createSelf({
434
+ issuer: signer.address,
435
+ });
436
+
437
+ // Return client
438
+ return client;
439
+ },
440
+ mocks: {
441
+ withLogs: async (closureName: string, closure: () => Promise<void>) => {
442
+ return mock_withLogs(hre, closureName, closure);
443
+ },
444
+ enableLogs: async (closureName?: string) => {
445
+ return mock_setLoggingEnabled(hre, true, closureName);
446
+ },
447
+ disableLogs: async () => {
448
+ return mock_setLoggingEnabled(hre, false);
449
+ },
450
+ deployMocks: async (options: DeployMocksArgs = {}) => {
451
+ return deployMocks(hre, { mocksDeployVerbosity: hre.config.cofhe.mocksDeployVerbosity, ...options });
452
+ },
453
+ getPlaintext: async (ctHash: bigint | string) => {
454
+ const [signer] = await hre.ethers.getSigners();
455
+ return mock_getPlaintext(signer.provider, ctHash);
456
+ },
457
+ expectPlaintext: async (ctHash: bigint | string, expectedValue: bigint) => {
458
+ const [signer] = await hre.ethers.getSigners();
459
+ return mock_expectPlaintext(signer.provider, ctHash, expectedValue);
460
+ },
461
+ getMockTaskManager: async () =>
462
+ getFixedMockContract(hre, MockTaskManagerArtifact) as unknown as Promise<MockTaskManager>,
463
+ getMockACL: async () => {
464
+ const taskManager = await getFixedMockContract(hre, MockTaskManagerArtifact);
465
+ const aclAddress = await taskManager.acl();
466
+ return hre.ethers.getContractAt([...MockACLArtifact.abi], aclAddress) as unknown as MockACL;
467
+ },
468
+ getMockThresholdNetwork: async () =>
469
+ getFixedMockContract(hre, MockThresholdNetworkArtifact) as unknown as Promise<MockThresholdNetwork>,
470
+ getMockZkVerifier: async () =>
471
+ getFixedMockContract(hre, MockZkVerifierArtifact) as unknown as Promise<MockZkVerifier>,
472
+ getTestBed: async () => getFixedMockContract(hre, TestBedArtifact) as unknown as Promise<TestBed>,
473
+ },
474
+ };
475
+ });
package/src/logging.ts ADDED
@@ -0,0 +1,72 @@
1
+ import chalk from 'chalk';
2
+ import { type HardhatRuntimeEnvironment } from 'hardhat/types';
3
+ import { getFixedMockContract } from './utils';
4
+ import { MockTaskManagerArtifact } from '@cofhe/mock-contracts';
5
+
6
+ const getLoggingEnabled = async (hre: HardhatRuntimeEnvironment): Promise<boolean> => {
7
+ const taskManager = await getFixedMockContract(hre, MockTaskManagerArtifact);
8
+ return taskManager.logOps();
9
+ };
10
+
11
+ const setLoggingEnabled = async (hre: HardhatRuntimeEnvironment, enabled: boolean) => {
12
+ const taskManager = await getFixedMockContract(hre, MockTaskManagerArtifact);
13
+ const tx = await taskManager.setLogOps(enabled);
14
+ await tx.wait();
15
+ };
16
+
17
+ // prettier-ignore
18
+ const printLogsEnabledMessage = (closureMessage: string) => {
19
+ console.log("┌──────────────────┬──────────────────────────────────────────────────");
20
+ console.log(`│ [COFHE-MOCKS] │ ${closureMessage}`);
21
+ console.log("├──────────────────┴──────────────────────────────────────────────────");
22
+ };
23
+
24
+ // prettier-ignore
25
+ const printLogsBlockEnd = () => {
26
+ console.log("└─────────────────────────────────────────────────────────────────────");
27
+ };
28
+
29
+ export const mock_setLoggingEnabled = async (
30
+ hre: HardhatRuntimeEnvironment,
31
+ enabled: boolean,
32
+ closureName?: string
33
+ ) => {
34
+ try {
35
+ const initiallyEnabled = await getLoggingEnabled(hre);
36
+
37
+ await setLoggingEnabled(hre, enabled);
38
+
39
+ // Only print if enabling logs
40
+ if (enabled) {
41
+ printLogsEnabledMessage(`${closureName ? `"${chalk.bold(closureName)}" logs:` : 'Logs:'}`);
42
+ }
43
+
44
+ // Only print if disabling logs AND logs currently enabled
45
+ if (!enabled && initiallyEnabled) {
46
+ printLogsBlockEnd();
47
+ }
48
+ } catch (error) {
49
+ console.log(chalk.red('mock_setLoggingEnabled error'), error);
50
+ }
51
+ };
52
+
53
+ export const mock_withLogs = async (
54
+ hre: HardhatRuntimeEnvironment,
55
+ closureName: string,
56
+ closure: () => Promise<void>
57
+ ) => {
58
+ const initiallyEnabled = await getLoggingEnabled(hre);
59
+
60
+ await setLoggingEnabled(hre, true);
61
+ printLogsEnabledMessage(`"${chalk.bold(closureName)}" logs:`);
62
+ try {
63
+ await closure();
64
+ } finally {
65
+ printLogsBlockEnd();
66
+
67
+ // If logs were disabled, disable them again
68
+ if (!initiallyEnabled) {
69
+ await setLoggingEnabled(hre, false);
70
+ }
71
+ }
72
+ };
package/src/utils.ts ADDED
@@ -0,0 +1,109 @@
1
+ import { TASK_MANAGER_ADDRESS, MOCKS_ZK_VERIFIER_ADDRESS } from '@cofhe/sdk';
2
+ import { expect } from 'chai';
3
+ import { Contract, ethers } from 'ethers';
4
+ import { type HardhatEthersProvider } from '@nomicfoundation/hardhat-ethers/internal/hardhat-ethers-provider';
5
+ import type { MockArtifact } from '@cofhe/mock-contracts';
6
+ import type { HardhatRuntimeEnvironment } from 'hardhat/types';
7
+
8
+ // Deployment utils
9
+
10
+ /// Deploys a mock contract from a pre-built artifact from the mock-contracts package
11
+ /// If the mock contract should be deployed to a fixed address, `hardhat_setCode` op is used to set the code at the fixed address
12
+ /// Otherwise, we deploy the contract using ethers.js to a non-fixed address
13
+ export const deployMockContractFromArtifact = async (hre: HardhatRuntimeEnvironment, artifact: MockArtifact) => {
14
+ // Use hardhat_setCode to deploy to fixed address
15
+ if (artifact.isFixed) {
16
+ await hre.network.provider.send('hardhat_setCode', [artifact.fixedAddress, artifact.deployedBytecode]);
17
+ return getFixedMockContract(hre, artifact);
18
+ }
19
+
20
+ // Use ethers.js to deploy to variable address
21
+ const [signer] = await hre.ethers.getSigners();
22
+ const factory = new hre.ethers.ContractFactory(artifact.abi, artifact.bytecode, signer);
23
+ const contract = await factory.deploy(/* constructor args */);
24
+ await contract.waitForDeployment();
25
+ return contract as Contract;
26
+ };
27
+
28
+ export const getFixedMockContract = async (hre: HardhatRuntimeEnvironment, artifact: MockArtifact) => {
29
+ if (!artifact.isFixed) {
30
+ throw new Error('Artifact is not fixed');
31
+ }
32
+ return await hre.ethers.getContractAt(artifact.abi, artifact.fixedAddress);
33
+ };
34
+
35
+ // Testing utils
36
+
37
+ const mock_checkIsTestnet = async (fnName: string, provider: HardhatEthersProvider | ethers.JsonRpcProvider) => {
38
+ // Testnet is checked by testing if MockZkVerifier is deployed
39
+
40
+ // Get bytecode at ZK_VERIFIER_ADDRESS
41
+ const bytecode = await provider.getCode(MOCKS_ZK_VERIFIER_ADDRESS);
42
+
43
+ // If bytecode is empty, we are on a testnet
44
+ const isTestnet = bytecode.length === 0;
45
+
46
+ // Log if we are on a testnet
47
+ if (isTestnet) {
48
+ console.log(`${fnName} - skipped on non-testnet chain`);
49
+ }
50
+
51
+ return isTestnet;
52
+ };
53
+
54
+ export const mock_getPlaintext = async (
55
+ provider: HardhatEthersProvider | ethers.JsonRpcProvider,
56
+ ctHash: bigint | string
57
+ ) => {
58
+ // Skip with log if called on a non-testnet chain
59
+ if (await mock_checkIsTestnet(mock_getPlaintext.name, provider)) return;
60
+
61
+ // Connect to MockTaskManager
62
+ const taskManager = new ethers.Contract(
63
+ TASK_MANAGER_ADDRESS,
64
+ ['function mockStorage(uint256) view returns (uint256)'],
65
+ provider
66
+ );
67
+
68
+ // Fetch the plaintext
69
+ const plaintext = await taskManager.mockStorage(BigInt(ctHash));
70
+
71
+ return plaintext;
72
+ };
73
+
74
+ export const mock_getPlaintextExists = async (
75
+ provider: HardhatEthersProvider | ethers.JsonRpcProvider,
76
+ ctHash: bigint | string
77
+ ) => {
78
+ // Skip with log if called on a non-testnet chain
79
+ if (await mock_checkIsTestnet(mock_getPlaintextExists.name, provider)) return;
80
+
81
+ // Connect to MockTaskManager
82
+ const taskManager = new ethers.Contract(
83
+ TASK_MANAGER_ADDRESS,
84
+ ['function inMockStorage(uint256) view returns (bool)'],
85
+ provider
86
+ );
87
+
88
+ // Fetch the plaintext exists
89
+ const plaintextExists = await taskManager.inMockStorage(BigInt(ctHash));
90
+
91
+ return plaintextExists;
92
+ };
93
+
94
+ export const mock_expectPlaintext = async (
95
+ provider: HardhatEthersProvider | ethers.JsonRpcProvider,
96
+ ctHash: bigint | string,
97
+ expectedValue: bigint
98
+ ) => {
99
+ // Skip with log if called on a non-testnet chain
100
+ if (await mock_checkIsTestnet(mock_expectPlaintext.name, provider)) return;
101
+
102
+ // Expect the plaintext to exist
103
+ const plaintextExists = await mock_getPlaintextExists(provider, ctHash);
104
+ expect(plaintextExists).equal(true, 'Plaintext does not exist');
105
+
106
+ // Expect the plaintext to have the expected value
107
+ const plaintext = await mock_getPlaintext(provider, ctHash);
108
+ expect(plaintext).equal(expectedValue, 'Plaintext value is incorrect');
109
+ };