hardhat 3.0.0 → 3.0.1

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 (57) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.js +1 -1
  3. package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.js.map +1 -1
  4. package/dist/src/internal/builtin-plugins/network-manager/network-manager.d.ts.map +1 -1
  5. package/dist/src/internal/builtin-plugins/network-manager/network-manager.js +5 -0
  6. package/dist/src/internal/builtin-plugins/network-manager/network-manager.js.map +1 -1
  7. package/dist/src/internal/builtin-plugins/solidity/build-system/compiler/compiler.d.ts.map +1 -1
  8. package/dist/src/internal/builtin-plugins/solidity/build-system/compiler/compiler.js +3 -5
  9. package/dist/src/internal/builtin-plugins/solidity/build-system/compiler/compiler.js.map +1 -1
  10. package/dist/src/internal/builtin-plugins/solidity-test/config.d.ts.map +1 -1
  11. package/dist/src/internal/builtin-plugins/solidity-test/config.js +1 -0
  12. package/dist/src/internal/builtin-plugins/solidity-test/config.js.map +1 -1
  13. package/dist/src/internal/builtin-plugins/solidity-test/edr-artifacts.d.ts.map +1 -1
  14. package/dist/src/internal/builtin-plugins/solidity-test/edr-artifacts.js +9 -4
  15. package/dist/src/internal/builtin-plugins/solidity-test/edr-artifacts.js.map +1 -1
  16. package/dist/src/internal/builtin-plugins/solidity-test/reporter.d.ts.map +1 -1
  17. package/dist/src/internal/builtin-plugins/solidity-test/reporter.js +4 -1
  18. package/dist/src/internal/builtin-plugins/solidity-test/reporter.js.map +1 -1
  19. package/dist/src/internal/builtin-plugins/solidity-test/runner.d.ts +1 -1
  20. package/dist/src/internal/builtin-plugins/solidity-test/runner.js +1 -1
  21. package/dist/src/internal/builtin-plugins/solidity-test/type-extensions.d.ts +1 -0
  22. package/dist/src/internal/builtin-plugins/solidity-test/type-extensions.d.ts.map +1 -1
  23. package/dist/src/internal/cli/help/utils.d.ts.map +1 -1
  24. package/dist/src/internal/cli/help/utils.js +8 -5
  25. package/dist/src/internal/cli/help/utils.js.map +1 -1
  26. package/dist/src/internal/cli/init/init.d.ts.map +1 -1
  27. package/dist/src/internal/cli/init/init.js +22 -19
  28. package/dist/src/internal/cli/init/init.js.map +1 -1
  29. package/dist/src/internal/cli/telemetry/sentry/anonymizer.d.ts +1 -0
  30. package/dist/src/internal/cli/telemetry/sentry/anonymizer.d.ts.map +1 -1
  31. package/dist/src/internal/cli/telemetry/sentry/anonymizer.js +83 -16
  32. package/dist/src/internal/cli/telemetry/sentry/anonymizer.js.map +1 -1
  33. package/dist/src/internal/cli/telemetry/sentry/reporter.js +1 -1
  34. package/dist/src/internal/cli/telemetry/sentry/reporter.js.map +1 -1
  35. package/dist/src/internal/cli/telemetry/sentry/subprocess.js +18 -11
  36. package/dist/src/internal/cli/telemetry/sentry/subprocess.js.map +1 -1
  37. package/dist/src/internal/cli/telemetry/sentry/transport.d.ts +1 -1
  38. package/dist/src/internal/cli/telemetry/sentry/transport.d.ts.map +1 -1
  39. package/dist/src/internal/cli/telemetry/sentry/transport.js +2 -2
  40. package/dist/src/internal/cli/telemetry/sentry/transport.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/internal/builtin-plugins/network-manager/edr/edr-provider.ts +1 -1
  43. package/src/internal/builtin-plugins/network-manager/network-manager.ts +5 -0
  44. package/src/internal/builtin-plugins/solidity/build-system/compiler/compiler.ts +3 -5
  45. package/src/internal/builtin-plugins/solidity-test/config.ts +1 -0
  46. package/src/internal/builtin-plugins/solidity-test/edr-artifacts.ts +18 -10
  47. package/src/internal/builtin-plugins/solidity-test/reporter.ts +4 -1
  48. package/src/internal/builtin-plugins/solidity-test/runner.ts +1 -1
  49. package/src/internal/builtin-plugins/solidity-test/type-extensions.ts +1 -1
  50. package/src/internal/cli/help/utils.ts +9 -5
  51. package/src/internal/cli/init/init.ts +32 -19
  52. package/src/internal/cli/telemetry/sentry/anonymizer.ts +119 -19
  53. package/src/internal/cli/telemetry/sentry/reporter.ts +1 -1
  54. package/src/internal/cli/telemetry/sentry/subprocess.ts +21 -11
  55. package/src/internal/cli/telemetry/sentry/transport.ts +2 -2
  56. package/templates/hardhat-3/01-node-test-runner-viem/package.json +10 -0
  57. package/templates/hardhat-3/02-mocha-ethers/package.json +12 -1
