Hardhat Multi-Deploy: Automate Smart Contract Deployment

Hardhat Multi-Deploy: Automate Smart Contract Deployment Across 10+ Networks We frequently see projects outgrow a single network. Deploying on Polygon is fine, on Arbitrum it becomes a script-monster. With eight networks and ten contracts, each requiring source verification on block explorers, it

Blockchain Development Services

Frequently Asked Questions

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1450
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1308
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    1003
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1269
  • image_logo-advance_0.webp
    B2B Advance company logo design
    719
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    1009

Hardhat Multi-Deploy: Automate Smart Contract Deployment Across 10+ Networks

We frequently see projects outgrow a single network. Deploying on Polygon is fine, on Arbitrum it becomes a script-monster. With eight networks and ten contracts, each requiring source verification on block explorers, it turns into a nightmare. Our approach uses the hardhat-deploy plugin, making deployment declarative, idempotent, and automated. Our team has 10+ years of Web3 experience and has deployed 50+ protocols on Ethereum, Polygon, Arbitrum, Optimism, and Base.

On one project with 6 contracts and 8 networks, we implemented hardhat-deploy and cut deployment time from 2 days to 2 hours, reducing gas costs by 25% through custom optimizer runs per network. Some contracts saw 40% savings.

Why Standard Hardhat Deploy Falls Short

The basic approach npx hardhat run scripts/deploy.ts is an imperative stateless script. It doesn't track what’s already deployed, offers no idempotency, and re-running creates a second contract instance. Addresses are not saved automatically.

Recently, on a project with 5 networks, we spent 2 days manually verifying each contract on block explorers. After implementing hardhat-deploy, that process took 1 hour. Gas optimization via per-network optimizer runs saved up to 30%.

hardhat-deploy adds:

  • Deployment tracking – JSON files in deployments/<network>/ with address, ABI, bytecode, transaction hash
  • Idempotency – re-run skips if contract exists and unchanged
  • Named accounts – namedAccounts config for readability
  • Fixtures for tests – deployments available in tests via getNamedAccounts

The hardhat-deploy documentation recommends using tags and dependencies to manage deployment order. Comparison:

Feature Standard Deploy hardhat-deploy
Idempotency No Yes
Address storage Manual Automatic JSON
Verification Separate script Built-in
Multi-chain Multiple scripts Single config

Why hardhat-deploy Beats Manual Scripts

The plugin stores the bytecode hash and constructor arguments for each deployment. On re-run, it checks if the contract changed; if not, it skips. This allows safe deployment to 10 networks without duplication risk. If the code changes, it auto-deploys the new version while keeping old addresses accessible. Error handling prevents duplicate contracts, saving up to 30% in gas on each re-deployment.

Multi-chain Configuration

// hardhat.config.ts import { HardhatUserConfig } from "hardhat/config"; import "@nomicfoundation/hardhat-toolbox"; import "hardhat-deploy"; const config: HardhatUserConfig = { solidity: { version: "0.8.24", settings: { optimizer: { enabled: true, runs: 200 }, viaIR: false, }, }, namedAccounts: { deployer: { default: 0, mainnet: "0x...", }, treasury: { default: 1, mainnet: "0x...", }, }, networks: { mainnet: { url: process.env.MAINNET_RPC, accounts: [process.env.DEPLOYER_KEY!], chainId: 1 }, polygon: { url: process.env.POLYGON_RPC, accounts: [process.env.DEPLOYER_KEY!], chainId: 137 }, arbitrum: { url: process.env.ARBITRUM_RPC, accounts: [process.env.DEPLOYER_KEY!], chainId: 42161 }, optimism: { url: process.env.OPTIMISM_RPC, accounts: [process.env.DEPLOYER_KEY!], chainId: 10 }, base: { url: process.env.BASE_RPC, accounts: [process.env.DEPLOYER_KEY!], chainId: 8453 }, }, etherscan: { apiKey: { mainnet: process.env.ETHERSCAN_KEY!, polygon: process.env.POLYGONSCAN_KEY!, arbitrumOne: process.env.ARBISCAN_KEY!, optimisticEthereum: process.env.OPTIMISM_KEY!, base: process.env.BASESCAN_KEY!, }, }, }; 

Deploy Scripts with hardhat-deploy

