@cofhe/hardhat-plugin 0.4.0 → 0.5.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/src/index.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  /* eslint-disable no-empty-pattern */
2
2
  /* eslint-disable turbo/no-undeclared-env-vars */
3
3
  import chalk from 'chalk';
4
+ import path from 'path';
5
+ import fs from 'fs';
4
6
  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 { extendConfig, extendEnvironment, task, subtask, types } from 'hardhat/config';
8
+ import { TASK_TEST, TASK_NODE, TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS } from 'hardhat/builtin-tasks/task-names';
7
9
  import { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';
8
10
  import {
9
11
  type CofheClient,
@@ -16,13 +18,12 @@ import { HardhatSignerAdapter } from '@cofhe/sdk/adapters';
16
18
 
17
19
  import { localcofheFundAccount } from './fund.js';
18
20
  import { TASK_COFHE_MOCKS_DEPLOY, TASK_COFHE_MOCKS_SET_LOG_OPS, TASK_COFHE_USE_FAUCET } from './consts.js';
19
- import { deployMocks, type DeployMocksArgs } from './deploy.js';
21
+ import { deployMocks, type DeployMocksArgs, type LogMocksDeploy } from './deploy.js';
20
22
  import { mock_setLoggingEnabled, mock_withLogs } from './logging.js';
21
23
  import { getFixedMockContract, mock_expectPlaintext } from './utils.js';
22
24
  import { mock_getPlaintext } from './utils.js';
23
25
  import { hardhat } from '@cofhe/sdk/chains';
24
26
  import {
25
- MockACLArtifact,
26
27
  MockThresholdNetworkArtifact,
27
28
  MockTaskManagerArtifact,
28
29
  MockZkVerifierArtifact,
@@ -45,6 +46,13 @@ declare module 'hardhat/types/config' {
45
46
  logMocks?: boolean;
46
47
  /** Whether to show gas usage warnings for mock operations (default: true) */
47
48
  gasWarning?: boolean;
49
+ /**
50
+ * Controls deploy-mocks console output.
51
+ * - `''` — silent, no output
52
+ * - `'v'` — single summary line (default)
53
+ * - `'vv'` — full per-contract deployment logs
54
+ */
55
+ mocksDeployVerbosity?: LogMocksDeploy;
48
56
  };
49
57
  }
50
58
 
@@ -54,10 +62,77 @@ declare module 'hardhat/types/config' {
54
62
  logMocks: boolean;
55
63
  /** Whether to show gas usage warnings for mock operations (default: true) */
56
64
  gasWarning: boolean;
65
+ mocksDeployVerbosity: LogMocksDeploy;
57
66
  };
58
67
  }
59
68
  }
60
69
 
