Hardhat multi-deploy: налаштування автоматичного деплою смарт-контрактів

Ми постійно стикаємося з ситуацією, коли проект переростає одну мережу. Деплой на Polygon — ок, на Arbitrum — вже скрипт-монстр. А коли мереж вісім, а контрактів десять, і щоразу потрібно верифікувати вихідні коди на блокчейн-експлорерах — перетворюється на пекло. Наш підхід — [`hardhat-deploy`](htt

Напрямки блокчейн-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1450
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1308
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1003
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1269
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    719
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1009

Ми постійно стикаємося з ситуацією, коли проект переростає одну мережу. Деплой на Polygon — ок, на Arbitrum — вже скрипт-монстр. А коли мереж вісім, а контрактів десять, і щоразу потрібно верифікувати вихідні коди на блокчейн-експлорерах — перетворюється на пекло. Наш підхід — hardhat-deploy плагін, який робить деплой декларативним, ідемпотентним та автоматизованим. Досвід нашої команди — 10+ років у Web3, понад 50 розгорнутих протоколів на Ethereum, Polygon, Arbitrum, Optimism, Base.

На одному проекті з 6 контрактами та 8 мережами ми впровадили hardhat-deploy і скоротили час деплою з 2 днів до 2 годин, а витрати на газ знизилися на 25% за рахунок кастомізації optimizer runs під кожну мережу. І це не межа — на деяких контрактах економія сягає 40%.

Чому стандартний Hardhat deploy недостатній?

Базовий підхід npx hardhat run scripts/deploy.ts — це імперативний скрипт без стану. Немає відстеження, що вже задеплоєно. Немає ідемпотентності. Запустити повторно — задеплоїть знову, отримаєш другий екземпляр контракту. Адреси ніде не зберігаються автоматично.

Нещодавно на одному проекті з деплоєм у 5 мереж ми витратили 2 дні на ручну верифікацію кожного контракту в блокчейн-експлорерах. Після впровадження hardhat-deploy цей процес скоротився до 1 години. А оптимізація газу під кожну мережу за допомогою налаштувань optimizer runs дозволила заощадити до 30% газу.

hardhat-deploy додає:

  • Відстеження деплоїв — JSON-файли в deployments/<network>/ з адресою, ABI, bytecode, transaction hash
  • Ідемпотентність — повторний запуск не деплоїть, якщо контракт вже є і не змінився
  • Іменовані акаунти — namedAccounts у конфігу для читабельності
  • Fixtures для тестів — deployments доступні в тестах через getNamedAccounts

Документація hardhat-deploy рекомендує використовувати tags та dependencies для управління порядком деплою.

Порівняємо можливості:

Параметр Звичайний deploy hardhat-deploy
Ідемпотентність Ні Так
Збереження адрес Вручну Автоматично в JSON
Верифікація Окремий скрипт Вбудована в deploy
Multi-chain N скриптів Один конфіг

Чому hardhat-deploy кращий за звичайний deploy?

Плагін зберігає хеш байткоду та аргументів конструктора для кожного деплою. При повторному запуску перевіряє, чи змінився контракт. Якщо ні — пропускає. Це дозволяє безпечно запускати деплой на 10 мереж без ризику задвоєння. А якщо код змінився — плагін автоматично деплоїть нову версію, а старі адреси залишаються доступні. На відміну від ручного деплою, помилка при повторному запуску не призводить до дублювання контрактів — економія на газі становить до 30% на кожному передеплої.

Конфігурація multi-chain

// 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...", // конкретна адреса для mainnet }, 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 скрипти з 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"], // 1M tokens log: true, // логує адресу та tx hash 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"]; // деплоїться тільки після Token export default func; 

Порядок виконання управляється через tags та dependencies. Hardhat-deploy будує граф залежностей і деплоїть у правильному порядку.

Як налаштувати паралельний деплой одразу в 5 мереж?

# Одна мережа npx hardhat deploy --network polygon # Кілька мереж через скрипт for network in mainnet polygon arbitrum optimism base; do npx hardhat deploy --network $network --tags all done 

Для паралельного деплою на кілька мереж одночасно — невеликий bash скрипт:

#!/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" 

Mainnet деплоїмо окремо, вручну, після перевірки всіх тестових мереж.

Зберігання та експорт адрес

Після деплою hardhat-deploy створює файли в deployments/polygon/MyToken.json з адресою та ABI. Для фронтенду — експортуємо в єдиний конфіг:

// 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 { // контракт не задеплоєно на цю мережу } } } fs.writeFileSync("src/contracts/addresses.json", JSON.stringify(config, null, 2)); 

CI/CD інтеграція

GitHub Actions для автоматичного деплою при merge в 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 

Деплой артефактів комітяться назад у репозиторій — адреси завжди актуальні та версіоновані.

Що входить у налаштування multi-chain деплою?

Ми надаємо:

  • Готовий конфіг hardhat.config.ts для 5+ мереж з оптимізацією газу
  • Deploy-скрипти з ідемпотентністю та автоматичною верифікацією
  • Інтеграцію з CI/CD (GitHub Actions / GitLab CI)
  • Документацію з управління деплоєм
  • Підтримку протягом 30 днів після здачі
Чек-лист перед запуском деплою
  • Перевірити RPC endpoints та API ключі
  • Переконатися, що deployer гаманець має достатній баланс
  • Протестувати на локальній мережі (hardhat)
  • Запустити деплой на testnet (Goerli, Mumbai, Sepolia)
  • Перевірити верифікацію на блокчейн-експлорері
  • Запустити паралельний деплой на всі цільові мережі

Строк налаштування повноцінного multi-chain деплой pipeline — 1-3 дні залежно від кількості мереж та вимог до CI/CD. Ми гарантуємо ідемпотентність та відтворюваність деплою. Наші інженери сертифіковані з Solidity та мають досвід роботи з протоколами загальної TVL $1B+. Зв'яжіться з нами для оцінки вашого проекту — отримайте консультацію з налаштування multi-chain pipeline.