hardhat 3.1.9 → 3.1.10

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 (132) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/src/config.d.ts +20 -0
  3. package/dist/src/config.d.ts.map +1 -1
  4. package/dist/src/config.js +31 -0
  5. package/dist/src/config.js.map +1 -1
  6. package/dist/src/internal/builtin-plugins/coverage/coverage-manager.d.ts.map +1 -1
  7. package/dist/src/internal/builtin-plugins/coverage/coverage-manager.js +33 -21
  8. package/dist/src/internal/builtin-plugins/coverage/coverage-manager.js.map +1 -1
  9. package/dist/src/internal/builtin-plugins/flatten/task-action.d.ts.map +1 -1
  10. package/dist/src/internal/builtin-plugins/flatten/task-action.js +1 -1
  11. package/dist/src/internal/builtin-plugins/flatten/task-action.js.map +1 -1
  12. package/dist/src/internal/builtin-plugins/gas-analytics/gas-analytics-manager.js +2 -2
  13. package/dist/src/internal/builtin-plugins/gas-analytics/gas-analytics-manager.js.map +1 -1
  14. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/config.d.ts.map +1 -1
  15. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/config.js +58 -20
  16. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/config.js.map +1 -1
  17. package/dist/src/internal/builtin-plugins/network-manager/type-validation.d.ts.map +1 -1
  18. package/dist/src/internal/builtin-plugins/network-manager/type-validation.js +8 -0
  19. package/dist/src/internal/builtin-plugins/network-manager/type-validation.js.map +1 -1
  20. package/dist/src/internal/builtin-plugins/node/task-action.d.ts.map +1 -1
  21. package/dist/src/internal/builtin-plugins/node/task-action.js +1 -2
  22. package/dist/src/internal/builtin-plugins/node/task-action.js.map +1 -1
  23. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.d.ts.map +1 -1
  24. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.js +1 -1
  25. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.js.map +1 -1
  26. package/dist/src/internal/builtin-plugins/solidity/build-system/compiler/downloader.d.ts +0 -3
  27. package/dist/src/internal/builtin-plugins/solidity/build-system/compiler/downloader.d.ts.map +1 -1
  28. package/dist/src/internal/builtin-plugins/solidity/build-system/compiler/downloader.js +3 -5
  29. package/dist/src/internal/builtin-plugins/solidity/build-system/compiler/downloader.js.map +1 -1
  30. package/dist/src/internal/builtin-plugins/solidity/build-system/dependency-graph-building.d.ts +2 -1
  31. package/dist/src/internal/builtin-plugins/solidity/build-system/dependency-graph-building.d.ts.map +1 -1
  32. package/dist/src/internal/builtin-plugins/solidity/build-system/dependency-graph-building.js +4 -2
  33. package/dist/src/internal/builtin-plugins/solidity/build-system/dependency-graph-building.js.map +1 -1
  34. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.d.ts +3 -1
  35. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.d.ts.map +1 -1
  36. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.js +8 -2
  37. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.js.map +1 -1
  38. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/error-messages.d.ts.map +1 -1
  39. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/error-messages.js +19 -5
  40. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/error-messages.js.map +1 -1
  41. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/remapped-npm-packages-graph.d.ts +8 -1
  42. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/remapped-npm-packages-graph.d.ts.map +1 -1
  43. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/remapped-npm-packages-graph.js +54 -20
  44. package/dist/src/internal/builtin-plugins/solidity/build-system/resolver/remapped-npm-packages-graph.js.map +1 -1
  45. package/dist/src/internal/builtin-plugins/solidity/type-extensions.d.ts +22 -0
  46. package/dist/src/internal/builtin-plugins/solidity/type-extensions.d.ts.map +1 -1
  47. package/dist/src/internal/builtin-plugins/solidity-test/config.d.ts.map +1 -1
  48. package/dist/src/internal/builtin-plugins/solidity-test/config.js +1 -0
  49. package/dist/src/internal/builtin-plugins/solidity-test/config.js.map +1 -1
  50. package/dist/src/internal/builtin-plugins/solidity-test/type-extensions.d.ts +1 -0
  51. package/dist/src/internal/builtin-plugins/solidity-test/type-extensions.d.ts.map +1 -1
  52. package/dist/src/internal/cli/error-handler.d.ts.map +1 -1
  53. package/dist/src/internal/cli/error-handler.js +15 -0
  54. package/dist/src/internal/cli/error-handler.js.map +1 -1
  55. package/dist/src/internal/cli/telemetry/sentry/reporter.d.ts.map +1 -1
  56. package/dist/src/internal/cli/telemetry/sentry/reporter.js +4 -0
  57. package/dist/src/internal/cli/telemetry/sentry/reporter.js.map +1 -1
  58. package/dist/src/internal/core/config-validation.d.ts +3 -3
  59. package/dist/src/internal/core/config-validation.d.ts.map +1 -1
  60. package/dist/src/internal/core/config-validation.js +48 -18
  61. package/dist/src/internal/core/config-validation.js.map +1 -1
  62. package/dist/src/internal/core/tasks/builders.d.ts +26 -16
  63. package/dist/src/internal/core/tasks/builders.d.ts.map +1 -1
  64. package/dist/src/internal/core/tasks/builders.js +65 -6
  65. package/dist/src/internal/core/tasks/builders.js.map +1 -1
  66. package/dist/src/internal/core/tasks/resolved-task.d.ts +2 -2
  67. package/dist/src/internal/core/tasks/resolved-task.d.ts.map +1 -1
  68. package/dist/src/internal/core/tasks/resolved-task.js +23 -9
  69. package/dist/src/internal/core/tasks/resolved-task.js.map +1 -1
  70. package/dist/src/internal/core/tasks/task-manager.d.ts.map +1 -1
  71. package/dist/src/internal/core/tasks/task-manager.js +14 -6
  72. package/dist/src/internal/core/tasks/task-manager.js.map +1 -1
  73. package/dist/src/internal/core/tasks/validations.d.ts +2 -0
  74. package/dist/src/internal/core/tasks/validations.d.ts.map +1 -1
  75. package/dist/src/internal/core/tasks/validations.js +11 -0
  76. package/dist/src/internal/core/tasks/validations.js.map +1 -1
  77. package/dist/src/internal/deprecated-module-imported-from-hardhat2-plugin.d.ts +2 -0
  78. package/dist/src/internal/deprecated-module-imported-from-hardhat2-plugin.d.ts.map +1 -0
  79. package/dist/src/internal/deprecated-module-imported-from-hardhat2-plugin.js +12 -0
  80. package/dist/src/internal/deprecated-module-imported-from-hardhat2-plugin.js.map +1 -0
  81. package/dist/src/internal/using-hardhat2-plugin-errors.d.ts +35 -0
  82. package/dist/src/internal/using-hardhat2-plugin-errors.d.ts.map +1 -0
  83. package/dist/src/internal/using-hardhat2-plugin-errors.js +98 -0
  84. package/dist/src/internal/using-hardhat2-plugin-errors.js.map +1 -0
  85. package/dist/src/plugins.d.ts +8 -0
  86. package/dist/src/plugins.d.ts.map +1 -1
  87. package/dist/src/plugins.js +13 -0
  88. package/dist/src/plugins.js.map +1 -1
  89. package/dist/src/types/index.d.ts +15 -0
  90. package/dist/src/types/index.d.ts.map +1 -0
  91. package/dist/src/types/index.js +15 -0
  92. package/dist/src/types/index.js.map +1 -0
  93. package/dist/src/types/plugins.d.ts +2 -2
  94. package/dist/src/types/solidity/errors.d.ts +18 -0
  95. package/dist/src/types/solidity/errors.d.ts.map +1 -1
  96. package/dist/src/types/solidity/errors.js.map +1 -1
  97. package/dist/src/types/tasks.d.ts +95 -34
  98. package/dist/src/types/tasks.d.ts.map +1 -1
  99. package/package.json +10 -6
  100. package/src/config.ts +37 -0
  101. package/src/internal/builtin-plugins/coverage/coverage-manager.ts +57 -50
  102. package/src/internal/builtin-plugins/flatten/task-action.ts +1 -0
  103. package/src/internal/builtin-plugins/gas-analytics/gas-analytics-manager.ts +4 -4
  104. package/src/internal/builtin-plugins/network-manager/hook-handlers/config.ts +65 -21
  105. package/src/internal/builtin-plugins/network-manager/type-validation.ts +9 -0
  106. package/src/internal/builtin-plugins/node/task-action.ts +2 -2
  107. package/src/internal/builtin-plugins/solidity/build-system/build-system.ts +1 -0
  108. package/src/internal/builtin-plugins/solidity/build-system/compiler/downloader.ts +7 -6
  109. package/src/internal/builtin-plugins/solidity/build-system/dependency-graph-building.ts +23 -1
  110. package/src/internal/builtin-plugins/solidity/build-system/resolver/dependency-resolver.ts +16 -2
  111. package/src/internal/builtin-plugins/solidity/build-system/resolver/error-messages.ts +19 -5
  112. package/src/internal/builtin-plugins/solidity/build-system/resolver/remapped-npm-packages-graph.ts +103 -29
  113. package/src/internal/builtin-plugins/solidity/type-extensions.ts +28 -0
  114. package/src/internal/builtin-plugins/solidity-test/config.ts +1 -0
  115. package/src/internal/builtin-plugins/solidity-test/type-extensions.ts +1 -0
  116. package/src/internal/cli/error-handler.ts +18 -0
  117. package/src/internal/cli/telemetry/sentry/reporter.ts +5 -0
  118. package/src/internal/core/config-validation.ts +67 -18
  119. package/src/internal/core/tasks/builders.ts +174 -30
  120. package/src/internal/core/tasks/resolved-task.ts +31 -13
  121. package/src/internal/core/tasks/task-manager.ts +23 -5
  122. package/src/internal/core/tasks/validations.ts +40 -0
  123. package/src/internal/deprecated-module-imported-from-hardhat2-plugin.ts +12 -0
  124. package/src/internal/using-hardhat2-plugin-errors.ts +108 -0
  125. package/src/plugins.ts +16 -0
  126. package/src/types/index.ts +14 -0
  127. package/src/types/plugins.ts +2 -2
  128. package/src/types/solidity/errors.ts +22 -0
  129. package/src/types/tasks.ts +133 -36
  130. package/templates/hardhat-3/01-node-test-runner-viem/package.json +10 -10
  131. package/templates/hardhat-3/02-mocha-ethers/package.json +16 -16
  132. package/templates/hardhat-3/03-minimal/package.json +1 -1
