UNPKG

@nomicfoundation/hardhat-viem

Version:
190 lines 6.51 kB
import { assertHardhatInvariant, HardhatError, } from "@nomicfoundation/hardhat-errors"; import { isObject } from "@nomicfoundation/hardhat-utils/lang"; import { extractChain } from "viem"; import * as chainsModule from "viem/chains"; import { hardhat, anvil, optimism } from "viem/chains"; /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: this assertion should not be necessary */ const chains = Object.values(chainsModule); const chainCache = new WeakMap(); const chainIdCache = new WeakMap(); const hardhatMetadataCache = new WeakMap(); const isAnvilNetworkCache = new WeakMap(); const HARDHAT_METADATA_METHOD = "hardhat_metadata"; const ANVIL_NODE_INFO_METHOD = "anvil_nodeInfo"; export async function getChain(provider, chainType, chainDescriptors, networkName) { const cachedChain = chainCache.get(provider); if (cachedChain !== undefined) { return cachedChain; } const chainId = await getChainId(provider); let chain = extractChain({ chains, id: chainId, }); if ((await isDevelopmentNetwork(provider)) || chain === undefined) { if (await isHardhatNetwork(provider)) { chain = createHardhatChain(provider, chainId, chainType); } else if (await isAnvilNetwork(provider)) { chain = { ...anvil, id: chainId, }; } else if (chain === undefined) { chain = resolveChain(chainId, networkName, chainDescriptors); } else { assertHardhatInvariant(false, "This should not happen, as we check in isDevelopmentNetwork that it's either hardhat or anvil"); } } chainCache.set(provider, chain); return chain; } export async function getChainId(provider) { const cachedChainId = chainIdCache.get(provider); if (cachedChainId !== undefined) { return cachedChainId; } const chainId = Number(await provider.request({ method: "eth_chainId" })); chainIdCache.set(provider, chainId); return chainId; } export async function isDevelopmentNetwork(provider) { if (await isHardhatNetwork(provider)) { return true; } if (await isAnvilNetwork(provider)) { return true; } return false; } export async function isHardhatNetwork(provider) { const cachedHardhatMetadata = hardhatMetadataCache.get(provider); if (cachedHardhatMetadata !== undefined) { return true; } try { const hardhatMetadata = await provider.request({ method: HARDHAT_METADATA_METHOD, }); assertHardhatInvariant(isHardhatMetadata(hardhatMetadata), "Expected valid hardhat metadata response"); hardhatMetadataCache.set(provider, hardhatMetadata); return true; } catch { hardhatMetadataCache.delete(provider); return false; } } export async function isAnvilNetwork(provider) { const cachedIsAnvil = isAnvilNetworkCache.get(provider); if (cachedIsAnvil !== undefined) { return cachedIsAnvil; } const isAnvil = await isMethodSupported(provider, ANVIL_NODE_INFO_METHOD); isAnvilNetworkCache.set(provider, isAnvil); return isAnvil; } export async function getMode(provider) { if (await isHardhatNetwork(provider)) { return "hardhat"; } if (await isAnvilNetwork(provider)) { return "anvil"; } throw new HardhatError(HardhatError.ERRORS.HARDHAT_VIEM.GENERAL.UNSUPPORTED_DEVELOPMENT_NETWORK); } async function isMethodSupported(provider, method) { try { await provider.request({ method }); return true; } catch { return false; } } function createHardhatChain(provider, chainId, chainType) { const hardhatMetadata = hardhatMetadataCache.get(provider); assertHardhatInvariant(hardhatMetadata !== undefined, "Expected hardhat metadata to be available"); if (hardhatMetadata.forkedNetwork?.chainId !== undefined) { const forkedChain = extractChain({ chains, id: hardhatMetadata.forkedNetwork.chainId, }); if (forkedChain !== undefined) { return { ...forkedChain, ...hardhat, id: chainId, }; } } const chain = { ...hardhat, id: chainId, }; if (chainType === "op") { // we add the optimism contracts to enable viem's L2 actions chain.contracts = { ...optimism.contracts, }; } return chain; } function isHardhatMetadata(value) { return (isObject(value) && (value.forkedNetwork === undefined || (isObject(value.forkedNetwork) && typeof value.forkedNetwork.chainId === "number"))); } /** * Resolves a viem chain for a given chain id using the provided chain * descriptors. If a matching descriptor is found, the chain is enriched with * the descriptor's name and block explorer. Otherwise, falls back to a minimal * chain using the network name. */ export function resolveChain(chainId, networkName, chainDescriptors) { const chainDescriptor = chainDescriptors.get(BigInt(chainId)); if (chainDescriptor === undefined) { return createMinimalChain(chainId, networkName); } const chain = createMinimalChain(chainId, chainDescriptor.name); const blockExplorer = getDefaultBlockExplorer(chainDescriptor); if (blockExplorer !== undefined) { chain.blockExplorers = { default: blockExplorer, }; } return chain; } /** * Creates a viem chain with the minimum fields required to satisfy the * {@link ViemChain} type. */ export function createMinimalChain(id, name) { return { id, name, nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, rpcUrls: { default: { http: [] }, }, }; } /** * Returns the default block explorer for a chain descriptor, preferring * Etherscan over Blockscout. Returns `undefined` if neither is configured. */ export function getDefaultBlockExplorer(chainDescriptor) { const { etherscan, blockscout } = chainDescriptor.blockExplorers; if (etherscan !== undefined) { return { name: etherscan.name ?? "Etherscan", url: etherscan.url }; } if (blockscout !== undefined) { return { name: blockscout.name ?? "Blockscout", url: blockscout.url }; } return undefined; } //# sourceMappingURL=chains.js.map