UNPKG

hardhat

Version:

Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.

241 lines 10.1 kB
import os from "node:os"; import { deepMerge, isObject } from "@nomicfoundation/hardhat-utils/lang"; import { resolveFromRoot } from "@nomicfoundation/hardhat-utils/path"; import { conditionalUnionType, incompatibleFieldType, validateUserConfigZodType, } from "@nomicfoundation/hardhat-zod-utils"; import { z } from "zod"; import { DEFAULT_BUILD_PROFILES } from "./build-profiles.js"; const sourcePathsType = conditionalUnionType([ [(data) => typeof data === "string", z.string()], [(data) => Array.isArray(data), z.array(z.string()).nonempty()], ], "Expected a string or an array of strings"); const commonSolcUserConfigType = z.object({ isolated: z.boolean().optional(), }); const solcUserConfigType = z.object({ version: z.string(), settings: z.any().optional(), compilers: incompatibleFieldType("This field is incompatible with `version`"), overrides: incompatibleFieldType("This field is incompatible with `version`"), profiles: incompatibleFieldType("This field is incompatible with `version`"), }); // NOTE: This is only to match the setup present in ./type-extensions.ts const singleVersionSolcUserConfigType = solcUserConfigType.extend({ isolated: z.boolean().optional(), preferWasm: z.boolean().optional(), }); const multiVersionSolcUserConfigType = commonSolcUserConfigType.extend({ compilers: z.array(solcUserConfigType).nonempty(), overrides: z.record(z.string(), solcUserConfigType).optional(), isolated: z.boolean().optional(), preferWasm: z.boolean().optional(), version: incompatibleFieldType("This field is incompatible with `compilers`"), settings: incompatibleFieldType("This field is incompatible with `compilers`"), }); const commonSolidityUserConfigType = z.object({ npmFilesToBuild: z.array(z.string()).optional(), }); const singleVersionSolidityUserConfigType = singleVersionSolcUserConfigType .merge(commonSolidityUserConfigType) .extend({ compilers: incompatibleFieldType("This field is incompatible with `version`"), overrides: incompatibleFieldType("This field is incompatible with `version`"), profiles: incompatibleFieldType("This field is incompatible with `version`"), }); const multiVersionSolidityUserConfigType = multiVersionSolcUserConfigType .merge(commonSolidityUserConfigType) .extend({ version: incompatibleFieldType("This field is incompatible with `compilers`"), profiles: incompatibleFieldType("This field is incompatible with `compilers`"), }); const buildProfilesSolidityUserConfigType = commonSolidityUserConfigType.extend({ profiles: z.record(z.string(), conditionalUnionType([ [ (data) => isObject(data) && "version" in data, singleVersionSolcUserConfigType, ], [ (data) => isObject(data) && "compilers" in data, multiVersionSolcUserConfigType, ], ], "Expected an object configuring one or more versions of Solidity")), version: incompatibleFieldType("This field is incompatible with `profiles`"), compilers: incompatibleFieldType("This field is incompatible with `profiles`"), overrides: incompatibleFieldType("This field is incompatible with `profiles`"), }); const soldityUserConfigType = conditionalUnionType([ [(data) => typeof data === "string", z.string()], [(data) => Array.isArray(data), z.array(z.string()).nonempty()], [ (data) => isObject(data) && "version" in data, singleVersionSolidityUserConfigType, ], [ (data) => isObject(data) && "compilers" in data, multiVersionSolidityUserConfigType, ], [ (data) => isObject(data) && "profiles" in data, buildProfilesSolidityUserConfigType, ], ], "Expected a version string, an array of version strings, or an object configuring one or more versions of Solidity or multiple build profiles"); const userConfigType = z.object({ paths: z .object({ sources: conditionalUnionType([ [isObject, z.object({ solidity: sourcePathsType.optional() })], [ (data) => typeof data === "string" || Array.isArray(data), sourcePathsType, ], ], "Expected a string, an array of strings, or an object with an optional 'solidity' property").optional(), }) .optional(), solidity: soldityUserConfigType.optional(), }); export function validateSolidityUserConfig(userConfig) { const result = validateUserConfigZodType(userConfig, userConfigType); if (isObject(userConfig) && isObject(userConfig.solidity) && isObject(userConfig.solidity.profiles) && !("default" in userConfig.solidity.profiles)) { result.push({ message: "The 'default' profile is required when using Solidity build profiles", path: ["solidity", "profiles"], }); } return result; } export async function resolveSolidityUserConfig(userConfig, resolvedConfig) { let sourcesPaths = userConfig.paths?.sources; // TODO: use isObject when the type narrowing issue is fixed sourcesPaths = typeof sourcesPaths === "object" && !Array.isArray(sourcesPaths) ? sourcesPaths.solidity : sourcesPaths; sourcesPaths ??= "contracts"; sourcesPaths = Array.isArray(sourcesPaths) ? sourcesPaths : [sourcesPaths]; const resolvedPaths = sourcesPaths.map((p) => resolveFromRoot(resolvedConfig.paths.root, p)); return { ...resolvedConfig, paths: { ...resolvedConfig.paths, sources: { ...resolvedConfig.paths.sources, solidity: resolvedPaths, }, }, solidity: resolveSolidityConfig(userConfig.solidity ?? "0.8.0"), }; } function resolveSolidityConfig(solidityConfig) { if (typeof solidityConfig === "string") { solidityConfig = [solidityConfig]; } // user provided an array of versions or a single version if (Array.isArray(solidityConfig)) { const defaultSolidityConfig = { compilers: solidityConfig.map((version) => ({ version })), }; return { profiles: { default: resolveBuildProfileConfig(defaultSolidityConfig), production: resolveBuildProfileConfig(copyFromDefault(defaultSolidityConfig), true), }, npmFilesToBuild: [], }; } // user provided a single version config or a multi version config if ("version" in solidityConfig || "compilers" in solidityConfig) { return { profiles: { default: resolveBuildProfileConfig(solidityConfig), production: resolveBuildProfileConfig(copyFromDefault(solidityConfig), true), }, npmFilesToBuild: solidityConfig.npmFilesToBuild ?? [], }; } // user provided a build profiles config const profiles = {}; for (const [profileName, profile] of Object.entries(solidityConfig.profiles)) { profiles[profileName] = resolveBuildProfileConfig(profile, profileName === "production"); } // This will generate default build profiles (e.g. production) when they are // not specified in the config, cloning from 'default', which is always present for (const profile of DEFAULT_BUILD_PROFILES) { if (!(profile in profiles)) { profiles[profile] = resolveBuildProfileConfig(copyFromDefault(solidityConfig.profiles.default), profile === "production"); } } return { profiles, npmFilesToBuild: solidityConfig.npmFilesToBuild ?? [], }; } function resolveBuildProfileConfig(solidityConfig, production = false) { if ("version" in solidityConfig) { return { compilers: [resolveSolcConfig(solidityConfig, production)], overrides: {}, isolated: solidityConfig.isolated ?? production, preferWasm: solidityConfig.preferWasm ?? (production && shouldUseWasm()), }; } return { compilers: solidityConfig.compilers.map((compiler) => resolveSolcConfig(compiler, production)), overrides: Object.fromEntries(Object.entries(solidityConfig.overrides ?? {}).map(([userSourceName, override]) => [ userSourceName, resolveSolcConfig(override, production), ])), isolated: solidityConfig.isolated ?? production, preferWasm: solidityConfig.preferWasm ?? (production && shouldUseWasm()), }; } function resolveSolcConfig(solcConfig, production = false) { const defaultSolcConfigSettings = { outputSelection: { "*": { "": ["ast"], "*": [ "abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers", "metadata", ], }, }, }; if (production) { defaultSolcConfigSettings.optimizer = { enabled: true, runs: 200, }; } return { version: solcConfig.version, settings: deepMerge(defaultSolcConfigSettings, solcConfig.settings ?? {}), path: solcConfig.path, }; } function copyFromDefault(defaultSolidityConfig) { if ("version" in defaultSolidityConfig) { return { version: defaultSolidityConfig.version, }; } return { compilers: defaultSolidityConfig.compilers.map((c) => ({ version: c.version, })), overrides: Object.fromEntries(Object.entries(defaultSolidityConfig.overrides ?? {}).map(([userSourceName, override]) => [ userSourceName, { version: override.version }, ])), }; } // We use wasm builds in production to avoid using unofficial builds for deployments // This should change once https://github.com/ethereum/solidity/issues/11351 gets resolved export function shouldUseWasm() { return os.platform() === "linux" && os.arch() === "arm64"; } //# sourceMappingURL=config.js.map