@@ -37,6 +37,17 @@ import { UserRemappingType } from "./types.js";
37
37
 
38
38
  const HARDHAT_PROJECT_INPUT_SOURCE_NAME_ROOT = "project";
39
39
 
40
+ export type RemappingsReaderFunction = (
41
+ packageName: string,
42
+ packageVersion: string,
43
+ packagePath: string,
44
+ defaultBehavior: (
45
+ name: string,
46
+ version: string,
47
+ path: string,
48
+ ) => Promise<Array<{ remappings: string[]; source: string }>>,
49
+ ) => Promise<Array<{ remappings: string[]; source: string }>>;
50
+
40
51
  export function isResolvedUserRemapping(
41
52
  remapping: Remapping | ResolvedUserRemapping,
42
53
  ): remapping is ResolvedUserRemapping {
@@ -55,6 +66,11 @@ export class RemappedNpmPackagesGraphImplementation
55
66
  */
56
67
  readonly #hardhatProjectPackage: ResolvedNpmPackage;
57
68
 
69
+ /**
70
+ * The remappings reader function to use when reading package remappings.
71
+ */
72
+ readonly #remappingsReader: RemappingsReaderFunction;
73
+
58
74
  /**
59
75
  * This is a map of all the npm packages. Every package that has been
60
76
  * loaded by this class, is present in this map.
@@ -104,6 +120,12 @@ export class RemappedNpmPackagesGraphImplementation
104
120
 
105
121
  public static async create(
106
122
  projectRootPath: string,
123
+ remappingsReader: RemappingsReaderFunction = (
124
+ packageName,
125
+ packageVersion,
126
+ packagePath,
127
+ defaultBehavior,
128
+ ) => defaultBehavior(packageName, packageVersion, packagePath),
107
129
  ): Promise<RemappedNpmPackagesGraphImplementation> {
108
130
  const projectPackageJson = await readJsonFile<PackageJson>(
109
131
  path.join(projectRootPath, "package.json"),
@@ -117,11 +139,18 @@ export class RemappedNpmPackagesGraphImplementation
117
139
  inputSourceNameRoot: HARDHAT_PROJECT_INPUT_SOURCE_NAME_ROOT,
118
140
  };
119
141
 
120
- return new RemappedNpmPackagesGraphImplementation(resolvedNpmPackage);
142
+ return new RemappedNpmPackagesGraphImplementation(
143
+ resolvedNpmPackage,
144
+ remappingsReader,
145
+ );
121
146
  }
122
147
 
123
- private constructor(hardhatProjectPackage: ResolvedNpmPackage) {
148
+ private constructor(
149
+ hardhatProjectPackage: ResolvedNpmPackage,
150
+ remappingsReader: RemappingsReaderFunction,
151
+ ) {
124
152
  this.#hardhatProjectPackage = hardhatProjectPackage;
153
+ this.#remappingsReader = remappingsReader;
125
154
  this.#insertNewPackage(hardhatProjectPackage);
126
155
  }
127
156
 
@@ -397,41 +426,88 @@ export class RemappedNpmPackagesGraphImplementation
397
426
  UserRemappingError[]
398
427
  >
399
428
  > {
400
- const remappingsTxtFiles = await getAllFilesMatching(
429
+ const allRemappings = await this.#remappingsReader(
430
+ npmPackage.name,
431
+ npmPackage.version,
401
432
  npmPackage.rootFsPath,
402
- (f) => path.basename(f) === "remappings.txt",
403
- (f) => !f.endsWith("node_modules"),
433
+ async (_packageName, _packageVersion, packagePath) =>
434
+ this.#defaultReadPackageRemappings(packagePath),
404
435
  );
405
436
 
406
- const remappings = [];
407
- const errors = [];
437
+ return this.#parseAndDeduplicateRemappings(npmPackage, allRemappings);
438
+ }
408
439
 
409
- for (const remappingsTxtFsPath of remappingsTxtFiles) {
410
- const packageRemappingsTxtContents =
411
- await readUtf8File(remappingsTxtFsPath);
440
+ /**
441
+ * The default behavior of reading all the remappings.txt files in a package.
442
+ * @param packagePath The fs path to the root of the package.
443
+ * @returns An array with one entry per remappings.txt file, with the
444
+ * contents of the file and the fs path to the file.
445
+ */
446
+ async #defaultReadPackageRemappings(
447
+ packagePath: string,
448
+ ): Promise<Array<{ remappings: string[]; source: string }>> {
449
+ const remappingsTxtFiles = await getAllFilesMatching(
450
+ packagePath,
451
+ (f) => path.basename(f) === "remappings.txt",
452
+ (f) => !f.endsWith("node_modules"),
453
+ );
412
454
 
413
- const rawUserRemappings = packageRemappingsTxtContents
455
+ const results: Array<{ remappings: string[]; source: string }> = [];
456
+ for (const file of remappingsTxtFiles) {
457
+ const contents = await readUtf8File(file);
458
+ const lines = contents
414
459
  .split("\n")
415
460
  .map((line) => line.trim())
416
- .filter((line) => line !== "")
417
- .filter((line) => !line.startsWith("#"));
461
+ .filter((line) => line !== "" && !line.startsWith("#"));
462
+ results.push({ remappings: lines, source: file });
463
+ }
464
+
465
+ return results;
466
+ }
418
467
 
