hardhat 3.9.1 → 3.10.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.
- package/CHANGELOG.md +24 -0
- package/dist/src/internal/builtin-plugins/network-manager/accounts/derive-private-keys.js +1 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.js +2 -0
- package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.js.map +1 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.d.ts +2 -2
- package/dist/src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.js.map +1 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/types/hardfork.d.ts +17 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/types/hardfork.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/types/hardfork.js +19 -2
- package/dist/src/internal/builtin-plugins/network-manager/edr/types/hardfork.js.map +1 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/utils/convert-to-edr.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/utils/convert-to-edr.js +5 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/utils/convert-to-edr.js.map +1 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/utils/hardfork.d.ts +8 -0
- package/dist/src/internal/builtin-plugins/network-manager/edr/utils/hardfork.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/utils/hardfork.js +30 -1
- package/dist/src/internal/builtin-plugins/network-manager/edr/utils/hardfork.js.map +1 -1
- package/dist/src/internal/builtin-plugins/node/task-action.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/node/task-action.js +2 -37
- package/dist/src/internal/builtin-plugins/node/task-action.js.map +1 -1
- package/dist/src/internal/builtin-plugins/node/utils/resolve-node-connection-params.d.ts +19 -0
- package/dist/src/internal/builtin-plugins/node/utils/resolve-node-connection-params.d.ts.map +1 -0
- package/dist/src/internal/builtin-plugins/node/utils/resolve-node-connection-params.js +47 -0
- package/dist/src/internal/builtin-plugins/node/utils/resolve-node-connection-params.js.map +1 -0
- package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.js +34 -7
- package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/config.d.ts +1 -0
- package/dist/src/internal/builtin-plugins/solidity-test/config.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/config.js +31 -5
- package/dist/src/internal/builtin-plugins/solidity-test/config.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/eip712/canonicalize.d.ts +9 -3
- package/dist/src/internal/builtin-plugins/solidity-test/eip712/canonicalize.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/eip712/canonicalize.js +60 -27
- package/dist/src/internal/builtin-plugins/solidity-test/eip712/canonicalize.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/eip712/index.d.ts +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/eip712/index.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/eip712/index.js +19 -10
- package/dist/src/internal/builtin-plugins/solidity-test/eip712/index.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/helpers.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/helpers.js +4 -1
- package/dist/src/internal/builtin-plugins/solidity-test/helpers.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/inline-config/index.d.ts +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/inline-config/index.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/inline-config/index.js +13 -5
- package/dist/src/internal/builtin-plugins/solidity-test/inline-config/index.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/task-action.js +2 -2
- package/dist/src/internal/builtin-plugins/solidity-test/task-action.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity-test/type-extensions.d.ts +6 -0
- package/dist/src/internal/builtin-plugins/solidity-test/type-extensions.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/internal/builtin-plugins/network-manager/accounts/derive-private-keys.ts +1 -1
- package/src/internal/builtin-plugins/network-manager/edr/edr-provider.ts +3 -0
- package/src/internal/builtin-plugins/network-manager/edr/stack-traces/solidity-stack-trace.ts +1 -1
- package/src/internal/builtin-plugins/network-manager/edr/types/hardfork.ts +20 -3
- package/src/internal/builtin-plugins/network-manager/edr/utils/convert-to-edr.ts +5 -0
- package/src/internal/builtin-plugins/network-manager/edr/utils/hardfork.ts +53 -1
- package/src/internal/builtin-plugins/node/task-action.ts +2 -52
- package/src/internal/builtin-plugins/node/utils/resolve-node-connection-params.ts +79 -0
- package/src/internal/builtin-plugins/solidity/build-system/build-system.ts +51 -8
- package/src/internal/builtin-plugins/solidity-test/config.ts +52 -5
- package/src/internal/builtin-plugins/solidity-test/eip712/canonicalize.ts +64 -25
- package/src/internal/builtin-plugins/solidity-test/eip712/index.ts +23 -13
- package/src/internal/builtin-plugins/solidity-test/helpers.ts +5 -1
- package/src/internal/builtin-plugins/solidity-test/inline-config/index.ts +20 -9
- package/src/internal/builtin-plugins/solidity-test/task-action.ts +2 -2
- package/src/internal/builtin-plugins/solidity-test/type-extensions.ts +6 -0
- package/templates/hardhat-3/01-node-test-runner-viem/package.json +1 -1
- package/templates/hardhat-3/02-mocha-ethers/package.json +3 -3
- package/templates/hardhat-3/03-minimal/package.json +1 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { EdrNetworkConfigOverride } from "../../../../types/config.js";
|
|
2
|
+
import type { ChainType } from "../../../../types/network.js";
|
|
3
|
+
|
|
4
|
+
import { HardhatError } from "@nomicfoundation/hardhat-errors";
|
|
5
|
+
|
|
6
|
+
import { isSupportedChainType } from "../../../edr/chain-type.js";
|
|
7
|
+
|
|
8
|
+
export interface NodeConnectionArguments {
|
|
9
|
+
chainType?: string;
|
|
10
|
+
chainId: number;
|
|
11
|
+
fork?: string;
|
|
12
|
+
forkBlockNumber: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ResolvedNodeConnectionParams {
|
|
16
|
+
network: string;
|
|
17
|
+
chainType?: ChainType;
|
|
18
|
+
override?: EdrNetworkConfigOverride;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolves the arguments passed to the `node` task into the connection
|
|
23
|
+
* parameters used to create the underlying network connection.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveNodeConnectionParams(
|
|
26
|
+
network: string,
|
|
27
|
+
args: NodeConnectionArguments,
|
|
28
|
+
): ResolvedNodeConnectionParams {
|
|
29
|
+
const connectionParams: ResolvedNodeConnectionParams = {
|
|
30
|
+
network,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// NOTE: We create an empty network config override here. We add to it based
|
|
34
|
+
// on the result of arguments parsing. We can expand the list of arguments
|
|
35
|
+
// as much as needed.
|
|
36
|
+
const networkConfigOverride: EdrNetworkConfigOverride = {};
|
|
37
|
+
|
|
38
|
+
if (args.chainType !== undefined) {
|
|
39
|
+
if (!isSupportedChainType(args.chainType)) {
|
|
40
|
+
throw new HardhatError(
|
|
41
|
+
HardhatError.ERRORS.CORE.ARGUMENTS.INVALID_VALUE_FOR_TYPE,
|
|
42
|
+
{
|
|
43
|
+
value: args.chainType,
|
|
44
|
+
type: "ChainType",
|
|
45
|
+
name: "chainType",
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
connectionParams.chainType = args.chainType;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (args.chainId !== -1) {
|
|
54
|
+
networkConfigOverride.chainId = args.chainId;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// NOTE: --fork-block-number is only valid if --fork is specified
|
|
58
|
+
if (args.fork !== undefined) {
|
|
59
|
+
networkConfigOverride.forking = {
|
|
60
|
+
enabled: true,
|
|
61
|
+
url: args.fork,
|
|
62
|
+
...(args.forkBlockNumber !== -1
|
|
63
|
+
? { blockNumber: args.forkBlockNumber }
|
|
64
|
+
: undefined),
|
|
65
|
+
};
|
|
66
|
+
} else if (args.forkBlockNumber !== -1) {
|
|
67
|
+
// NOTE: We could make the error more specific here.
|
|
68
|
+
throw new HardhatError(
|
|
69
|
+
HardhatError.ERRORS.CORE.ARGUMENTS.MISSING_VALUE_FOR_ARGUMENT,
|
|
70
|
+
{
|
|
71
|
+
argument: "fork",
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
connectionParams.override = networkConfigOverride;
|
|
77
|
+
|
|
78
|
+
return connectionParams;
|
|
79
|
+
}
|
|
@@ -1327,20 +1327,63 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
|
|
|
1327
1327
|
options?: CompileBuildInfoOptions,
|
|
1328
1328
|
): Promise<CompilerOutput> {
|
|
1329
1329
|
const quiet = options?.quiet ?? false;
|
|
1330
|
+
const compilerType = buildInfo.compilerType ?? "solc";
|
|
1330
1331
|
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1332
|
+
if (compilerType === "solc") {
|
|
1333
|
+
// Build info recompilation for solc is self-contained: solc build info
|
|
1334
|
+
// files must be replayed with the same solc version. We bypass both
|
|
1335
|
+
// downloadCompilers and getCompiler hooks to preserve the historical solc
|
|
1336
|
+
// path and avoid making it depend on the current project config.
|
|
1337
|
+
await downloadSolcCompilers(new Set([buildInfo.solcVersion]), quiet);
|
|
1336
1338
|
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1339
|
+
const solcCompiler = await getCompiler(buildInfo.solcVersion, {
|
|
1340
|
+
preferWasm: false,
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
return await solcCompiler.compile(buildInfo.input);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
const partialCompilerConfig =
|
|
1347
|
+
this.#getPartialSolidityCompilerConfigFromBuildInfo(buildInfo);
|
|
1348
|
+
|
|
1349
|
+
await this.#hooks.runParallelHandlers("solidity", "downloadCompilers", [
|
|
1350
|
+
[partialCompilerConfig],
|
|
1351
|
+
quiet,
|
|
1352
|
+
]);
|
|
1353
|
+
|
|
1354
|
+
const compiler = await this.#hooks.runHandlerChain(
|
|
1355
|
+
"solidity",
|
|
1356
|
+
"getCompiler",
|
|
1357
|
+
[partialCompilerConfig],
|
|
1358
|
+
async () => {
|
|
1359
|
+
throw new HardhatError(
|
|
1360
|
+
HardhatError.ERRORS.CORE.SOLIDITY.BUILD_INFO_COMPILER_TYPE_NOT_HANDLED,
|
|
1361
|
+
{
|
|
1362
|
+
compilerType,
|
|
1363
|
+
version: buildInfo.solcVersion,
|
|
1364
|
+
},
|
|
1365
|
+
);
|
|
1366
|
+
},
|
|
1367
|
+
);
|
|
1340
1368
|
|
|
1341
1369
|
return await compiler.compile(buildInfo.input);
|
|
1342
1370
|
}
|
|
1343
1371
|
|
|
1372
|
+
#getPartialSolidityCompilerConfigFromBuildInfo(
|
|
1373
|
+
buildInfo: SolidityBuildInfo,
|
|
1374
|
+
): SolidityCompilerConfig {
|
|
1375
|
+
return {
|
|
1376
|
+
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions --
|
|
1377
|
+
Build infos may come from setups whose compiler type is unknown to the
|
|
1378
|
+
current Hardhat type definitions, but plugins can still handle them via
|
|
1379
|
+
the compiler hooks. */
|
|
1380
|
+
type: (buildInfo.compilerType ??
|
|
1381
|
+
"solc") as SolidityCompilerConfig["type"],
|
|
1382
|
+
version: buildInfo.solcVersion,
|
|
1383
|
+
settings: buildInfo.input.settings,
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1344
1387
|
async #downloadConfiguredCompilers(quiet = false): Promise<void> {
|
|
1345
1388
|
// We always print that we are downloading the compilers
|
|
1346
1389
|
quiet = false;
|
|
@@ -32,6 +32,22 @@ import { DEFAULT_TEST_PROFILE } from "./test-profiles.js";
|
|
|
32
32
|
export const DEFAULT_FUZZ_SEED =
|
|
33
33
|
"0x7727ea51af0441c20da14dcd68a15dac8c9ebd589c5be8fa8c87c1d3720450bc";
|
|
34
34
|
|
|
35
|
+
const nonnegativeSafeIntOrBigInt = unionType(
|
|
36
|
+
[z.number().int().nonnegative().safe(), z.bigint().nonnegative()],
|
|
37
|
+
"Expected a nonnegative safe int or a nonnegative bigint",
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const nonnegativeSafeIntOrSafeBigInt = unionType(
|
|
41
|
+
[
|
|
42
|
+
z.number().int().nonnegative().safe(),
|
|
43
|
+
z
|
|
44
|
+
.bigint()
|
|
45
|
+
.nonnegative()
|
|
46
|
+
.refine((value) => value <= BigInt(Number.MAX_SAFE_INTEGER)),
|
|
47
|
+
],
|
|
48
|
+
"Expected a nonnegative safe int or a nonnegative safe bigint",
|
|
49
|
+
);
|
|
50
|
+
|
|
35
51
|
const solidityTestProfileUserConfigType = z.object({
|
|
36
52
|
fsPermissions: z
|
|
37
53
|
.object({
|
|
@@ -54,6 +70,7 @@ const solidityTestProfileUserConfigType = z.object({
|
|
|
54
70
|
blockTimestamp: z.bigint().optional(),
|
|
55
71
|
prevRandao: z.bigint().optional(),
|
|
56
72
|
gasLimit: z.bigint().optional(),
|
|
73
|
+
memoryLimit: nonnegativeSafeIntOrBigInt.optional(),
|
|
57
74
|
blockGasLimit: z.number().or(z.bigint()).or(z.literal(false)).optional(),
|
|
58
75
|
transactionGasCap: z.number().or(z.bigint()).or(z.literal(false)).optional(),
|
|
59
76
|
fuzz: z
|
|
@@ -67,6 +84,7 @@ const solidityTestProfileUserConfigType = z.object({
|
|
|
67
84
|
includeStorage: z.boolean().optional(),
|
|
68
85
|
includePushBytes: z.boolean().optional(),
|
|
69
86
|
showLogs: z.boolean().optional(),
|
|
87
|
+
timeout: nonnegativeSafeIntOrSafeBigInt.optional(),
|
|
70
88
|
})
|
|
71
89
|
.optional(),
|
|
72
90
|
forking: z
|
|
@@ -92,6 +110,7 @@ const solidityTestProfileUserConfigType = z.object({
|
|
|
92
110
|
includeStorage: z.boolean().optional(),
|
|
93
111
|
includePushBytes: z.boolean().optional(),
|
|
94
112
|
shrinkRunLimit: z.number().optional(),
|
|
113
|
+
timeout: nonnegativeSafeIntOrSafeBigInt.optional(),
|
|
95
114
|
})
|
|
96
115
|
.optional(),
|
|
97
116
|
eip712Types: z
|
|
@@ -222,12 +241,22 @@ export async function resolveSolidityTestUserConfig(
|
|
|
222
241
|
resolveConfigurationVariable,
|
|
223
242
|
);
|
|
224
243
|
|
|
244
|
+
const {
|
|
245
|
+
memoryLimit,
|
|
246
|
+
fuzz,
|
|
247
|
+
invariant,
|
|
248
|
+
eip712Types,
|
|
249
|
+
...otherProfileUserConfig
|
|
250
|
+
} = profileUserConfig ?? {};
|
|
251
|
+
|
|
225
252
|
const resolvedDefaultProfile = {
|
|
226
253
|
rpcCachePath: defaultRpcCachePath,
|
|
227
|
-
...
|
|
228
|
-
|
|
254
|
+
...otherProfileUserConfig,
|
|
255
|
+
memoryLimit: memoryLimit !== undefined ? BigInt(memoryLimit) : undefined,
|
|
256
|
+
fuzz: resolveFuzzConfig(fuzz),
|
|
257
|
+
invariant: resolveInvariantConfig(invariant),
|
|
229
258
|
forking: resolvedForking,
|
|
230
|
-
eip712Types: resolveEip712TypesConfig(
|
|
259
|
+
eip712Types: resolveEip712TypesConfig(eip712Types),
|
|
231
260
|
};
|
|
232
261
|
|
|
233
262
|
return {
|
|
@@ -251,9 +280,27 @@ export async function resolveSolidityTestUserConfig(
|
|
|
251
280
|
export function resolveFuzzConfig(
|
|
252
281
|
fuzzUserConfig: SolidityTestProfileUserConfig["fuzz"] = {},
|
|
253
282
|
): SolidityTestProfileConfig["fuzz"] {
|
|
283
|
+
const { seed, timeout, ...otherFuzzUserConfig } = fuzzUserConfig;
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
...otherFuzzUserConfig,
|
|
287
|
+
seed: seed ?? DEFAULT_FUZZ_SEED,
|
|
288
|
+
...(timeout !== undefined ? { timeout: Number(timeout) } : {}),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function resolveInvariantConfig(
|
|
293
|
+
invariantUserConfig: SolidityTestProfileUserConfig["invariant"],
|
|
294
|
+
): SolidityTestProfileConfig["invariant"] {
|
|
295
|
+
if (invariantUserConfig === undefined) {
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const { timeout, ...otherInvariantUserConfig } = invariantUserConfig;
|
|
300
|
+
|
|
254
301
|
return {
|
|
255
|
-
...
|
|
256
|
-
|
|
302
|
+
...otherInvariantUserConfig,
|
|
303
|
+
...(timeout !== undefined ? { timeout: Number(timeout) } : {}),
|
|
257
304
|
};
|
|
258
305
|
}
|
|
259
306
|
|
|
@@ -2,6 +2,12 @@ import type { CollectedStruct } from "./ast-walker.js";
|
|
|
2
2
|
|
|
3
3
|
import { HardhatError } from "@nomicfoundation/hardhat-errors";
|
|
4
4
|
|
|
5
|
+
export const SELECTED_CONFLICT_REMEDIATION =
|
|
6
|
+
"Rename one of the structs, or scope your `test.solidity.eip712Types.include` / `exclude` globs in `hardhat.config.ts` so that only one of them is selected.";
|
|
7
|
+
|
|
8
|
+
export const DEPENDENCY_CONFLICT_REMEDIATION =
|
|
9
|
+
"Rename one of the structs so the name has a single definition. The clashing name is pulled in as a dependency of a selected struct, so if you don't need that selected struct, deselecting it with your `test.solidity.eip712Types.include` / `exclude` globs in `hardhat.config.ts` also resolves the conflict.";
|
|
10
|
+
|
|
5
11
|
/**
|
|
6
12
|
* Produces the flat list of canonical EIP-712 type strings expected by EDR.
|
|
7
13
|
*
|
|
@@ -25,14 +31,28 @@ import { HardhatError } from "@nomicfoundation/hardhat-errors";
|
|
|
25
31
|
* constructs and propagates `None` through the dep graph so dependents are
|
|
26
32
|
* also dropped.
|
|
27
33
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
34
|
+
* Selection is scoped by source, not by struct name: `selectedSources` holds
|
|
35
|
+
* the project-relative paths matched by the user's include/exclude globs. Only
|
|
36
|
+
* structs from those sources are emitted, but structs elsewhere still feed dep
|
|
37
|
+
* resolution so cross-file deps inline correctly. Scoping by source is what
|
|
38
|
+
* lets a non-selected file define a same-named struct without it being mistaken
|
|
39
|
+
* for selected.
|
|
30
40
|
*/
|
|
31
41
|
export function canonicalizeStructs(
|
|
32
42
|
structs: CollectedStruct[],
|
|
33
|
-
|
|
43
|
+
selectedSources: Set<string>,
|
|
34
44
|
): string[] {
|
|
35
|
-
|
|
45
|
+
// A name is "selected" if any of its definitions lives in a selected source.
|
|
46
|
+
// Drives what's emitted and, in `indexByName`, whether a selected definition
|
|
47
|
+
// exists to win a name collision.
|
|
48
|
+
const selectedNames = new Set<string>();
|
|
49
|
+
for (const struct of structs) {
|
|
50
|
+
if (selectedSources.has(struct.sourcePath)) {
|
|
51
|
+
selectedNames.add(struct.name);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const byName = indexByName(structs, selectedSources, selectedNames);
|
|
36
56
|
const knownNames = new Set(byName.keys());
|
|
37
57
|
const encodable = computeEncodable(byName, knownNames);
|
|
38
58
|
const result: string[] = [];
|
|
@@ -189,15 +209,27 @@ function transitiveDeps(
|
|
|
189
209
|
* non decodable definition silently win over an encodable one, dropping the
|
|
190
210
|
* struct from the canonical output.
|
|
191
211
|
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
212
|
+
* A name clash aborts the run only when it could change the output; otherwise
|
|
213
|
+
* the first definition wins silently. Selection is scoped by source
|
|
214
|
+
* (`selectedSources`), not by name, so a non-selected source whose struct name
|
|
215
|
+
* collides with a selected one stays non-selected. `selectedNames` is the
|
|
216
|
+
* derived set of names with at least one selected definition. Selected structs
|
|
217
|
+
* are processed first, so the already-stored definition in any collision is
|
|
218
|
+
* selected when one exists. Two rules resolve conflicts:
|
|
219
|
+
*
|
|
220
|
+
* - Both sides selected: throw immediately — genuinely ambiguous which to
|
|
221
|
+
* emit.
|
|
222
|
+
* - Current side non-selected: defer; the stored definition keeps the name.
|
|
223
|
+
* Throw only if the name is reachable from a selected struct (its
|
|
224
|
+
* transitive deps), since then which copy got inlined would depend on
|
|
225
|
+
* iteration order. Names unreachable from the selected set are silently
|
|
226
|
+
* deduped (first wins), so a non-selected struct that merely shares a
|
|
227
|
+
* name with a selected root or an unreferenced struct never aborts the
|
|
228
|
+
* run.
|
|
198
229
|
*/
|
|
199
230
|
function indexByName(
|
|
200
231
|
structs: CollectedStruct[],
|
|
232
|
+
selectedSources: Set<string>,
|
|
201
233
|
selectedNames: Set<string>,
|
|
202
234
|
): Map<string, CollectedStruct> {
|
|
203
235
|
const byName = new Map<string, CollectedStruct>();
|
|
@@ -209,8 +241,8 @@ function indexByName(
|
|
|
209
241
|
>();
|
|
210
242
|
|
|
211
243
|
const ordered = [
|
|
212
|
-
...structs.filter((s) =>
|
|
213
|
-
...structs.filter((s) => !
|
|
244
|
+
...structs.filter((s) => selectedSources.has(s.sourcePath)),
|
|
245
|
+
...structs.filter((s) => !selectedSources.has(s.sourcePath)),
|
|
214
246
|
];
|
|
215
247
|
|
|
216
248
|
for (const struct of ordered) {
|
|
@@ -228,24 +260,31 @@ function indexByName(
|
|
|
228
260
|
continue;
|
|
229
261
|
}
|
|
230
262
|
|
|
231
|
-
if (
|
|
232
|
-
|
|
233
|
-
|
|
263
|
+
if (selectedSources.has(struct.sourcePath)) {
|
|
264
|
+
// Selected-vs-selected conflict: both this and the stored definition are
|
|
265
|
+
// selected (selected structs come first), so it's ambiguous which to emit.
|
|
266
|
+
throw new HardhatError(
|
|
267
|
+
HardhatError.ERRORS.CORE.SOLIDITY_TESTS.EIP712_DUPLICATE_STRUCT_NAME,
|
|
268
|
+
{
|
|
269
|
+
name: struct.name,
|
|
234
270
|
firstSource: sourceByName.get(struct.name) ?? "<unknown>",
|
|
235
271
|
secondSource: struct.sourcePath,
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
272
|
+
remediation: SELECTED_CONFLICT_REMEDIATION,
|
|
273
|
+
},
|
|
274
|
+
);
|
|
239
275
|
}
|
|
240
276
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
277
|
+
// Non-selected side, so the stored definition (selected if the name has
|
|
278
|
+
// one, since selected sources come first) keeps the name. Defer: throw
|
|
279
|
+
// later only if the name is reachable from a selected struct. Sharing a
|
|
280
|
+
// name with a selected root or an unreferenced struct is harmless and
|
|
281
|
+
// must not abort.
|
|
282
|
+
if (!deferredConflicts.has(struct.name)) {
|
|
283
|
+
deferredConflicts.set(struct.name, {
|
|
245
284
|
firstSource: sourceByName.get(struct.name) ?? "<unknown>",
|
|
246
285
|
secondSource: struct.sourcePath,
|
|
247
|
-
}
|
|
248
|
-
|
|
286
|
+
});
|
|
287
|
+
}
|
|
249
288
|
}
|
|
250
289
|
|
|
251
290
|
if (deferredConflicts.size > 0) {
|
|
@@ -254,7 +293,7 @@ function indexByName(
|
|
|
254
293
|
if (reachable.has(name)) {
|
|
255
294
|
throw new HardhatError(
|
|
256
295
|
HardhatError.ERRORS.CORE.SOLIDITY_TESTS.EIP712_DUPLICATE_STRUCT_NAME,
|
|
257
|
-
{ name, ...sources },
|
|
296
|
+
{ name, ...sources, remediation: DEPENDENCY_CONFLICT_REMEDIATION },
|
|
258
297
|
);
|
|
259
298
|
}
|
|
260
299
|
}
|
|
@@ -2,10 +2,12 @@ import type { CollectedStruct } from "./ast-walker.js";
|
|
|
2
2
|
import type { SolidityBuildInfoOutput } from "../../../../types/solidity/solidity-artifacts.js";
|
|
3
3
|
import type { BuildInfoAndOutput } from "../edr-artifacts.js";
|
|
4
4
|
|
|
5
|
+
import { HardhatError } from "@nomicfoundation/hardhat-errors";
|
|
5
6
|
import {
|
|
6
7
|
bytesIncludesUtf8String,
|
|
7
|
-
|
|
8
|
+
parseJsonBytes,
|
|
8
9
|
} from "@nomicfoundation/hardhat-utils/bytes";
|
|
10
|
+
import { ensureError } from "@nomicfoundation/hardhat-utils/error";
|
|
9
11
|
|
|
10
12
|
import { isPathSelected } from "../../../utils/glob.js";
|
|
11
13
|
import { toUserSourceName } from "../../solidity/source-names.js";
|
|
@@ -35,11 +37,11 @@ export interface Eip712TypesConfig {
|
|
|
35
37
|
* When `include` is empty/unset the feature is off: collection short-circuits
|
|
36
38
|
* and returns an empty list without parsing any build info.
|
|
37
39
|
*/
|
|
38
|
-
export function collectEip712CanonicalTypes(
|
|
40
|
+
export async function collectEip712CanonicalTypes(
|
|
39
41
|
buildInfosAndOutputs: BuildInfoAndOutput[],
|
|
40
42
|
inputToUserSource: ReadonlyMap<string, string>,
|
|
41
43
|
config: Eip712TypesConfig,
|
|
42
|
-
): string[] {
|
|
44
|
+
): Promise<string[]> {
|
|
43
45
|
const { include, exclude } = config;
|
|
44
46
|
|
|
45
47
|
if (include.length === 0) {
|
|
@@ -47,18 +49,26 @@ export function collectEip712CanonicalTypes(
|
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
const collected: CollectedStruct[] = [];
|
|
50
|
-
const
|
|
52
|
+
const selectedSources = new Set<string>();
|
|
51
53
|
|
|
52
|
-
for (const { buildInfo, output } of buildInfosAndOutputs) {
|
|
54
|
+
for (const { buildInfoId, buildInfo, output } of buildInfosAndOutputs) {
|
|
53
55
|
// Byte-level fast path: a build info whose source bytes don't contain
|
|
54
56
|
// `struct ` can't define any EIP-712 type, so skip JSON-parsing its output.
|
|
55
57
|
if (!bytesIncludesUtf8String(buildInfo, "struct ")) {
|
|
56
58
|
continue;
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
let parsedOutput: SolidityBuildInfoOutput;
|
|
62
|
+
try {
|
|
63
|
+
parsedOutput = await parseJsonBytes<SolidityBuildInfoOutput>(output);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
ensureError(error);
|
|
66
|
+
throw new HardhatError(
|
|
67
|
+
HardhatError.ERRORS.CORE.SOLIDITY.BUILD_INFO_OUTPUT_PARSE_ERROR,
|
|
68
|
+
{ buildInfoId },
|
|
69
|
+
error,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
62
72
|
|
|
63
73
|
const sources = parsedOutput.output.sources;
|
|
64
74
|
if (sources === undefined) {
|
|
@@ -92,7 +102,9 @@ export function collectEip712CanonicalTypes(
|
|
|
92
102
|
toUserSourceName(inputSourceName);
|
|
93
103
|
|
|
94
104
|
// Collect every source so non-selected files can serve as dep targets;
|
|
95
|
-
//
|
|
105
|
+
// `selectedSources` enforces selection at emit time. Tracked per source,
|
|
106
|
+
// not per struct name, so a non-selected source defining a same-named
|
|
107
|
+
// struct isn't treated as selected (see `canonicalizeStructs`).
|
|
96
108
|
const structs = extractStructsFromAst(
|
|
97
109
|
source.ast,
|
|
98
110
|
userSourceName,
|
|
@@ -101,12 +113,10 @@ export function collectEip712CanonicalTypes(
|
|
|
101
113
|
collected.push(...structs);
|
|
102
114
|
|
|
103
115
|
if (isPathSelected(userSourceName, include, exclude)) {
|
|
104
|
-
|
|
105
|
-
selectedNames.add(s.name);
|
|
106
|
-
}
|
|
116
|
+
selectedSources.add(userSourceName);
|
|
107
117
|
}
|
|
108
118
|
}
|
|
109
119
|
}
|
|
110
120
|
|
|
111
|
-
return canonicalizeStructs(collected,
|
|
121
|
+
return canonicalizeStructs(collected, selectedSources);
|
|
112
122
|
}
|
|
@@ -25,6 +25,7 @@ import { hexStringToBytes } from "@nomicfoundation/hardhat-utils/hex";
|
|
|
25
25
|
import { DEFAULT_VERBOSITY, OPTIMISM_CHAIN_TYPE } from "../../constants.js";
|
|
26
26
|
import { resolveHardfork } from "../network-manager/config-resolution.js";
|
|
27
27
|
import { hardhatHardforkToEdrSpecId } from "../network-manager/edr/utils/convert-to-edr.js";
|
|
28
|
+
import { warnIfExperimentalHardfork } from "../network-manager/edr/utils/hardfork.js";
|
|
28
29
|
import { verbosityToIncludeTraces } from "../network-manager/edr/utils/trace-formatters.js";
|
|
29
30
|
|
|
30
31
|
import { formatArtifactId } from "./formatters.js";
|
|
@@ -88,8 +89,11 @@ export async function solidityTestConfigToSolidityTestRunnerConfigArgs({
|
|
|
88
89
|
const txOrigin = hexToBytes(config.txOrigin);
|
|
89
90
|
const blockCoinbase = hexToBytes(config.coinbase);
|
|
90
91
|
|
|
92
|
+
const resolvedHardforkName = resolveHardfork(hardfork, chainType);
|
|
93
|
+
warnIfExperimentalHardfork(resolvedHardforkName, chainType);
|
|
94
|
+
|
|
91
95
|
const resolvedHardfork = hardhatHardforkToEdrSpecId(
|
|
92
|
-
|
|
96
|
+
resolvedHardforkName,
|
|
93
97
|
chainType,
|
|
94
98
|
);
|
|
95
99
|
|
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
HardhatError,
|
|
11
11
|
assertHardhatInvariant,
|
|
12
12
|
} from "@nomicfoundation/hardhat-errors";
|
|
13
|
-
import {
|
|
13
|
+
import { parseJsonBytes } from "@nomicfoundation/hardhat-utils/bytes";
|
|
14
|
+
import { ensureError } from "@nomicfoundation/hardhat-utils/error";
|
|
14
15
|
|
|
15
16
|
import { getFullyQualifiedName } from "../../../../utils/contract-names.js";
|
|
16
17
|
|
|
@@ -48,11 +49,11 @@ interface CollectedOverrides {
|
|
|
48
49
|
* in the solc AST. It only extracts them from the build info where each
|
|
49
50
|
* test artifact's file was compiled as a root file.
|
|
50
51
|
*/
|
|
51
|
-
export function getTestFunctionOverrides(
|
|
52
|
+
export async function getTestFunctionOverrides(
|
|
52
53
|
testSuiteArtifacts: EdrArtifactWithMetadata[],
|
|
53
54
|
buildInfosAndOutputs: BuildInfoAndOutput[],
|
|
54
|
-
): TestFunctionOverride[] {
|
|
55
|
-
const allRawOverrides = collectRawOverrides(
|
|
55
|
+
): Promise<TestFunctionOverride[]> {
|
|
56
|
+
const allRawOverrides = await collectRawOverrides(
|
|
56
57
|
testSuiteArtifacts,
|
|
57
58
|
buildInfosAndOutputs,
|
|
58
59
|
);
|
|
@@ -62,10 +63,10 @@ export function getTestFunctionOverrides(
|
|
|
62
63
|
return buildTestFunctionOverrides(allRawOverrides);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
function collectRawOverrides(
|
|
66
|
+
async function collectRawOverrides(
|
|
66
67
|
testSuiteArtifacts: EdrArtifactWithMetadata[],
|
|
67
68
|
buildInfosAndOutputs: BuildInfoAndOutput[],
|
|
68
|
-
): CollectedOverrides {
|
|
69
|
+
): Promise<CollectedOverrides> {
|
|
69
70
|
const overrides: RawInlineOverride[] = [];
|
|
70
71
|
const methodIdentifiersByContract = new Map<string, Record<string, string>>();
|
|
71
72
|
|
|
@@ -153,9 +154,19 @@ function collectRawOverrides(
|
|
|
153
154
|
continue;
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
let buildInfoOutput: SolidityBuildInfoOutput;
|
|
158
|
+
try {
|
|
159
|
+
buildInfoOutput = await parseJsonBytes<SolidityBuildInfoOutput>(
|
|
160
|
+
buildInfoAndOutput.output,
|
|
161
|
+
);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
ensureError(error);
|
|
164
|
+
throw new HardhatError(
|
|
165
|
+
HardhatError.ERRORS.CORE.SOLIDITY.BUILD_INFO_OUTPUT_PARSE_ERROR,
|
|
166
|
+
{ buildInfoId },
|
|
167
|
+
error,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
159
170
|
|
|
160
171
|
for (const [
|
|
161
172
|
inputSourceName,
|
|
@@ -231,12 +231,12 @@ const runSolidityTests: NewTaskActionFunction<TestActionArguments> = async (
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
const testFunctionOverrides = getTestFunctionOverrides(
|
|
234
|
+
const testFunctionOverrides = await getTestFunctionOverrides(
|
|
235
235
|
testSuiteArtifacts,
|
|
236
236
|
allBuildInfosAndOutputs,
|
|
237
237
|
);
|
|
238
238
|
|
|
239
|
-
const eip712CanonicalTypes = collectEip712CanonicalTypes(
|
|
239
|
+
const eip712CanonicalTypes = await collectEip712CanonicalTypes(
|
|
240
240
|
allBuildInfosAndOutputs,
|
|
241
241
|
sourceNameToUserSourceName,
|
|
242
242
|
eip712Types,
|
|
@@ -33,6 +33,7 @@ declare module "../../../types/test.js" {
|
|
|
33
33
|
includeStorage?: boolean;
|
|
34
34
|
includePushBytes?: boolean;
|
|
35
35
|
shrinkRunLimit?: number;
|
|
36
|
+
timeout?: number | bigint;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export interface SolidityTestFuzzUserConfig {
|
|
@@ -45,6 +46,7 @@ declare module "../../../types/test.js" {
|
|
|
45
46
|
includeStorage?: boolean;
|
|
46
47
|
includePushBytes?: boolean;
|
|
47
48
|
showLogs?: boolean;
|
|
49
|
+
timeout?: number | bigint;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
export interface SolidityTestForkingUserConfig {
|
|
@@ -66,6 +68,7 @@ declare module "../../../types/test.js" {
|
|
|
66
68
|
blockTimestamp?: bigint;
|
|
67
69
|
prevRandao?: bigint;
|
|
68
70
|
gasLimit?: bigint;
|
|
71
|
+
memoryLimit?: number | bigint;
|
|
69
72
|
blockGasLimit?: number | bigint | false;
|
|
70
73
|
transactionGasCap?: number | bigint | false;
|
|
71
74
|
fuzz?: SolidityTestFuzzUserConfig;
|
|
@@ -108,6 +111,7 @@ declare module "../../../types/test.js" {
|
|
|
108
111
|
includeStorage?: boolean;
|
|
109
112
|
includePushBytes?: boolean;
|
|
110
113
|
shrinkRunLimit?: number;
|
|
114
|
+
timeout?: number;
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
export interface SolidityTestFuzzConfig {
|
|
@@ -120,6 +124,7 @@ declare module "../../../types/test.js" {
|
|
|
120
124
|
includeStorage?: boolean;
|
|
121
125
|
includePushBytes?: boolean;
|
|
122
126
|
showLogs?: boolean;
|
|
127
|
+
timeout?: number;
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
export interface SolidityTestForkingConfig {
|
|
@@ -142,6 +147,7 @@ declare module "../../../types/test.js" {
|
|
|
142
147
|
blockTimestamp?: bigint;
|
|
143
148
|
prevRandao?: bigint;
|
|
144
149
|
gasLimit?: bigint;
|
|
150
|
+
memoryLimit?: bigint;
|
|
145
151
|
blockGasLimit?: number | bigint | false;
|
|
146
152
|
transactionGasCap?: number | bigint | false;
|
|
147
153
|
fuzz: SolidityTestFuzzConfig;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"description": "A TypeScript Hardhat project using Node Test Runner and Viem",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"devDependencies": {
|
|
8
|
-
"hardhat": "workspace:^3.
|
|
8
|
+
"hardhat": "workspace:^3.10.0",
|
|
9
9
|
"@nomicfoundation/hardhat-toolbox-viem": "workspace:^5.0.7",
|
|
10
10
|
"@nomicfoundation/hardhat-ignition": "workspace:^3.1.8",
|
|
11
11
|
"@types/node": "^22.8.5",
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
"description": "A TypeScript Hardhat project using Mocha and Ethers.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"devDependencies": {
|
|
8
|
-
"hardhat": "workspace:^3.
|
|
8
|
+
"hardhat": "workspace:^3.10.0",
|
|
9
9
|
"@nomicfoundation/hardhat-toolbox-mocha-ethers": "workspace:^3.0.7",
|
|
10
|
-
"@nomicfoundation/hardhat-ethers": "workspace:^4.0.
|
|
10
|
+
"@nomicfoundation/hardhat-ethers": "workspace:^4.0.15",
|
|
11
11
|
"@nomicfoundation/hardhat-ignition": "workspace:^3.1.8",
|
|
12
12
|
"@types/chai": "^5.2.3",
|
|
13
13
|
"@types/chai-as-promised": "^8.0.1",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"typescript": "~6.0.3"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@nomicfoundation/hardhat-ethers": "workspace:^4.0.
|
|
23
|
+
"@nomicfoundation/hardhat-ethers": "workspace:^4.0.15",
|
|
24
24
|
"@nomicfoundation/hardhat-ethers-chai-matchers": "workspace:^3.0.11",
|
|
25
25
|
"@nomicfoundation/hardhat-ignition": "workspace:^3.1.8",
|
|
26
26
|
"@nomicfoundation/hardhat-ignition-ethers": "workspace:^3.1.6",
|