UNPKG

hardhat

Version:

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

430 lines (398 loc) 12.7 kB
/* eslint-disable no-restricted-syntax -- hack */ import type { EdrNetworkAccountConfig, EdrNetworkAccountsConfig, ChainDescriptorsConfig, EdrNetworkForkingConfig, EdrNetworkMempoolConfig, EdrNetworkMiningConfig, } from "../../../../../types/config.js"; import type { ChainType } from "../../../../../types/network.js"; import type { GasMeasurement } from "../../../gas-analytics/types.js"; import type { IntervalRange, ChainOverride, ForkConfig, GasReport, } from "@nomicfoundation/edr"; import { GasReportExecutionStatus, MineOrdering, OpHardfork, SpecId, FRONTIER, HOMESTEAD, DAO_FORK, TANGERINE, SPURIOUS_DRAGON, BYZANTIUM, CONSTANTINOPLE, PETERSBURG, ISTANBUL, MUIR_GLACIER, BERLIN, LONDON, ARROW_GLACIER, GRAY_GLACIER, MERGE, SHANGHAI, CANCUN, PRAGUE, OSAKA, BEDROCK, REGOLITH, CANYON, ECOTONE, FJORD, GRANITE, HOLOCENE, ISTHMUS, } from "@nomicfoundation/edr"; import { GENERIC_CHAIN_TYPE, L1_CHAIN_TYPE, OPTIMISM_CHAIN_TYPE, } from "../../../../constants.js"; import { FixedValueConfigurationVariable } from "../../../../core/configuration-variables.js"; import { derivePrivateKeys } from "../../accounts/derive-private-keys.js"; import { DEFAULT_EDR_NETWORK_BALANCE, EDR_NETWORK_DEFAULT_PRIVATE_KEYS, isDefaultEdrNetworkHDAccountsConfig, } from "../edr-provider.js"; import { L1HardforkName, OpHardforkName } from "../types/hardfork.js"; import { getL1HardforkName, getOpHardforkName } from "./hardfork.js"; export function edrL1HardforkToHardhatL1HardforkName( hardfork: SpecId, ): L1HardforkName { switch (hardfork) { case SpecId.Frontier: return L1HardforkName.FRONTIER; case SpecId.FrontierThawing: return L1HardforkName.FRONTIER; case SpecId.Homestead: return L1HardforkName.HOMESTEAD; case SpecId.DaoFork: return L1HardforkName.DAO; case SpecId.Tangerine: return L1HardforkName.TANGERINE_WHISTLE; case SpecId.SpuriousDragon: return L1HardforkName.SPURIOUS_DRAGON; case SpecId.Byzantium: return L1HardforkName.BYZANTIUM; case SpecId.Constantinople: return L1HardforkName.CONSTANTINOPLE; case SpecId.Petersburg: return L1HardforkName.PETERSBURG; case SpecId.Istanbul: return L1HardforkName.ISTANBUL; case SpecId.MuirGlacier: return L1HardforkName.MUIR_GLACIER; case SpecId.Berlin: return L1HardforkName.BERLIN; case SpecId.London: return L1HardforkName.LONDON; case SpecId.ArrowGlacier: return L1HardforkName.ARROW_GLACIER; case SpecId.GrayGlacier: return L1HardforkName.GRAY_GLACIER; case SpecId.Merge: return L1HardforkName.MERGE; case SpecId.Shanghai: return L1HardforkName.SHANGHAI; case SpecId.Cancun: return L1HardforkName.CANCUN; case SpecId.Prague: return L1HardforkName.PRAGUE; case SpecId.Osaka: return L1HardforkName.OSAKA; // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- trust but verify default: const _exhaustiveCheck: never = hardfork; throw new Error( // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- we want to print the fork `Unknown L1 hardfork '${hardfork as SpecId}', this shouldn't happen`, ); } } export function edrOpHardforkToHardhatOpHardforkName( hardfork: OpHardfork, ): OpHardforkName { switch (hardfork) { case OpHardfork.Bedrock: return OpHardforkName.BEDROCK; case OpHardfork.Regolith: return OpHardforkName.REGOLITH; case OpHardfork.Canyon: return OpHardforkName.CANYON; case OpHardfork.Ecotone: return OpHardforkName.ECOTONE; case OpHardfork.Fjord: return OpHardforkName.FJORD; case OpHardfork.Granite: return OpHardforkName.GRANITE; case OpHardfork.Holocene: return OpHardforkName.HOLOCENE; case OpHardfork.Isthmus: return OpHardforkName.ISTHMUS; // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- trust but verify default: const _exhaustiveCheck: never = hardfork; throw new Error( // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- we want to print the fork `Unknown OP hardfork '${hardfork as OpHardfork}', this shouldn't happen`, ); } } export function hardhatHardforkToEdrSpecId( hardfork: string, chainType: ChainType, ): string { return chainType === OPTIMISM_CHAIN_TYPE ? hardhatOpHardforkToEdrSpecId(hardfork) : hardhatL1HardforkToEdrSpecId(hardfork); } function hardhatOpHardforkToEdrSpecId(hardfork: string): string { const hardforkName = getOpHardforkName(hardfork); switch (hardforkName) { case OpHardforkName.BEDROCK: return BEDROCK; case OpHardforkName.REGOLITH: return REGOLITH; case OpHardforkName.CANYON: return CANYON; case OpHardforkName.ECOTONE: return ECOTONE; case OpHardforkName.FJORD: return FJORD; case OpHardforkName.GRANITE: return GRANITE; case OpHardforkName.HOLOCENE: return HOLOCENE; case OpHardforkName.ISTHMUS: return ISTHMUS; // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- trust but verify default: const _exhaustiveCheck: never = hardforkName; throw new Error( // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- we want to print the fork `Unknown hardfork name '${hardforkName as string}', this shouldn't happen`, ); } } function hardhatL1HardforkToEdrSpecId(hardfork: string): string { const hardforkName = getL1HardforkName(hardfork); switch (hardforkName) { case L1HardforkName.FRONTIER: return FRONTIER; case L1HardforkName.HOMESTEAD: return HOMESTEAD; case L1HardforkName.DAO: return DAO_FORK; case L1HardforkName.TANGERINE_WHISTLE: return TANGERINE; case L1HardforkName.SPURIOUS_DRAGON: return SPURIOUS_DRAGON; case L1HardforkName.BYZANTIUM: return BYZANTIUM; case L1HardforkName.CONSTANTINOPLE: return CONSTANTINOPLE; case L1HardforkName.PETERSBURG: return PETERSBURG; case L1HardforkName.ISTANBUL: return ISTANBUL; case L1HardforkName.MUIR_GLACIER: return MUIR_GLACIER; case L1HardforkName.BERLIN: return BERLIN; case L1HardforkName.LONDON: return LONDON; case L1HardforkName.ARROW_GLACIER: return ARROW_GLACIER; case L1HardforkName.GRAY_GLACIER: return GRAY_GLACIER; case L1HardforkName.MERGE: return MERGE; case L1HardforkName.SHANGHAI: return SHANGHAI; case L1HardforkName.CANCUN: return CANCUN; case L1HardforkName.PRAGUE: return PRAGUE; case L1HardforkName.OSAKA: return OSAKA; // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- we want to print the fork default: const _exhaustiveCheck: never = hardforkName; throw new Error( // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- an enum can be safely cast to a string `Unknown hardfork name '${hardfork as string}', this shouldn't happen`, ); } } export function hardhatMiningIntervalToEdrMiningInterval( config: EdrNetworkMiningConfig["interval"], ): bigint | IntervalRange | undefined { if (typeof config === "number") { // Is interval mining disabled? if (config === 0) { return undefined; } else { return BigInt(config); } } else { return { min: BigInt(config[0]), max: BigInt(config[1]), }; } } export function hardhatMempoolOrderToEdrMineOrdering( mempoolOrder: EdrNetworkMempoolConfig["order"], ): MineOrdering { switch (mempoolOrder) { case "fifo": return MineOrdering.Fifo; case "priority": return MineOrdering.Priority; } } export async function hardhatAccountsToEdrOwnedAccounts( accounts: EdrNetworkAccountsConfig, ): Promise<Array<{ secretKey: string; balance: bigint }>> { const normalizedAccounts = await normalizeEdrNetworkAccountsConfig(accounts); const accountPromises = normalizedAccounts.map(async (account) => ({ secretKey: await account.privateKey.getHexString(), balance: account.balance, })); return Promise.all(accountPromises); } export async function normalizeEdrNetworkAccountsConfig( accounts: EdrNetworkAccountsConfig, ): Promise<EdrNetworkAccountConfig[]> { if (Array.isArray(accounts)) { return accounts; } const isDefaultConfig = await isDefaultEdrNetworkHDAccountsConfig(accounts); const derivedPrivateKeys = isDefaultConfig ? EDR_NETWORK_DEFAULT_PRIVATE_KEYS : await derivePrivateKeys( await accounts.mnemonic.get(), accounts.path, accounts.initialIndex, accounts.count, await accounts.passphrase.get(), ); return derivedPrivateKeys.map((privateKey) => ({ privateKey: new FixedValueConfigurationVariable(privateKey), balance: accounts.accountsBalance ?? DEFAULT_EDR_NETWORK_BALANCE, })); } export function hardhatChainDescriptorsToEdrChainOverrides( chainDescriptors: ChainDescriptorsConfig, chainType: ChainType, ): ChainOverride[] { return ( Array.from(chainDescriptors) // Skip chain descriptors that don't match the expected chain type .filter(([_, descriptor]) => { if (chainType === GENERIC_CHAIN_TYPE) { // When "generic" is requested, include both "generic" and "l1" chains return ( descriptor.chainType === GENERIC_CHAIN_TYPE || descriptor.chainType === L1_CHAIN_TYPE ); } return descriptor.chainType === chainType; }) .map(([chainId, descriptor]) => { const chainOverride: ChainOverride = { chainId, name: descriptor.name, }; if (descriptor.hardforkHistory !== undefined) { chainOverride.hardforkActivationOverrides = Array.from( descriptor.hardforkHistory, ).map(([hardfork, { blockNumber, timestamp }]) => ({ condition: blockNumber !== undefined ? { blockNumber: BigInt(blockNumber) } : { timestamp: BigInt(timestamp) }, hardfork: hardhatHardforkToEdrSpecId( hardfork, descriptor.chainType, ), })); } return chainOverride; }) ); } export async function hardhatForkingConfigToEdrForkConfig( forkingConfig: EdrNetworkForkingConfig | undefined, chainDescriptors: ChainDescriptorsConfig, chainType: ChainType, ): Promise<ForkConfig | undefined> { let fork: ForkConfig | undefined; if (forkingConfig !== undefined && forkingConfig.enabled === true) { const httpHeaders = forkingConfig.httpHeaders !== undefined ? Object.entries(forkingConfig.httpHeaders).map(([name, value]) => ({ name, value, })) : undefined; fork = { blockNumber: forkingConfig.blockNumber, cacheDir: forkingConfig.cacheDir, chainOverrides: hardhatChainDescriptorsToEdrChainOverrides( chainDescriptors, chainType, ), httpHeaders, url: await forkingConfig.url.getUrl(), }; } return fork; } /** * Converts EDR's nested GasReport structure into a flat array of gas entries. * Filters out reverted transactions. */ export function edrGasReportToHardhatGasMeasurements( gasReport: GasReport, excludedContractFqns: string[] = [], ): GasMeasurement[] { const gasMeasurements: GasMeasurement[] = []; for (const [contractFqn, data] of Object.entries(gasReport.contracts)) { if (excludedContractFqns.includes(contractFqn)) { continue; } // Process deployments for (const deployment of data.deployments) { if (deployment.status === GasReportExecutionStatus.Success) { gasMeasurements.push({ contractFqn, type: "deployment", gas: Number(deployment.gas), runtimeSize: Number(deployment.runtimeSize), }); } } // Process function calls for (const [functionSig, calls] of Object.entries(data.functions)) { for (const call of calls) { if (call.status === GasReportExecutionStatus.Success) { gasMeasurements.push({ contractFqn, type: "function", functionSig, gas: Number(call.gas), proxyChain: call.proxyChain, }); } } } } return gasMeasurements; }