UNPKG

hardhat

Version:

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

386 lines (339 loc) 10.5 kB
import type EthereumjsUtilT from "@ethereumjs/util"; import picocolors from "picocolors"; import debug from "debug"; import fsExtra from "fs-extra"; import { HARDHAT_NETWORK_NAME } from "../internal/constants"; import { subtask, task, types } from "../internal/core/config/config-env"; import { HardhatError } from "../internal/core/errors"; import { ERRORS } from "../internal/core/errors-list"; import { createProvider } from "../internal/core/providers/construction"; import { normalizeHardhatNetworkAccountsConfig } from "../internal/core/providers/util"; import { JsonRpcServer as JsonRpcServerImpl, JsonRpcServerConfig, } from "../internal/hardhat-network/jsonrpc/server"; import { Reporter } from "../internal/sentry/reporter"; import { EthereumProvider, HardhatNetworkConfig, JsonRpcServer, } from "../types"; import { HARDHAT_NETWORK_MNEMONIC } from "../internal/core/config/default-config"; import { TASK_NODE, TASK_NODE_CREATE_SERVER, TASK_NODE_GET_PROVIDER, TASK_NODE_SERVER_CREATED, TASK_NODE_SERVER_READY, } from "./task-names"; import { watchCompilerOutput, Watcher } from "./utils/watch"; const log = debug("hardhat:core:tasks:node"); function printDefaultConfigWarning() { console.log( picocolors.bold( "WARNING: These accounts, and their private keys, are publicly known." ) ); console.log( picocolors.bold( "Any funds sent to them on Mainnet or any other live network WILL BE LOST." ) ); } function logHardhatNetworkAccounts(networkConfig: HardhatNetworkConfig) { const isDefaultConfig = !Array.isArray(networkConfig.accounts) && networkConfig.accounts.mnemonic === HARDHAT_NETWORK_MNEMONIC; const { bytesToHex: bufferToHex, privateToAddress, toBytes, toChecksumAddress, } = require("@ethereumjs/util") as typeof EthereumjsUtilT; console.log("Accounts"); console.log("========"); if (isDefaultConfig) { console.log(); printDefaultConfigWarning(); console.log(); } const accounts = normalizeHardhatNetworkAccountsConfig( networkConfig.accounts ); for (const [index, account] of accounts.entries()) { const address = toChecksumAddress( bufferToHex(privateToAddress(toBytes(account.privateKey))) ); const balance = (BigInt(account.balance) / 10n ** 18n).toString(10); let entry = `Account #${index}: ${address} (${balance} ETH)`; if (isDefaultConfig) { const privateKey = bufferToHex(toBytes(account.privateKey)); entry += ` Private Key: ${privateKey}`; } console.log(entry); console.log(); } if (isDefaultConfig) { printDefaultConfigWarning(); console.log(); } } subtask(TASK_NODE_GET_PROVIDER) .addOptionalParam("forkUrl", undefined, undefined, types.string) .addOptionalParam("forkBlockNumber", undefined, undefined, types.int) .setAction( async ( { forkBlockNumber: forkBlockNumberParam, forkUrl: forkUrlParam, }: { forkBlockNumber?: number; forkUrl?: string; }, { artifacts, config, network, userConfig } ): Promise<EthereumProvider> => { let provider = network.provider; if (network.name !== HARDHAT_NETWORK_NAME) { log(`Creating hardhat provider for JSON-RPC server`); provider = await createProvider( config, HARDHAT_NETWORK_NAME, artifacts ); } const hardhatNetworkConfig = config.networks[HARDHAT_NETWORK_NAME]; const forkUrlConfig = hardhatNetworkConfig.forking?.url; const forkBlockNumberConfig = hardhatNetworkConfig.forking?.blockNumber; const forkUrl = forkUrlParam ?? forkUrlConfig; const forkBlockNumber = forkBlockNumberParam ?? forkBlockNumberConfig; // we throw an error if the user specified a forkBlockNumber but not a // forkUrl if (forkBlockNumber !== undefined && forkUrl === undefined) { throw new HardhatError( ERRORS.BUILTIN_TASKS.NODE_FORK_BLOCK_NUMBER_WITHOUT_URL ); } // if the url or the block is different to the one in the configuration, // we use hardhat_reset to set the fork if ( forkUrl !== forkUrlConfig || forkBlockNumber !== forkBlockNumberConfig ) { await provider.request({ method: "hardhat_reset", params: [ { forking: { jsonRpcUrl: forkUrl, blockNumber: forkBlockNumber, }, }, ], }); } const hardhatNetworkUserConfig = userConfig.networks?.[HARDHAT_NETWORK_NAME] ?? {}; // enable logging await provider.request({ method: "hardhat_setLoggingEnabled", params: [hardhatNetworkUserConfig.loggingEnabled ?? true], }); return provider; } ); subtask(TASK_NODE_CREATE_SERVER) .addParam("hostname", undefined, undefined, types.string) .addParam("port", undefined, undefined, types.int) .addParam("provider", undefined, undefined, types.any) .setAction( async ({ hostname, port, provider, }: { hostname: string; port: number; provider: EthereumProvider; }): Promise<JsonRpcServer> => { const serverConfig: JsonRpcServerConfig = { hostname, port, provider, }; const server = new JsonRpcServerImpl(serverConfig); return server; } ); /** * This task will be called when the server was successfully created, but it's * not ready for receiving requests yet. */ subtask(TASK_NODE_SERVER_CREATED) .addParam("hostname", undefined, undefined, types.string) .addParam("port", undefined, undefined, types.int) .addParam("provider", undefined, undefined, types.any) .addParam("server", undefined, undefined, types.any) .setAction( async ({}: { hostname: string; port: number; provider: EthereumProvider; server: JsonRpcServer; }) => { // this task is meant to be overridden by plugin writers } ); /** * This subtask will be run when the server is ready to accept requests */ subtask(TASK_NODE_SERVER_READY) .addParam("address", undefined, undefined, types.string) .addParam("port", undefined, undefined, types.int) .addParam("provider", undefined, undefined, types.any) .addParam("server", undefined, undefined, types.any) .setAction( async ( { address, port, }: { address: string; port: number; provider: EthereumProvider; server: JsonRpcServer; }, { config } ) => { console.log( picocolors.green( `Started HTTP and WebSocket JSON-RPC server at http://${address}:${port}/` ) ); console.log(); const networkConfig = config.networks[HARDHAT_NETWORK_NAME]; logHardhatNetworkAccounts(networkConfig); } ); task(TASK_NODE, "Starts a JSON-RPC server on top of Hardhat Network") .addOptionalParam( "hostname", "The host to which to bind to for new connections (Defaults to 127.0.0.1 running locally, and 0.0.0.0 in Docker)", undefined, types.string ) .addOptionalParam( "port", "The port on which to listen for new connections", 8545, types.int ) .addOptionalParam( "fork", "The URL of the JSON-RPC server to fork from", undefined, types.string ) .addOptionalParam( "forkBlockNumber", "The block number to fork from", undefined, types.int ) .setAction( async ( { forkBlockNumber, fork: forkUrl, hostname: hostnameParam, port, }: { forkBlockNumber?: number; fork?: string; hostname?: string; port: number; }, { config, hardhatArguments, network, run } ) => { // we throw if the user specified a network argument and it's not hardhat if ( network.name !== HARDHAT_NETWORK_NAME && hardhatArguments.network !== undefined ) { throw new HardhatError( ERRORS.BUILTIN_TASKS.JSONRPC_UNSUPPORTED_NETWORK ); } try { const provider: EthereumProvider = await run(TASK_NODE_GET_PROVIDER, { forkBlockNumber, forkUrl, }); // the default hostname is "127.0.0.1" unless we are inside a docker // container, in that case we use "0.0.0.0" let hostname: string; if (hostnameParam !== undefined) { hostname = hostnameParam; } else { const insideDocker = fsExtra.existsSync("/.dockerenv"); if (insideDocker) { hostname = "0.0.0.0"; } else { hostname = "127.0.0.1"; } } const server: JsonRpcServer = await run(TASK_NODE_CREATE_SERVER, { hostname, port, provider, }); await run(TASK_NODE_SERVER_CREATED, { hostname, port, provider, server, }); const { port: actualPort, address } = await server.listen(); let watcher: Watcher | undefined; try { watcher = await watchCompilerOutput(provider, config.paths); } catch (error) { console.warn( picocolors.yellow( "There was a problem watching the compiler output, changes in the contracts won't be reflected in the Hardhat Network. Run Hardhat with --verbose to learn more." ) ); log( "Compilation output can't be watched. Please report this to help us improve Hardhat.\n", error ); if (error instanceof Error) { Reporter.reportError(error); } } await run(TASK_NODE_SERVER_READY, { address, port: actualPort, provider, server, }); await server.waitUntilClosed(); await watcher?.close(); } catch (error) { if (HardhatError.isHardhatError(error)) { throw error; } if (error instanceof Error) { throw new HardhatError( ERRORS.BUILTIN_TASKS.JSONRPC_SERVER_ERROR, { error: error.message, }, error ); } // eslint-disable-next-line @nomicfoundation/hardhat-internal-rules/only-hardhat-error throw error; } } );