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. צור קשר כדי להעריך את הפרויקט שלך ולקבל ייעוץ להקמת צינור ריבוי רשתות.







