UNPKG

hardhat

Version:

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

343 lines 14.1 kB
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 { getUnprefixedHexString } from "@nomicfoundation/hardhat-utils/hex"; 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) { 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 = hardfork; throw new Error( // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- we want to print the fork `Unknown L1 hardfork '${hardfork}', this shouldn't happen`); } } export function edrOpHardforkToHardhatOpHardforkName(hardfork) { 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 = hardfork; throw new Error( // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- we want to print the fork `Unknown OP hardfork '${hardfork}', this shouldn't happen`); } } export function hardhatHardforkToEdrSpecId(hardfork, chainType) { return chainType === OPTIMISM_CHAIN_TYPE ? hardhatOpHardforkToEdrSpecId(hardfork) : hardhatL1HardforkToEdrSpecId(hardfork); } function hardhatOpHardforkToEdrSpecId(hardfork) { 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 = hardforkName; throw new Error( // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- we want to print the fork `Unknown hardfork name '${hardforkName}', this shouldn't happen`); } } function hardhatL1HardforkToEdrSpecId(hardfork) { 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 = 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}', this shouldn't happen`); } } export function hardhatMiningIntervalToEdrMiningInterval(config) { 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) { switch (mempoolOrder) { case "fifo": return MineOrdering.Fifo; case "priority": return MineOrdering.Priority; } } // TODO: EDR should handle this conversion. This is a temporary solution. export function edrRpcDebugTraceToHardhat(debugTraceResult) { const structLogs = debugTraceResult.structLogs.map((log) => { const result = { depth: Number(log.depth), gas: Number(log.gas), gasCost: Number(log.gasCost), op: log.opName, pc: Number(log.pc), }; if (log.memory !== undefined) { result.memory = log.memory; } if (log.stack !== undefined) { // Remove 0x prefix which is required by EIP-3155, but not expected by Hardhat. result.stack = log.stack.map(getUnprefixedHexString); } if (log.storage !== undefined) { result.storage = Object.fromEntries(Object.entries(log.storage).map(([key, value]) => [ getUnprefixedHexString(key), getUnprefixedHexString(value), ])); } if (log.error !== undefined) { result.error = { message: log.error, }; } return result; }); // REVM trace adds initial STOP that Hardhat doesn't expect // TODO: double check with EDR team that this is still the case if (structLogs.length > 0 && structLogs[0].op === "STOP") { structLogs.shift(); } /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- debugTraceResult.output is a string, but it's typed as Buffer in Edr */ let returnValue = debugTraceResult.output ?? "0x"; if (returnValue === "0x") { returnValue = ""; } return { failed: !debugTraceResult.pass, gas: Number(debugTraceResult.gasUsed), returnValue, structLogs, }; } export async function hardhatAccountsToEdrOwnedAccounts(accounts) { 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) { 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, chainType) { 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 = { 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, chainDescriptors, chainType) { let fork; 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, excludedContractFqns = []) { const gasMeasurements = []; 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), size: Number(deployment.size), }); } } // 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), }); } } } } return gasMeasurements; } //# sourceMappingURL=convert-to-edr.js.map