hardhat 3.0.5 → 3.0.7

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 (114) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/src/internal/builtin-plugins/coverage/hook-handlers/solidity.d.ts.map +1 -1
  3. package/dist/src/internal/builtin-plugins/coverage/hook-handlers/solidity.js +1 -2
  4. package/dist/src/internal/builtin-plugins/coverage/hook-handlers/solidity.js.map +1 -1
  5. package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.d.ts +1 -0
  6. package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.d.ts.map +1 -1
  7. package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.js +9 -2
  8. package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.js.map +1 -1
  9. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/hre.d.ts.map +1 -1
  10. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/hre.js +13 -5
  11. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/hre.js.map +1 -1
  12. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/network.js +1 -1
  13. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/network.js.map +1 -1
  14. package/dist/src/internal/builtin-plugins/network-manager/network-manager.d.ts +4 -3
  15. package/dist/src/internal/builtin-plugins/network-manager/network-manager.d.ts.map +1 -1
  16. package/dist/src/internal/builtin-plugins/network-manager/network-manager.js +79 -37
  17. package/dist/src/internal/builtin-plugins/network-manager/network-manager.js.map +1 -1
  18. package/dist/src/internal/builtin-plugins/network-manager/type-validation.js +2 -2
  19. package/dist/src/internal/builtin-plugins/network-manager/type-validation.js.map +1 -1
  20. package/dist/src/internal/builtin-plugins/node/artifacts/build-info-watcher.d.ts +25 -0
  21. package/dist/src/internal/builtin-plugins/node/artifacts/build-info-watcher.d.ts.map +1 -0
  22. package/dist/src/internal/builtin-plugins/node/artifacts/build-info-watcher.js +56 -0
  23. package/dist/src/internal/builtin-plugins/node/artifacts/build-info-watcher.js.map +1 -0
  24. package/dist/src/internal/builtin-plugins/node/helpers.d.ts +12 -0
  25. package/dist/src/internal/builtin-plugins/node/helpers.d.ts.map +1 -1
  26. package/dist/src/internal/builtin-plugins/node/helpers.js +32 -0
  27. package/dist/src/internal/builtin-plugins/node/helpers.js.map +1 -1
  28. package/dist/src/internal/builtin-plugins/node/json-rpc/server.d.ts +3 -10
  29. package/dist/src/internal/builtin-plugins/node/json-rpc/server.d.ts.map +1 -1
  30. package/dist/src/internal/builtin-plugins/node/json-rpc/server.js +1 -1
  31. package/dist/src/internal/builtin-plugins/node/json-rpc/server.js.map +1 -1
  32. package/dist/src/internal/builtin-plugins/node/task-action.d.ts.map +1 -1
  33. package/dist/src/internal/builtin-plugins/node/task-action.js +14 -4
  34. package/dist/src/internal/builtin-plugins/node/task-action.js.map +1 -1
  35. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.d.ts +13 -5
  36. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.d.ts.map +1 -1
  37. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.js +126 -58
  38. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.js.map +1 -1
  39. package/dist/src/internal/builtin-plugins/solidity/build-system/cache.d.ts +1 -1
  40. package/dist/src/internal/builtin-plugins/solidity/build-system/cache.d.ts.map +1 -1
  41. package/dist/src/internal/builtin-plugins/solidity/hook-handlers/hre.d.ts.map +1 -1
  42. package/dist/src/internal/builtin-plugins/solidity/hook-handlers/hre.js +14 -6
  43. package/dist/src/internal/builtin-plugins/solidity/hook-handlers/hre.js.map +1 -1
  44. package/dist/src/internal/builtin-plugins/solidity/index.d.ts.map +1 -1
  45. package/dist/src/internal/builtin-plugins/solidity/index.js +8 -0
  46. package/dist/src/internal/builtin-plugins/solidity/index.js.map +1 -1
  47. package/dist/src/internal/builtin-plugins/solidity/tasks/build.d.ts +4 -2
  48. package/dist/src/internal/builtin-plugins/solidity/tasks/build.d.ts.map +1 -1
  49. package/dist/src/internal/builtin-plugins/solidity/tasks/build.js +37 -10
  50. package/dist/src/internal/builtin-plugins/solidity/tasks/build.js.map +1 -1
  51. package/dist/src/internal/builtin-plugins/solidity-test/config.d.ts +4 -2
  52. package/dist/src/internal/builtin-plugins/solidity-test/config.d.ts.map +1 -1
  53. package/dist/src/internal/builtin-plugins/solidity-test/config.js +24 -4
  54. package/dist/src/internal/builtin-plugins/solidity-test/config.js.map +1 -1
  55. package/dist/src/internal/builtin-plugins/solidity-test/helpers.d.ts +3 -1
  56. package/dist/src/internal/builtin-plugins/solidity-test/helpers.d.ts.map +1 -1
  57. package/dist/src/internal/builtin-plugins/solidity-test/helpers.js +26 -3
  58. package/dist/src/internal/builtin-plugins/solidity-test/helpers.js.map +1 -1
  59. package/dist/src/internal/builtin-plugins/solidity-test/hook-handlers/config.d.ts.map +1 -1
  60. package/dist/src/internal/builtin-plugins/solidity-test/hook-handlers/config.js +1 -1
  61. package/dist/src/internal/builtin-plugins/solidity-test/hook-handlers/config.js.map +1 -1
  62. package/dist/src/internal/builtin-plugins/solidity-test/task-action.d.ts.map +1 -1
  63. package/dist/src/internal/builtin-plugins/solidity-test/task-action.js +26 -43
  64. package/dist/src/internal/builtin-plugins/solidity-test/task-action.js.map +1 -1
  65. package/dist/src/internal/builtin-plugins/solidity-test/type-extensions.d.ts +17 -7
  66. package/dist/src/internal/builtin-plugins/solidity-test/type-extensions.d.ts.map +1 -1
  67. package/dist/src/internal/core/hre.d.ts +29 -1
  68. package/dist/src/internal/core/hre.d.ts.map +1 -1
  69. package/dist/src/internal/core/hre.js +57 -20
  70. package/dist/src/internal/core/hre.js.map +1 -1
  71. package/dist/src/internal/core/plugins/resolve-plugin-list.js +40 -3
  72. package/dist/src/internal/core/plugins/resolve-plugin-list.js.map +1 -1
  73. package/dist/src/internal/utils/package.d.ts +1 -0
  74. package/dist/src/internal/utils/package.d.ts.map +1 -1
  75. package/dist/src/internal/utils/package.js +17 -1
  76. package/dist/src/internal/utils/package.js.map +1 -1
  77. package/dist/src/types/hre.d.ts +4 -0
  78. package/dist/src/types/hre.d.ts.map +1 -1
  79. package/dist/src/types/network.d.ts +40 -0
  80. package/dist/src/types/network.d.ts.map +1 -1
  81. package/dist/src/types/plugins.d.ts +8 -0
  82. package/dist/src/types/plugins.d.ts.map +1 -1
  83. package/dist/src/types/solidity/build-system.d.ts +23 -8
  84. package/dist/src/types/solidity/build-system.d.ts.map +1 -1
  85. package/package.json +5 -4
  86. package/src/internal/builtin-plugins/coverage/hook-handlers/solidity.ts +2 -3
  87. package/src/internal/builtin-plugins/network-manager/edr/edr-provider.ts +20 -1
  88. package/src/internal/builtin-plugins/network-manager/hook-handlers/hre.ts +36 -18
  89. package/src/internal/builtin-plugins/network-manager/hook-handlers/network.ts +1 -1
  90. package/src/internal/builtin-plugins/network-manager/network-manager.ts +137 -60
  91. package/src/internal/builtin-plugins/network-manager/type-validation.ts +2 -2
  92. package/src/internal/builtin-plugins/node/artifacts/build-info-watcher.ts +82 -0
  93. package/src/internal/builtin-plugins/node/helpers.ts +64 -0
  94. package/src/internal/builtin-plugins/node/json-rpc/server.ts +3 -10
  95. package/src/internal/builtin-plugins/node/task-action.ts +31 -5
  96. package/src/internal/builtin-plugins/solidity/build-system/build-system.ts +203 -98
  97. package/src/internal/builtin-plugins/solidity/build-system/cache.ts +1 -1
  98. package/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts +22 -5
  99. package/src/internal/builtin-plugins/solidity/index.ts +8 -0
  100. package/src/internal/builtin-plugins/solidity/tasks/build.ts +59 -16
  101. package/src/internal/builtin-plugins/solidity-test/config.ts +46 -3
  102. package/src/internal/builtin-plugins/solidity-test/helpers.ts +44 -4
  103. package/src/internal/builtin-plugins/solidity-test/hook-handlers/config.ts +5 -1
  104. package/src/internal/builtin-plugins/solidity-test/task-action.ts +39 -54
  105. package/src/internal/builtin-plugins/solidity-test/type-extensions.ts +23 -9
  106. package/src/internal/core/hre.ts +102 -32
  107. package/src/internal/core/plugins/resolve-plugin-list.ts +57 -3
  108. package/src/internal/utils/package.ts +31 -1
  109. package/src/types/hre.ts +4 -0
  110. package/src/types/network.ts +45 -0
  111. package/src/types/plugins.ts +5 -0
  112. package/src/types/solidity/build-system.ts +23 -7
  113. package/templates/hardhat-3/01-node-test-runner-viem/package.json +3 -3
  114. package/templates/hardhat-3/02-mocha-ethers/package.json +4 -4
