diff --git a/src/index.ts b/src/index.ts index 0452049..4dca847 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,12 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { extendConfig, subtask, task } from "hardhat/config"; import { xdeployConfigExtender } from "./config"; -import { networks, explorers } from "./networks"; +import { + networks, + getTxHashLink, + getAddressLink, + networksInfo, +} from "./networks"; import { CREATE2_DEPLOYER_ADDRESS, PLUGIN_NAME, @@ -50,7 +55,6 @@ task( let initcode: any; let computedContractAddress: any; let chainId: any; - let idx: number; console.log( "\nThe deployment is starting... Please bear with me, this may take a minute or two. Anyway, WAGMI!", @@ -98,10 +102,9 @@ task( signers[i] = wallets[i].connect(providers[i]); - if ( - hre.config.xdeploy.networks[i] !== "hardhat" && - hre.config.xdeploy.networks[i] !== "localhost" - ) { + const network = hre.config.xdeploy.networks[i]; + + if (!["hardhat", "localhost"].includes(network)) { create2Deployer[i] = new hre.ethers.Contract( CREATE2_DEPLOYER_ADDRESS, abi, @@ -136,7 +139,7 @@ task( if ((await providers[i].getCode(computedContractAddress)) !== "0x") { throw new NomicLabsHardhatPluginError( PLUGIN_NAME, - `The address of the contract you want to deploy already has existing bytecode on ${hre.config.xdeploy.networks[i]}. + `The address of the contract you want to deploy already has existing bytecode on ${network}. It is very likely that you have deployed this contract before with the same salt parameter value. Please try using a different salt value.`, ); @@ -150,28 +153,17 @@ task( ); chainId = createReceipt[i].chainId; - idx = networks.indexOf(hre.config.xdeploy.networks[i]); createReceipt[i] = await createReceipt[i].wait(); result[i] = { - network: hre.config.xdeploy.networks[i], + network: network, chainId: chainId.toString(), contract: hre.config.xdeploy.contract, txHash: createReceipt[i].hash, - txHashLink: - hre.config.xdeploy.networks[i].slice(0, 8) == "filecoin" - ? `${explorers[idx]}message/${createReceipt[i].hash}` - : hre.config.xdeploy.networks[i].slice(0, 16) == - "seiArcticTestnet" - ? `${explorers[idx]}transactions/${createReceipt[i].hash}` - : `${explorers[idx]}tx/${createReceipt[i].hash}`, + txHashLink: getTxHashLink(network, createReceipt[i].hash), address: computedContractAddress, - addressLink: - hre.config.xdeploy.networks[i].slice(0, 16) == - "seiArcticTestnet" - ? `${explorers[idx]}account/${computedContractAddress}` - : `${explorers[idx]}address/${computedContractAddress}`, + addressLink: getAddressLink(network, computedContractAddress), receipt: createReceipt[i].toJSON(), deployed: true, error: undefined, @@ -185,7 +177,7 @@ task( path.join( hre.config.paths.root, "deployments", - `${hre.config.xdeploy.contract}_${hre.config.xdeploy.networks[i]}_deployment.json`, + `${hre.config.xdeploy.contract}_${network}_deployment.json`, ), ); @@ -207,7 +199,7 @@ task( ); } catch (err) { result[i] = { - network: hre.config.xdeploy.networks[i], + network: network, chainId: undefined, contract: hre.config.xdeploy.contract, txHash: undefined, @@ -227,7 +219,7 @@ task( path.join( hre.config.paths.root, "deployments", - `${hre.config.xdeploy.contract}_${hre.config.xdeploy.networks[i]}_deployment_debug.json`, + `${hre.config.xdeploy.contract}_${network}_deployment_debug.json`, ), ); @@ -247,15 +239,12 @@ task( ); } } - } else if ( - hre.config.xdeploy.networks[i] === "hardhat" || - hre.config.xdeploy.networks[i] === "localhost" - ) { + } else if (["hardhat", "localhost"].includes(network)) { let hhcreate2Deployer = await hre.ethers.getContractFactory( "Create2DeployerLocal", ); - if (hre.config.xdeploy.networks[i] === "localhost") { + if (network === "localhost") { hhcreate2Deployer = await hre.ethers.getContractFactory( "Create2DeployerLocal", signers[i], @@ -297,18 +286,17 @@ task( ); chainId = createReceipt[i].chainId; - idx = networks.indexOf(hre.config.xdeploy.networks[i]); createReceipt[i] = await createReceipt[i].wait(); result[i] = { - network: hre.config.xdeploy.networks[i], + network: network, chainId: chainId.toString(), contract: hre.config.xdeploy.contract, txHash: createReceipt[i].hash, - txHashLink: explorers[idx], + txHashLink: networksInfo[network], address: computedContractAddress, - addressLink: explorers[idx], + addressLink: networksInfo[network], receipt: createReceipt[i].toJSON(), deployed: true, error: undefined, @@ -322,7 +310,7 @@ task( path.join( hre.config.paths.root, "deployments", - `${hre.config.xdeploy.contract}_${hre.config.xdeploy.networks[i]}_deployment.json`, + `${hre.config.xdeploy.contract}_${network}_deployment.json`, ), ); @@ -344,7 +332,7 @@ task( ); } catch (err) { result[i] = { - network: hre.config.xdeploy.networks[i], + network: network, chainId: undefined, contract: hre.config.xdeploy.contract, txHash: undefined, @@ -364,7 +352,7 @@ task( path.join( hre.config.paths.root, "deployments", - `${hre.config.xdeploy.contract}_${hre.config.xdeploy.networks[i]}_deployment_debug.json`, + `${hre.config.xdeploy.contract}_${network}_deployment_debug.json`, ), ); @@ -405,7 +393,7 @@ subtask(TASK_VERIFY_NETWORK_ARGUMENTS).setAction(async (_, hre) => { subtask(TASK_VERIFY_SUPPORTED_NETWORKS).setAction(async (_, hre) => { const unsupported = hre?.config?.xdeploy?.networks?.filter( - (v) => !networks.includes(v), + (v) => !networksInfo[v], ); if (unsupported && unsupported.length > 0) { throw new NomicLabsHardhatPluginError( diff --git a/src/networks.ts b/src/networks.ts index dea34f8..b1fd043 100644 --- a/src/networks.ts +++ b/src/networks.ts @@ -1,185 +1,110 @@ -// List of supported networks -export const networks = [ - "localhost", - "hardhat", - "sepolia", - "holesky", - "bscTestnet", - "optimismSepolia", - "arbitrumSepolia", - "amoy", - "polygonZkEVMTestnet", - "fantomTestnet", - "fuji", - "chiado", - "moonbaseAlpha", - "alfajores", - "auroraTestnet", - "harmonyTestnet", - "spark", - "cronosTestnet", - "evmosTestnet", - "bobaTestnet", - "cantoTestnet", - "baseSepolia", - "mantleTestnet", - "filecoinTestnet", - "scrollSepolia", - "lineaTestnet", - "zoraSepolia", - "luksoTestnet", - "mantaTestnet", - "blastTestnet", - "dosTestnet", - "fraxtalTestnet", - "metisTestnet", - "modeTestnet", - "seiArcticTestnet", - "xlayerTestnet", - "bobTestnet", - "coreTestnet", - "telosTestnet", - "rootstockTestnet", - "chilizTestnet", - "taraxaTestnet", - "taikoTestnet", - "zetaChainTestnet", - "ethMain", - "bscMain", - "optimismMain", - "arbitrumOne", - "arbitrumNova", - "polygon", - "polygonZkEVMMain", - "fantomMain", - "avalanche", - "gnosis", - "moonriver", - "moonbeam", - "celo", - "auroraMain", - "harmonyMain", - "fuse", - "cronosMain", - "evmosMain", - "bobaMain", - "cantoMain", - "baseMain", - "mantleMain", - "filecoinMain", - "scrollMain", - "lineaMain", - "zoraMain", - "luksoMain", - "mantaMain", - "blastMain", - "dosMain", - "fraxtalMain", - "enduranceMain", - "kavaMain", - "metisMain", - "modeMain", - "xlayerMain", - "bobMain", - "coreMain", - "telosMain", - "rootstockMain", - "chilizMain", - "taraxaMain", - "gravityAlphaMain", - "taikoMain", - "zetaChainMain", -]; +// List of supported networks with corresponding block explorer links +export const networksInfo = { + localhost: "N/A", + hardhat: "N/A", + sepolia: "https://sepolia.etherscan.io/", + holesky: "https://holesky.etherscan.io/", + bscTestnet: "https://testnet.bscscan.com/", + optimismSepolia: "https://sepolia-optimism.etherscan.io/", + arbitrumSepolia: "https://sepolia.arbiscan.io/", + amoy: "https://amoy.polygonscan.com/", + polygonZkEVMTestnet: "https://cardona-zkevm.polygonscan.com/", + fantomTestnet: "https://testnet.ftmscan.com/", + fuji: "https://testnet.snowtrace.io/", + chiado: "https://gnosis-chiado.blockscout.com/", + moonbaseAlpha: "https://moonbase.moonscan.io/", + alfajores: "https://alfajores.celoscan.io/", + auroraTestnet: "https://explorer.testnet.aurora.dev/", + harmonyTestnet: "https://explorer.testnet.harmony.one/", + spark: "https://explorer.fusespark.io/", + cronosTestnet: "https://cronos.org/explorer/testnet3/", + evmosTestnet: "https://www.mintscan.io/evmos-testnet/", + bobaTestnet: "https://testnet.bobascan.com/", + cantoTestnet: "https://testnet.tuber.build/", + baseSepolia: "https://sepolia.basescan.org/", + mantleTestnet: "https://sepolia.mantlescan.xyz/", + filecoinTestnet: "https://calibration.filfox.info/en/", + scrollSepolia: "https://sepolia.scrollscan.com/", + lineaTestnet: "https://sepolia.lineascan.build/", + zoraSepolia: "https://sepolia.explorer.zora.energy/", + luksoTestnet: "https://explorer.execution.testnet.lukso.network/", + mantaTestnet: "https://pacific-explorer.sepolia-testnet.manta.network/", + blastTestnet: "https://sepolia.blastscan.io/", + dosTestnet: "https://test.doscan.io/", + fraxtalTestnet: "https://holesky.fraxscan.com/", + metisTestnet: "https://sepolia-explorer.metisdevops.link/", + modeTestnet: "https://sepolia.explorer.mode.network/", + seiArcticTestnet: "https://seistream.app/", + xlayerTestnet: "https://www.oklink.com/xlayer-test/", + bobTestnet: "https://testnet-explorer.gobob.xyz/", + coreTestnet: "https://scan.test.btcs.network/", + telosTestnet: "https://testnet.teloscan.io/", + rootstockTestnet: "https://rootstock-testnet.blockscout.com/", + chilizTestnet: "https://testnet.chiliscan.com/", + taraxaTestnet: "https://testnet.explorer.taraxa.io/", + taikoTestnet: "https://hekla.taikoscan.io/", + zetaChainTestnet: "https://athens.explorer.zetachain.com/", + ethMain: "https://etherscan.io/", + bscMain: "https://bscscan.com/", + optimismMain: "https://optimistic.etherscan.io/", + arbitrumOne: "https://arbiscan.io/", + arbitrumNova: "https://nova.arbiscan.io/", + polygon: "https://polygonscan.com/", + polygonZkEVMMain: "https://zkevm.polygonscan.com/", + fantomMain: "https://ftmscan.com/", + avalanche: "https://snowtrace.io/", + gnosis: "https://gnosisscan.io/", + moonriver: "https://moonriver.moonscan.io/", + moonbeam: "https://moonbeam.moonscan.io/", + celo: "https://celoscan.io/", + auroraMain: "https://explorer.mainnet.aurora.dev/", + harmonyMain: "https://explorer.harmony.one/", + fuse: "https://explorer.fuse.io/", + cronosMain: "https://cronoscan.com/", + evmosMain: "https://www.mintscan.io/evmos/", + bobaMain: "https://bobascan.com/", + cantoMain: "https://tuber.build/", + baseMain: "https://basescan.org/", + mantleMain: "https://mantlescan.xyz/", + filecoinMain: "https://filfox.info/en/", + scrollMain: "https://scrollscan.com/", + lineaMain: "https://lineascan.build/", + zoraMain: "https://explorer.zora.energy/", + luksoMain: "https://explorer.execution.mainnet.lukso.network/", + mantaMain: "https://pacific-explorer.manta.network/", + blastMain: "https://blastscan.io/", + dosMain: "https://doscan.io/", + fraxtalMain: "https://fraxscan.com/", + enduranceMain: "https://explorer-endurance.fusionist.io/", + kavaMain: "https://kavascan.com/", + metisMain: "https://andromeda-explorer.metis.io/", + modeMain: "https://explorer.mode.network/", + xlayerMain: "https://www.oklink.com/xlayer/", + bobMain: "https://explorer.gobob.xyz/", + coreMain: "https://scan.coredao.org/", + telosMain: "https://www.teloscan.io/", + rootstockMain: "https://rootstock.blockscout.com/", + chilizMain: "https://chiliscan.com/", + taraxaMain: "https://mainnet.explorer.taraxa.io/", + gravityAlphaMain: "https://explorer.gravity.xyz/", + taikoMain: "https://taikoscan.io/", + zetaChainMain: "https://explorer.zetachain.com/", +} as const; -// List of block explorers, in the same order as `networks` -export const explorers = [ - "N/A", - "N/A", - "https://sepolia.etherscan.io/", - "https://holesky.etherscan.io/", - "https://testnet.bscscan.com/", - "https://sepolia-optimism.etherscan.io/", - "https://sepolia.arbiscan.io/", - "https://amoy.polygonscan.com/", - "https://cardona-zkevm.polygonscan.com/", - "https://testnet.ftmscan.com/", - "https://testnet.snowtrace.io/", - "https://gnosis-chiado.blockscout.com/", - "https://moonbase.moonscan.io/", - "https://alfajores.celoscan.io/", - "https://explorer.testnet.aurora.dev/", - "https://explorer.testnet.harmony.one/", - "https://explorer.fusespark.io/", - "https://cronos.org/explorer/testnet3/", - "https://www.mintscan.io/evmos-testnet/", - "https://testnet.bobascan.com/", - "https://testnet.tuber.build/", - "https://sepolia.basescan.org/", - "https://sepolia.mantlescan.xyz/", - "https://calibration.filfox.info/en/", - "https://sepolia.scrollscan.com/", - "https://sepolia.lineascan.build/", - "https://sepolia.explorer.zora.energy/", - "https://explorer.execution.testnet.lukso.network/", - "https://pacific-explorer.sepolia-testnet.manta.network/", - "https://sepolia.blastscan.io/", - "https://test.doscan.io/", - "https://holesky.fraxscan.com/", - "https://sepolia-explorer.metisdevops.link/", - "https://sepolia.explorer.mode.network/", - "https://seistream.app/", - "https://www.oklink.com/xlayer-test/", - "https://testnet-explorer.gobob.xyz/", - "https://scan.test.btcs.network/", - "https://testnet.teloscan.io/", - "https://rootstock-testnet.blockscout.com/", - "https://testnet.chiliscan.com/", - "https://testnet.explorer.taraxa.io/", - "https://hekla.taikoscan.io/", - "https://athens.explorer.zetachain.com/", - "https://etherscan.io/", - "https://bscscan.com/", - "https://optimistic.etherscan.io/", - "https://arbiscan.io/", - "https://nova.arbiscan.io/", - "https://polygonscan.com/", - "https://zkevm.polygonscan.com/", - "https://ftmscan.com/", - "https://snowtrace.io/", - "https://gnosisscan.io/", - "https://moonriver.moonscan.io/", - "https://moonbeam.moonscan.io/", - "https://celoscan.io/", - "https://explorer.mainnet.aurora.dev/", - "https://explorer.harmony.one/", - "https://explorer.fuse.io/", - "https://cronoscan.com/", - "https://www.mintscan.io/evmos/", - "https://bobascan.com/", - "https://tuber.build/", - "https://basescan.org/", - "https://mantlescan.xyz/", - "https://filfox.info/en/", - "https://scrollscan.com/", - "https://lineascan.build/", - "https://explorer.zora.energy/", - "https://explorer.execution.mainnet.lukso.network/", - "https://pacific-explorer.manta.network/", - "https://blastscan.io/", - "https://doscan.io/", - "https://fraxscan.com/", - "https://explorer-endurance.fusionist.io/", - "https://kavascan.com/", - "https://andromeda-explorer.metis.io/", - "https://explorer.mode.network/", - "https://www.oklink.com/xlayer/", - "https://explorer.gobob.xyz/", - "https://scan.coredao.org/", - "https://www.teloscan.io/", - "https://rootstock.blockscout.com/", - "https://chiliscan.com/", - "https://mainnet.explorer.taraxa.io/", - "https://explorer.gravity.xyz/", - "https://taikoscan.io/", - "https://explorer.zetachain.com/", -]; +// Define a type `SupportedNetwork` that represents the union of all possible network names +// from the `networksInfo` object. This type ensures that any value assigned to a variable +// of type `SupportedNetwork` is a valid network key in `networksInfo`. It provides +// compile-time safety by preventing the use of unsupported or misspelled network names, +// allowing us to catch potential errors early and ensure consistency when working with +// network-specific functions or data +export type SupportedNetwork = keyof typeof networksInfo; +export const networks = Object.keys(networksInfo); +export const explorers = Object.values(networksInfo); + +// Generate the transaction hash link +export const getTxHashLink = (network: SupportedNetwork, hash: string) => + `${networksInfo[network]}${network.startsWith("filecoin") ? "message" : network === "seiArcticTestnet" ? "transactions" : "tx"}/${hash}`; + +// Generate the contract address link +export const getAddressLink = (network: SupportedNetwork, address: string) => + `${networksInfo[network]}${network === "seiArcticTestnet" ? "account" : "address"}/${address}`; diff --git a/src/types.ts b/src/types.ts index 04bead0..3566f40 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,10 +1,12 @@ +import { SupportedNetwork } from "./networks"; + /* eslint-disable @typescript-eslint/no-explicit-any */ export interface XdeployConfig { contract?: string; constructorArgsPath?: string; salt?: string; signer?: any; - networks?: string[]; + networks?: SupportedNetwork[]; rpcUrls?: any[]; gasLimit?: number; } diff --git a/test/xdeploy_sepolia_holesky_with_constructor.test.ts b/test/xdeploy_sepolia_holesky_with_constructor.test.ts index 2fd461f..b3b1bc5 100644 --- a/test/xdeploy_sepolia_holesky_with_constructor.test.ts +++ b/test/xdeploy_sepolia_holesky_with_constructor.test.ts @@ -1,6 +1,7 @@ import { useEnvironment } from "./helpers"; import { assert, expect } from "chai"; import { NomicLabsHardhatPluginError } from "hardhat/plugins"; +import { SupportedNetwork } from "./networks"; describe("Plugin test xdeploy on Sepolia and Holešky with constructor", function () { describe("Hardhat Runtime Environment (HRE) extension", function () { @@ -50,7 +51,10 @@ describe("Plugin test xdeploy on Sepolia and Holešky with constructor", functio }); it("should fail due to unsupported network argument", async function () { - this.hre.config.xdeploy.networks = ["hardhat", "WAGMI"]; + this.hre.config.xdeploy.networks = [ + "hardhat", + "WAGMI", + ] as SupportedNetwork[]; return this.hre .run("xdeploy") .then(() => { diff --git a/test/xdeploy_with_constructor.test.ts b/test/xdeploy_with_constructor.test.ts index a987926..dc81680 100644 --- a/test/xdeploy_with_constructor.test.ts +++ b/test/xdeploy_with_constructor.test.ts @@ -1,6 +1,7 @@ import { useEnvironment } from "./helpers"; import { assert, expect } from "chai"; import { NomicLabsHardhatPluginError } from "hardhat/plugins"; +import { SupportedNetwork } from "./networks"; describe("Plugin test xdeploy on Hardhat with constructor", function () { describe("Hardhat Runtime Environment (HRE) extension", function () { @@ -50,7 +51,10 @@ describe("Plugin test xdeploy on Hardhat with constructor", function () { }); it("should fail due to unsupported network argument", async function () { - this.hre.config.xdeploy.networks = ["hardhat", "WAGMI"]; + this.hre.config.xdeploy.networks = [ + "hardhat", + "WAGMI", + ] as SupportedNetwork[]; return this.hre .run("xdeploy") .then(() => { diff --git a/test/xdeploy_without_constructor.test.ts b/test/xdeploy_without_constructor.test.ts index 46723af..b838a72 100644 --- a/test/xdeploy_without_constructor.test.ts +++ b/test/xdeploy_without_constructor.test.ts @@ -1,6 +1,7 @@ import { useEnvironment } from "./helpers"; import { assert, expect } from "chai"; import { NomicLabsHardhatPluginError } from "hardhat/plugins"; +import { SupportedNetwork } from "./networks"; describe("Plugin test xdeploy on Hardhat without constructor", function () { describe("Hardhat Runtime Environment (HRE) extension", function () { @@ -50,7 +51,10 @@ describe("Plugin test xdeploy on Hardhat without constructor", function () { }); it("should fail due to unsupported network argument", async function () { - this.hre.config.xdeploy.networks = ["hardhat", "WAGMI"]; + this.hre.config.xdeploy.networks = [ + "hardhat", + "WAGMI", + ] as SupportedNetwork[]; return this.hre .run("xdeploy") .then(() => {