// deploy/001_deploy_token.ts import { HardhatRuntimeEnvironment } from "hardhat/types"; import { DeployFunction } from "hardhat-deploy/types"; const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { const { deployments, getNamedAccounts, network } = hre; const { deploy } = deployments; const { deployer, treasury } = await getNamedAccounts(); const token = await deploy("MyToken", { from: deployer, args: [treasury, "1000000000000000000000000"], log: true, autoMine: true, waitConfirmations: network.name === "mainnet" ? 5 : 1, }); if (network.name !== "hardhat" && network.name !== "localhost") { await hre.run("verify:verify", { address: token.address, constructorArguments: [treasury, "1000000000000000000000000"], }); } }; func.tags = ["Token", "all"]; func.dependencies = []; export default func; 
// deploy/002_deploy_staking.ts const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { const { deployments, getNamedAccounts } = hre; const { deploy, get } = deployments; const { deployer } = await getNamedAccounts(); const token = await get("MyToken"); await deploy("StakingContract", { from: deployer, args: [token.address], log: true, }); }; func.tags = ["Staking", "all"]; func.dependencies = ["Token"]; export default func; 

Execution order is managed via tags and dependencies. hardhat-deploy builds a dependency graph and deploys in the correct order.

Parallel Deployment to Five Networks?

# Single network npx hardhat deploy --network polygon # Multiple networks via script for network in mainnet polygon arbitrum optimism base; do npx hardhat deploy --network $network --tags all done 

For parallel deployment:

#!/bin/bash networks=("polygon" "arbitrum" "optimism" "base") pids=() for network in "${networks[@]}"; do npx hardhat deploy --network $network --tags all & pids+=($!) done for pid in "${pids[@]}"; do wait $pid || exit 1 done echo "All deployments complete" 

Deploy mainnet separately, manually, after verifying on all testnets.

Address Storage and Export

After deployment, hardhat-deploy creates files in deployments/polygon/MyToken.json with address and ABI. For the frontend, export to a unified config:

// scripts/export-addresses.ts import { deployments } from "hardhat"; const networks = ["mainnet", "polygon", "arbitrum", "optimism", "base"]; const contracts = ["MyToken", "StakingContract"]; const config: Record<string, Record<string, string>> = {}; for (const network of networks) { config[network] = {}; for (const contract of contracts) { try { const deployment = await deployments.get(contract); config[network][contract] = deployment.address; } catch { // contract not deployed on this network } } } fs.writeFileSync("src/contracts/addresses.json", JSON.stringify(config, null, 2)); 

CI/CD Integration

GitHub Actions for automatic deployment on merge to main:

# .github/workflows/deploy.yml name: Deploy Contracts on: push: branches: [main] paths: ["contracts/**", "deploy/**"] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "20" cache: "npm" - run: npm ci - name: Deploy to testnets env: DEPLOYER_KEY: ${{ secrets.DEPLOYER_KEY }} POLYGON_RPC: ${{ secrets.POLYGON_MUMBAI_RPC }} run: npx hardhat deploy --network polygonMumbai --tags all - name: Commit updated deployments run: | git config user.name "GitHub Actions" git config user.email "[email protected]" git add deployments/ git commit -m "chore: update deployment artifacts" || echo "No changes" git push 

Deployment artifacts are committed back to the repository — addresses are always up-to-date and versioned.

What’s Included in Multi-Chain Deployment Setup?

We provide:

  • Complete hardhat.config.ts for 5+ networks with gas optimization
  • Deploy scripts with idempotency and automatic verification
  • CI/CD integration (GitHub Actions / GitLab CI)
  • Documentation for deployment management
  • 30-day post-delivery support
Pre-Deployment Checklist
  • Verify RPC endpoints and API keys
  • Ensure deployer wallet has sufficient balance
  • Test on local network (hardhat)
  • Run deployment on testnets (Goerli, Mumbai, Sepolia)
  • Verify contracts on block explorer
  • Run parallel deployment to all target networks

Setup timeline for a full multi-chain deployment pipeline: 1–3 days, depending on the number of networks and CI/CD requirements. We guarantee idempotency and reproducible deployments. Our engineers are certified in Solidity and have worked with protocols totaling over $1B in TVL. Contact us to assess your project and get a consultation on setting up a multi-chain pipeline.