@diamondslab/diamonds-hardhat-foundry 2.2.3 → 2.3.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.
@@ -0,0 +1,307 @@
1
+ import { task, types } from "hardhat/config";
2
+ import { HardhatRuntimeEnvironment } from "hardhat/types";
3
+ import { Logger } from "../utils/logger";
4
+
5
+ /**
6
+ * Task: diamonds-forge:coverage
7
+ *
8
+ * Runs Forge coverage with Diamond deployment.
9
+ * - Ensures Diamond deployment exists
10
+ * - Generates Solidity helpers
11
+ * - Runs forge coverage with specified options
12
+ *
13
+ * Use Hardhat's built-in --network flag to specify the network
14
+ */
15
+ task("diamonds-forge:coverage", "Run forge coverage for Diamond contracts")
16
+ .addParam(
17
+ "diamondName",
18
+ "Name of the Diamond to analyze",
19
+ undefined,
20
+ types.string
21
+ )
22
+ // Report options
23
+ .addOptionalParam(
24
+ "report",
25
+ "Report type (summary, lcov, debug, bytecode) - can be used multiple times by passing comma-separated values",
26
+ undefined,
27
+ types.string
28
+ )
29
+ .addOptionalParam(
30
+ "reportFile",
31
+ "Output path for report file",
32
+ undefined,
33
+ types.string
34
+ )
35
+ .addOptionalParam(
36
+ "lcovVersion",
37
+ "LCOV format version",
38
+ undefined,
39
+ types.string
40
+ )
41
+ .addFlag("includeLibs", "Include libraries in coverage report")
42
+ .addFlag("excludeTests", "Exclude tests from coverage report")
43
+ .addFlag("irMinimum", "Enable viaIR with minimum optimization")
44
+ // Test filtering
45
+ .addOptionalParam(
46
+ "matchTest",
47
+ "Run tests matching pattern (--match-test)",
48
+ undefined,
49
+ types.string
50
+ )
51
+ .addOptionalParam(
52
+ "noMatchTest",
53
+ "Exclude tests matching pattern (--no-match-test)",
54
+ undefined,
55
+ types.string
56
+ )
57
+ .addOptionalParam(
58
+ "matchContract",
59
+ "Run contracts matching pattern (--match-contract)",
60
+ undefined,
61
+ types.string
62
+ )
63
+ .addOptionalParam(
64
+ "noMatchContract",
65
+ "Exclude contracts matching pattern (--no-match-contract)",
66
+ undefined,
67
+ types.string
68
+ )
69
+ .addOptionalParam(
70
+ "matchPath",
71
+ "Run files matching glob (--match-path)",
72
+ undefined,
73
+ types.string
74
+ )
75
+ .addOptionalParam(
76
+ "noMatchPath",
77
+ "Exclude files matching glob (--no-match-path)",
78
+ undefined,
79
+ types.string
80
+ )
81
+ .addOptionalParam(
82
+ "noMatchCoverage",
83
+ "Exclude files from coverage report (--no-match-coverage)",
84
+ undefined,
85
+ types.string
86
+ )
87
+ // Display options
88
+ .addOptionalParam(
89
+ "verbosity",
90
+ "Verbosity level (1-5, more v's = more verbose)",
91
+ undefined,
92
+ types.int
93
+ )
94
+ .addFlag("quiet", "Suppress log output")
95
+ .addFlag("json", "Format output as JSON")
96
+ .addFlag("md", "Format output as Markdown")
97
+ .addOptionalParam(
98
+ "color",
99
+ "Color mode (auto, always, never)",
100
+ undefined,
101
+ types.string
102
+ )
103
+ // Test execution options
104
+ .addOptionalParam(
105
+ "threads",
106
+ "Number of threads to use",
107
+ undefined,
108
+ types.int
109
+ )
110
+ .addOptionalParam(
111
+ "fuzzRuns",
112
+ "Number of fuzz runs",
113
+ undefined,
114
+ types.int
115
+ )
116
+ .addOptionalParam(
117
+ "fuzzSeed",
118
+ "Seed for fuzz randomness",
119
+ undefined,
120
+ types.string
121
+ )
122
+ .addFlag("failFast", "Stop on first failure")
123
+ .addFlag("allowFailure", "Exit 0 even if tests fail")
124
+ // EVM options
125
+ .addOptionalParam(
126
+ "forkBlockNumber",
127
+ "Fork from specific block number",
128
+ undefined,
129
+ types.int
130
+ )
131
+ .addOptionalParam(
132
+ "initialBalance",
133
+ "Initial balance for test contracts",
134
+ undefined,
135
+ types.string
136
+ )
137
+ .addOptionalParam(
138
+ "sender",
139
+ "Test sender address",
140
+ undefined,
141
+ types.string
142
+ )
143
+ .addFlag("ffi", "Enable FFI cheatcode")
144
+ // Build options
145
+ .addFlag("force", "Force recompile and redeploy")
146
+ .addFlag("noCache", "Disable cache")
147
+ .addFlag("optimize", "Enable Solidity optimizer")
148
+ .addOptionalParam(
149
+ "optimizerRuns",
150
+ "Optimizer runs",
151
+ undefined,
152
+ types.int
153
+ )
154
+ .addFlag("viaIr", "Use Yul IR compilation")
155
+ .setAction(async (taskArgs, hre: HardhatRuntimeEnvironment) => {
156
+ Logger.section("Running Forge Coverage with Diamond");
157
+
158
+ const diamondName = taskArgs.diamondName;
159
+ const networkName = hre.network.name;
160
+
161
+ // Validate required parameters
162
+ if (!diamondName) {
163
+ Logger.error("--diamond-name is required");
164
+ process.exitCode = 1;
165
+ return;
166
+ }
167
+
168
+ Logger.info(`Diamond: ${diamondName}`);
169
+ Logger.info(`Network: ${networkName}`);
170
+
171
+ try {
172
+ // Lazy-load framework classes to avoid circular dependency
173
+ const { DeploymentManager } = await import("../framework/DeploymentManager.js");
174
+ const { HelperGenerator } = await import("../framework/HelperGenerator.js");
175
+
176
+ // Step 1: Ensure Diamond deployment
177
+ Logger.section("Step 1/3: Ensuring Diamond Deployment");
178
+
179
+ const deploymentManager = new DeploymentManager(hre);
180
+
181
+ await deploymentManager.ensureDeployment(
182
+ diamondName,
183
+ networkName,
184
+ taskArgs.force || false,
185
+ false // Don't write deployment data for coverage (ephemeral by default)
186
+ );
187
+
188
+ // Step 2: Generate helpers
189
+ Logger.section("Step 2/3: Generating Solidity Helpers");
190
+
191
+ const deployment = await deploymentManager.getDeployment(
192
+ diamondName,
193
+ networkName
194
+ );
195
+
196
+ if (!deployment) {
197
+ Logger.error("No deployment found. Cannot generate helpers.");
198
+ process.exitCode = 1;
199
+ return;
200
+ }
201
+
202
+ const provider = hre.ethers.provider;
203
+ const network = await provider.getNetwork();
204
+ const chainId = Number(network.chainId);
205
+ const deploymentData = deployment.getDeployedDiamondData();
206
+
207
+ const helperGenerator = new HelperGenerator(hre);
208
+ await helperGenerator.generateDeploymentHelpers(
209
+ diamondName,
210
+ networkName,
211
+ chainId,
212
+ deploymentData,
213
+ deployment
214
+ );
215
+
216
+ // Step 3: Run coverage
217
+ Logger.section("Step 3/3: Running Forge Coverage");
218
+
219
+ // Construct fork URL for network
220
+ // Coverage requires forking from a running network to access deployed contracts
221
+ let forkUrl: string;
222
+ if (networkName !== "hardhat") {
223
+ // Use the configured network's URL
224
+ forkUrl = (provider as any)._hardhatProvider?._wrapped?.url || "http://127.0.0.1:8545";
225
+ Logger.info(`Forking from ${networkName}: ${forkUrl}`);
226
+ } else {
227
+ // Default to localhost for hardhat network
228
+ // This assumes user has `npx hardhat node` running
229
+ forkUrl = "http://127.0.0.1:8545";
230
+ Logger.warn(`⚠️ Network is "${networkName}" - defaulting to localhost fork: ${forkUrl}`);
231
+ Logger.warn(`💡 Make sure Hardhat node is running: npx hardhat node`);
232
+ Logger.warn(`💡 Or specify network explicitly: --network localhost`);
233
+ }
234
+
235
+ // Lazy-load framework to avoid circular dependency
236
+ const { ForgeCoverageFramework } = await import("../framework/ForgeCoverageFramework.js");
237
+ type CoverageOptions = Parameters<InstanceType<typeof ForgeCoverageFramework>["runCoverage"]>[0];
238
+
239
+ // Build coverage options from task args
240
+ const options: CoverageOptions = {
241
+ // Fork URL
242
+ forkUrl,
243
+
244
+ // Report options
245
+ report: taskArgs.report ? taskArgs.report.split(",") : undefined,
246
+ reportFile: taskArgs.reportFile,
247
+ lcovVersion: taskArgs.lcovVersion,
248
+ includeLibs: taskArgs.includeLibs,
249
+ excludeTests: taskArgs.excludeTests,
250
+ irMinimum: taskArgs.irMinimum,
251
+
252
+ // Test filtering
253
+ matchTest: taskArgs.matchTest,
254
+ noMatchTest: taskArgs.noMatchTest,
255
+ matchContract: taskArgs.matchContract,
256
+ noMatchContract: taskArgs.noMatchContract,
257
+ matchPath: taskArgs.matchPath,
258
+ noMatchPath: taskArgs.noMatchPath,
259
+ noMatchCoverage: taskArgs.noMatchCoverage,
260
+
261
+ // Display options
262
+ verbosity: taskArgs.verbosity,
263
+ quiet: taskArgs.quiet,
264
+ json: taskArgs.json,
265
+ md: taskArgs.md,
266
+ color: taskArgs.color as "auto" | "always" | "never" | undefined,
267
+
268
+ // Test execution options
269
+ threads: taskArgs.threads,
270
+ fuzzRuns: taskArgs.fuzzRuns,
271
+ fuzzSeed: taskArgs.fuzzSeed,
272
+ failFast: taskArgs.failFast,
273
+ allowFailure: taskArgs.allowFailure,
274
+
275
+ // EVM options
276
+ forkBlockNumber: taskArgs.forkBlockNumber,
277
+ initialBalance: taskArgs.initialBalance,
278
+ sender: taskArgs.sender,
279
+ ffi: taskArgs.ffi,
280
+
281
+ // Build options
282
+ force: taskArgs.force,
283
+ noCache: taskArgs.noCache,
284
+ optimize: taskArgs.optimize,
285
+ optimizerRuns: taskArgs.optimizerRuns,
286
+ viaIr: taskArgs.viaIr,
287
+ };
288
+
289
+ // Run coverage
290
+ const framework = new ForgeCoverageFramework(hre);
291
+ const success = await framework.runCoverage(options);
292
+
293
+ if (success) {
294
+ Logger.section("Coverage Analysis Complete");
295
+ Logger.success("Coverage analysis completed successfully!");
296
+ process.exitCode = 0;
297
+ } else {
298
+ Logger.section("Coverage Analysis Complete");
299
+ Logger.error("Coverage analysis failed");
300
+ process.exitCode = 1;
301
+ }
302
+ } catch (error: any) {
303
+ Logger.error(`Coverage execution failed: ${error.message}`);
304
+ process.exitCode = 1;
305
+ throw error;
306
+ }
307
+ });
@@ -52,3 +52,97 @@ export const DEFAULT_CONFIG: Required<DiamondsFoundryConfig> = {
52
52
  reuseDeployment: true,
53
53
  forgeTestArgs: [],
54
54
  };