419
- for (const userRemapping of rawUserRemappings) {
420
- const result = await this.#parseUserRemapping(
468
+ /**
469
+ * Parses and deduplicates by "context:prefix" all the remappings from the
470
+ * package.
471
+ *
472
+ * @param npmPackage The npm package.
473
+ * @param allRemappings An array with all the remappings.txt files in the
474
+ * package and their content.
475
+ * @returns A result with the parsed and deduplicated remappings, or an error
476
+ * if there was a problem parsing any of them.
477
+ */
478
+ #parseAndDeduplicateRemappings(
479
+ npmPackage: ResolvedNpmPackage,
480
+ allRemappings: Array<{ remappings: string[]; source: string }>,
481
+ ): Result<
482
+ Array<LocalUserRemapping | UnresolvedNpmUserRemapping>,
483
+ UserRemappingError[]
484
+ > {
485
+ const remappings: Array<LocalUserRemapping | UnresolvedNpmUserRemapping> =
486
+ [];
487
+ const errors: UserRemappingError[] = [];
488
+ const seen = new Set<string>(); // Track by "context:prefix"
489
+
490
+ for (const { remappings: remappingStrings, source } of allRemappings) {
491
+ for (const remappingString of remappingStrings) {
492
+ const result = this.#parseUserRemapping(
421
493
  npmPackage,
422
- remappingsTxtFsPath,
423
- userRemapping,
494
+ source,
495
+ remappingString,
424
496
  );
425
497
 
426
498
  if (!result.success) {
427
499
  errors.push(result.error);
428
- } else {
429
- // If parsing returned `undefined`, it means that it should be
430
- // ignored.
431
- if (result.value === undefined) {
432
- continue;
433
- }
500
+ continue;
501
+ }
502
+
503
+ if (result.value === undefined) {
504
+ continue;
505
+ }
434
506
 
507
+ // Deduplicate by (context + prefix) - first occurrence wins
508
+ const key = `${result.value.context}:${result.value.prefix}`;
509
+ if (!seen.has(key)) {
510
+ seen.add(key);
435
511
  remappings.push(result.value);
436
512
  }
437
513
  }
@@ -454,15 +530,13 @@ export class RemappedNpmPackagesGraphImplementation
454
530
  * @returns The parsed user remapping, or undefined if it should be ignored.
455
531
  * If the parsing and validation fails, an error is returned.
456
532
  */
457
- async #parseUserRemapping(
533
+ #parseUserRemapping(
458
534
  npmPackage: ResolvedNpmPackage,
459
535
  sourceOfTheRemapping: string,
460
536
  remappingString: string,
461
- ): Promise<
462
- Result<
463
- LocalUserRemapping | UnresolvedNpmUserRemapping | undefined,
464
- UserRemappingError
465
- >
537
+ ): Result<
538
+ LocalUserRemapping | UnresolvedNpmUserRemapping | undefined,
539
+ UserRemappingError
466
540
  > {
467
541
  // We first parse the remapping string and validate that it doesn't have
468
542
  // a context starting with `npm/`, and that the prefix and targets end in /.
@@ -242,5 +242,33 @@ declare module "../../../types/hooks.js" {
242
242
  nextSolcConfig: SolcConfig,
243
243
  ) => Promise<CompilerOutput>,
244
244
  ): Promise<CompilerOutput>;
