Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cold-deer-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hardhat": patch
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a separate changeset for the new config hook.

Copy link
Copy Markdown
Member

@alcuadrado alcuadrado Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

---

Introduce multi-compiler abstraction that allows plugins to define new Solidity compiler types ([#8008](https://github.com/NomicFoundation/hardhat/pull/8008)).
6 changes: 6 additions & 0 deletions .changeset/tall-seals-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@nomicfoundation/hardhat-errors": patch
"hardhat": patch
---

Introduce the `ConfigHooks#validateResolvedConfig` hook and the `HardhatConfigValidationError` type to be able to run global validations on the resolved config ([#8008](https://github.com/NomicFoundation/hardhat/pull/8008)).
2 changes: 2 additions & 0 deletions cspell.dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ schaable
sokol
solcjs
SOLCJS
solx
Solx
sourcify
Sourcify
SOURCIFY
Expand Down
8 changes: 8 additions & 0 deletions v-next/hardhat-errors/src/descriptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,14 @@ Please install Hardhat locally using pnpm, npm or yarn, and try again.`,
websiteTitle: "Global option cannot be hidden",
websiteDescription: `A global option was defined as hidden, but global options cannot be hidden.`,
},
INVALID_RESOLVED_CONFIG: {
number: 24,
messageTemplate: `Your configuration is invalid once resolved:
{errors}
`,
websiteTitle: "Invalid resolved config",
websiteDescription: `The configuration you provided is seemingly valid, but once resolved it contains errors. Please check the documentation to learn how to configure Hardhat correctly.`,
},
},
INTERNAL: {
ASSERTION_ERROR: {
Expand Down
1 change: 1 addition & 0 deletions v-next/hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"./console.sol": "./console.sol",
"./internal/coverage": "./dist/src/internal/builtin-plugins/coverage/exports.js",
"./internal/gas-analytics": "./dist/src/internal/builtin-plugins/gas-analytics/exports.js",
"./internal/solidity": "./dist/src/internal/builtin-plugins/solidity/exports.js",
Comment on lines +40 to +42
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package.json exports map now includes "./internal/coverage" and "./internal/gas-analytics" pointing at dist/src/internal/builtin-plugins/{coverage,gas-analytics}/exports.js, but there are no corresponding source entrypoints (exports.ts) in those directories. This will produce broken package exports at publish/build time unless those files are added or the export targets are updated to existing built artifacts (e.g. index.js).

Suggested change
"./internal/coverage": "./dist/src/internal/builtin-plugins/coverage/exports.js",
"./internal/gas-analytics": "./dist/src/internal/builtin-plugins/gas-analytics/exports.js",
"./internal/solidity": "./dist/src/internal/builtin-plugins/solidity/exports.js",
"./internal/coverage": "./dist/src/internal/builtin-plugins/coverage/index.js",
"./internal/gas-analytics": "./dist/src/internal/builtin-plugins/gas-analytics/index.js",
"./internal/solidity": "./dist/src/internal/builtin-plugins/solidity/index.js",

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't pay attention to this comment, LGTM

"./utils/contract-names": "./dist/src/utils/contract-names.js",
"./utils/result": "./dist/src/utils/result.js",
"./types/runtime": "./dist/src/internal/deprecated-module-imported-from-hardhat2-plugin.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,20 @@ export class NetworkManagerImplementation implements NetworkManager {
);

if (!configResolutionResult.success) {
if (configResolutionResult.configValidationErrors !== undefined) {
throw new HardhatError(
HardhatError.ERRORS.CORE.NETWORK.INVALID_CONFIG_OVERRIDE,
{
errors: `\t${configResolutionResult.configValidationErrors
.map(
(error) =>
`* Error in resolved config ${error.path.join(".")}: ${error.message}`,
)
.join("\n\t")}`,
},
);
}

throw new HardhatError(
HardhatError.ERRORS.CORE.NETWORK.INVALID_CONFIG_OVERRIDE,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import type {
import { assertHardhatInvariant } from "@nomicfoundation/hardhat-errors";
import { readBinaryFile } from "@nomicfoundation/hardhat-utils/fs";

const BUILD_INFO_FORMAT =
/^solc-(?<major>\d+)_(?<minor>\d+)_(?<patch>\d+)-[0-9a-fA-F]*$/;
export const BUILD_INFO_FORMAT: RegExp =
/^solc-(?<major>\d+)_(?<minor>\d+)_(?<patch>\d+)(?:-(?<compilerType>[a-zA-Z][a-zA-Z0-9]*))?-[0-9a-fA-F]*$/;

/**
* This function returns all the build infos and associated outputs.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Artifact, BuildInfo } from "../../../../types/artifacts.js";
import type { Artifact } from "../../../../types/artifacts.js";
import type { CompilationJob } from "../../../../types/solidity/compilation-job.js";
import type {
CompilerOutput,
Expand Down Expand Up @@ -107,11 +107,16 @@ declare module "hardhat/types/artifacts" {
export async function getBuildInfo(
compilationJob: CompilationJob,
): Promise<SolidityBuildInfo> {
const buildInfo: Required<BuildInfo> = {
// Defaulting to "solc" is safe here: if it's already "solc" or undefined,
// this doesn't alter the build info id.
const compilerType = compilationJob.solcConfig.type ?? "solc";

const buildInfo: SolidityBuildInfo = {
_format: "hh3-sol-build-info-1",
id: await compilationJob.getBuildId(),
solcVersion: compilationJob.solcConfig.version,
solcLongVersion: compilationJob.solcLongVersion,
compilerType,
userSourceNameMap:
compilationJob.dependencyGraph.getRootsUserSourceNameMap(),
input: await compilationJob.getSolcInput(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { CompileCache } from "./cache.js";
import type { DependencyGraphImplementation } from "./dependency-graph.js";
import type { Artifact } from "../../../../types/artifacts.js";
import type { SolcConfig, SolidityConfig } from "../../../../types/config.js";
import type {
SolidityCompilerConfig,
SolcSolidityCompilerConfig,
SolidityConfig,
} from "../../../../types/config.js";
import type { HookManager } from "../../../../types/hooks.js";
import type {
SolidityBuildSystem,
Expand Down Expand Up @@ -79,14 +83,26 @@ import { shouldSuppressWarning } from "./warning-suppression.js";
const log = debug("hardhat:core:solidity:build-system");

/**
* Resolves the preferWasm setting for a given solc config, falling back
* Returns true if the given compiler config is a SolcSolidityCompilerConfig.
*/
export function isSolcSolidityCompilerConfig(
config: SolidityCompilerConfig,
): config is SolcSolidityCompilerConfig {
return config.type === undefined || config.type === "solc";
}

/**
* Resolves the preferWasm setting for a given compiler config, falling back
* to the build profile's preferWasm if not set on the compiler.
*/
function resolvePreferWasm(
solcConfig: SolcConfig,
compilerConfig: SolidityCompilerConfig,
buildProfilePreferWasm: boolean,
): boolean {
return solcConfig.preferWasm ?? buildProfilePreferWasm;
if (isSolcSolidityCompilerConfig(compilerConfig)) {
return compilerConfig.preferWasm ?? buildProfilePreferWasm;
}
return false;
}

// Compiler warnings to suppress from build output.
Expand Down Expand Up @@ -439,7 +455,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
);

let subgraphsWithConfig: Array<
[SolcConfig, DependencyGraphImplementation]
[SolidityCompilerConfig, DependencyGraphImplementation]
> = [];
Comment on lines 457 to 459
Copy link

Copilot AI Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

subgraphsWithConfig now carries SolidityCompilerConfig, but the build-system logic downstream still treats compiler configs as solc-only (e.g. compiler metadata caches keyed by version, and getCompiler(version, { preferWasm, compilerPath })). With multiple compiler types this can conflate different compilers that share a version string and makes it easy to accidentally run the solc pipeline for non-solc configs. Consider incorporating type into cache keys / compiler selection, or rejecting mixed/non-solc compiler types until the build-system paths are properly split by type.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very good point tbh

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we would need to add type to CompileCacheEntry.

Copy link
Copy Markdown
Member

@alcuadrado alcuadrado Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for (const [rootFile, resolvedFile] of dependencyGraph.getRoots()) {
log(
Expand All @@ -459,19 +475,47 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
}

// get longVersion and isWasm from the compiler for each version
const solcVersionToLongVersion = new Map<string, string>();
const versionIsWasm = new Map<string, boolean>();
for (const [solcConfig] of subgraphsWithConfig) {
let solcLongVersion = solcVersionToLongVersion.get(solcConfig.version);

if (solcLongVersion === undefined) {
const compiler = await getCompiler(solcConfig.version, {
preferWasm: resolvePreferWasm(solcConfig, buildProfile.preferWasm),
compilerPath: solcConfig.path,
// These maps are keyed by compiler type first, then version, to avoid
// collisions between different compiler types using the same version string.
const solidityVersionToLongVersionPerCompilerType = new Map<
string,
Map<string, string>
>();
const versionIsWasmPerCompilerType = new Map<
string,
Map<string, boolean>
>();
for (const [compilerConfig] of subgraphsWithConfig) {
const compilerType = compilerConfig.type ?? "solc";
let longVersionMap =
solidityVersionToLongVersionPerCompilerType.get(compilerType);
if (longVersionMap === undefined) {
longVersionMap = new Map();
solidityVersionToLongVersionPerCompilerType.set(
compilerType,
longVersionMap,
);
}

let isWasmMap = versionIsWasmPerCompilerType.get(compilerType);
if (isWasmMap === undefined) {
isWasmMap = new Map();
versionIsWasmPerCompilerType.set(compilerType, isWasmMap);
}

let longVersion = longVersionMap.get(compilerConfig.version);

if (longVersion === undefined) {
const compiler = await getCompiler(compilerConfig.version, {
preferWasm: resolvePreferWasm(
compilerConfig,
buildProfile.preferWasm,
),
compilerPath: compilerConfig.path,
});
solcLongVersion = compiler.longVersion;
solcVersionToLongVersion.set(solcConfig.version, solcLongVersion);
versionIsWasm.set(solcConfig.version, compiler.isSolcJs);
longVersion = compiler.longVersion;
longVersionMap.set(compilerConfig.version, longVersion);
isWasmMap.set(compilerConfig.version, compiler.isSolcJs);
}
}

Expand All @@ -480,17 +524,26 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
const sharedContentHashes = new Map<string, string>();
await Promise.all(
subgraphsWithConfig.map(async ([config, subgraph]) => {
const solcLongVersion = solcVersionToLongVersion.get(config.version);
const compilerType = config.type ?? "solc";
const longVersionMap =
solidityVersionToLongVersionPerCompilerType.get(compilerType);

assertHardhatInvariant(
solcLongVersion !== undefined,
"solcLongVersion should not be undefined",
longVersionMap !== undefined,
`No long version map for compiler type ${compilerType}`,
);

const longVersion = longVersionMap.get(config.version);

assertHardhatInvariant(
longVersion !== undefined,
"longVersion should not be undefined",
);

const individualJob = new CompilationJobImplementation(
subgraph,
config,
solcLongVersion,
longVersion,
this.#hooks,
sharedContentHashes,
);
Expand All @@ -516,7 +569,15 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
for (const [rootFile, compilationJob] of indexedIndividualJobs.entries()) {
const jobHash = await compilationJob.getBuildId();
const cacheResult = this.#compileCache[rootFile];
const isWasm = versionIsWasm.get(compilationJob.solcConfig.version);
const compilerType = compilationJob.solcConfig.type ?? "solc";
const isWasmMap = versionIsWasmPerCompilerType.get(compilerType);

assertHardhatInvariant(
isWasmMap !== undefined,
`No isWasm map for compiler type ${compilerType}`,
);

const isWasm = isWasmMap.get(compilationJob.solcConfig.version);

assertHardhatInvariant(
isWasm !== undefined,
Expand All @@ -529,6 +590,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
cacheResult === undefined ||
cacheResult.jobHash !== jobHash ||
cacheResult.isolated !== isolated ||
cacheResult.compilerType !== compilerType ||
cacheResult.wasm !== isWasm
) {
rootFilesToCompile.add(rootFile);
Expand Down Expand Up @@ -576,13 +638,15 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
log(`Merging compilation jobs`);

const mergedSubgraphsByConfig: Map<
SolcConfig,
SolidityCompilerConfig,
DependencyGraphImplementation
> = new Map();

// Note: This groups the subgraphs by solc config. It compares the configs
// based on reference, and not by deep equality. It misses some merging
// opportunities, but this is Hardhat v2's behavior and works well enough.
// Note: This groups the subgraphs by compiler config. It compares the
// configs based on reference, and not by deep equality. This is
// inherently type-aware: two configs with different types will always be
// different references. It misses some merging opportunities, but this is
// Hardhat v2's behavior and works well enough.
for (const [config, subgraph] of subgraphsWithConfig) {
const rootFile = getSingleRootFilePath(subgraph);

Expand Down Expand Up @@ -613,18 +677,27 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
}

const compilationJobsPerFile = new Map<string, CompilationJob>();
for (const [solcConfig, subgraph] of subgraphsWithConfig) {
const solcLongVersion = solcVersionToLongVersion.get(solcConfig.version);
for (const [compilerConfig, subgraph] of subgraphsWithConfig) {
const compilerType = compilerConfig.type ?? "solc";
const longVersionMap =
solidityVersionToLongVersionPerCompilerType.get(compilerType);

assertHardhatInvariant(
longVersionMap !== undefined,
`No long version map for compiler type ${compilerType}`,
);

const longVersion = longVersionMap.get(compilerConfig.version);

assertHardhatInvariant(
solcLongVersion !== undefined,
"solcLongVersion should not be undefined",
longVersion !== undefined,
"longVersion should not be undefined",
);

const runnableCompilationJob = new CompilationJobImplementation(
subgraph,
solcConfig,
solcLongVersion,
compilerConfig,
longVersion,
this.#hooks,
sharedContentHashes,
);
Expand Down Expand Up @@ -1124,6 +1197,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
this.#compileCache[rootFilePath] = {
jobHash,
isolated,
compilerType: individualJob.solcConfig.type ?? "solc",
artifactPaths,
buildInfoPath: emitArtifactsResult.buildInfoPath,
buildInfoOutputPath: emitArtifactsResult.buildInfoOutputPath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type CompileCache = Record<string, CompileCacheEntry>;
export interface CompileCacheEntry {
jobHash: string;
isolated: boolean;
compilerType: string;
buildInfoPath: string;
buildInfoOutputPath: string;
artifactPaths: string[];
Expand Down
Loading
Loading