55
+
56
+ /**
57
+ * Type for forge coverage report formats
58
+ */
59
+ export type CoverageReportType = "summary" | "lcov" | "debug" | "bytecode";
60
+
61
+ /**
62
+ * Type for color output modes
63
+ */
64
+ export type ColorMode = "auto" | "always" | "never";
65
+
66
+ /**
67
+ * Coverage options for forge coverage command
68
+ * Maps to all available forge coverage command-line options
69
+ */
70
+ export interface CoverageOptions {
71
+ // Report options
72
+ /** Report type(s) - can specify multiple */
73
+ report?: CoverageReportType[];
74
+ /** Output path for report file */
75
+ reportFile?: string;
76
+ /** LCOV format version (default: 1) */
77
+ lcovVersion?: string;
78
+ /** Include libraries in coverage report */
79
+ includeLibs?: boolean;
80
+ /** Exclude tests from coverage report */
81
+ excludeTests?: boolean;
82
+ /** Enable viaIR with minimum optimization */
83
+ irMinimum?: boolean;
84
+
85
+ // Test filtering
86
+ /** Run tests matching regex pattern */
87
+ matchTest?: string;
88
+ /** Exclude tests matching regex pattern */
89
+ noMatchTest?: string;
90
+ /** Run contracts matching regex pattern */
91
+ matchContract?: string;
92
+ /** Exclude contracts matching regex pattern */
93
+ noMatchContract?: string;
94
+ /** Run files matching glob pattern */
95
+ matchPath?: string;
96
+ /** Exclude files matching glob pattern */
97
+ noMatchPath?: string;
98
+ /** Exclude files from coverage report matching regex */
99
+ noMatchCoverage?: string;
100
+
101
+ // Display options
102
+ /** Verbosity level (1-5) - more v's = more verbose */
103
+ verbosity?: number;
104
+ /** Suppress log output */
105
+ quiet?: boolean;
106
+ /** Format output as JSON */
107
+ json?: boolean;
108
+ /** Format output as Markdown */
109
+ md?: boolean;
110
+ /** Color output mode */
111
+ color?: ColorMode;
112
+
113
+ // Test execution options
114
+ /** Number of threads to use for parallel execution */
115
+ threads?: number;
116
+ /** Number of fuzz test runs */
117
+ fuzzRuns?: number;
118
+ /** Seed for fuzz test randomness (for reproducibility) */
119
+ fuzzSeed?: string;
120
+ /** Stop running tests after first failure */
121
+ failFast?: boolean;
122
+ /** Exit with code 0 even if tests fail */
123
+ allowFailure?: boolean;
124
+
125
+ // EVM options
126
+ /** Fork URL for network state */
127
+ forkUrl?: string;
128
+ /** Fork from specific block number */
129
+ forkBlockNumber?: number;
130
+ /** Initial balance for deployed test contracts */
131
+ initialBalance?: string;
132
+ /** Address to use as test sender */
133
+ sender?: string;
134
+ /** Enable FFI (Foreign Function Interface) cheatcode */
135
+ ffi?: boolean;
136
+
137
+ // Build options
138
+ /** Force recompilation and cache clearing */
139
+ force?: boolean;
140
+ /** Disable compiler cache */
141
+ noCache?: boolean;
142
+ /** Enable Solidity optimizer */
143
+ optimize?: boolean;
144
+ /** Number of optimizer runs */
145
+ optimizerRuns?: number;
146
+ /** Use Yul intermediate representation compilation */
147
+ viaIr?: boolean;
148
+ }