@@ -2,36 +2,40 @@ import type { CoverageConfig } from "./edr/types/coverage.js";
2
2
  import type { ArtifactManager } from "../../../types/artifacts.js";
3
3
  import type {
4
4
  ChainDescriptorsConfig,
5
+ HardhatUserConfig,
5
6
  NetworkConfig,
6
7
  NetworkConfigOverride,
7
- NetworkUserConfig,
8
8
  } from "../../../types/config.js";
9
9
  import type { HookManager } from "../../../types/hooks.js";
10
10
  import type {
11
11
  ChainType,
12
12
  DefaultChainType,
13
+ JsonRpcServer,
13
14
  NetworkConnection,
14
15
  NetworkConnectionParams,
15
16
  NetworkManager,
16
17
  } from "../../../types/network.js";
18
+ import type { HardhatPlugin } from "../../../types/plugins.js";
17
19
  import type {
18
20
  EthereumProvider,
19
21
  JsonRpcRequest,
20
22
  JsonRpcResponse,
21
23
  } from "../../../types/providers.js";
22
24
 
23
- import { HardhatError } from "@nomicfoundation/hardhat-errors";
24
- import { readBinaryFile } from "@nomicfoundation/hardhat-utils/fs";
25
+ import {
26
+ HardhatError,
27
+ assertHardhatInvariant,
28
+ } from "@nomicfoundation/hardhat-errors";
29
+ import { exists, readBinaryFile } from "@nomicfoundation/hardhat-utils/fs";
25
30
  import { deepMerge } from "@nomicfoundation/hardhat-utils/lang";