@@ -24,7 +24,7 @@ export function parseGlobalOptions(
24
24
  return [...globalOptionDefinitions].map(([, { option }]) => ({
25
25
  name: toCommandLineOption(option.name),
26
26
  shortName: toShortCommandLineOption(option.shortName),
27
- description: option.description,
27
+ description: trimFullStop(option.description),
28
28
  }));
29
29
  }
30
30
 
@@ -42,7 +42,7 @@ export function parseTasks(taskMap: Map<string, Task>): {
42
42
  continue;
43
43
  }
44
44
 
45
- tasks.push({ name: taskName, description: task.description });
45
+ tasks.push({ name: taskName, description: trimFullStop(task.description) });
46
46
  }
47
47
 
48
48
  return { tasks, subtasks };
@@ -54,7 +54,7 @@ export function parseSubtasks(task: Task): ArgumentDescriptor[] {
54
54
  for (const [, subtask] of task.subtasks) {
55
55
  subtasks.push({
56
56
  name: subtask.id.join(" "),
57
- description: subtask.description,
57
+ description: trimFullStop(subtask.description),
58
58
  });
59
59
  }
60
60
 
@@ -72,7 +72,7 @@ export function parseOptions(task: Task): {
72
72
  options.push({
73
73
  name: toCommandLineOption(optionName),
74
74
  shortName: toShortCommandLineOption(option.shortName),
75
- description: option.description,
75
+ description: trimFullStop(option.description),
76
76
  type: option.type,
77
77
  ...(option.defaultValue !== undefined && {
78
78
  defaultValue: option.defaultValue,
@@ -83,7 +83,7 @@ export function parseOptions(task: Task): {
83
83
  for (const { name, description, defaultValue } of task.positionalArguments) {
84
84
  positionalArguments.push({
85
85
  name,
86
- description,
86
+ description: trimFullStop(description),
87
87
  isRequired: defaultValue === undefined,
88
88
  ...(defaultValue !== undefined && {
89
89
  defaultValue: Array.isArray(defaultValue)
@@ -131,6 +131,10 @@ export function getSection(
131
131
  .join("\n")}\n`;
132
132
  }
133
133
 
134
+ function trimFullStop(str: string): string {
135
+ return str.endsWith(".") ? str.slice(0, -1) : str;
136
+ }
137
+
134
138
  function getNameString(name: string, shortName?: string): string {
135
139
  return shortName !== undefined ? [name, shortName].join(", ") : name;
136
140
  }
@@ -7,6 +7,7 @@ import {
7
7
  assertHardhatInvariant,
8
8
  HardhatError,
9
9
  } from "@nomicfoundation/hardhat-errors";
10
+ import { ensureError } from "@nomicfoundation/hardhat-utils/error";
10
11
  import {
11
12
  copy,
12
13
  ensureDir,
@@ -19,12 +20,17 @@ import {
19
20
  } from "@nomicfoundation/hardhat-utils/fs";
20
21
  import { resolveFromRoot } from "@nomicfoundation/hardhat-utils/path";
21
22
  import chalk from "chalk";
23
+ import debug from "debug";
22
24
  import * as semver from "semver";
23
25
 
24
26
  import { findClosestHardhatConfig } from "../../config-loading.js";
25
27
  import { HARDHAT_NAME } from "../../constants.js";
26
- import { getHardhatVersion } from "../../utils/package.js";
28
+ import {
29
+ getHardhatVersion,
30
+ getLatestHardhatVersion,
31
+ } from "../../utils/package.js";
27
32
  import { sendProjectTypeAnalytics } from "../telemetry/analytics/analytics.js";
33
+ import { sendErrorTelemetry } from "../telemetry/sentry/reporter.js";
28
34
 
29
35
  import {
30
36
  getDevDependenciesInstallationCommand,
@@ -52,6 +58,8 @@ export interface InitHardhatOptions {
52
58
  install?: boolean;
53
59
  }
54
60
 
61
+ const log = debug("hardhat:cli:init");
62
+
55
63
  /**
56
64
  * initHardhat implements the project initialization wizard flow.
57
65
  *
@@ -164,24 +172,29 @@ export async function printWelcomeMessage(): Promise<void> {
164
172
  chalk.cyan(`👷 Welcome to ${HARDHAT_NAME} v${hardhatVersion} 👷\n`),
165
173
  );
166
174
 
167
- // TODO: Disabled this until the first release of v3
168
- // // Warn the user if they are using an outdated version of Hardhat
169
- // try {
170
- // const latestHardhatVersion = await getLatestHardhatVersion();
171
- // if (hardhatVersion !== latestHardhatVersion) {
172
- // console.warn(
173
- // chalk.yellow.bold(
174
- // `⚠️ You are using an outdated version of Hardhat. The latest version is v${latestHardhatVersion}. Please consider upgrading to the latest version before continuing with the project initialization. ⚠️\n`,
175
- // ),
176
- // );
177
- // }
178
- // } catch (e) {
179
- // console.warn(
180
- // chalk.yellow.bold(
181
- // `⚠️ We couldn't check if you are using the latest version of Hardhat. Please consider upgrading to the latest version if you are not using it yet. ⚠️\n`,
182
- // ),
183
- // );
184
- // }
175
+ // Warn the user if they are using an outdated version of Hardhat
176
+ try {
177
+ const latestHardhatVersion = await getLatestHardhatVersion();
178
+ if (hardhatVersion !== latestHardhatVersion) {
179
+ console.warn(
180
+ chalk.yellow.bold(
181
+ `⚠️ You are using an outdated version of Hardhat. The latest version is v${latestHardhatVersion}. Please consider upgrading to the latest version before continuing with the project initialization. ⚠️\n`,
182
+ ),
183
+ );
184
+ }
185
+ } catch (error) {
186
+ ensureError(error);
187
+ try {
188
+ await sendErrorTelemetry(error);
189
+ } catch (e) {
190
+ log("Couldn't report error to sentry: %O", e);
191
+ }
192
+ console.warn(
193
+ chalk.yellow.bold(
194
+ `⚠️ We couldn't check if you are using the latest version of Hardhat. Please consider upgrading to the latest version if you are not using it yet. ⚠️\n`,
195
+ ),
196
+ );
197
+ }
185
198
  }
186
199
 
187
200
  /**
@@ -37,7 +37,6 @@ export type AnonymizeEventResult =
37
37
  | { success: true; event: Event }
38
38
  | { success: false; error: string };
39
39
 
40
- const ANONYMIZED_FILE = ANONYMIZED_PATH;
41
40
  const ANONYMIZED_MNEMONIC = "<mnemonic>";
42
41
  const MNEMONIC_PHRASE_LENGTH_THRESHOLD = 7;
43
42
  const MINIMUM_AMOUNT_OF_WORDS_TO_ANONYMIZE = 4;
@@ -57,7 +56,7 @@ export class Anonymizer {
57
56
  ): Promise<AnonymizeEnvelopeResult> {
58
57
  for (const item of envelope[1]) {
59
58
  if (item[0].type === "event") {
60
- /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions
59
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions
61
60
  -- We know that the item is an event item */
62
61
  const eventItem = item as EventItem;
63
62
 
@@ -136,6 +135,16 @@ export class Anonymizer {
136
135
  public anonymizeErrorMessage(errorMessage: string): string {
137
136
  errorMessage = this.#anonymizeMnemonic(errorMessage);
138
137
 
138
+ // We intentionally replace the config path with its own
139
+ // anonymized token, to help differentiate the config
140
+ // file in exception reports while keeping it anonymized.
141
+ if (this.#configPath !== undefined) {
142
+ errorMessage = errorMessage.replaceAll(
143
+ this.#configPath,
144
+ "<hardhat-config-file>",
145
+ );
146
+ }
147
+
139
148
  // hide hex strings of 20 chars or more
140
149
  const hexRegex = /(0x)?[0-9A-Fa-f]{20,}/g;
141
150
 
@@ -144,6 +153,26 @@ export class Anonymizer {
144
153
  );
145
154
  }
146
155
 
156
+ public filterOutEventsWithExceptionsNotRaisedByHardhat(
157
+ envelope: Envelope,
158
+ ): Envelope {
159
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions --
160
+ We are just filtering events in place, not changing any type */
161
+ envelope[1] = envelope[1].filter((item) => {
162
+ if (item[0].type !== "event") {
163
+ return true;
164
+ }
165
+
166
+ /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions
167
+ -- We know that the item is an event item */
168
+ const eventItem = item as EventItem;
169
+
170
+ return this.raisedByHardhat(eventItem[1]);
171
+ }) as any;
172
+
173
+ return envelope;
174
+ }
175
+
147
176
  public raisedByHardhat(event: Event): boolean {
148
177
  const exceptions = event?.exception?.values;
149
178
 
@@ -155,6 +184,10 @@ export class Anonymizer {
155
184
 
156
185
  const originalException = exceptions[exceptions.length - 1];
157
186
 
187
+ if (!this.#isErrorMessageAllowed(originalException)) {
188
+ return false;
189
+ }
190
+
158
191
  const frames = originalException?.stacktrace?.frames;
159
192
 
160
193
  if (frames === undefined) {
@@ -166,38 +199,94 @@ export class Anonymizer {
166
199
  continue;
167
200
  }
168
201
 
169
- if (this.#errorRaisedByPackageToIgnore(frame.filename)) {
202
+ // We don't report errors from the Hardhat.config file
203
+ if (
204
+ this.#configPath !== undefined &&
205
+ this.#configPath.includes(frame.filename)
206
+ ) {
170
207
  return false;
171
208
  }
172
209
 
173
- // we stop after finding either a hardhat file or a file from the user's
174
- // project
175
- if (this.#isHardhatFile(frame.filename)) {
176
- return true;
177
- }
210
+ if (this.#isPackageFile(frame.filename)) {
211
+ // We don't report errors from the ignored package list e.g. `ethers`
212
+ // even when buried as a subpackage of a hardhat package
213
+ if (this.#errorRaisedByPackageToIgnore(frame.filename)) {
214
+ return false;
215
+ }
178
216
 
179
- if (frame.filename === ANONYMIZED_FILE) {
180
- return false;
217
+ // We report errors from Hardhat packages, we exclude
218
+ // those from non-hardhat packages
219
+ return this.#isHardhatFile(frame.filename);
181
220
  }
182
221
 
183
- if (
184
- this.#configPath !== undefined &&
185
- this.#configPath.includes(frame.filename)
186
- ) {
222
+ // Error originating not in packages, but in the user project
223
+ // should be filtered.
224
+ if (this.#isUserProjectFile(frame.filename)) {
187
225
  return false;
188
226
  }
227
+
228
+ // Otherwise look at the next frame up
189
229
  }
190
230
 
191
231
  // if we didn't find any hardhat frame, we don't report the error
192
232
  return false;
193
233
  }
194
234
 
235
+ #isErrorMessageAllowed(originalException: Exception): boolean {
236
+ const exceptionType = originalException.type;
237
+ const exceptionMessage = originalException.value;
238
+
239
+ // Without an exception message, we can't filter so allow it
240
+ if (exceptionMessage === undefined) {
241
+ return true;
242
+ }
243
+
244
+ // Filter out required not defined in ES Modules errors
245
+ if (
246
+ exceptionType === "ReferenceError" &&
247
+ exceptionMessage ===
248
+ "require is not defined in ES module scope, you can use import instead"
249
+ ) {
250
+ return false;
251
+ }
252
+
253
+ // Filter out cannot find package when importing errors
254
+ if (
255
+ exceptionType === "Error" &&
256
+ /^Cannot find package '([^']+)' imported from .+$/.test(exceptionMessage)
257
+ ) {
258
+ return false;
259
+ }
260
+
261
+ // Filter out cannot find module errors
262
+ if (
263
+ exceptionType === "Error" &&
264
+ /^Cannot find module '([^']+)'/.test(exceptionMessage)
265
+ ) {
266
+ return false;
267
+ }
268
+
269
+ // Filter out "require() cannot be sued on an ESM graph"
270
+ if (
271
+ exceptionType === "Error" &&
272
+ exceptionMessage.startsWith(
273
+ "require() cannot be used on an ESM graph with top-level await. Use import() instead.",
274
+ )
275
+ ) {
276
+ return false;
277
+ }
278
+
279
+ return true;
280
+ }
281
+
195
282
  #errorRaisedByPackageToIgnore(filename: string): boolean {
283
+ // List of external packages that we don't want to report errors from
196
284
  const pkgsToIgnore: string[] = [
197
- path.join("node_modules", "@ethersproject"), // List of external packages that we don't want to report errors from
285
+ path.join("node_modules", "@ethersproject"),
198
286
  ];
199
287
 
200
- const pkgs = filename.match(/node_modules[\/\\][^\/\\]+/g); // Match path separators both for Windows and Unix
288
+ // Match path separators both for Windows and Unix
289
+ const pkgs = filename.match(/node_modules[\/\\][^\/\\]+/g);
201
290
 
202
291
  if (pkgs === null) {
203
292
  return false;
@@ -212,15 +301,26 @@ export class Anonymizer {
212
301
  const nomicFoundationPath = path.join("node_modules", "@nomicfoundation");
213
302
  const ignoredOrgPath = path.join("node_modules", "@ignored");
214
303
  const hardhatPath = path.join("node_modules", "hardhat");
304
+
215
305
  filename = filename.toLowerCase();
216
306
 
217
307
  return (
218
- filename.startsWith(nomicFoundationPath) ||
219
- filename.startsWith(ignoredOrgPath) ||
220
- filename.startsWith(hardhatPath)
308
+ filename.includes(nomicFoundationPath) ||
309
+ filename.includes(ignoredOrgPath) ||
310
+ filename.includes(hardhatPath)
221
311
  );
222
312
  }
223
313
 
314
+ #isPackageFile(filename: string): boolean {
315
+ return filename.includes("node_modules");
316
+ }
317
+
318
+ #isUserProjectFile(filename: string): boolean {
319
+ const anonymizedUserPath = anonymizeUserPaths(filename);
320
+
321
+ return anonymizedUserPath === ANONYMIZED_PATH;
322
+ }
323
+
224
324
  async #anonymizeExceptions(exceptions: Exception[]): Promise<Exception[]> {
225
325
  const anonymizedExceptions = await Promise.all(
226
326
  exceptions.map((exception) => this.#anonymizeException(exception)),
@@ -89,7 +89,7 @@ class Reporter {
89
89
  SENTRY_DSN,
90
90
  release,
91
91
  environment,
92
- this.#hardhatConfigPath,
92
+ () => this.#hardhatConfigPath,
93
93
  ),
94
94
  release,
95
95
  environment,
@@ -24,6 +24,7 @@ if (process.argv.length !== 7) {
24
24
  process.exit(1);
25
25
  }
26
26
 
27
+ log("Config path:", configPath);
27
28
  log("Received envelope to be sent to Sentry from a subprocess");
28
29
 
29
30
  init({
@@ -36,22 +37,31 @@ init({
36
37
  const envelope = JSON.parse(serializedEnvelope);
37
38
 
38
39
  const anonymizer = new Anonymizer(configPath);
39
- const anonymizeResult = await anonymizer.anonymizeEventsFromEnvelope(envelope);
40
40
 
41
- if (!anonymizeResult.success) {
42
- log("Failed to anonymize envelope", anonymizeResult.error);
43
- captureMessage(anonymizeResult.error);
41
+ const filteredEnvelope =
42
+ anonymizer.filterOutEventsWithExceptionsNotRaisedByHardhat(envelope);
43
+
44
+ if (filteredEnvelope[1].length === 0) {
45
+ log("The events weren't raised by Hardhat, so we don't report them");
44
46
  } else {
45
- try {
46
- log("Sending received envelope to Sentry");
47
+ const anonymizeResult =
48
+ await anonymizer.anonymizeEventsFromEnvelope(filteredEnvelope);
49
+
50
+ if (!anonymizeResult.success) {
51
+ log("Failed to anonymize envelope", anonymizeResult.error);
52
+ captureMessage(anonymizeResult.error);
53
+ } else {
54
+ try {
55
+ log("Sending received envelope to Sentry");
47
56
 
48
- await sendEnvelopeToSentryBackend(dsn, anonymizeResult.envelope);
57
+ await sendEnvelopeToSentryBackend(dsn, anonymizeResult.envelope);
49
58
 
50
- log("Successfully sent received envelope to Sentry");
51
- } catch (e) {
52
- log("Failed to send received envelope to Sentry", e);
59
+ log("Successfully sent received envelope to Sentry");
60
+ } catch (e) {
61
+ log("Failed to send received envelope to Sentry", e);
53
62
 
54
- captureException(e);
63
+ captureException(e);
64
+ }
55
65
  }
56
66
  }
57
67
 
@@ -32,7 +32,7 @@ export function createDetachedProcessTransport(
32
32
  dsn: string,
33
33
  release: string,
34
34
  environment: string,
35
- configPath?: string,
35
+ getConfigPath: () => string | undefined,
36
36
  ): Transport {
37
37
  return {
38
38
  send: (envelope) => {
@@ -52,7 +52,7 @@ export function createDetachedProcessTransport(
52
52
  let args = [
53
53
  subprocessPath,
54
54
  serializedEnvelope,
55
- configPath ?? "",
55
+ getConfigPath() ?? "",
56
56
  dsn,
57
57
  release,
58
58
  environment,
@@ -12,5 +12,15 @@
12
12
  "forge-std": "foundry-rs/forge-std#v1.9.4",
13
13
  "typescript": "~5.8.0",
14
14
  "viem": "^2.30.0"
15
+ },
16
+ "peerDependencies": {
17
+ "@nomicfoundation/hardhat-ignition-viem": "workspace:^3.0.0",
18
+ "@nomicfoundation/hardhat-keystore": "workspace:^3.0.0",
19
+ "@nomicfoundation/hardhat-network-helpers": "workspace:^3.0.0",
20
+ "@nomicfoundation/hardhat-node-test-runner": "workspace:^3.0.0",
21
+ "@nomicfoundation/hardhat-viem": "workspace:^3.0.0",
22
+ "@nomicfoundation/hardhat-viem-assertions": "workspace:^3.0.0",
23
+ "@nomicfoundation/hardhat-verify": "workspace:^3.0.0",
24
+ "@nomicfoundation/ignition-core": "workspace:^3.0.0"
15
25
  }
16
26
  }
@@ -8,7 +8,6 @@
8
8
  "hardhat": "workspace:^3.0.0",
9
9
  "@nomicfoundation/hardhat-toolbox-mocha-ethers": "workspace:^3.0.0",
10
10
  "@nomicfoundation/hardhat-ignition": "workspace:^3.0.0",
11
- "@nomicfoundation/hardhat-ethers": "workspace:^4.0.0",
12
11
  "@types/chai": "^4.2.0",
13
12
  "@types/chai-as-promised": "^8.0.1",
14
13
  "@types/mocha": ">=10.0.10",
@@ -18,5 +17,17 @@
18
17
  "forge-std": "foundry-rs/forge-std#v1.9.4",
19
18
  "mocha": "^11.0.0",
20
19
  "typescript": "~5.8.0"
20
+ },
21
+ "peerDependencies": {
22
+ "@nomicfoundation/hardhat-ethers": "workspace:^4.0.0",
23
+ "@nomicfoundation/hardhat-ethers-chai-matchers": "workspace:^3.0.0",
24
+ "@nomicfoundation/hardhat-ignition": "workspace:^3.0.0",
25
+ "@nomicfoundation/hardhat-ignition-ethers": "workspace:^3.0.0",
26
+ "@nomicfoundation/hardhat-keystore": "workspace:^3.0.0",
27
+ "@nomicfoundation/hardhat-mocha": "workspace:^3.0.0",
28
+ "@nomicfoundation/hardhat-network-helpers": "workspace:^3.0.0",
29
+ "@nomicfoundation/hardhat-typechain": "workspace:^3.0.0",
30
+ "@nomicfoundation/hardhat-verify": "workspace:^3.0.0",
31
+ "@nomicfoundation/ignition-core": "workspace:^3.0.0"
21
32
  }
22
33
  }