70
+ const MOCK_STUBS_DIR_NAME = 'cofhe-stubs';
71
+ const MOCK_STUBS_FILE_NAME = 'CofheMocksImports.sol';
72
+
73
+ /**
74
+ * Returns the absolute paths of every .sol file shipped inside the
75
+ * @cofhe/mock-contracts/contracts directory.
76
+ */
77
+ function getMockContractsSolFiles(): string[] {
78
+ let contractsDir: string;
79
+ try {
80
+ const pkgJson = require.resolve('@cofhe/mock-contracts/package.json');
81
+ contractsDir = path.join(path.dirname(pkgJson), 'contracts');
82
+ } catch {
83
+ contractsDir = path.join(process.cwd(), 'node_modules', '@cofhe', 'mock-contracts', 'contracts');
84
+ }
85
+
86
+ if (!fs.existsSync(contractsDir)) {
87
+ return [];
88
+ }
89
+
90
+ return fs
91
+ .readdirSync(contractsDir)
92
+ .filter((f) => f.endsWith('.sol'))
93
+ .map((f) => path.join(contractsDir, f));
94
+ }
95
+
96
+ /**
97
+ * Generates a stub Solidity file that imports every contract from
98
+ * @cofhe/mock-contracts/contracts. Writing it to the Hardhat cache directory
99
+ * (which is inside the project root) means:
100
+ * - localPathToSourceName() accepts it (no HH1007)
101
+ * - Hardhat resolves the npm-style "@cofhe/mock-contracts/..." imports
102
+ * the same way it resolves any other node_modules dependency
103
+ * - All mock contract artifacts end up in hre.artifacts, giving Hardhat
104
+ * full ABI knowledge so it can decode reverts and traces
105
+ */
106
+ function generateMockContractsStubContent(): string {
107
+ const imports = getMockContractsSolFiles()
108
+ .map((f) => `import "@cofhe/mock-contracts/contracts/${path.basename(f)}";`)
109
+ .join('\n');
110
+
111
+ return [
112
+ '// SPDX-License-Identifier: UNLICENSED',
113
+ '// Auto-generated by @cofhe/hardhat-plugin — do not edit.',
114
+ '// Importing mock contracts here causes Hardhat to compile them and register',
115
+ '// their artifacts, enabling proper revert decoding during tests.',
116
+ 'pragma solidity >=0.8.25 <0.9.0;',
117
+ '',
118
+ imports,
119
+ '',
120
+ ].join('\n');
121
+ }
122
+
123
+ subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction(async (taskArgs: { sourcePath?: string }, hre, runSuper) => {
124
+ const existing: string[] = await runSuper(taskArgs);
125
+
126
+ // Write the stub inside the Hardhat cache dir so that it lives within the
127
+ // project root — a requirement of localPathToSourceName (HH1007).
128
+ const stubDir = path.join(hre.config.paths.cache, MOCK_STUBS_DIR_NAME);
129
+ const stubPath = path.join(stubDir, MOCK_STUBS_FILE_NAME);
130
+ fs.mkdirSync(stubDir, { recursive: true });
131
+ fs.writeFileSync(stubPath, generateMockContractsStubContent(), 'utf8');
132
+
133
+ return [...new Set([...existing, stubPath])];
134
+ });
135
+
61
136
  extendConfig((config, userConfig) => {
62
137
  // Allow users to override the localcofhe network config
63
138
  if (userConfig.networks && userConfig.networks.localcofhe) {
@@ -110,8 +185,9 @@ extendConfig((config, userConfig) => {
110
185
 
111
186
  // Add cofhe config
112
187
  config.cofhe = {
113
- logMocks: userConfig.cofhe?.logMocks ?? true,
114
- gasWarning: userConfig.cofhe?.gasWarning ?? true,
188
+ logMocks: userConfig.cofhe?.logMocks ?? false,
189
+ gasWarning: userConfig.cofhe?.gasWarning ?? false,
190
+ mocksDeployVerbosity: (userConfig.cofhe?.mocksDeployVerbosity ?? 'v') as LogMocksDeploy,
115
191
  };
116
192
  });
117
193
 
@@ -148,12 +224,11 @@ task(TASK_COFHE_USE_FAUCET, 'Fund an account from the funder')
148
224
 
149
225
  task(TASK_COFHE_MOCKS_DEPLOY, 'Deploys the mock contracts on the Hardhat network')
150
226
  .addOptionalParam('deployTestBed', 'Whether to deploy the test bed', true, types.boolean)
151
- .addOptionalParam('silent', 'Whether to suppress output', false, types.boolean)
152
- .setAction(async ({ deployTestBed, silent }: DeployMocksArgs, hre) => {
227
+ .setAction(async ({ deployTestBed }: DeployMocksArgs, hre) => {
153
228
  await deployMocks(hre, {
154
229
  deployTestBed: deployTestBed ?? true,
155
230
  gasWarning: hre.config.cofhe.gasWarning ?? true,
156
- silent: silent ?? false,
231
+ mocksDeployVerbosity: hre.config.cofhe.mocksDeployVerbosity,
157
232
  });
158
233
  });
159
234
 
@@ -169,6 +244,7 @@ task(TASK_TEST, 'Deploy mock contracts on hardhat').setAction(async ({}, hre, ru
169
244
  await deployMocks(hre, {
170
245
  deployTestBed: true,
171
246
  gasWarning: hre.config.cofhe.gasWarning ?? true,
247
+ mocksDeployVerbosity: hre.config.cofhe.mocksDeployVerbosity,
172
248
  });
173
249
  }
174
250
  return runSuper();
@@ -185,6 +261,7 @@ task(TASK_NODE, 'Deploy mock contracts on hardhat').setAction(async ({}, hre, ru
185
261
  await deployMocks(hre, {
186
262
  deployTestBed: true,
187
263
  gasWarning: hre.config.cofhe.gasWarning ?? true,
264
+ mocksDeployVerbosity: hre.config.cofhe.mocksDeployVerbosity,
188
265
  });
189
266
  }
190
267
  return runSuper();
@@ -438,7 +515,7 @@ extendEnvironment((hre) => {
438
515
  return mock_setLoggingEnabled(hre, false);
439
516
  },
440
517
  deployMocks: async (options: DeployMocksArgs = {}) => {
441
- return deployMocks(hre, options);
518
+ return deployMocks(hre, { mocksDeployVerbosity: hre.config.cofhe.mocksDeployVerbosity, ...options });
442
519
  },
443
520
  getPlaintext: async (ctHash: bigint | string) => {
444
521
  const [signer] = await hre.ethers.getSigners();
@@ -453,7 +530,8 @@ extendEnvironment((hre) => {
453
530
  getMockACL: async () => {
454
531
  const taskManager = await getFixedMockContract(hre, MockTaskManagerArtifact);
455
532
  const aclAddress = await taskManager.acl();
456
- return hre.ethers.getContractAt(MockACLArtifact.abi, aclAddress) as unknown as MockACL;
533
+ const aclArtifact = await hre.artifacts.readArtifact('MockACL');
534
+ return hre.ethers.getContractAt(aclArtifact.abi, aclAddress) as unknown as MockACL;
457
535
  },
458
536
  getMockThresholdNetwork: async () =>
459
537
  getFixedMockContract(hre, MockThresholdNetworkArtifact) as unknown as Promise<MockThresholdNetwork>,
package/src/logging.ts CHANGED
@@ -59,11 +59,14 @@ export const mock_withLogs = async (
59
59
 
60
60
  await setLoggingEnabled(hre, true);
61
61
  printLogsEnabledMessage(`"${chalk.bold(closureName)}" logs:`);
62
- await closure();
63
- printLogsBlockEnd();
62
+ try {
63
+ await closure();
64
+ } finally {
65
+ printLogsBlockEnd();
64
66
 
65
- // If logs were disabled, disable them again
66
- if (!initiallyEnabled) {
67
- await setLoggingEnabled(hre, false);
67
+ // If logs were disabled, disable them again
68
+ if (!initiallyEnabled) {
69
+ await setLoggingEnabled(hre, false);
70
+ }
68
71
  }
69
72
  };
package/src/utils.ts CHANGED
@@ -7,20 +7,22 @@ import type { HardhatRuntimeEnvironment } from 'hardhat/types';
7
7
 
8
8
  // Deployment utils
9
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
10
+ /// Deploys a mock contract from a pre-built artifact from the mock-contracts package.
11
+ /// ABI and bytecode are sourced from Hardhat's artifact registry (populated during compilation)
12
+ /// so that Hardhat can decode reverts and trace calls against these contracts.
13
+ /// If the mock contract should be deployed to a fixed address, `hardhat_setCode` is used.
14
+ /// Otherwise, ethers.js deploys it to a normal address.
13
15
  export const deployMockContractFromArtifact = async (hre: HardhatRuntimeEnvironment, artifact: MockArtifact) => {
14
- // Use hardhat_setCode to deploy to fixed address
16
+ const hardhatArtifact = await hre.artifacts.readArtifact(artifact.contractName);
17
+
15
18
  if (artifact.isFixed) {
16
- await hre.network.provider.send('hardhat_setCode', [artifact.fixedAddress, artifact.deployedBytecode]);
17
- return getFixedMockContract(hre, artifact);
19
+ await hre.network.provider.send('hardhat_setCode', [artifact.fixedAddress, hardhatArtifact.deployedBytecode]);
20
+ return hre.ethers.getContractAt(hardhatArtifact.abi, artifact.fixedAddress);
18
21
  }
19
22
 
20
- // Use ethers.js to deploy to variable address
21
23
  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
+ const factory = new hre.ethers.ContractFactory(hardhatArtifact.abi, hardhatArtifact.bytecode, signer);
25
+ const contract = await factory.deploy();
24
26
  await contract.waitForDeployment();
25
27
  return contract as Contract;
26
28
  };
@@ -29,7 +31,8 @@ export const getFixedMockContract = async (hre: HardhatRuntimeEnvironment, artif
29
31
  if (!artifact.isFixed) {
30
32
  throw new Error('Artifact is not fixed');
31
33
  }
32
- return await hre.ethers.getContractAt(artifact.abi, artifact.fixedAddress);
34
+ const hardhatArtifact = await hre.artifacts.readArtifact(artifact.contractName);
35
+ return hre.ethers.getContractAt(hardhatArtifact.abi, artifact.fixedAddress);
33
36
  };
34
37
 
35
38
  // Testing utils