26
31
 
27
- import { resolveConfigurationVariable } from "../../core/configuration-variables.js";
32
+ import { resolveUserConfigToHardhatConfig } from "../../core/hre.js";
28
33
  import { isSupportedChainType } from "../../edr/chain-type.js";
34
+ import { JsonRpcServerImplementation } from "../node/json-rpc/server.js";
29
35
 
30
- import { resolveEdrNetwork, resolveHttpNetwork } from "./config-resolution.js";
31
36
  import { EdrProvider } from "./edr/edr-provider.js";
32
37
  import { HttpProvider } from "./http-provider.js";
33
38
  import { NetworkConnectionImplementation } from "./network-connection.js";
34
- import { validateNetworkConfigOverride } from "./type-validation.js";
35
39
 
36
40
  export type JsonRpcRequestWrapperFunction = (
37
41
  request: JsonRpcRequest,
@@ -44,8 +48,10 @@ export class NetworkManagerImplementation implements NetworkManager {
44
48
  readonly #networkConfigs: Readonly<Record<string, Readonly<NetworkConfig>>>;
45
49
  readonly #hookManager: Readonly<HookManager>;
46
50
  readonly #artifactsManager: Readonly<ArtifactManager>;
47
- readonly #userConfigNetworks: Readonly<Record<string, NetworkUserConfig>>;
51
+ readonly #userConfig: Readonly<HardhatUserConfig>;
48
52
  readonly #chainDescriptors: Readonly<ChainDescriptorsConfig>;
53
+ readonly #userProvidedConfigPath: Readonly<string | undefined>;
54
+ readonly #projectRoot: string;
49
55
 
50
56
  #nextConnectionId = 0;
51
57
 
@@ -55,16 +61,20 @@ export class NetworkManagerImplementation implements NetworkManager {
55
61
  networkConfigs: Record<string, NetworkConfig>,
56
62
  hookManager: HookManager,
57
63
  artifactsManager: ArtifactManager,
58
- userConfigNetworks: Record<string, NetworkUserConfig> | undefined,
64
+ userConfig: HardhatUserConfig,
59
65
  chainDescriptors: ChainDescriptorsConfig,
66
+ userProvidedConfigPath: string | undefined,
67
+ projectRoot: string,
60
68
  ) {
61
69
  this.#defaultNetwork = defaultNetwork;
62
70
  this.#defaultChainType = defaultChainType;
63
71
  this.#networkConfigs = networkConfigs;
64
72
  this.#hookManager = hookManager;
65
73
  this.#artifactsManager = artifactsManager;
66
- this.#userConfigNetworks = userConfigNetworks ?? {};
74
+ this.#userConfig = userConfig;
67
75
  this.#chainDescriptors = chainDescriptors;
76
+ this.#userProvidedConfigPath = userProvidedConfigPath;
77
+ this.#projectRoot = projectRoot;
68
78
  }
69
79
 
70
80
  public async connect<
@@ -97,6 +107,23 @@ export class NetworkManagerImplementation implements NetworkManager {
97
107
  return networkConnection as NetworkConnection<ChainTypeT>;
98
108
  }
99
109
 
110
+ public async createServer(
111
+ networkOrParams: NetworkConnectionParams | string = "default",
112
+ _hostname?: string,
113
+ port?: number,
114
+ ): Promise<JsonRpcServer> {
115
+ const insideDocker = await exists("/.dockerenv");
116
+ const hostname = _hostname ?? (insideDocker ? "0.0.0.0" : "127.0.0.1");
117
+
118
+ const { provider } = await this.connect(networkOrParams);
119
+
120
+ return new JsonRpcServerImplementation({
121
+ hostname,
122
+ port,
123
+ provider,
124
+ });
125
+ }
126
+
100
127
  async #initializeNetworkConnection<ChainTypeT extends ChainType | string>(
101
128
  networkName?: string,
102
129
  chainType?: ChainTypeT,
@@ -113,57 +140,10 @@ export class NetworkManagerImplementation implements NetworkManager {
113
140
  );
114
141
  }
115
142
 
116
- let resolvedNetworkConfigOverride: NetworkConfig | undefined;
117
- if (networkConfigOverride !== undefined) {
118
- if (
119
- "type" in networkConfigOverride &&
120
- networkConfigOverride.type !==
121
- this.#networkConfigs[resolvedNetworkName].type
122
- ) {
123
- throw new HardhatError(
124
- HardhatError.ERRORS.CORE.NETWORK.INVALID_CONFIG_OVERRIDE,
125
- {
126
- errors: `\t* The type of the network cannot be changed.`,
127
- },
128
- );
129
- }
130
-
131
- const newConfig = deepMerge(
132
- this.#userConfigNetworks[resolvedNetworkName],
133
- networkConfigOverride,
134
- );
135
-
136
- // As normalizeNetworkConfigOverride is not type-safe, we validate the
137
- // normalized network config override immediately after normalizing it.
138
- const validationErrors = await validateNetworkConfigOverride(newConfig);
139
- if (validationErrors.length > 0) {
140
- throw new HardhatError(
141
- HardhatError.ERRORS.CORE.NETWORK.INVALID_CONFIG_OVERRIDE,
142
- {
143
- errors: `\t${validationErrors
144
- .map((error) =>
145
- error.path.length > 0
146
- ? `* Error in ${error.path.join(".")}: ${error.message}`
147
- : `* ${error.message}`,
148
- )
149
- .join("\n\t")}`,
150
- },
151
- );
152
- }
153
-
154
- resolvedNetworkConfigOverride =
155
- newConfig.type === "http"
156
- ? resolveHttpNetwork(newConfig, (strOrConfigVar) =>
157
- resolveConfigurationVariable(this.#hookManager, strOrConfigVar),
158
- )
159
- : resolveEdrNetwork(newConfig, "", (strOrConfigVar) =>
160
- resolveConfigurationVariable(this.#hookManager, strOrConfigVar),
161
- );
162
- }
163
-
164
- const resolvedNetworkConfig =
165
- resolvedNetworkConfigOverride ??
166
- this.#networkConfigs[resolvedNetworkName];
143
+ const resolvedNetworkConfig = await this.#resolveNetworkConfig(
144
+ resolvedNetworkName,
145
+ networkConfigOverride,
146
+ );
167
147
 
168
148
  /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions
169
149
  -- Cast to ChainTypeT because we know it's valid */
@@ -293,6 +273,88 @@ export class NetworkManagerImplementation implements NetworkManager {
293
273
  );
294
274
  }
295
275
 
276
+ /**
277
+ * Resolve the network connection configuration settings for the network name
278
+ * and taking into account any configuration overrides.
279
+ *
280
+ * @param resolvedNetworkName the network name for selecting the appropriate network config
281
+ * @param networkConfigOverride any network config options to override the
282
+ * defaults for the named network
283
+ * @returns a valid network configuration including any config additions from
284
+ * plugins
285
+ */
286
+ async #resolveNetworkConfig(
287
+ resolvedNetworkName: string,
288
+ networkConfigOverride: NetworkConfigOverride | undefined,
289
+ ): Promise<NetworkConfig> {
290
+ if (networkConfigOverride === undefined) {
291
+ return this.#networkConfigs[resolvedNetworkName];
292
+ }
293
+
294
+ if (
295
+ "type" in networkConfigOverride &&
296
+ networkConfigOverride.type !==
297
+ this.#networkConfigs[resolvedNetworkName].type
298
+ ) {
299
+ throw new HardhatError(
300
+ HardhatError.ERRORS.CORE.NETWORK.INVALID_CONFIG_OVERRIDE,
301
+ {
302
+ errors: `\t* The type of the network cannot be changed.`,
303
+ },
304
+ );
305
+ }
306
+
307
+ const newConfig = deepMerge(this.#userConfig, {
308
+ networks: {
309
+ [resolvedNetworkName]: networkConfigOverride,
310
+ },
311
+ });
312
+
313
+ // This is safe, the plugins used in resolution are registered
314
+ // with the hook handler, this property is only used for
315
+ // ensuring the original plugins are available at the end
316
+ // of resolution.
317
+ const resolvedPlugins: HardhatPlugin[] = [];
318
+
319
+ const configResolutionResult = await resolveUserConfigToHardhatConfig(
320
+ newConfig,
321
+ this.#hookManager,
322
+ this.#projectRoot,
323
+ this.#userProvidedConfigPath,
324
+ resolvedPlugins,
325
+ );
326
+
327
+ if (!configResolutionResult.success) {
328
+ throw new HardhatError(
329
+ HardhatError.ERRORS.CORE.NETWORK.INVALID_CONFIG_OVERRIDE,
330
+ {
331
+ errors: `\t${configResolutionResult.userConfigValidationErrors
332
+ .map((error) => {
333
+ const path = this.#normaliseErrorPathToNetworkConfig(
334
+ error.path,
335
+ resolvedNetworkName,
336
+ );
337
+
338
+ return path.length > 0
339
+ ? `* Error in ${path.join(".")}: ${error.message}`
340
+ : `* ${error.message}`;
341
+ })
342
+ .join("\n\t")}`,
343
+ },
344
+ );
345
+ }
346
+
347
+ const resolvedNetworkConfigOverride =
348
+ configResolutionResult.config.networks[resolvedNetworkName];
349
+
350
+ assertHardhatInvariant(
351
+ resolvedNetworkConfigOverride !== undefined,
352
+ "The overridden network config should translate through the hook resolution of user config",
353
+ );
354
+
355
+ return resolvedNetworkConfigOverride;
356
+ }
357
+
296
358
  async #getBuildInfosAndOutputsAsBuffers(): Promise<
