UNPKG

hardhat

Version:

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

246 lines 13.6 kB
import { HardhatError, assertHardhatInvariant, } from "@nomicfoundation/hardhat-errors"; import { exists, readBinaryFile } from "@nomicfoundation/hardhat-utils/fs"; import { deepMerge } from "@nomicfoundation/hardhat-utils/lang"; import { resolveUserConfigToHardhatConfig } from "../../core/hre.js"; import { isSupportedChainType } from "../../edr/chain-type.js"; import { JsonRpcServerImplementation } from "../node/json-rpc/server.js"; import { EdrProvider } from "./edr/edr-provider.js"; import { getHardforks } from "./edr/types/hardfork.js"; import { edrGasReportToHardhatGasMeasurements } from "./edr/utils/convert-to-edr.js"; import { HttpProvider } from "./http-provider.js"; import { NetworkConnectionImplementation } from "./network-connection.js"; export class NetworkManagerImplementation { #defaultNetwork; #defaultChainType; #networkConfigs; #hookManager; #artifactsManager; #userConfig; #chainDescriptors; #userProvidedConfigPath; #projectRoot; #nextConnectionId = 0; constructor(defaultNetwork, defaultChainType, networkConfigs, hookManager, artifactsManager, userConfig, chainDescriptors, userProvidedConfigPath, projectRoot) { this.#defaultNetwork = defaultNetwork; this.#defaultChainType = defaultChainType; this.#networkConfigs = networkConfigs; this.#hookManager = hookManager; this.#artifactsManager = artifactsManager; this.#userConfig = userConfig; this.#chainDescriptors = chainDescriptors; this.#userProvidedConfigPath = userProvidedConfigPath; this.#projectRoot = projectRoot; } async connect(networkOrParams) { let networkName; let chainType; let override; if (typeof networkOrParams === "string") { networkName = networkOrParams; } else if (networkOrParams !== undefined) { networkName = networkOrParams.network; chainType = networkOrParams.chainType; override = networkOrParams.override; } const networkConnection = await this.#hookManager.runHandlerChain("network", "newConnection", [], async (_context) => this.#initializeNetworkConnection(networkName, chainType, override)); /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Cast to NetworkConnection<ChainTypeT> because we know it's valid */ return networkConnection; } async createServer(networkOrParams = "default", _hostname, port) { const insideDocker = await exists("/.dockerenv"); const hostname = _hostname ?? (insideDocker ? "0.0.0.0" : "127.0.0.1"); const { provider } = await this.connect(networkOrParams); return new JsonRpcServerImplementation({ hostname, port, provider, }); } async #initializeNetworkConnection(networkName, chainType, networkConfigOverride) { const resolvedNetworkName = networkName ?? this.#defaultNetwork; const existingNetworkConfig = this.#networkConfigs[resolvedNetworkName]; if (existingNetworkConfig === undefined) { throw new HardhatError(HardhatError.ERRORS.CORE.NETWORK.NETWORK_NOT_FOUND, { networkName: resolvedNetworkName, }); } /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- * Type assertion is safe: defaultChainType ensures non-undefined, and the * resolved value will be ChainTypeT (if provided) or a fallback that * satisfies the ChainType | string constraint */ const resolvedChainType = (chainType ?? existingNetworkConfig.chainType ?? this.#defaultChainType); const resolvedNetworkConfig = await this.#resolveNetworkConfig(resolvedNetworkName, networkConfigOverride, resolvedChainType); /* Capture the hook manager in a local variable to avoid retaining a reference to the NetworkManager instance, allowing the garbage collector to clean up the NetworkConnectionImplementation instances properly. */ const hookManager = this.#hookManager; const createProvider = async (networkConnection) => { const jsonRpcRequestWrapper = (request, defaultBehavior) => hookManager.runHandlerChain("network", "onRequest", [networkConnection, request], async (_context, _connection, req) => defaultBehavior(req)); if (resolvedNetworkConfig.type === "edr-simulated") { if (!isSupportedChainType(resolvedChainType)) { throw new HardhatError(HardhatError.ERRORS.CORE.GENERAL.UNSUPPORTED_OPERATION, { operation: `Simulating chain type ${resolvedChainType}` }); } let coverageConfig; const shouldEnableCoverage = await hookManager.hasHandlers("network", "onCoverageData"); if (shouldEnableCoverage) { coverageConfig = { onCollectedCoverageCallback: async (coverageData) => { // NOTE: We cast the tag we receive from EDR to a hex string to // make it easier to debug. const tags = coverageData.map((tag) => Buffer.from(tag).toString("hex")); await hookManager.runParallelHandlers("network", "onCoverageData", [tags]); }, }; } let gasReportConfig; const shouldEnableGasStats = await hookManager.hasHandlers("network", "onGasMeasurement"); if (shouldEnableGasStats) { gasReportConfig = { onCollectedGasReportCallback: async (gasReport) => { const gasMeasurements = edrGasReportToHardhatGasMeasurements(gasReport); for (const measurement of gasMeasurements) { await hookManager.runParallelHandlers("network", "onGasMeasurement", [measurement]); } }, }; } return EdrProvider.create({ chainDescriptors: this.#chainDescriptors, // The resolvedNetworkConfig can have its chainType set to `undefined` // so we default to the default chain type here. networkConfig: { ...resolvedNetworkConfig, // When coverage is enabled, we set allowUnlimitedContractSize to true // because the added coverage data can push the contract size over the limit. allowUnlimitedContractSize: shouldEnableCoverage ? true : resolvedNetworkConfig.allowUnlimitedContractSize, /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- This case is safe because we have a check above */ chainType: resolvedChainType, }, jsonRpcRequestWrapper, tracingConfig: { buildInfos: await this.#getBuildInfosAndOutputsAsBuffers(), ignoreContracts: false, }, coverageConfig, gasReportConfig, }); } return HttpProvider.create({ url: await resolvedNetworkConfig.url.getUrl(), networkName: resolvedNetworkName, extraHeaders: resolvedNetworkConfig.httpHeaders, timeout: resolvedNetworkConfig.timeout, jsonRpcRequestWrapper, }); }; return NetworkConnectionImplementation.create(this.#nextConnectionId++, resolvedNetworkName, resolvedChainType, resolvedNetworkConfig, async (connection) => { await hookManager.runHandlerChain("network", "closeConnection", [connection], async (_context, conn) => { await conn.provider.close(); }); }, createProvider); } /** * Resolve the network connection configuration settings for the network name * and taking into account any configuration overrides. * * @param resolvedNetworkName the network name for selecting the appropriate network config * @param networkConfigOverride any network config options to override the * defaults for the named network * @returns a valid network configuration including any config additions from * plugins */ async #resolveNetworkConfig(resolvedNetworkName, networkConfigOverride = {}, resolvedChainType) { const existingNetworkConfig = this.#networkConfigs[resolvedNetworkName]; if (Object.keys(networkConfigOverride).length === 0 && resolvedChainType === existingNetworkConfig.chainType) { return existingNetworkConfig; } if ("type" in networkConfigOverride && networkConfigOverride.type !== existingNetworkConfig.type) { throw new HardhatError(HardhatError.ERRORS.CORE.NETWORK.INVALID_CONFIG_OVERRIDE, { errors: `\t* The type of the network cannot be changed.`, }); } if ("chainType" in networkConfigOverride && networkConfigOverride.chainType !== existingNetworkConfig.chainType) { throw new HardhatError(HardhatError.ERRORS.CORE.NETWORK.INVALID_CONFIG_OVERRIDE, { errors: `\t* The chainType cannot be specified in config overrides. Pass it at the top level instead: hre.network.connect({ chainType: 'op' })`, }); } const userConfigWithOverrides = deepMerge(this.#userConfig, { networks: { [resolvedNetworkName]: { ...networkConfigOverride, chainType: resolvedChainType, }, }, }); // This is safe, the plugins used in resolution are registered // with the hook handler, this property is only used for // ensuring the original plugins are available at the end // of resolution. const resolvedPlugins = []; const configResolutionResult = await resolveUserConfigToHardhatConfig(userConfigWithOverrides, this.#hookManager, this.#projectRoot, this.#userProvidedConfigPath, resolvedPlugins); if (!configResolutionResult.success) { throw new HardhatError(HardhatError.ERRORS.CORE.NETWORK.INVALID_CONFIG_OVERRIDE, { errors: `\t${configResolutionResult.userConfigValidationErrors .map((error) => { const path = this.#normaliseErrorPathToNetworkConfig(error.path, resolvedNetworkName); let errorMessage = error.message; // When chainType is changed but the network has a configured hardfork, // provide a specific message explaining the hardfork must also be updated if (path[0] === "hardfork") { errorMessage = `Your configured hardfork is incompatible with chainType ${resolvedChainType}. ` + `You need to update the hardfork in your network config or pass a valid hardfork ` + `in the overrides when connecting to the network. ` + `Valid hardforks for chainType ${resolvedChainType} are: ` + /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We know resolvedChainType is a valid ChainType */ `${getHardforks(resolvedChainType).join(", ")}.`; } return path.length > 0 ? `* Error in ${path.join(".")}: ${errorMessage}` : `* ${errorMessage}`; }) .join("\n\t")}`, }); } const resolvedNetworkConfigOverride = configResolutionResult.config.networks[resolvedNetworkName]; assertHardhatInvariant(resolvedNetworkConfigOverride !== undefined, "The overridden network config should translate through the hook resolution of user config"); return resolvedNetworkConfigOverride; } async #getBuildInfosAndOutputsAsBuffers() { const results = []; for (const id of await this.#artifactsManager.getAllBuildInfoIds()) { const buildInfoPath = await this.#artifactsManager.getBuildInfoPath(id); const buildInfoOutputPath = await this.#artifactsManager.getBuildInfoOutputPath(id); if (buildInfoPath !== undefined && buildInfoOutputPath !== undefined) { const buildInfo = await readBinaryFile(buildInfoPath); const output = await readBinaryFile(buildInfoOutputPath); results.push({ buildInfo, output, }); } } return results; } #normaliseErrorPathToNetworkConfig(path, resolvedNetworkName) { if (path[0] !== undefined && path[0] === "networks") { path = path.slice(1); } if (path[0] !== undefined && path[0] === resolvedNetworkName) { path = path.slice(1); } return path; } } //# sourceMappingURL=network-manager.js.map