@nomicfoundation/hardhat-utils 3.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +5 -0
  3. package/dist/src/bigint.d.ts +32 -0
  4. package/dist/src/bigint.d.ts.map +1 -0
  5. package/dist/src/bigint.js +54 -0
  6. package/dist/src/bigint.js.map +1 -0
  7. package/dist/src/bytecode.d.ts +27 -0
  8. package/dist/src/bytecode.d.ts.map +1 -0
  9. package/dist/src/bytecode.js +60 -0
  10. package/dist/src/bytecode.js.map +1 -0
  11. package/dist/src/bytes.d.ts +34 -0
  12. package/dist/src/bytes.d.ts.map +1 -0
  13. package/dist/src/bytes.js +47 -0
  14. package/dist/src/bytes.js.map +1 -0
  15. package/dist/src/ci.d.ts +7 -0
  16. package/dist/src/ci.d.ts.map +1 -0
  17. package/dist/src/ci.js +18 -0
  18. package/dist/src/ci.js.map +1 -0
  19. package/dist/src/common-errors.d.ts +5 -0
  20. package/dist/src/common-errors.d.ts.map +1 -0
  21. package/dist/src/common-errors.js +7 -0
  22. package/dist/src/common-errors.js.map +1 -0
  23. package/dist/src/crypto.d.ts +25 -0
  24. package/dist/src/crypto.d.ts.map +1 -0
  25. package/dist/src/crypto.js +32 -0
  26. package/dist/src/crypto.js.map +1 -0
  27. package/dist/src/date.d.ts +21 -0
  28. package/dist/src/date.d.ts.map +1 -0
  29. package/dist/src/date.js +27 -0
  30. package/dist/src/date.js.map +1 -0
  31. package/dist/src/debug.d.ts +16 -0
  32. package/dist/src/debug.d.ts.map +1 -0
  33. package/dist/src/debug.js +28 -0
  34. package/dist/src/debug.js.map +1 -0
  35. package/dist/src/error.d.ts +80 -0
  36. package/dist/src/error.d.ts.map +1 -0
  37. package/dist/src/error.js +97 -0
  38. package/dist/src/error.js.map +1 -0
  39. package/dist/src/errors/bytecode.d.ts +18 -0
  40. package/dist/src/errors/bytecode.d.ts.map +1 -0
  41. package/dist/src/errors/bytecode.js +59 -0
  42. package/dist/src/errors/bytecode.js.map +1 -0
  43. package/dist/src/errors/fs.d.ts +25 -0
  44. package/dist/src/errors/fs.d.ts.map +1 -0
  45. package/dist/src/errors/fs.js +41 -0
  46. package/dist/src/errors/fs.js.map +1 -0
  47. package/dist/src/errors/package.d.ts +8 -0
  48. package/dist/src/errors/package.d.ts.map +1 -0
  49. package/dist/src/errors/package.js +12 -0
  50. package/dist/src/errors/package.js.map +1 -0
  51. package/dist/src/errors/request.d.ts +25 -0
  52. package/dist/src/errors/request.d.ts.map +1 -0
  53. package/dist/src/errors/request.js +64 -0
  54. package/dist/src/errors/request.js.map +1 -0
  55. package/dist/src/errors/subprocess.d.ts +8 -0
  56. package/dist/src/errors/subprocess.d.ts.map +1 -0
  57. package/dist/src/errors/subprocess.js +12 -0
  58. package/dist/src/errors/subprocess.js.map +1 -0
  59. package/dist/src/eth.d.ts +56 -0
  60. package/dist/src/eth.d.ts.map +1 -0
  61. package/dist/src/eth.js +78 -0
  62. package/dist/src/eth.js.map +1 -0
  63. package/dist/src/fs.d.ts +241 -0
  64. package/dist/src/fs.d.ts.map +1 -0
  65. package/dist/src/fs.js +581 -0
  66. package/dist/src/fs.js.map +1 -0
  67. package/dist/src/global-dir.d.ts +28 -0
  68. package/dist/src/global-dir.d.ts.map +1 -0
  69. package/dist/src/global-dir.js +42 -0
  70. package/dist/src/global-dir.js.map +1 -0
  71. package/dist/src/hex.d.ts +117 -0
  72. package/dist/src/hex.d.ts.map +1 -0
  73. package/dist/src/hex.js +187 -0
  74. package/dist/src/hex.js.map +1 -0
  75. package/dist/src/internal/bytecode.d.ts +45 -0
  76. package/dist/src/internal/bytecode.d.ts.map +1 -0
  77. package/dist/src/internal/bytecode.js +73 -0
  78. package/dist/src/internal/bytecode.js.map +1 -0
  79. package/dist/src/internal/eth.d.ts +21 -0
  80. package/dist/src/internal/eth.d.ts.map +1 -0
  81. package/dist/src/internal/eth.js +58 -0
  82. package/dist/src/internal/eth.js.map +1 -0
  83. package/dist/src/internal/global-dir.d.ts +4 -0
  84. package/dist/src/internal/global-dir.d.ts.map +1 -0
  85. package/dist/src/internal/global-dir.js +6 -0
  86. package/dist/src/internal/global-dir.js.map +1 -0
  87. package/dist/src/internal/hex.d.ts +2 -0
  88. package/dist/src/internal/hex.d.ts.map +1 -0
  89. package/dist/src/internal/hex.js +16 -0
  90. package/dist/src/internal/hex.js.map +1 -0
  91. package/dist/src/internal/lang.d.ts +2 -0
  92. package/dist/src/internal/lang.d.ts.map +1 -0
  93. package/dist/src/internal/lang.js +9 -0
  94. package/dist/src/internal/lang.js.map +1 -0
  95. package/dist/src/internal/package.d.ts +2 -0
  96. package/dist/src/internal/package.d.ts.map +1 -0
  97. package/dist/src/internal/package.js +15 -0
  98. package/dist/src/internal/package.js.map +1 -0
  99. package/dist/src/internal/request.d.ts +20 -0
  100. package/dist/src/internal/request.d.ts.map +1 -0
  101. package/dist/src/internal/request.js +101 -0
  102. package/dist/src/internal/request.js.map +1 -0
  103. package/dist/src/lang.d.ts +30 -0
  104. package/dist/src/lang.d.ts.map +1 -0
  105. package/dist/src/lang.js +41 -0
  106. package/dist/src/lang.js.map +1 -0
  107. package/dist/src/number.d.ts +23 -0
  108. package/dist/src/number.d.ts.map +1 -0
  109. package/dist/src/number.js +30 -0
  110. package/dist/src/number.js.map +1 -0
  111. package/dist/src/package.d.ts +75 -0
  112. package/dist/src/package.d.ts.map +1 -0
  113. package/dist/src/package.js +97 -0
  114. package/dist/src/package.js.map +1 -0
  115. package/dist/src/path.d.ts +26 -0
  116. package/dist/src/path.d.ts.map +1 -0
  117. package/dist/src/path.js +38 -0
  118. package/dist/src/path.js.map +1 -0
  119. package/dist/src/request.d.ts +118 -0
  120. package/dist/src/request.d.ts.map +1 -0
  121. package/dist/src/request.js +208 -0
  122. package/dist/src/request.js.map +1 -0
  123. package/dist/src/stream.d.ts +10 -0
  124. package/dist/src/stream.d.ts.map +1 -0
  125. package/dist/src/stream.js +16 -0
  126. package/dist/src/stream.js.map +1 -0
  127. package/dist/src/string.d.ts +45 -0
  128. package/dist/src/string.d.ts.map +1 -0
  129. package/dist/src/string.js +60 -0
  130. package/dist/src/string.js.map +1 -0
  131. package/dist/src/subprocess.d.ts +13 -0
  132. package/dist/src/subprocess.d.ts.map +1 -0
  133. package/dist/src/subprocess.js +33 -0
  134. package/dist/src/subprocess.js.map +1 -0
  135. package/dist/src/synchronization.d.ts +6 -0
  136. package/dist/src/synchronization.d.ts.map +1 -0
  137. package/dist/src/synchronization.js +107 -0
  138. package/dist/src/synchronization.js.map +1 -0
  139. package/package.json +90 -0
  140. package/src/bigint.ts +61 -0
  141. package/src/bytecode.ts +90 -0
  142. package/src/bytes.ts +51 -0
  143. package/src/ci.ts +19 -0
  144. package/src/common-errors.ts +7 -0
  145. package/src/crypto.ts +35 -0
  146. package/src/date.ts +28 -0
  147. package/src/debug.ts +38 -0
  148. package/src/error.ts +105 -0
  149. package/src/errors/bytecode.ts +75 -0
  150. package/src/errors/fs.ts +47 -0
  151. package/src/errors/package.ts +13 -0
  152. package/src/errors/request.ts +84 -0
  153. package/src/errors/subprocess.ts +17 -0
  154. package/src/eth.ts +91 -0
  155. package/src/fs.ts +678 -0
  156. package/src/global-dir.ts +51 -0
  157. package/src/hex.ts +232 -0
  158. package/src/internal/bytecode.ts +129 -0
  159. package/src/internal/eth.ts +71 -0
  160. package/src/internal/global-dir.ts +10 -0
  161. package/src/internal/hex.ts +21 -0
  162. package/src/internal/lang.ts +12 -0
  163. package/src/internal/package.ts +15 -0
  164. package/src/internal/request.ts +165 -0
  165. package/src/lang.ts +48 -0
  166. package/src/number.ts +38 -0
  167. package/src/package.ts +143 -0
  168. package/src/path.ts +45 -0
  169. package/src/request.ts +341 -0
  170. package/src/stream.ts +16 -0
  171. package/src/string.ts +69 -0
  172. package/src/subprocess.ts +46 -0
  173. package/src/synchronization.ts +126 -0
