hardhat 2.19.4 → 2.19.5

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/README.md +6 -0
  2. package/builtin-tasks/compile.js +11 -2
  3. package/builtin-tasks/compile.js.map +1 -1
  4. package/internal/cli/cli.js +33 -0
  5. package/internal/cli/cli.js.map +1 -1
  6. package/internal/cli/project-creation.js +3 -6
  7. package/internal/cli/project-creation.js.map +1 -1
  8. package/internal/cli/version-notifier.d.ts +2 -0
  9. package/internal/cli/version-notifier.d.ts.map +1 -0
  10. package/internal/cli/version-notifier.js +217 -0
  11. package/internal/cli/version-notifier.js.map +1 -0
  12. package/internal/core/config/config-loading.d.ts.map +1 -1
  13. package/internal/core/config/config-loading.js +3 -3
  14. package/internal/core/config/config-loading.js.map +1 -1
  15. package/internal/core/config/config-resolution.d.ts.map +1 -1
  16. package/internal/core/config/config-resolution.js +10 -6
  17. package/internal/core/config/config-resolution.js.map +1 -1
  18. package/internal/core/config/config-validation.d.ts +2 -2
  19. package/internal/core/config/config-validation.d.ts.map +1 -1
  20. package/internal/core/config/config-validation.js +2 -2
  21. package/internal/core/config/config-validation.js.map +1 -1
  22. package/internal/core/providers/gas-providers.d.ts.map +1 -1
  23. package/internal/core/providers/gas-providers.js +7 -0
  24. package/internal/core/providers/gas-providers.js.map +1 -1
  25. package/internal/hardhat-network/stack-traces/panic-errors.js +1 -1
  26. package/internal/hardhat-network/stack-traces/panic-errors.js.map +1 -1
  27. package/internal/solidity/compiler/downloader.d.ts.map +1 -1
  28. package/internal/solidity/compiler/downloader.js +2 -2
  29. package/internal/solidity/compiler/downloader.js.map +1 -1
  30. package/internal/solidity/compiler/index.d.ts +2 -1
  31. package/internal/solidity/compiler/index.d.ts.map +1 -1
  32. package/internal/solidity/compiler/index.js +25 -2
  33. package/internal/solidity/compiler/index.js.map +1 -1
  34. package/internal/util/fs-utils.d.ts +1 -1
  35. package/internal/util/fs-utils.js +1 -1
  36. package/internal/util/multi-process-mutex.d.ts +12 -0
  37. package/internal/util/multi-process-mutex.d.ts.map +1 -0
  38. package/internal/util/multi-process-mutex.js +116 -0
  39. package/internal/util/multi-process-mutex.js.map +1 -0
  40. package/internal/util/report-telemetry-consent.js +3 -1
  41. package/internal/util/report-telemetry-consent.js.map +1 -1
  42. package/package.json +2 -1
  43. package/src/builtin-tasks/compile.ts +20 -2
  44. package/src/internal/cli/cli.ts +13 -1
  45. package/src/internal/cli/project-creation.ts +4 -9
  46. package/src/internal/cli/version-notifier.ts +268 -0
  47. package/src/internal/core/config/config-loading.ts +2 -1
  48. package/src/internal/core/config/config-resolution.ts +7 -1
  49. package/src/internal/core/config/config-validation.ts +9 -2
  50. package/src/internal/core/providers/gas-providers.ts +8 -0
  51. package/src/internal/hardhat-network/stack-traces/panic-errors.ts +1 -1
  52. package/src/internal/sentry/transport.ts +1 -1
  53. package/src/internal/solidity/compiler/downloader.ts +2 -2
  54. package/src/internal/solidity/compiler/index.ts +22 -2
  55. package/src/internal/util/fs-utils.ts +1 -1
  56. package/src/internal/util/multi-process-mutex.ts +133 -0
  57. package/src/internal/util/report-telemetry-consent.ts +4 -1
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MultiProcessMutex = void 0;
7
+ /* eslint-disable @nomicfoundation/hardhat-internal-rules/only-hardhat-error */
8
+ const debug_1 = __importDefault(require("debug"));
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const node_os_1 = __importDefault(require("node:os"));
12
+ // Logic explanation: the fs.writeFile function, when used with the wx+ flag, performs an atomic operation to create a file.
13
+ // If multiple processes try to create the same file simultaneously, only one will succeed.
14
+ // This logic can be utilized to implement a mutex.
15
+ // ATTENTION: in the current implementation, there's still a risk of two processes running simultaneously.
16
+ // For example, if processA has locked the mutex and is running, processB will wait.
17
+ // During this wait, processB continuously checks the elapsed time since the mutex lock file was created.
18
+ // If an excessive amount of time has passed, processB will assume ownership of the mutex to avoid stale locks.
19
+ // However, there's a possibility that processB might take ownership because the mutex creation file is outdated, even though processA is still running
20
+ // For more info check the Nomic Notion page (internal link).
21
+ const log = (0, debug_1.default)("hardhat:util:multi-process-mutex");
22
+ const DEFAULT_MAX_MUTEX_LIFESPAN_IN_MS = 60000;
23
+ const MUTEX_LOOP_WAITING_TIME_IN_MS = 100;
24
+ class MultiProcessMutex {
25
+ constructor(mutexName, maxMutexLifespanInMs) {
26
+ log(`Creating mutex with name '${mutexName}'`);
27
+ this._mutexFilePath = node_path_1.default.join(node_os_1.default.tmpdir(), `${mutexName}.txt`);
28
+ this._mutexLifespanInMs =
29
+ maxMutexLifespanInMs ?? DEFAULT_MAX_MUTEX_LIFESPAN_IN_MS;
30
+ }
31
+ async use(f) {
32
+ log(`Starting mutex process with mutex file '${this._mutexFilePath}'`);
33
+ while (true) {
34
+ if (await this._tryToAcquireMutex()) {
35
+ // Mutex has been acquired
36
+ return this._executeFunctionAndReleaseMutex(f);
37
+ }
38
+ // Mutex not acquired
39
+ if (this._isMutexFileTooOld()) {
40
+ // If the mutex file is too old, it likely indicates a stale lock, so the file should be removed
41
+ log(`Current mutex file is too old, removing it at path '${this._mutexFilePath}'`);
42
+ this._deleteMutexFile();
43
+ }
44
+ else {
45
+ // wait
46
+ await this._waitMs();
47
+ }
48
+ }
49
+ }
50
+ async _tryToAcquireMutex() {
51
+ try {
52
+ // Create a file only if it does not exist
53
+ node_fs_1.default.writeFileSync(this._mutexFilePath, "", { flag: "wx+" });
54
+ return true;
55
+ }
56
+ catch (error) {
57
+ if (error.code === "EEXIST") {
58
+ // File already exists, so the mutex is already acquired
59
+ return false;
60
+ }
61
+ throw error;
62
+ }
63
+ }
64
+ async _executeFunctionAndReleaseMutex(f) {
65
+ log(`Mutex acquired at path '${this._mutexFilePath}'`);
66
+ try {
67
+ const res = await f();
68
+ // Release the mutex
69
+ log(`Mutex released at path '${this._mutexFilePath}'`);
70
+ this._deleteMutexFile();
71
+ log(`Mutex released at path '${this._mutexFilePath}'`);
72
+ return res;
73
+ }
74
+ catch (error) {
75
+ // Catch any error to avoid stale locks.
76
+ // Remove the mutex file and re-throw the error
77
+ this._deleteMutexFile();
78
+ throw error;
79
+ }
80
+ }
81
+ _isMutexFileTooOld() {
82
+ let fileStat;
83
+ try {
84
+ fileStat = node_fs_1.default.statSync(this._mutexFilePath);
85
+ }
86
+ catch (error) {
87
+ if (error.code === "ENOENT") {
88
+ // The file might have been deleted by another process while this function was trying to access it.
89
+ return false;
90
+ }
91
+ throw error;
92
+ }
93
+ const now = new Date();
94
+ const fileDate = new Date(fileStat.ctime);
95
+ const diff = now.getTime() - fileDate.getTime();
96
+ return diff > this._mutexLifespanInMs;
97
+ }
98
+ _deleteMutexFile() {
99
+ try {
100
+ log(`Deleting mutex file at path '${this._mutexFilePath}'`);
101
+ node_fs_1.default.unlinkSync(this._mutexFilePath);
102
+ }
103
+ catch (error) {
104
+ if (error.code === "ENOENT") {
105
+ // The file might have been deleted by another process while this function was trying to access it.
106
+ return;
107
+ }
108
+ throw error;
109
+ }
110
+ }
111
+ async _waitMs() {
112
+ return new Promise((resolve) => setTimeout(resolve, MUTEX_LOOP_WAITING_TIME_IN_MS));
113
+ }
114
+ }
115
+ exports.MultiProcessMutex = MultiProcessMutex;
116
+ //# sourceMappingURL=multi-process-mutex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-process-mutex.js","sourceRoot":"","sources":["../../src/internal/util/multi-process-mutex.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const analytics_1 = require("../cli/analytics");
4
4
  async function main() {
5
- const [telemetryConsent] = process.argv.slice(2);
5
+ // This default value shouldn't be necessary, but we add one to make it
6
+ // easier to recognize if the telemetry consent value is not passed.
7
+ const [telemetryConsent = "<undefined-telemetry-consent>"] = process.argv.slice(2);
6
8
  // we pass undefined as the telemetryConsent value because
7
9
  // this hit is done before the consent is saved
8
10
  const analytics = await analytics_1.Analytics.getInstance(undefined);
@@ -1 +1 @@
1
- {"version":3,"file":"report-telemetry-consent.js","sourceRoot":"","sources":["../../src/internal/util/report-telemetry-consent.ts"],"names":[],"mappings":";;AAAA,gDAA6C;AAE7C,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,gBAAgB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEjD,0DAA0D;IAC1D,+CAA+C;IAC/C,MAAM,SAAS,GAAG,MAAM,qBAAS,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAEzD,MAAM,CAAC,CAAC,EAAE,iBAAiB,CAAC,GAAG,MAAM,SAAS,CAAC,uBAAuB,CACpE,gBAAgC,CACjC,CAAC;IACF,MAAM,iBAAiB,CAAC;AAC1B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"report-telemetry-consent.js","sourceRoot":"","sources":["../../src/internal/util/report-telemetry-consent.ts"],"names":[],"mappings":";;AAAA,gDAA6C;AAE7C,KAAK,UAAU,IAAI;IACjB,uEAAuE;IACvE,oEAAoE;IACpE,MAAM,CAAC,gBAAgB,GAAG,+BAA+B,CAAC,GACxD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAExB,0DAA0D;IAC1D,+CAA+C;IAC/C,MAAM,SAAS,GAAG,MAAM,qBAAS,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IAEzD,MAAM,CAAC,CAAC,EAAE,iBAAiB,CAAC,GAAG,MAAM,SAAS,CAAC,uBAAuB,CACpE,gBAAgC,CACjC,CAAC;IACF,MAAM,iBAAiB,CAAC;AAC1B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hardhat",
3
- "version": "2.19.4",
3
+ "version": "2.19.5",
4
4
  "author": "Nomic Labs LLC",
5
5
  "license": "MIT",
6
6
  "homepage": "https://hardhat.org",
@@ -101,6 +101,7 @@
101
101
  "adm-zip": "^0.4.16",
102
102
  "aggregate-error": "^3.0.0",
103
103
  "ansi-escapes": "^4.3.0",
104
+ "boxen": "^5.1.2",
104
105
  "chalk": "^2.4.2",
105
106
  "chokidar": "^3.4.0",
106
107
  "ci-info": "^2.0.0",
@@ -674,9 +674,26 @@ subtask(TASK_COMPILE_SOLIDITY_RUN_SOLCJS)
674
674
  subtask(TASK_COMPILE_SOLIDITY_RUN_SOLC)
675
675
  .addParam("input", undefined, undefined, types.any)
676
676
  .addParam("solcPath", undefined, undefined, types.string)
677
+ .addOptionalParam("solcVersion", undefined, undefined, types.string)
677
678
  .setAction(
678
- async ({ input, solcPath }: { input: CompilerInput; solcPath: string }) => {
679
- const compiler = new NativeCompiler(solcPath);
679
+ async ({
680
+ input,
681
+ solcPath,
682
+ solcVersion,
683
+ }: {
684
+ input: CompilerInput;
685
+ solcPath: string;
686
+ solcVersion?: string;
687
+ }) => {
688
+ if (solcVersion !== undefined && semver.valid(solcVersion) === null) {
689
+ throw new HardhatError(ERRORS.ARGUMENTS.INVALID_VALUE_FOR_TYPE, {
690
+ value: solcVersion,
691
+ name: "solcVersion",
692
+ type: "string",
693
+ });
694
+ }
695
+
696
+ const compiler = new NativeCompiler(solcPath, solcVersion);
680
697
 
681
698
  return compiler.compile(input);
682
699
  }
@@ -740,6 +757,7 @@ subtask(TASK_COMPILE_SOLIDITY_COMPILE_SOLC)
740
757
  output = await run(TASK_COMPILE_SOLIDITY_RUN_SOLC, {
741
758
  input,
742
759
  solcPath: solcBuild.compilerPath,
760
+ solcVersion,
743
761
  });
744
762
  }
745
763
 
@@ -1,6 +1,7 @@
1
1
  import chalk from "chalk";
2
2
  import debug from "debug";
3
3
  import "source-map-support/register";
4
+
4
5
  import {
5
6
  TASK_COMPILE,
6
7
  TASK_HELP,
@@ -36,7 +37,6 @@ import {
36
37
  writePromptedForHHVSCode,
37
38
  } from "../util/global-dir";
38
39
  import { getPackageJson } from "../util/packageInfo";
39
-
40
40
  import { saveFlamegraph } from "../core/flamegraph";
41
41
  import { Analytics, requestTelemetryConsent } from "./analytics";
42
42
  import { ArgumentsParser } from "./ArgumentsParser";
@@ -367,6 +367,18 @@ async function main() {
367
367
  if (process.exitCode !== 0) {
368
368
  showViaIRWarning(resolvedConfig);
369
369
  }
370
+
371
+ // we notify of new versions only if the tests failed
372
+ if (process.exitCode !== 0) {
373
+ try {
374
+ const { showNewVersionNotification } = await import(
375
+ "./version-notifier"
376
+ );
377
+ await showNewVersionNotification();
378
+ } catch {
379
+ // ignore possible version notifier errors
380
+ }
381
+ }
370
382
  }
371
383
 
372
384
  log(`Killing Hardhat after successfully running task ${taskName}`);
@@ -568,12 +568,8 @@ async function doesNpmAutoInstallPeerDependencies() {
568
568
  async function installRecommendedDependencies(dependencies: Dependencies) {
569
569
  console.log("");
570
570
 
571
- // The reason we don't quote the dependencies here is because they are going
572
- // to be used in child_process.spawn, which doesn't require escaping string,
573
- // and can actually fail if you do.
574
571
  const installCmd = await getRecommendedDependenciesInstallationCommand(
575
- dependencies,
576
- false
572
+ dependencies
577
573
  );
578
574
  return installDependencies(installCmd[0], installCmd.slice(1));
579
575
  }
@@ -611,11 +607,10 @@ async function installDependencies(
611
607
  }
612
608
 
613
609
  async function getRecommendedDependenciesInstallationCommand(
614
- dependencies: Dependencies,
615
- quoteDependencies = true
610
+ dependencies: Dependencies
616
611
  ): Promise<string[]> {
617
- const deps = Object.entries(dependencies).map(([name, version]) =>
618
- quoteDependencies ? `"${name}@${version}"` : `${name}@${version}`
612
+ const deps = Object.entries(dependencies).map(
613
+ ([name, version]) => `"${name}@${version}"`
619
614
  );
620
615
 
621
616
  if (await isYarnProject()) {
@@ -0,0 +1,268 @@
1
+ import boxen from "boxen";
2
+ import chalk from "chalk";
3
+ import fsExtra from "fs-extra";
4
+ import { join } from "node:path";
5
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
6
+ import semver from "semver";
7
+
8
+ import { getCacheDir } from "../util/global-dir";
9
+ import { getHardhatVersion } from "../util/packageInfo";
10
+
11
+ const GITHUB_API_URL = "https://api.github.com";
12
+ const GITHUB_OWNER = "NomicFoundation";
13
+ const GITHUB_REPO = "hardhat";
14
+ const V3_RELEASE_TAG = "hardhat@3.0.0";
15
+ const V3_RELEASE_VERSION_NOTIFIER_ASSET_NAME = "version-notifier-message.txt";
16
+ const V3_RELEASE_MAX_TIMES_SHOWN = 5;
17
+ const CURRENT_HARDHAT_MAJOR_VERSION = 2;
18
+
19
+ const boxenOptions = {
20
+ padding: 1,
21
+ borderStyle: "round",
22
+ borderColor: "yellow",
23
+ } as const;
24
+
25
+ interface VersionNotifierCache {
26
+ lastCheck: string | 0;
27
+ v3TimesShown: number;
28
+ v3Release?: Release;
29
+ v3ReleaseMessage?: string;
30
+ }
31
+
32
+ /* eslint-disable @typescript-eslint/naming-convention */
33
+ interface Release {
34
+ name: string;
35
+ tag_name: string;
36
+ draft: boolean;
37
+ prerelease: boolean;
38
+ published_at: string;
39
+ html_url: string;
40
+ assets: Array<{
41
+ name: string;
42
+ browser_download_url: string;
43
+ }>;
44
+ body: string; // release notes
45
+ }
46
+ /* eslint-enable @typescript-eslint/naming-convention */
47
+
48
+ export async function showNewVersionNotification() {
49
+ const cache = await readCache();
50
+
51
+ const lastCheckDate = new Date(cache.lastCheck);
52
+ const now = new Date();
53
+ const oneDay = 1000 * 60 * 60 * 24;
54
+
55
+ if (now.getTime() - lastCheckDate.getTime() < oneDay) {
56
+ return;
57
+ }
58
+
59
+ const hardhatVersion = getHardhatVersion();
60
+
61
+ const releases = await getReleases();
62
+
63
+ const sortedV2Versions = releases
64
+ // filter and map releases to versions
65
+ .flatMap((release) => {
66
+ const [packageName, rawPackageVersion] = release.tag_name.split("@");
67
+
68
+ const packageVersion = semver.valid(rawPackageVersion);
69
+
70
+ // filter out a release if:
71
+ // - it's not a hardhat-core release
72
+ // - it's a draft or a prerelease
73
+ // - the version is invalid
74
+ // - the major version is not the current major
75
+ if (
76
+ packageName !== GITHUB_REPO ||
77
+ release.draft ||
78
+ release.prerelease ||
79
+ packageVersion === null ||
80
+ semver.major(packageVersion) !== CURRENT_HARDHAT_MAJOR_VERSION
81
+ ) {
82
+ return [];
83
+ }
84
+
85
+ return [packageVersion];
86
+ })
87
+ // sort in descending order by version
88
+ .sort((releaseAVersion, releaseBVersion) => {
89
+ return semver.rcompare(releaseAVersion, releaseBVersion);
90
+ });
91
+
92
+ const latestV2Version: string | undefined = sortedV2Versions[0];
93
+
94
+ const v3Release = cache.v3Release ?? (await getV3Release());
95
+
96
+ if (latestV2Version === undefined && v3Release === undefined) {
97
+ // this should never happen unless the github api is down
98
+ return;
99
+ }
100
+
101
+ if (
102
+ latestV2Version !== undefined &&
103
+ semver.gt(latestV2Version, hardhatVersion)
104
+ ) {
105
+ let installationCommand = "npm install";
106
+ if (await fsExtra.pathExists("yarn.lock")) {
107
+ installationCommand = "yarn add";
108
+ } else if (await fsExtra.pathExists("pnpm-lock.yaml")) {
109
+ installationCommand = "pnpm install";
110
+ }
111
+
112
+ console.log(
113
+ boxen(
114
+ `New Hardhat release available! ${chalk.red(
115
+ hardhatVersion
116
+ )} -> ${chalk.green(latestV2Version)}.
117
+
118
+ Changelog: https://hardhat.org/release/${latestV2Version}
119
+
120
+ Run "${installationCommand} hardhat@latest" to update.`,
121
+ boxenOptions
122
+ )
123
+ );
124
+ }
125
+
126
+ if (
127
+ v3Release !== undefined &&
128
+ cache.v3TimesShown < V3_RELEASE_MAX_TIMES_SHOWN
129
+ ) {
130
+ const releaseVersion = semver.valid(v3Release.tag_name.split("@")[1]);
131
+
132
+ if (releaseVersion !== null) {
133
+ cache.v3ReleaseMessage ??= await getV3ReleaseMessage(v3Release);
134
+ if (cache.v3ReleaseMessage !== undefined) {
135
+ console.log(boxen(cache.v3ReleaseMessage, boxenOptions));
136
+ cache.v3TimesShown++;
137
+ }
138
+ }
139
+ }
140
+
141
+ await writeCache({
142
+ ...cache,
143
+ lastCheck: now.toISOString(),
144
+ v3Release,
145
+ });
146
+ }
147
+
148
+ async function readCache(): Promise<VersionNotifierCache> {
149
+ const cacheDir = await getCacheDir();
150
+ const versionNotifierCachePath = join(cacheDir, "version-notifier.json");
151
+
152
+ let cache: VersionNotifierCache = {
153
+ lastCheck: 0, // new Date(0) represents the unix epoch
154
+ v3TimesShown: 0,
155
+ };
156
+ try {
157
+ const fileContents = await readFile(versionNotifierCachePath, "utf-8");
158
+ const { lastCheck, v3TimesShown } = JSON.parse(fileContents);
159
+
160
+ cache = {
161
+ lastCheck: typeof lastCheck === "string" ? lastCheck : 0,
162
+ v3TimesShown: typeof v3TimesShown === "number" ? v3TimesShown : 0,
163
+ };
164
+ } catch (error: any) {
165
+ // We don't care if it fails
166
+ }
167
+
168
+ return cache;
169
+ }
170
+
171
+ async function writeCache(cache: VersionNotifierCache) {
172
+ const cacheDir = await getCacheDir();
173
+ const versionNotifierCachePath = join(cacheDir, "version-notifier.json");
174
+
175
+ try {
176
+ await mkdir(cacheDir, { recursive: true });
177
+ await writeFile(versionNotifierCachePath, JSON.stringify(cache, null, 2));
178
+ } catch (error) {
179
+ // We don't care if it fails
180
+ }
181
+ }
182
+
183
+ async function getReleases(): Promise<Release[]> {
184
+ const { request } = await import("undici");
185
+ let releases: Release[] = [];
186
+
187
+ try {
188
+ const githubResponse = await request(
189
+ `${GITHUB_API_URL}/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases`,
190
+ {
191
+ method: "GET",
192
+ headers: {
193
+ "User-Agent": "Hardhat",
194
+ "X-GitHub-Api-Version": "2022-11-28",
195
+ },
196
+ query: {
197
+ per_page: 100,
198
+ },
199
+ }
200
+ );
201
+ releases = (await githubResponse.body.json()) as Release[];
202
+ } catch (error: any) {
203
+ // We don't care if it fails
204
+ }
205
+
206
+ return releases;
207
+ }
208
+
209
+ async function getV3Release(): Promise<Release | undefined> {
210
+ const { request } = await import("undici");
211
+ let v3Release: Release | undefined;
212
+
213
+ try {
214
+ const githubResponse = await request(
215
+ `${GITHUB_API_URL}/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases/tags/${V3_RELEASE_TAG}`,
216
+ {
217
+ method: "GET",
218
+ headers: {
219
+ "User-Agent": "Hardhat",
220
+ "X-GitHub-Api-Version": "2022-11-28",
221
+ },
222
+ }
223
+ );
224
+
225
+ const jsonResponse = (await githubResponse.body.json()) as any;
226
+ if (jsonResponse.message === "Not Found") {
227
+ // eslint-disable-next-line @nomicfoundation/hardhat-internal-rules/only-hardhat-error
228
+ throw new Error("Not Found");
229
+ }
230
+
231
+ v3Release = jsonResponse as Release;
232
+ } catch (error: any) {
233
+ // We don't care if it fails
234
+ }
235
+
236
+ return v3Release;
237
+ }
238
+
239
+ async function getV3ReleaseMessage(
240
+ v3Release: Release
241
+ ): Promise<string | undefined> {
242
+ const { request } = await import("undici");
243
+
244
+ const versionNotifierAsset = v3Release.assets.find(
245
+ ({ name }) => name === V3_RELEASE_VERSION_NOTIFIER_ASSET_NAME
246
+ );
247
+
248
+ if (versionNotifierAsset === undefined) {
249
+ return;
250
+ }
251
+
252
+ let v3ReleaseMessage;
253
+ try {
254
+ const githubResponse = await request(
255
+ versionNotifierAsset.browser_download_url,
256
+ {
257
+ method: "GET",
258
+ maxRedirections: 10,
259
+ }
260
+ );
261
+
262
+ v3ReleaseMessage = await githubResponse.body.text();
263
+ } catch (error: any) {
264
+ // We don't care if it fails
265
+ }
266
+
267
+ return v3ReleaseMessage;
268
+ }
@@ -20,7 +20,6 @@ import { getUserConfigPath } from "../project-structure";
20
20
 
21
21
  import { SUPPORTED_SOLIDITY_VERSION_RANGE } from "../../hardhat-network/stack-traces/constants";
22
22
  import { resolveConfig } from "./config-resolution";
23
- import { validateConfig, validateResolvedConfig } from "./config-validation";
24
23
  import { DEFAULT_SOLC_VERSION } from "./default-config";
25
24
 
26
25
  const log = debug("hardhat:core:config");
@@ -68,6 +67,8 @@ export function loadConfigAndTasks(
68
67
  showSolidityConfigWarnings: false,
69
68
  }
70
69
  ): { resolvedConfig: HardhatConfig; userConfig: HardhatUserConfig } {
70
+ const { validateConfig, validateResolvedConfig } =
71
+ require("./config-validation") as typeof import("./config-validation");
71
72
  let configPath =
72
73
  hardhatArguments !== undefined ? hardhatArguments.config : undefined;
73
74
 
@@ -1,4 +1,5 @@
1
- import cloneDeep from "lodash/cloneDeep";
1
+ import type { LoDashStatic } from "lodash";
2
+
2
3
  import path from "path";
3
4
  import semver from "semver";
4
5
 
@@ -62,6 +63,7 @@ export function resolveConfig(
62
63
  userConfigPath: string,
63
64
  userConfig: HardhatUserConfig
64
65
  ): HardhatConfig {
66
+ const cloneDeep = require("lodash/cloneDeep") as LoDashStatic["cloneDeep"];
65
67
  userConfig = cloneDeep(userConfig);
66
68
 
67
69
  return {
@@ -77,6 +79,7 @@ export function resolveConfig(
77
79
  function resolveNetworksConfig(
78
80
  networksConfig: NetworksUserConfig = {}
79
81
  ): NetworksConfig {
82
+ const cloneDeep = require("lodash/cloneDeep") as LoDashStatic["cloneDeep"];
80
83
  const hardhatNetworkConfig = networksConfig[HARDHAT_NETWORK_NAME];
81
84
 
82
85
  const localhostNetworkConfig =
@@ -128,6 +131,7 @@ function normalizeHexString(str: string): string {
128
131
  function resolveHardhatNetworkConfig(
129
132
  hardhatNetworkConfig: HardhatNetworkUserConfig = {}
130
133
  ): HardhatNetworkConfig {
134
+ const cloneDeep = require("lodash/cloneDeep") as LoDashStatic["cloneDeep"];
131
135
  const clonedDefaultHardhatNetworkParams = cloneDeep(
132
136
  defaultHardhatNetworkParams
133
137
  );
@@ -246,6 +250,7 @@ function isHdAccountsConfig(
246
250
  function resolveHttpNetworkConfig(
247
251
  networkConfig: HttpNetworkUserConfig
248
252
  ): HttpNetworkConfig {
253
+ const cloneDeep = require("lodash/cloneDeep") as LoDashStatic["cloneDeep"];
249
254
  const accounts: HttpNetworkAccountsConfig =
250
255
  networkConfig.accounts === undefined
251
256
  ? defaultHttpNetworkParams.accounts
@@ -427,6 +432,7 @@ function resolveCompiler(compiler: SolcUserConfig): SolcConfig {
427
432
  }
428
433
 
429
434
  function resolveMochaConfig(userConfig: HardhatUserConfig): Mocha.MochaOptions {
435
+ const cloneDeep = require("lodash/cloneDeep") as LoDashStatic["cloneDeep"];
430
436
  return {
431
437
  ...cloneDeep(defaultMochaOptions),
432
438
  ...userConfig.mocha,
@@ -1,8 +1,12 @@
1
1
  import type { HardhatConfig as HardhatConfigT } from "../../../types";
2
+ import type {
3
+ Context,
4
+ ValidationError,
5
+ getFunctionName as getFunctionNameT,
6
+ } from "io-ts/lib";
7
+ import type { Reporter } from "io-ts/lib/Reporter";
2
8
 
3
9
  import * as t from "io-ts";
4
- import { Context, getFunctionName, ValidationError } from "io-ts/lib";
5
- import { Reporter } from "io-ts/lib/Reporter";
6
10
 
7
11
  import {
8
12
  HARDHAT_MEMPOOL_SUPPORTED_ORDERS,
@@ -19,6 +23,9 @@ import { defaultHardhatNetworkParams } from "./default-config";
19
23
 
20
24
  function stringify(v: any): string {
21
25
  if (typeof v === "function") {
26
+ const { getFunctionName } = require("io-ts/lib") as {
27
+ getFunctionName: typeof getFunctionNameT;
28
+ };
22
29
  return getFunctionName(v);
23
30
  }
24
31
  if (typeof v === "number" && !isFinite(v)) {
@@ -273,6 +273,14 @@ export class AutomaticGasPriceProvider extends ProviderWrapper {
273
273
  }
274
274
  }
275
275
 
276
+ // If after all of these we still have a 0 wei maxPriorityFeePerGas, we
277
+ // use 1 wei. This is to improve the UX of the automatic gas price
278
+ // on chains that are very empty (i.e local testnets). This will be very
279
+ // unlikely to trigger on a live chain.
280
+ if (maxPriorityFeePerGas === 0n) {
281
+ maxPriorityFeePerGas = 1n;
282
+ }
283
+
276
284
  return {
277
285
  // Each block increases the base fee by 1/8 at most, when full.
278
286
  // We have the next block's base fee, so we compute a cap for the
@@ -13,7 +13,7 @@ function panicErrorCodeToReason(errorCode: bigint): string | undefined {
13
13
  case 0x1n:
14
14
  return "Assertion error";
15
15
  case 0x11n:
16
- return "Arithmetic operation underflowed or overflowed outside of an unchecked block";
16
+ return "Arithmetic operation overflowed outside of an unchecked block";
17
17
  case 0x12n:
18
18
  return "Division or modulo division by zero";
19
19
  case 0x21n:
@@ -1,4 +1,4 @@
1
- import { Event, Response } from "@sentry/node";
1
+ import type { Event, Response } from "@sentry/node";
2
2
  import { spawn } from "child_process";
3
3
  import * as path from "path";
4
4
 
@@ -8,7 +8,7 @@ import { promisify } from "util";
8
8
  import { download } from "../../util/download";
9
9
  import { assertHardhatInvariant, HardhatError } from "../../core/errors";
10
10
  import { ERRORS } from "../../core/errors-list";
11
- import { Mutex } from "../../vendor/await-semaphore";
11
+ import { MultiProcessMutex } from "../../util/multi-process-mutex";
12
12
 
13
13
  const log = debug("hardhat:core:solidity:downloader");
14
14
 
@@ -126,7 +126,7 @@ export class CompilerDownloader implements ICompilerDownloader {
126
126
  }
127
127
 
128
128
  public static defaultCompilerListCachePeriod = 3_600_00;
129
- private readonly _mutex = new Mutex();
129
+ private readonly _mutex = new MultiProcessMutex("compiler-download");
130
130
 
131
131
  /**
132
132
  * Use CompilerDownloader.getConcurrencySafeDownloader instead