245
+
246
+ /**
247
+ * Provide a handler for this hook to supply remappings for npm packages.
248
+ *
249
+ * This hook is called when the resolver needs to read remappings for a package.
250
+ * Handlers can provide remappings from alternative sources (e.g., foundry.toml)
251
+ * in addition to the default remappings.txt files.
252
+ *
253
+ * @param context The hook context.
254
+ * @param packageName The name of the npm package.
255
+ * @param packageVersion The version of the npm package.
256
+ * @param packagePath The absolute filesystem path to the package root.
257
+ * @param next A function to get remappings from other sources (including default behavior).
258
+ * @returns An array of remapping sources, each containing an array of remapping strings
259
+ * and the source path they came from.
260
+ */
261
+ readNpmPackageRemappings: (
262
+ context: HookContext,
263
+ packageName: string,
264
+ packageVersion: string,
265
+ packagePath: string,
266
+ next: (
267
+ nextContext: HookContext,
268
+ nextPackageName: string,
269
+ nextPackageVersion: string,
270
+ nextPackagePath: string,
271
+ ) => Promise<Array<{ remappings: string[]; source: string }>>,
272
+ ) => Promise<Array<{ remappings: string[]; source: string }>>;
245
273
  }
246
274
  }
@@ -44,6 +44,7 @@ const solidityTestUserConfigType = z.object({
44
44
  coinbase: z.string().startsWith("0x").optional(),
45
45
  blockTimestamp: z.bigint().optional(),
46
46
  prevRandao: z.bigint().optional(),
47
+ gasLimit: z.bigint().optional(),
47
48
  blockGasLimit: z.bigint().or(z.literal(false)).optional(),
48
49
  fuzz: z
49
50
  .object({
@@ -35,6 +35,7 @@ declare module "../../../types/test.js" {
35
35
  coinbase?: string; // 0x-prefixed hex string
36
36
  blockTimestamp?: bigint;
37
37
  prevRandao?: bigint;
38
+ gasLimit?: bigint;
38
39
  blockGasLimit?: bigint | false;
39
40
 
40
41
  fuzz?: {
@@ -5,6 +5,7 @@ import {
5
5
  import chalk from "chalk";
6
6
 
7
7
  import { HARDHAT_NAME, HARDHAT_WEBSITE_URL } from "../constants.js";
8
+ import { UsingHardhat2PluginError } from "../using-hardhat2-plugin-errors.js";
8
9
 
9
10
  /**
10
11
  * The different categories of errors that can be handled by hardhat cli.
@@ -69,6 +70,11 @@ export function printErrorMessages(
69
70
  shouldShowStackTraces: boolean = false,
70
71
  print: (message: string | Error) => void = console.error,
71
72
  ): void {
73
+ if (error instanceof UsingHardhat2PluginError) {
74
+ printUsingHardhat2Error(error, print);
75
+ return;
76
+ }
77
+
72
78
  const showStackTraces =
73
79
  shouldShowStackTraces ||
74
80
  getErrorWithCategory(error).category === ErrorCategory.OTHER;
@@ -146,3 +152,15 @@ function getErrorMessages(error: Error): ErrorMessages {
146
152
  };
147
153
  }
148
154
  }
155
+ function printUsingHardhat2Error(
156
+ error: UsingHardhat2PluginError,
157
+ print: (message: string | Error) => void = console.error,
158
+ ): void {
159
+ print(chalk.red.bold(`Hardhat 3 installation error:`));
160
+ print("");
161
+ if (error.callerRelativePath !== undefined) {
162
+ print(error.message);
163
+ } else {
164
+ print(error.stack);
165
+ }
166
+ }
@@ -8,6 +8,7 @@ import {
8
8
  ProviderError,
9
9
  UnknownError,
10
10
  } from "../../../builtin-plugins/network-manager/provider-errors.js";
11
+ import { UsingHardhat2PluginError } from "../../../using-hardhat2-plugin-errors.js";
11
12
  import { getHardhatVersion } from "../../../utils/package.js";
12
13
  import { isTelemetryAllowed } from "../telemetry-permissions.js";
13
14
 
@@ -134,6 +135,10 @@ class Reporter {
134
135
  return false;
135
136
  }
136
137
 
138
+ if (error instanceof UsingHardhat2PluginError) {
139
+ return false;
140
+ }
141
+
137
142
  if (HardhatPluginError.isHardhatPluginError(error)) {
138
143
  // Don't log errors from third-party plugins
139
144
  return false;
@@ -18,9 +18,9 @@ import {
18
18
  } from "../../types/arguments.js";
19
19
  import {
20
20
  type EmptyTaskDefinition,
21
+ TaskDefinitionType,
21
22
  type NewTaskDefinition,
22
23
  type TaskDefinition,
23
- TaskDefinitionType,
24
24
  type TaskOverrideDefinition,
25
25
  } from "../../types/tasks.js";
26
26
 
@@ -185,6 +185,7 @@ function validatePath(
185
185
  export function validateTasksConfig(
186
186
  tasks: TaskDefinition[],
187
187
  path: Array<string | number> = [],
188
+ isPlugin: boolean = false,
188
189
  ): HardhatUserConfigValidationError[] {
189
190
  const validationErrors: HardhatUserConfigValidationError[] = [];
190
191
 
@@ -207,13 +208,13 @@ export function validateTasksConfig(
207
208
  }
208
209
  case TaskDefinitionType.NEW_TASK: {
209
210
  validationErrors.push(
210
- ...validateNewTask(task, [...path, "tasks", index]),
211
+ ...validateNewTask(task, [...path, "tasks", index], isPlugin),
211
212
  );
212
213
  break;
213
214
  }
214
215
  case TaskDefinitionType.TASK_OVERRIDE: {
215
216
  validationErrors.push(
216
- ...validateTaskOverride(task, [...path, "tasks", index]),
217
+ ...validateTaskOverride(task, [...path, "tasks", index], isPlugin),
217
218
  );
218
219
  break;
219
220
  }
@@ -252,6 +253,7 @@ export function validateEmptyTask(
252
253
  export function validateNewTask(
253
254
  task: NewTaskDefinition,
254
255
  path: Array<string | number>,
256
+ isPlugin: boolean = false,
255
257
  ): HardhatUserConfigValidationError[] {
256
258
  const validationErrors: HardhatUserConfigValidationError[] = [];
257
259
 
@@ -272,13 +274,7 @@ export function validateNewTask(
272
274
  });
273
275
  }
274
276
 
275
- if (typeof task.action !== "function") {
276
- validationErrors.push({
277
- path: [...path, "action"],
278
- message:
279
- "task action must be a lazy import function returning a module with a default export",
280
- });
281
- }
277
+ validationErrors.push(...validateActionFields(task, path, isPlugin));
282
278
 
283
279
  if (isObject(task.options)) {
284
280
  validationErrors.push(
@@ -308,6 +304,7 @@ export function validateNewTask(
308
304
  export function validateTaskOverride(
309
305
  task: TaskOverrideDefinition,
310
306
  path: Array<string | number>,
307
+ isPlugin: boolean = false,
311
308
  ): HardhatUserConfigValidationError[] {
312
309
  const validationErrors: HardhatUserConfigValidationError[] = [];
313
310
 
@@ -328,13 +325,7 @@ export function validateTaskOverride(
328
325
  });
329
326
  }
330
327
 
331
- if (typeof task.action !== "function") {
332
- validationErrors.push({
333
- path: [...path, "action"],
334
- message:
335
- "task action must be a lazy import function returning a module with a default export",
336
- });
337
- }
328
+ validationErrors.push(...validateActionFields(task, path, isPlugin));
338
329
 
339
330
  if (isObject(task.options)) {
340
331
  validationErrors.push(
@@ -350,6 +341,60 @@ export function validateTaskOverride(
350
341
  return validationErrors;
351
342
  }
352
343
 
344
+ function validateActionFields(
345
+ task: { action?: unknown; inlineAction?: unknown },
346
+ path: Array<string | number>,
347
+ isPlugin: boolean = false,
348
+ ): HardhatUserConfigValidationError[] {
349
+ const validationErrors: HardhatUserConfigValidationError[] = [];
350
+
351
+ // Mutual exclusivity: cannot have both action and inlineAction
352
+ if (task.action !== undefined && task.inlineAction !== undefined) {
353
+ validationErrors.push({
354
+ path: [...path],
355
+ message: 'task cannot define both "action" and "inlineAction"',
356
+ });
357
+ }
358
+
359
+ if (isPlugin && task.inlineAction !== undefined) {
360
+ validationErrors.push({
361
+ path: [...path, "inlineAction"],
362
+ message:
363
+ "plugins cannot use inline actions. Use a lazy action import instead",
364
+ });
365
+ }
366
+
367
+ // At least one action must be defined
368
+ if (task.action === undefined && task.inlineAction === undefined) {
369
+ validationErrors.push({
370
+ path: [...path, "action"],
371
+ message: 'task must define either "action" or "inlineAction"',
372
+ });
373
+ }
374
+
375
+ if (task.action !== undefined) {
376
+ if (typeof task.action !== "function") {
377
+ validationErrors.push({
378
+ path: [...path, "action"],
379
+ message:
380
+ "task action must be a lazy import function returning a module with a default export",
381
+ });
382
+ }
383
+ }
384
+
385
+ if (task.inlineAction !== undefined) {
386
+ if (typeof task.inlineAction !== "function") {
387
+ validationErrors.push({
388
+ path: [...path, "inlineAction"],
389
+ message:
390
+ "task inlineAction must be a function implementing the task's behavior",
391
+ });
392
+ }
393
+ }
394
+
395
+ return validationErrors;
396
+ }
397
+
353
398
  export function validateOptions(
354
399
  options: Record<string, OptionDefinition>,
355
400
  path: Array<string | number>,
@@ -665,7 +710,11 @@ export function validatePluginsConfig(
665
710
  if (plugin.tasks !== undefined) {
666
711
  if (Array.isArray(plugin.tasks)) {
667
712
  validationErrors.push(
668
- ...validateTasksConfig(plugin.tasks, [...path, "plugins", index]),
713
+ ...validateTasksConfig(
714
+ plugin.tasks,
715
+ [...path, "plugins", index],
716
+ true,
717
+ ),
669
718
  );
670
719
  } else {
671
720
  validationErrors.push({