@@ -0,0 +1,107 @@
1
+ // Logic explanation: the fs.writeFile function, when used with the wx+ flag, performs an atomic operation to create a file.
2
+ // If multiple processes try to create the same file simultaneously, only one will succeed.
3
+ // This logic can be utilized to implement a mutex.
4
+ // ATTENTION: in the current implementation, there's still a risk of two processes running simultaneously.
5
+ // For example, if processA has locked the mutex and is running, processB will wait.
6
+ // During this wait, processB continuously checks the elapsed time since the mutex lock file was created.
7
+ // If an excessive amount of time has passed, processB will assume ownership of the mutex to avoid stale locks.
8
+ // However, there's a possibility that processB might take ownership because the mutex creation file is outdated, even though processA is still running
9
+ import fs from "node:fs";
10
+ import os from "node:os";
11
+ import path from "node:path";
12
+ import debug from "debug";
13
+ import { ensureError } from "./error.js";
14
+ import { FileSystemAccessError } from "./errors/fs.js";
15
+ import { sleep } from "./lang.js";
16
+ const log = debug("hardhat:util:multi-process-mutex");
17
+ const DEFAULT_MAX_MUTEX_LIFESPAN_IN_MS = 60000;
18
+ const MUTEX_LOOP_WAITING_TIME_IN_MS = 100;
19
+ export class MultiProcessMutex {
20
+ #mutexFilePath;
21
+ #mutexLifespanInMs;
22
+ constructor(mutexName, maxMutexLifespanInMs) {
23
+ log(`Creating mutex with name '${mutexName}'`);
24
+ this.#mutexFilePath = path.join(os.tmpdir(), `${mutexName}.txt`);
25
+ this.#mutexLifespanInMs =
26
+ maxMutexLifespanInMs ?? DEFAULT_MAX_MUTEX_LIFESPAN_IN_MS;
27
+ }
28
+ async use(f) {
29
+ log(`Starting mutex process with mutex file '${this.#mutexFilePath}'`);
30
+ while (true) {
31
+ if (await this.#tryToAcquireMutex()) {
32
+ // Mutex has been acquired
33
+ return this.#executeFunctionAndReleaseMutex(f);
34
+ }
35
+ // Mutex not acquired
36
+ if (this.#isMutexFileTooOld()) {
37
+ // If the mutex file is too old, it likely indicates a stale lock, so the file should be removed
38
+ log(`Current mutex file is too old, removing it at path '${this.#mutexFilePath}'`);
39
+ this.#deleteMutexFile();
40
+ }
41
+ else {
42
+ // wait
43
+ await sleep(MUTEX_LOOP_WAITING_TIME_IN_MS / 1000);
44
+ }
45
+ }
46
+ }
47
+ async #tryToAcquireMutex() {
48
+ try {
49
+ // Create a file only if it does not exist
50
+ fs.writeFileSync(this.#mutexFilePath, "", { flag: "wx+" });
51
+ return true;
52
+ }
53
+ catch (e) {
54
+ ensureError(e);
55
+ if (e.code === "EEXIST") {
56
+ // File already exists, so the mutex is already acquired
57
+ return false;
58
+ }
59
+ throw new FileSystemAccessError(e.message, e);
60
+ }
61
+ }
62
+ async #executeFunctionAndReleaseMutex(f) {
63
+ log(`Mutex acquired at path '${this.#mutexFilePath}'`);
64
+ try {
65
+ return await f();
66
+ }
67
+ finally {
68
+ // Release the mutex
69
+ log(`Mutex released at path '${this.#mutexFilePath}'`);
70
+ this.#deleteMutexFile();
71
+ log(`Mutex released at path '${this.#mutexFilePath}'`);
72
+ }
73
+ }
74
+ #isMutexFileTooOld() {
75
+ let fileStat;
76
+ try {
77
+ fileStat = fs.statSync(this.#mutexFilePath);
78
+ }
79
+ catch (e) {
80
+ ensureError(e);
81
+ if (e.code === "ENOENT") {
82
+ // The file might have been deleted by another process while this function was trying to access it.
83
+ return false;
84
+ }
85
+ throw new FileSystemAccessError(e.message, e);
86
+ }
87
+ const now = new Date();
88
+ const fileDate = new Date(fileStat.ctime);
89
+ const diff = now.getTime() - fileDate.getTime();
90
+ return diff > this.#mutexLifespanInMs;
91
+ }
92
+ #deleteMutexFile() {
93
+ try {
94
+ log(`Deleting mutex file at path '${this.#mutexFilePath}'`);
95
+ fs.unlinkSync(this.#mutexFilePath);
96
+ }
97
+ catch (e) {
98
+ ensureError(e);
99
+ if (e.code === "ENOENT") {
100
+ // The file might have been deleted by another process while this function was trying to access it.
101
+ return;
102
+ }
103
+ throw new FileSystemAccessError(e.message, e);
104
+ }
105
+ }
106
+ }
107
+ //# sourceMappingURL=synchronization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synchronization.js","sourceRoot":"","sources":["../../src/synchronization.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "@nomicfoundation/hardhat-utils",
3
+ "version": "3.0.0-next.0",
4
+ "description": "Utilities for Hardhat and its plugins",
5
+ "homepage": "https://github.com/nomicfoundation/hardhat/tree/v-next/v-next/hardhat-utils",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/NomicFoundation/hardhat",
9
+ "directory": "v-next/hardhat-utils"
10
+ },
11
+ "author": "Nomic Foundation",
12
+ "license": "MIT",
13
+ "type": "module",
14
+ "exports": {
15
+ "./bigint": "./dist/src/bigint.js",
16
+ "./bytecode": "./dist/src/bytecode.js",
17
+ "./bytes": "./dist/src/bytes.js",
18
+ "./ci": "./dist/src/ci.js",
19
+ "./common-errors": "./dist/src/common-errors.js",
20
+ "./crypto": "./dist/src/crypto.js",
21
+ "./date": "./dist/src/date.js",
22
+ "./debug": "./dist/src/debug.js",
23
+ "./error": "./dist/src/error.js",
24
+ "./eth": "./dist/src/eth.js",
25
+ "./fs": "./dist/src/fs.js",
26
+ "./global-dir": "./dist/src/global-dir.js",
27
+ "./hex": "./dist/src/hex.js",
28
+ "./lang": "./dist/src/lang.js",
29
+ "./number": "./dist/src/number.js",
30
+ "./package": "./dist/src/package.js",
31
+ "./path": "./dist/src/path.js",
32
+ "./request": "./dist/src/request.js",
33
+ "./string": "./dist/src/string.js",
34
+ "./stream": "./dist/src/stream.js",
35
+ "./subprocess": "./dist/src/subprocess.js",
36
+ "./synchronization": "./dist/src/synchronization.js"
37
+ },
38
+ "keywords": [
39
+ "ethereum",
40
+ "smart-contracts",
41
+ "hardhat"
42
+ ],
43
+ "files": [
44
+ "dist/src/",
45
+ "src/",
46
+ "CHANGELOG.md",
47
+ "LICENSE",
48
+ "README.md"
49
+ ],
50
+ "devDependencies": {
51
+ "@eslint-community/eslint-plugin-eslint-comments": "^4.3.0",
52
+ "@nomicfoundation/hardhat-node-test-reporter": "^3.0.0-next.0",
53
+ "@types/bn.js": "^5.1.5",
54
+ "@types/debug": "^4.1.7",
55
+ "@types/node": "^20.14.9",
56
+ "@typescript-eslint/eslint-plugin": "^7.7.1",
57
+ "@typescript-eslint/parser": "^7.7.1",
58
+ "eslint": "8.57.0",
59
+ "eslint-config-prettier": "9.1.0",
60
+ "eslint-import-resolver-typescript": "^3.6.1",
61
+ "eslint-plugin-import": "2.29.1",
62
+ "eslint-plugin-no-only-tests": "3.1.0",
63
+ "expect-type": "^0.19.0",
64
+ "prettier": "3.2.5",
65
+ "rimraf": "^5.0.5",
66
+ "tsx": "^4.19.3",
67
+ "typescript": "~5.5.0",
68
+ "typescript-eslint": "7.7.1"
69
+ },
70
+ "dependencies": {
71
+ "debug": "^4.3.2",
72
+ "env-paths": "^2.2.0",
73
+ "ethereum-cryptography": "^2.2.1",
74
+ "fast-equals": "^5.0.1",
75
+ "rfdc": "^1.3.1",
76
+ "undici": "^6.16.1"
77
+ },
78
+ "scripts": {
79
+ "lint": "pnpm prettier --check && pnpm eslint",
80
+ "lint:fix": "pnpm prettier --write && pnpm eslint --fix",
81
+ "eslint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
82
+ "prettier": "prettier \"**/*.{ts,js,md,json}\"",
83
+ "test": "node --import tsx/esm --test --test-reporter=@nomicfoundation/hardhat-node-test-reporter \"test/*.ts\" \"test/!(fixture-projects|helpers)/**/*.ts\"",
84
+ "test:only": "node --import tsx/esm --test --test-only --test-reporter=@nomicfoundation/hardhat-node-test-reporter \"test/*.ts\" \"test/!(fixture-projects|helpers)/**/*.ts\"",
85
+ "pretest": "pnpm build",
86
+ "pretest:only": "pnpm build",
87
+ "build": "tsc --build .",
88
+ "clean": "rimraf dist"
89
+ }
90
+ }
package/src/bigint.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { InvalidParameterError } from "./common-errors.js";
2
+ import { unreachable } from "./error.js";
3
+
4
+ /**
5
+ * Returns the minimum of two bigints.
6
+ *
7
+ * @param x The first number to compare.
8
+ * @param y The second number to compare.
9
+ * @returns The smaller of the two numbers.
10
+ */
11
+ export function min(x: bigint, y: bigint): bigint {
12
+ return x < y ? x : y;
13
+ }
14
+
15
+ /**
16
+ * Returns the maximum of two bigints.
17
+ *
18
+ * @param x The first number to compare.
19
+ * @param y The second number to compare.
20
+ * @returns The larger of the two numbers.
21
+ */
22
+ export function max(x: bigint, y: bigint): bigint {
23
+ return x > y ? x : y;
24
+ }
25
+
26
+ /**
27
+ * Converts a value to a bigint.
28
+ *
29
+ * This function supports several types of input:
30
+ * - `number`: Must be an integer and a safe integer. If it's not, an error is thrown.
31
+ * - `bigint`: Returned as is.
32
+ * - `string`: Converted to a bigint using the BigInt constructor.
33
+ *
34
+ * If the input is of an unsupported type, an error is thrown.
35
+ *
36
+ * @param value The value to convert to a bigint.
37
+ * @returns The input value converted to a bigint.
38
+ * @throws InvalidParameterError If the input value cannot be converted to a bigint.
39
+ */
40
+ export function toBigInt(value: number | string | bigint): bigint {
41
+ switch (typeof value) {
42
+ case "number":
43
+ if (!Number.isInteger(value)) {
44
+ throw new InvalidParameterError(`${value} is not an integer`);
45
+ }
46
+ if (!Number.isSafeInteger(value)) {
47
+ throw new InvalidParameterError(
48
+ `Integer ${value} is unsafe. Consider using ${value}n instead. For more details, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger`,
49
+ );
50
+ }
51
+ // `break;` intentionally omitted. fallthrough desired.
52
+ case "string":
53
+ case "bigint":
54
+ return BigInt(value);
55
+ default:
56
+ unreachable(
57
+ value,
58
+ new InvalidParameterError(`Unsupported type: ${typeof value}`),
59
+ );
60
+ }
61
+ }
@@ -0,0 +1,90 @@
1
+ import type { PrefixedHexString } from "./hex.js";
2
+ import type {
3
+ Artifact,
4
+ LibraryAddresses,
5
+ LibraryLink,
6
+ } from "./internal/bytecode.js";
7
+
8
+ import { getPrefixedHexString, getUnprefixedHexString } from "./hex.js";
9
+ import {
10
+ checkAmbiguousOrUnnecessaryLinks,
11
+ checkMissingLibraryAddresses,
12
+ checkOverlappingLibraryNames,
13
+ checkProvidedLibraryAddresses,
14
+ } from "./internal/bytecode.js";
15
+
16
+ /**
17
+ * Resolves the linked bytecode for a given contract artifact by substituting
18
+ * the required library placeholders within the bytecode with the provided
19
+ * library addresses.
20
+ *
21
+ * @param artifact The contract artifact containing the bytecode and link references.
22
+ * @param providedLibraries An object containing library names as keys and their addresses as values.
23
+ * @returns The linked bytecode with all required libraries correctly linked.
24
+ * @throws InvalidLibraryAddressError If any provided library address is invalid.
25
+ * @throws AmbiguousLibraryNameError If any provided library name matches multiple needed libraries.
26
+ * @throws UnnecessaryLibraryError If any provided library name is not needed by the contract.
27
+ * @throws OverlappingLibrariesError If any library is provided more than once.
28
+ * @throws MissingLibrariesError If any needed library address is missing.
29
+ */
30
+ export function resolveLinkedBytecode(
31
+ artifact: Artifact,
32
+ providedLibraries: LibraryAddresses,
33
+ ): PrefixedHexString {
34
+ checkProvidedLibraryAddresses(providedLibraries);
35
+
36
+ const neededLibraries: LibraryLink[] = [];
37
+ for (const [sourceName, sourceLibraries] of Object.entries(
38
+ artifact.linkReferences,
39
+ )) {
40
+ for (const libraryName of Object.keys(sourceLibraries)) {
41
+ const libraryFqn = `${sourceName}:${libraryName}`;
42
+ const address =
43
+ providedLibraries[libraryFqn] ?? providedLibraries[libraryName];
44
+
45
+ neededLibraries.push({
46
+ sourceName,
47
+ libraryName,
48
+ libraryFqn,
49
+ address,
50
+ });
51
+ }
52
+ }
53
+
54
+ checkAmbiguousOrUnnecessaryLinks(providedLibraries, neededLibraries);
55
+ checkOverlappingLibraryNames(providedLibraries, neededLibraries);
56
+ checkMissingLibraryAddresses(neededLibraries);
57
+
58
+ return linkBytecode(artifact, neededLibraries);
59
+ }
60
+
61
+ /**
62
+ * Links the bytecode of a contract artifact with the provided library addresses.
63
+ * This function does not perform any validation on the provided libraries.
64
+ *
65
+ * @param artifact The contract artifact containing the bytecode and link references.
66
+ * @param libraries An array of LibraryLink objects representing the libraries to be linked.
67
+ * @returns The linked bytecode with all provided libraries correctly linked.
68
+ */
69
+ export function linkBytecode(
70
+ artifact: Artifact,
71
+ libraries: LibraryLink[],
72
+ ): PrefixedHexString {
73
+ const { bytecode, linkReferences } = artifact;
74
+ let linkedBytecode = bytecode;
75
+
76
+ for (const { sourceName, libraryName, address } of libraries) {
77
+ const contractLinkReferences =
78
+ linkReferences[sourceName]?.[libraryName] ?? [];
79
+ const unprefixedAddress = getUnprefixedHexString(address);
80
+
81
+ for (const { start, length } of contractLinkReferences) {
82
+ linkedBytecode =
83
+ linkedBytecode.substring(0, 2 + start * 2) +
84
+ unprefixedAddress +
85
+ linkedBytecode.substring(2 + (start + length) * 2);
86
+ }
87
+ }
88
+
89
+ return getPrefixedHexString(linkedBytecode);
90
+ }
package/src/bytes.ts ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Checks if a value is an instance of Uint8Array.
3
+ *
4
+ * @param value The value to check.
5
+ * @returns True if the value is a Uint8Array, false otherwise.
6
+ */
7
+ export function isBytes(value: unknown): value is Uint8Array {
8
+ return value instanceof Uint8Array;
9
+ }
10
+
11
+ /**
12
+ * Pads a Uint8Array with zeros on the left to a specified length, or truncates
13
+ * it from the left if it's too long.
14
+ *
15
+ * @param bytes The Uint8Array to pad or truncate.
16
+ * @param length The desired length of the Uint8Array.
17
+ * @returns The padded or truncated Uint8Array.
18
+ */
19
+ export function setLengthLeft(bytes: Uint8Array, length: number): Uint8Array {
20
+ if (bytes.length < length) {
21
+ const padded = new Uint8Array(length);
22
+ padded.set(bytes, length - bytes.length);
23
+ return padded;
24
+ }
25
+
26
+ return bytes.subarray(-length);
27
+ }
28
+
29
+ /**
30
+ * Checks if two Uint8Arrays are equal.
31
+ *
32
+ * @param x The first Uint8Array to compare.
33
+ * @param y The second Uint8Array to compare.
34
+ * @returns True if the Uint8Arrays are equal, false otherwise.
35
+ */
36
+ export function equalsBytes(x: Uint8Array, y: Uint8Array): boolean {
37
+ return x.length === y.length && x.every((xVal, i) => xVal === y[i]);
38
+ }
39
+
40
+ /**
41
+ * Converts a UTF-8 encoded string into a byte array.
42
+ *
43
+ * @param utf8String The UTF-8 encoded string to convert to bytes.
44
+ * @returns A Uint8Array representing the byte sequence of the input UTF-8 string.
45
+ */
46
+ export function utf8StringToBytes(utf8String: string): Uint8Array {
47
+ return new TextEncoder().encode(utf8String);
48
+ }
49
+
50
+ export { bytesToBigInt, bytesToNumber, numberToBytes } from "./number.js";
51
+ export { bytesToHexString, hexStringToBytes } from "./hex.js";
package/src/ci.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Checks whether the current process is running in a CI environment.
3
+ *
4
+ * @returns True if the current process is running in a CI environment.
5
+ */
6
+ export function isCi(): boolean {
7
+ const env = process.env;
8
+
9
+ return (
10
+ env.GITHUB_ACTIONS !== undefined || // GitHub Actions
11
+ env.NOW !== undefined || // Vercel Now
12
+ env.DEPLOYMENT_ID !== undefined || // Vercel Now
13
+ env.CODEBUILD_BUILD_NUMBER !== undefined || // AWS CodeBuild
14
+ env.CI !== undefined || // Travis CI, CircleCI, Cirrus CI, GitLab CI, Appveyor, CodeShip, dsari
15
+ env.CONTINUOUS_INTEGRATION !== undefined || // Travis CI, Cirrus CI
16
+ env.BUILD_NUMBER !== undefined || // Jenkins, TeamCity
17
+ env.RUN_ID !== undefined // TaskCluster, dsari
18
+ );
19
+ }
@@ -0,0 +1,7 @@
1
+ import { CustomError } from "./error.js";
2
+
3
+ export class InvalidParameterError extends CustomError {
4
+ constructor(message: string, cause?: Error) {
5
+ super(message, cause);
6
+ }
7
+ }
package/src/crypto.ts ADDED
@@ -0,0 +1,35 @@
1
+ import { keccak256 as keccak256Impl } from "ethereum-cryptography/keccak";
2
+
3
+ /**
4
+ * Computes the Keccak-256 hash of the input bytes.
5
+ *
6
+ * @param bytes The input bytes to hash.
7
+ * @returns The Keccak-256 hash of the input bytes.
8
+ */
9
+ export async function keccak256(bytes: Uint8Array): Promise<Uint8Array> {
10
+ return keccak256Impl(bytes);
11
+ }
12
+
13
+ /**
14
+ * Creates a non-cryptographic hash-based identifier for the given input.
15
+ *
16
+ * This function is primarily intended for generating unique identifiers from
17
+ * a given input string.
18
+ * It uses the SHA-1 hash algorithm, which is not cryptographically secure, but
19
+ * is sufficient for this use case as long as the input is not generated by an
20
+ * attacker.
21
+ *
22
+ * Note: The exact algorithm used (SHA-1) is not crucial for the function's
23
+ * purpose of generating unique identifiers, and could be replaced if needed.
24
+ *
25
+ * @param data The input string to be hashed.
26
+ * @returns The SHA-1 hash of the input string, represented as a
27
+ * hexadecimal string.
28
+ */
29
+ export async function createNonCryptographicHashId(
30
+ data: string,
31
+ ): Promise<string> {
32
+ const message = new TextEncoder().encode(data);
33
+ const buffer = await crypto.subtle.digest("SHA-1", message);
34
+ return Buffer.from(buffer).toString("hex");
35
+ }
package/src/date.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Converts a string, number, or Date object to a Unix timestamp (seconds since the Unix Epoch).
3
+ *
4
+ * @param value The string to convert.
5
+ * @returns The Unix timestamp.
6
+ */
7
+ export function toSeconds(value: string | number | Date): number {
8
+ return Math.floor(new Date(value).getTime() / 1000);
9
+ }
10
+
11
+ /**
12
+ * Converts a Unix timestamp to a Date object.
13
+ *
14
+ * @param timestamp The Unix timestamp to convert.
15
+ * @returns The Date object.
16
+ */
17
+ export function secondsToDate(timestamp: number): Date {
18
+ return new Date(timestamp * 1000);
19
+ }
20
+
21
+ /**
22
+ * Gets the current Unix timestamp (seconds since the Unix Epoch).
23
+ *
24
+ * @returns The current Unix timestamp.
25
+ */
26
+ export function now(): number {
27
+ return Math.floor(Date.now() / 1000);
28
+ }
package/src/debug.ts ADDED
@@ -0,0 +1,38 @@
1
+ import debugLib from "debug";
2
+
3
+ /**
4
+ * A simple decorator that adds debug logging for when a method is entered and exited.
5
+ *
6
+ * This decorator is meant to be used for debugging purposes only. It should not be committed in runtime code.
7
+ *
8
+ * Example usage:
9
+ *
10
+ * ```
11
+ * class MyClass {
12
+ * @withDebugLogs("MyClass:exampleClassMethod")
13
+ * public function exampleClassMethod(...)
14
+ * }
15
+ * ```
16
+ */
17
+ export function withDebugLogs<This, Args extends any[], Return>(
18
+ tag: string = "",
19
+ ) {
20
+ return function actualDecorator(
21
+ originalMethod: (this: This, ...args: Args) => Return,
22
+ _context: ClassMethodDecoratorContext<
23
+ This,
24
+ (this: This, ...args: Args) => Return
25
+ >,
26
+ ): (this: This, ...args: Args) => Return {
27
+ const log = debugLib(`hardhat:dev:core${tag === "" ? "" : `:${tag}`}`);
28
+
29
+ function replacementMethod(this: This, ...args: Args): Return {
30
+ log(`Entering method with args:`, args);
31
+ const result = originalMethod.call(this, ...args);
32
+ log(`Exiting method.`);
33
+ return result;
34
+ }
35
+
36
+ return replacementMethod;
37
+ };
38
+ }
package/src/error.ts ADDED
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Abstract custom error class, which inherits from the built-in Error class,
3
+ * making sure that the standard errror properties are set, and the stack trace
4
+ * is not polluted with the custom error class' code.
5
+ *
6
+ * This class supports the `cause` property, which can be used to pass the
7
+ * original error that caused the custom error to be thrown. Note that it needs
8
+ * to be an instance of the built-in Error class, or a subclass of it. See `ensureError`
9
+ * for a convinient way of using it.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * class MyCustomError extends CustomError {
14
+ * }
15
+ *
16
+ * try {
17
+ * mayThrow();
18
+ * } catch (error) {
19
+ * ensureError(error);
20
+ * throw new MyCustomError('Something went wrong', error);
21
+ * }
22
+ * ```
23
+ */
24
+ export abstract class CustomError extends Error {
25
+ public override stack!: string;
26
+
27
+ constructor(message: string, cause?: Error) {
28
+ super(message, cause !== undefined ? { cause } : undefined);
29
+ this.name = this.constructor.name;
30
+ Error.captureStackTrace(this, this.constructor);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Ensures that the provided value is an instance of an error.
36
+ *
37
+ * @example
38
+ * This function is meant to be used in a catch block to ensure that you caught the right error.
39
+ *
40
+ * ```ts
41
+ * // Ensuring that you got an Error
42
+ * try {
43
+ * mayThrow();
44
+ * } catch (error) {
45
+ * ensureError(error);
46
+ * console.err(error.message);
47
+ * }
48
+ *
49
+ * // Ensuring that you got a specific error
50
+ * try {
51
+ * mayThrow();
52
+ * } catch (error) {
53
+ * ensureError(error, MyError);
54
+ * console.err(error.myMessage());
55
+ * }
56
+ * ```
57
+ *
58
+ * @param thrown The value to check.
59
+ * @param ErrorType The error type to check against.
60
+ * @throws The value if its not an instance of the specified error type.
61
+ */
62
+ export function ensureError<ErrorT extends Error>(
63
+ thrown: unknown,
64
+ ErrorType?: new (...args: any[]) => ErrorT,
65
+ ): asserts thrown is ErrorT {
66
+ if (ErrorType === undefined) {
67
+ if (thrown instanceof Error) {
68
+ return;
69
+ }
70
+
71
+ throw thrown;
72
+ }
73
+
74
+ if (thrown instanceof ErrorType) {
75
+ return;
76
+ }
77
+
78
+ throw thrown;
79
+ }
80
+
81
+ /**
82
+ * Throws an error for an unreachable code path. This function is typically
83
+ * used in a default case of a switch statement where all possible values of
84
+ * the switched variable should be handled in other cases. If the default case
85
+ * is reached, it means that an unexpected value was encountered, so an error
86
+ * is thrown.
87
+ *
88
+ * Note: The `@typescript-eslint/switch-exhaustiveness-check` rule checks for
89
+ * exhaustiveness in switch statements by comparing the types of the switch
90
+ * expression and the case clauses. However, it only works with union types and
91
+ * enum types. If you're switching on the result of the `typeof` operator or
92
+ * any other expression that doesn't return a union type or an enum type, this
93
+ * rule cannot enforce exhaustiveness. In such cases, you can use this function
94
+ * in the default case to ensure that an error is thrown if an unexpected value
95
+ * is encountered.
96
+ *
97
+ * @param _value The unexpected value. This parameter is unused and is only for
98
+ * the purpose of type checking.
99
+ * @param error The error to throw.
100
+ * @returns This function never returns normally. It always throws an error.
101
+ * @throws Will throw an error when called.
102
+ */
103
+ export function unreachable(_value: never, error: Error): never {
104
+ throw error;
105
+ }