Hardhat Multi-Deploy: אוטומציה של פריסת חוזים חכמים

פריסה רב-רשתית של חוזים חכמים הופכת לעיתים קרובות לכאוס: הסקריפטים גדלים, כתובות אובדות, והאימות אורך ימים. אנו מגדירים את hardhat-deploy כדי להפוך את הפריסה לאוטומטית על פני עשרות רשתות—באופן אידמפוטנטי, תוך שמירה על כתובות ואימות קוד. הצוות שלנו מספק את הפרויקט במפתח מלא, ומבטיח פעילות יציבה ותמיכה שוטפת.

שירותי פיתוח בלוקצ'יין

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1481
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1335
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1034
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1293
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    738
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1031

Hardhat Multi-Deploy: אוטומציה של פריסת חוזים חכמים ביותר מ-10 רשתות

לעתים קרובות אנו רואים פרויקטים שגדלים מעבר לרשת אחת. פריסה על Polygon היא בסדר, על Arbitrum זה הופך ל"מפלצת סקריפטים". עם שמונה רשתות ועשרה חוזים, שכל אחד מהם דורש אימות קוד בסיירי בלוקצ'יין, זה הופך לסיוט. הגישה שלנו משתמשת בתוסף hardhat-deploy, שהופך את הפריסה להצהרתית, אידמפוטנטית ואוטומטית. לצוות שלנו יש ניסיון של 10+ שנים ב-Web3 והוא פרס 50+ פרוטוקולים על Ethereum, Polygon, Arbitrum, Optimism ו-Base.

בפרויקט אחד עם 6 חוזים ו-8 רשתות, יישמנו hardhat-deploy וקיצרנו את זמן הפריסה מיומיים לשעתיים, תוך הפחתת עלויות גז ב-25% באמצעות ריצות אופטימיזציה מותאמות לכל רשת. חלק מהחוזים זכו לחיסכון של 40%.

למה פריסת Hardhat סטנדרטית לא מספיקה

הגישה הבסיסית npx hardhat run scripts/deploy.ts היא סקריפט אימפרטיבי ללא מצב. היא לא עוקבת אחר מה שכבר נפרס, לא מציעה אידמפוטנטיות, והרצה חוזרת יוצרת מופע חוזה שני. כתובות לא נשמרות אוטומטית.

לאחרונה, בפרויקט עם 5 רשתות, בילינו יומיים באימות ידני של כל חוזה בסיירי בלוקצ'יין. לאחר יישום hardhat-deploy, התהליך ארך שעה. אופטימיזציית גז באמצעות hardhat-deploy לכל רשת חסכה עד 30%.

deployments/<network>/ מוסיף:

  • מעקב פריסה – קבצי JSON ב-namedAccounts עם כתובת, ABI, bytecode, והאש של הטרנזקציה
  • אידמפוטנטיות – הרצה חוזרת מדלגת אם החוזה קיים ולא השתנה
  • חשבונות בעלי שם – תצורת deployments לקריאות טובה יותר
  • Fixtures לבדיקות – getNamedAccounts זמין בבדיקות דרך // 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!, }, }, };

התיעוד של hardhat-deploy ממליץ להשתמש בתגיות ותלויות לניהול סדר הפריסה. השוואה:

תכונה פריסה סטנדרטית hardhat-deploy
אידמפוטנטיות לא כן
אחסון כתובות ידני JSON אוטומטי
אימות סקריפט נפרד מובנה
ריבוי רשתות סקריפטים מרובים תצורה אחת

למה hardhat-deploy עדיף על סקריפטים ידניים

התוסף מאחסן את ה-hash של ה-bytecode וארגומנטים של הקונסטרקטור לכל פריסה. בהרצה חוזרת, הוא בודק אם החוזה השתנה; אם לא, הוא מדלג. זה מאפשר פריסה בטוחה ל-10 רשתות ללא סיכון כפילות. אם הקוד משתנה, הוא פורס אוטומטית את הגרסה החדשה תוך שמירה על כתובות ישנות נגישות. טיפול בשגיאות מונע חוזים כפולים, וחוסך עד 30% בגז בכל פריסה מחדש.

תצורת ריבוי רשתות

// 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!,
    },
  },
};

סקריפטי פריסה עם 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;

סדר הביצוע מנוהל באמצעות // 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; . hardhat-deploy בונה גרף תלויות ופורס בסדר הנכון.

פריסה מקבילה לחמש רשתות?

# Одна сеть
npx hardhat deploy --network polygon

# Несколько сетей через скрипт
for network in mainnet polygon arbitrum optimism base; do
  npx hardhat deploy --network $network --tags all
done

לפריסה מקבילה:

#!/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 בנפרד, ידנית, לאחר אימות בכל רשתות הבדיקה.

אחסון כתובות וייצוא

לאחר הפריסה, tags יוצר קבצים ב-dependencies עם כתובת ו-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 לפריסה אוטומטית בעת מיזוג ל-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
---

ארטיפקטים של פריסה נדחפים חזרה לריפוזיטורי — כתובות תמיד מעודכנות ומבוקרות גרסאות.

מה כלול בהקמת פריסת ריבוי רשתות?

אנו מספקים:

  • # Одна сеть npx hardhat deploy --network polygon # Несколько сетей через скрипт for network in mainnet polygon arbitrum optimism base; do npx hardhat deploy --network $network --tags all done מלא ל-5+ רשתות עם אופטימיזציית גז
  • סקריפטי פריסה עם אידמפוטנטיות ואימות אוטומטי
  • אינטגרציית CI/CD (GitHub Actions / GitLab CI)
  • תיעוד לניהול פריסה
  • תמיכה ל-30 יום לאחר המסירה
רשימת בדיקה לפני פריסה
  • ודא נקודות קצה RPC ומפתחות API
  • ודא שלארנק הפורס יש יתרה מספקת
  • בדוק על רשת מקומית (hardhat)
  • הרץ פריסה על רשתות בדיקה (Goerli, Mumbai, Sepolia)
  • אמת חוזים בסייר הבלוקצ'יין
  • הרץ פריסה מקבילה לכל רשתות היעד

ציר זמן הקמה לצינור פריסת ריבוי רשתות מלא: 1–3 ימים, תלוי במספר הרשתות ובדרישות CI/CD. אנו מבטיחים אידמפוטנטיות ופריסות ניתנות לשחזור. המהנדסים שלנו מוסמכים ב-Solidity ועבדו עם פרוטוקולים בהיקף כולל של למעלה מ-$1B ב-TVL. צור קשר כדי להעריך את הפרויקט שלך ולקבל ייעוץ להקמת צינור ריבוי רשתות.