diff --git a/.changeset/tiny-mangos-glow.md b/.changeset/tiny-mangos-glow.md new file mode 100644 index 00000000000..807bf8e6d07 --- /dev/null +++ b/.changeset/tiny-mangos-glow.md @@ -0,0 +1,7 @@ +--- +"hardhat": patch +--- + +Update the default outputSelection setting of solc to decrease the artifacts size. + +NOTE: This change can lead to build info ids changing, despite compilation output's bytecodes being identical. diff --git a/packages/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/packages/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts index 4666e42982e..19a8608fbc1 100644 --- a/packages/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/packages/hardhat/src/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -16,6 +16,7 @@ import { ResolvedFileType, type ResolvedFile, } from "../../../../types/solidity.js"; +import { DEFAULT_OUTPUT_SELECTION } from "../constants.js"; import { getEvmVersionFromSolcVersion } from "./solc-info.js"; @@ -130,19 +131,14 @@ export class CompilationJobImplementation implements CompilationJob { // from other files (e.g. new Foo()), and it won't output its bytecode if // it's not asked for. This would prevent EDR from doing any runtime // analysis. - const outputSelection = await deepClone(settings.outputSelection ?? {}); + const outputSelection: CompilerInput["settings"]["outputSelection"] = + await deepClone(settings.outputSelection ?? {}); outputSelection["*"] ??= {}; outputSelection["*"][""] ??= []; outputSelection["*"]["*"] ??= []; - outputSelection["*"][""].push("ast"); - outputSelection["*"]["*"].push( - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata", - ); + outputSelection["*"][""].push(...DEFAULT_OUTPUT_SELECTION["*"][""]); + outputSelection["*"]["*"].push(...DEFAULT_OUTPUT_SELECTION["*"]["*"]); const sources: { [sourceName: string]: { content: string } } = {}; diff --git a/packages/hardhat/src/internal/builtin-plugins/solidity/config.ts b/packages/hardhat/src/internal/builtin-plugins/solidity/config.ts index 788d1e621bf..fd1b4b1e1b4 100644 --- a/packages/hardhat/src/internal/builtin-plugins/solidity/config.ts +++ b/packages/hardhat/src/internal/builtin-plugins/solidity/config.ts @@ -32,6 +32,7 @@ import { hasOfficialArm64Build, missesSomeOfficialNativeBuilds, } from "./build-system/solc-info.js"; +import { DEFAULT_OUTPUT_SELECTION } from "./constants.js"; /** * The top-level type SolidityUserConfig is a union type too complex for @@ -515,18 +516,7 @@ function resolveSolidityCompilerConfig( production: boolean = false, ): SolidityCompilerConfig { const defaultSettings: SolidityCompilerConfig["settings"] = { - outputSelection: { - "*": { - "": ["ast"], - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata", - ], - }, - }, + outputSelection: DEFAULT_OUTPUT_SELECTION, }; if (production && isSolcSolidityCompilerUserConfig(compilerConfig)) { diff --git a/packages/hardhat/src/internal/builtin-plugins/solidity/constants.ts b/packages/hardhat/src/internal/builtin-plugins/solidity/constants.ts new file mode 100644 index 00000000000..f0b5fbbce60 --- /dev/null +++ b/packages/hardhat/src/internal/builtin-plugins/solidity/constants.ts @@ -0,0 +1,21 @@ +import type { CompilerInput } from "../../../types/solidity.js"; + +export const DEFAULT_OUTPUT_SELECTION: CompilerInput["settings"]["outputSelection"] = + { + "*": { + "": ["ast"], + "*": [ + "abi", + "evm.bytecode.linkReferences", + "evm.bytecode.object", + "evm.bytecode.opcodes", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.immutableReferences", + "evm.deployedBytecode.linkReferences", + "evm.deployedBytecode.object", + "evm.deployedBytecode.opcodes", + "evm.deployedBytecode.sourceMap", + "evm.methodIdentifiers", + ], + }, + }; diff --git a/packages/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts b/packages/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts index b419a4b31e3..96a83d9849a 100644 --- a/packages/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts +++ b/packages/hardhat/test/internal/builtin-plugins/solidity/build-system/compilation-job.ts @@ -541,10 +541,16 @@ describe("CompilationJobImplementation", () => { "*": { "*": [ "abi", - "evm.bytecode", - "evm.deployedBytecode", + "evm.bytecode.linkReferences", + "evm.bytecode.object", + "evm.bytecode.opcodes", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.immutableReferences", + "evm.deployedBytecode.linkReferences", + "evm.deployedBytecode.object", + "evm.deployedBytecode.opcodes", + "evm.deployedBytecode.sourceMap", "evm.methodIdentifiers", - "metadata", "storageLayout", ], "": ["ast"], @@ -560,7 +566,13 @@ describe("CompilationJobImplementation", () => { settings: { outputSelection: { "*": { - "*": ["storageLayout", "storageLayout", "abi", "abi"], + "*": [ + "storageLayout", + "evm.bytecode.object", + "storageLayout", + "abi", + "abi", + ], }, }, }, @@ -573,10 +585,16 @@ describe("CompilationJobImplementation", () => { "*": { "*": [ "abi", - "evm.bytecode", - "evm.deployedBytecode", + "evm.bytecode.linkReferences", + "evm.bytecode.object", + "evm.bytecode.opcodes", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.immutableReferences", + "evm.deployedBytecode.linkReferences", + "evm.deployedBytecode.object", + "evm.deployedBytecode.opcodes", + "evm.deployedBytecode.sourceMap", "evm.methodIdentifiers", - "metadata", "storageLayout", ], "": ["ast"], diff --git a/packages/hardhat/test/internal/builtin-plugins/solidity/build-system/output-selection.ts b/packages/hardhat/test/internal/builtin-plugins/solidity/build-system/output-selection.ts new file mode 100644 index 00000000000..8b79d1585bc --- /dev/null +++ b/packages/hardhat/test/internal/builtin-plugins/solidity/build-system/output-selection.ts @@ -0,0 +1,178 @@ +import type { CompilerOutput } from "../../../../../src/types/solidity.js"; + +import assert from "node:assert/strict"; +import path from "node:path"; +import { describe, it } from "node:test"; + +import { gte } from "semver"; + +import { createHardhatRuntimeEnvironment } from "../../../../../src/internal/hre-initialization.js"; + +import { useTestProjectTemplate } from "./resolver/helpers.js"; + +const FIRST_VERSION_WITH_IMMUTABLE_REFERENCES = "0.6.5"; + +const CONTRACTS: Record = { + // Oldest version supported by hardhat + "0.4.11": `pragma solidity ^0.4.11;\ncontract Foo {}`, + // Last version without immutable references + "0.6.4": `pragma solidity ^0.6.4;\ncontract Foo {}`, + // First version with immutable references + "0.6.5": [ + "pragma solidity ^0.6.5;", + "contract Foo {", + " uint256 public immutable x;", + " constructor() public { x = 1; }", + "}", + ].join("\n"), + // Modern version + "0.8.28": [ + "// SPDX-License-Identifier: UNLICENSED", + "pragma solidity ^0.8.0;", + "contract Foo {", + " uint256 public immutable x;", + " constructor() { x = 1; }", + "}", + ].join("\n"), +}; + +/** + * Returns the first contract output from a CompilerOutput. + */ +function getContractOutput(output: CompilerOutput) { + assert(output.contracts !== undefined, "Expected contracts in output"); + + const sourceEntries = Object.values(output.contracts); + assert(sourceEntries.length > 0, "Expected at least one source in output"); + + const contractEntries = Object.values(sourceEntries[0]); + assert( + contractEntries.length > 0, + "Expected at least one contract in output", + ); + + return contractEntries[0]; +} + +describe( + "Default output selection compatibility across solc versions", + { skip: process.env.HARDHAT_DISABLE_SLOW_TESTS === "true" }, + () => { + for (const [version, contractSource] of Object.entries(CONTRACTS)) { + it(`should produce expected output fields with solc ${version}`, async () => { + await using project = await useTestProjectTemplate({ + name: "output-selection-test", + version: "1.0.0", + files: { + "contracts/Foo.sol": contractSource, + }, + }); + + const hre = await createHardhatRuntimeEnvironment( + { solidity: { version } }, + {}, + project.path, + ); + + const rootFilePath = path.join(project.path, "contracts/Foo.sol"); + + const jobsResult = await hre.solidity.getCompilationJobs( + [rootFilePath], + { force: true, quiet: true }, + ); + + assert(jobsResult.success, `getCompilationJobs failed for ${version}`); + + const compilationJob = jobsResult.compilationJobsPerFile + .values() + .next().value; + assert(compilationJob !== undefined, "Expected a compilation job"); + + const { output } = await hre.solidity.runCompilationJob( + compilationJob, + { quiet: true }, + ); + + const errors = (output.errors ?? []).filter( + (e) => e.severity === "error", + ); + assert.equal( + errors.length, + 0, + `Compilation errors with solc ${version}: ${errors.map((e) => e.message).join(", ")}`, + ); + + const contract = getContractOutput(output); + + assert(contract.abi !== undefined, "Expected abi in output"); + + assert(contract.evm !== undefined, "Expected evm in output"); + + assert( + contract.evm.bytecode !== undefined, + "Expected evm.bytecode in output", + ); + assert( + typeof contract.evm.bytecode.object === "string", + "Expected evm.bytecode.object", + ); + assert( + typeof contract.evm.bytecode.opcodes === "string", + "Expected evm.bytecode.opcodes", + ); + assert( + typeof contract.evm.bytecode.sourceMap === "string", + "Expected evm.bytecode.sourceMap", + ); + assert( + contract.evm.bytecode.linkReferences !== undefined, + "Expected evm.bytecode.linkReferences", + ); + + assert( + contract.evm.deployedBytecode !== undefined, + "Expected evm.deployedBytecode in output", + ); + assert( + typeof contract.evm.deployedBytecode.object === "string", + "Expected evm.deployedBytecode.object", + ); + assert( + typeof contract.evm.deployedBytecode.opcodes === "string", + "Expected evm.deployedBytecode.opcodes", + ); + assert( + typeof contract.evm.deployedBytecode.sourceMap === "string", + "Expected evm.deployedBytecode.sourceMap", + ); + assert( + contract.evm.deployedBytecode.linkReferences !== undefined, + "Expected evm.deployedBytecode.linkReferences", + ); + + assert( + contract.evm.methodIdentifiers !== undefined, + "Expected evm.methodIdentifiers in output", + ); + + const supportsImmutables = gte( + version, + FIRST_VERSION_WITH_IMMUTABLE_REFERENCES, + ); + + if (supportsImmutables) { + assert( + contract.evm.deployedBytecode.immutableReferences !== undefined, + `Expected evm.deployedBytecode.immutableReferences in solc ${version} output`, + ); + } else { + assert.equal( + contract.evm.deployedBytecode.immutableReferences, + undefined, + `Did not expect evm.deployedBytecode.immutableReferences in solc ${version} output`, + ); + } + }); + } + }, +); diff --git a/packages/ignition-core/test/helpers/execution-result-fixtures.ts b/packages/ignition-core/test/helpers/execution-result-fixtures.ts index 043af8e38ec..11207e7935c 100644 --- a/packages/ignition-core/test/helpers/execution-result-fixtures.ts +++ b/packages/ignition-core/test/helpers/execution-result-fixtures.ts @@ -14,8 +14,8 @@ import { RawStaticCallResult } from "../../src/internal/execution/types/jsonrpc. // On Linux ARM64 we download from a mirror that does not provide the long version, hence the known build info id has to change when running the test suite on that platform. const buildInfoId = os.platform() === "linux" && os.arch() === "arm64" - ? "solc-0_8_19-d17b87acbe94849750bc6495e7e18cd2b1dc3d73" - : "solc-0_8_19-fa69743fd6914c0dd508b989815c073281a7e58e"; + ? "solc-0_8_19-137efa74d09076ba20821c90499e30d7337016be" + : "solc-0_8_19-b38bcc141d1254d1ac323f4e7ddd61668a74da0b"; export const staticCallResultFixtures: { [contractName: string]: { [functionName: string]: RawStaticCallResult };