297
359
  Array<{ buildInfo: Uint8Array; output: Uint8Array }>
298
360
  > {
@@ -315,4 +377,19 @@ export class NetworkManagerImplementation implements NetworkManager {
315
377
 
316
378
  return results;
317
379
  }
380
+
381
+ #normaliseErrorPathToNetworkConfig(
382
+ path: Array<string | number>,
383
+ resolvedNetworkName: string,
384
+ ): Array<string | number> {
385
+ if (path[0] !== undefined && path[0] === "networks") {
386
+ path = path.slice(1);
387
+ }
388
+
389
+ if (path[0] !== undefined && path[0] === resolvedNetworkName) {
390
+ path = path.slice(1);
391
+ }
392
+
393
+ return path;
394
+ }
318
395
  }
@@ -404,8 +404,8 @@ const userConfigSchema = z.object({
404
404
 
405
405
  const networkConfigOverrideSchema = z
406
406
  .discriminatedUnion("type", [
407
- httpNetworkUserConfigSchema.strict(),
408
- edrNetworkUserConfigSchema.strict(),
407
+ httpNetworkUserConfigSchema.passthrough(),
408
+ edrNetworkUserConfigSchema.passthrough(),
409
409
  ])
410
410
  .superRefine(refineEdrNetworkUserConfig);
411
411
 
@@ -0,0 +1,82 @@
1
+ import type { FSWatcher } from "chokidar";
2
+
3
+ import path from "node:path";
4
+
5
+ import debug from "debug";
6
+
7
+ export type BuildInfoWatcher = FSWatcher;
8
+ export type BuildInfoHandler = (buildId: string) => Promise<void>;
9
+
10
+ const log = debug("hardhat:core:tasks:node:artifacts");
11
+
12
+ const STABILITY_THRESHOLD = 250;
13
+ const POLL_INTERVAL = 50;
14
+
15
+ /**
16
+ * `listener` is a callback function invoked when a new file is added to the
17
+ * build info directory. It processes the received event to check whether it is
18
+ * for a build info file.
19
+ *
20
+ * If so, it extracts the build id from it and triggers the provided handler
21
+ * with the extracted build id as an argument. Any errors encountered during
22
+ * handler execution are captured and logged.
23
+ *
24
+ * @param absolutePath - The absolute path of the file added to the build info directory.
25
+ *
26
+ * This function is exposed for testing purposes only.
27
+ */
28
+ export async function listener(
29
+ handler: BuildInfoHandler,
30
+ absolutePath: string,
31
+ ): Promise<void> {
32
+ log(`Detected change in ${absolutePath}`);
33
+
34
+ const isBuildInfoFile =
35
+ absolutePath.endsWith(".json") && !absolutePath.endsWith(".output.json");
36
+
37
+ if (!isBuildInfoFile) {
38
+ log(`File ${absolutePath} is not a build info file`);
39
+ return;
40
+ }
41
+
42
+ const buildId = path.basename(absolutePath).replace(".json", "");
43
+
44
+ await handler(buildId).catch(async (error: unknown) => {
45
+ log(
46
+ `There was a problem executing the handler for build ${buildId}.`,
47
+ error,
48
+ );
49
+ });
50
+ }
51
+
52
+ /**
53
+ * `watchBuildInfo` is a function that creates a watch over provided build info
54
+ * directory. If it encounters a build info file being added, it will trigger
55
+ * the provided handler, passing the build id as an argument. This allows for
56
+ * further processing or actions to be taken upon completion of a build.
57
+ */
58
+ export async function watchBuildInfo(
59
+ buildInfoDirPath: string,
60
+ handler: BuildInfoHandler,
61
+ ): Promise<BuildInfoWatcher> {
62
+ const { watch } = await import("chokidar");
63
+
64
+ // NOTE: Deleting the build info directory while it is being watched will
65
+ // effectively cause the watcher to stop working.
66
+ // NOTE: We use chokidar's `awaitWriteFinish` option because we are certain
67
+ // the build info file will be added after the build info output file.
68
+ const watcher = watch(buildInfoDirPath, {
69
+ persistent: true,
70
+ ignoreInitial: true,
71
+ awaitWriteFinish: {
72
+ stabilityThreshold: STABILITY_THRESHOLD,
73
+ pollInterval: POLL_INTERVAL,
74
+ },
75
+ });
76
+
77
+ // NOTE: We listen only to the "add" event because the contents of the build info
78
+ // files identified by a build id should be considered immutable under usual circumstances.
79
+ watcher.on("add", listener.bind(null, handler));
80
+
81
+ return watcher;
82
+ }
@@ -1,9 +1,19 @@
1
+ import type { BuildInfo } from "../../../types/artifacts.js";
1
2
  import type { EdrNetworkAccountsConfig } from "../../../types/config.js";
3
+ import type { SolidityBuildInfoOutput } from "../../../types/solidity.js";
4
+ import type { EdrProvider } from "../network-manager/edr/edr-provider.js";
2
5
 
6
+ import path from "node:path";
7
+
8
+ import {
9
+ readJsonFile,
10
+ readJsonFileAsStream,
11
+ } from "@nomicfoundation/hardhat-utils/fs";
3
12
  import { hexStringToBytes } from "@nomicfoundation/hardhat-utils/hex";
4
13
  import chalk from "chalk";
5
14
  import { addr } from "micro-eth-signer";
6
15
 
16
+ import { sendErrorTelemetry } from "../../cli/telemetry/sentry/reporter.js";
7
17
  import { isDefaultEdrNetworkHDAccountsConfig } from "../network-manager/edr/edr-provider.js";
8
18
  import { normalizeEdrNetworkAccountsConfig } from "../network-manager/edr/utils/convert-to-edr.js";
9
19
 
@@ -64,6 +74,60 @@ export async function formatEdrNetworkConfigAccounts(
64
74
  return formattedAccountsLines.join("\n");
65
75
  }
66
76
 
77
+ /**
78
+ * Creates a handler function that will be called on buildInfo file creations
79
+ * (triggered from the compilation pipeline); the handler reads the build info
80
+ * file and uploads the key details into the EDR instance.
81
+ *
82
+ * @param buildInfoDirPath - The path (under artifacts) to the build info
83
+ * directory
84
+ * @param provider - The EDR provider being updated.
85
+ * @returns The handler function that is called with the buildId to upload.
86
+ */
87
+ export function createBuildInfoUploadHandlerFrom(
88
+ buildInfoDirPath: string,
89
+ provider: EdrProvider,
90
+ log: debug.Debugger,
91
+ ): (buildId: string) => Promise<void> {
92
+ const buildInfoHandler = async (buildId: string) => {
93
+ try {
94
+ log(`Adding new compilation result for build ${buildId} to the node`);
95
+ const buildInfo: BuildInfo = await readJsonFile(
96
+ path.join(buildInfoDirPath, `${buildId}.json`),
97
+ );
98
+ const buildInfoOutput: SolidityBuildInfoOutput =
99
+ await readJsonFileAsStream(
100
+ path.join(buildInfoDirPath, `${buildId}.output.json`),
101
+ );
102
+
103
+ await provider.addCompilationResult(
104
+ buildInfo.solcVersion,
105
+ buildInfo.input,
106
+ buildInfoOutput.output,
107
+ );
108
+
109
+ log(`Added compiler result for ${buildId}`);
110
+ } catch (error) {
111
+ console.warn(
112
+ chalk.yellow(
113
+ `There was a problem adding the new compiler result for build ${buildId}.`,
114
+ ),
115
+ );
116
+
117
+ log(
118
+ "Last compilation result couldn't be added. Please report this to help us improve Hardhat.\n",
119
+ error,
120
+ );
121
+
122
+ if (error instanceof Error) {
123
+ await sendErrorTelemetry(error);
124
+ }
125
+ }
126
+ };
127
+
128
+ return buildInfoHandler;
129
+ }
130
+
67
131
  // NOTE: This function is exported for testing purposes only
68
132
  export function getPublicPrivateKeysWarning(): string {
69
133
  return chalk.bold(
@@ -1,3 +1,4 @@
1
+ import type { JsonRpcServer } from "../../../../types/network.js";
1
2
  import type { EthereumProvider } from "../../../../types/providers.js";
2
3
  import type { Server } from "node:http";
3
4
  import type { AddressInfo } from "node:net";
@@ -11,17 +12,9 @@ import { JsonRpcHandler } from "./handler.js";
11
12
 
12
13
  const log = debug("hardhat:core:tasks:node:json-rpc:server");
13
14
 
14
- export interface JsonRpcServer {
15
- listen(): Promise<{ address: string; port: number }>;
16
- waitUntilClosed(): Promise<void>;
17
-
18
- close(): Promise<void>;
19
- }
20
-
21
15
  export interface JsonRpcServerConfig {
22
16
  hostname: string;
23
- port: number;
24
-
17
+ port?: number;
25
18
  provider: EthereumProvider;
26
19
  }
27
20
 
@@ -56,7 +49,7 @@ export class JsonRpcServerImplementation implements JsonRpcServer {
56
49
  });
57
50
  };
58
51
 
59
- public waitUntilClosed = async (): Promise<void> => {
52
+ public afterClosed = async (): Promise<void> => {
60
53
  const httpServerClosed = new Promise((resolve) => {
61
54
  this.#httpServer.once("close", resolve);
62
55
  });
@@ -1,19 +1,30 @@
1
1
  import type { EdrNetworkConfigOverride } from "../../../types/config.js";
2
2
  import type { NewTaskActionFunction } from "../../../types/tasks.js";
3
3
 
4
+ import path from "node:path";
5
+
4
6
  import {
5
7
  assertHardhatInvariant,
6
8
  HardhatError,
7
9
  } from "@nomicfoundation/hardhat-errors";
8
- import { exists } from "@nomicfoundation/hardhat-utils/fs";
10
+ import { ensureDir, exists } from "@nomicfoundation/hardhat-utils/fs";
9
11
  import chalk from "chalk";
12
+ import debug from "debug";
10
13
 
11
14
  import { DEFAULT_NETWORK_NAME } from "../../constants.js";
12
15
  import { isSupportedChainType } from "../../edr/chain-type.js";
16
+ import { BUILD_INFO_DIR_NAME } from "../artifacts/artifact-manager.js";
17
+ import { EdrProvider } from "../network-manager/edr/edr-provider.js";
13
18
 
14
- import { formatEdrNetworkConfigAccounts } from "./helpers.js";
19
+ import { watchBuildInfo } from "./artifacts/build-info-watcher.js";
20
+ import {
21
+ createBuildInfoUploadHandlerFrom,
22
+ formatEdrNetworkConfigAccounts,
23
+ } from "./helpers.js";
15
24
  import { JsonRpcServerImplementation } from "./json-rpc/server.js";
16
25
 
26
+ const log = debug("hardhat:core:tasks:node");
27
+
17
28
  interface NodeActionArguments {
18
29
  hostname?: string;
19
30
  port: number;
@@ -94,6 +105,11 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
94
105
  override: networkConfigOverride,
95
106
  });
96
107
 
108
+ assertHardhatInvariant(
109
+ provider instanceof EdrProvider,
110
+ "Provider must be EdrProvider, since only edr networks are supported",
111
+ );
112
+
97
113
  // NOTE: We enable logging for the node
98
114
  await provider.request({
99
115
  method: "hardhat_setLoggingEnabled",
@@ -112,7 +128,7 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
112
128
  }
113
129
  }
114
130
 
115
- const server: JsonRpcServerImplementation = new JsonRpcServerImplementation({
131
+ const server = new JsonRpcServerImplementation({
116
132
  hostname,
117
133
  port: args.port,
118
134
  provider,
@@ -128,7 +144,16 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
128
144
 
129
145
  console.log();
130
146
 
131
- // TODO(https://github.com/NomicFoundation/hardhat/issues/6040): Add build info watcher here
147
+ const buildInfoDirPath = path.join(
148
+ hre.config.paths.artifacts,
149
+ BUILD_INFO_DIR_NAME,
150
+ );
151
+ await ensureDir(buildInfoDirPath);
152
+
153
+ const buildInfoWatcher = await watchBuildInfo(
154
+ buildInfoDirPath,
155
+ createBuildInfoUploadHandlerFrom(buildInfoDirPath, provider, log),
156
+ );
132
157
 
133
158
  // NOTE: Before creating the node, we check if the input network config is of type edr.
134
159
  // We only proceed if it is. Hence, we can assume that the output network config is of type edr as well.
@@ -139,7 +164,8 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
139
164
 
140
165
  console.log(await formatEdrNetworkConfigAccounts(networkConfig.accounts));
141
166
 
142
- await server.waitUntilClosed();
167
+ await server.afterClosed();
168
+ await buildInfoWatcher.close();
143
169
  };
144
170
 
145
171
  export default nodeAction;