פיתוח מערכת פריסה אוטומטית לרשתות מרובות
הבעיה מתעוררת בפריסה השלישית או הרביעית של אותו פרוטוקול לרשתות שונות: מישהו פרס ב-Arbitrum עם ערך קבוע שונה, מישהו השתמש בגרסה שונה של OpenZeppelin ב-Base, כתובות פרוקסי לא נשמרו כראוי, ועכשיו לא ברור מה נפרס ואיפה. הצוות שלנו פותר זאת עם מערכת פריסה אוטומטית רב-רשתית שמבטיחה שחזוריות ומעקב אחר פריסות. אנו מפתחים מערכות כאלה במפתח מלא — צרו קשר להערכה מקדימה של הפרויקט.
למה CREATE2 הוא התקן לפריסה רב-רשתית
הדרישה העיקרית לפריסה רב-רשתית: כתובות זהות בכל הרשתות. זה מפשט את חוויית המשתמש, התיעוד והאינטגרציות בין-רשתיות. CREATE2 מאפשר לחשב את כתובת החוזה לפני הפריסה:
// Формула CREATE2 address = keccak256(0xff ++ deployerAddress ++ salt ++ keccak256(bytecode))[12:] אם לפרוס יש כתובת זהה בכל רשתות ה-EVM (באמצעות Nick's Factory או פרוס מותאם אישית דרך deterministicDeploy), ה-bytecode זהה, ה-salt זהה — הכתובת תהיה זהה בכל מקום. Foundry תומך בזה באופן טבעי, ואנו משתמשים בזה באופן פעיל בפרויקטים. מידע נוסף על CREATE2 ניתן לקרוא ב-EIP-1014.
השוואת שיטות: CREATE לעומת CREATE2
| מאפיין | CREATE | CREATE2 |
|---|---|---|
| כתובת החוזה | תלויה ב-nonce של הפרוס | תלויה ב-salt וב-bytecode |
| דטרמיניזם | לא (שינוי ב-nonce משנה את הכתובת) | כן, אם ה-salt וה-bytecode קבועים |
| יכולת לחזות כתובת | רק לאחר ה-nonce | לפני הפריסה |
| שימוש ברב-רשתיות | הכתובות שונות | אידיאלי |
| עלויות גז | CREATE רגיל (כ-32000 גז) | CREATE2 (כ-32000 + תוספת עבור salt) |
מערכת הגדרת רשתות
מקור אמת יחיד לכל הרשתות נשמר ב-// Формула CREATE2 address = keccak256(0xff ++ deployerAddress ++ salt ++ keccak256(bytecode))[12:] . קובץ מרכזי זה מכיל כתובות RPC, כתובות פרוסים, הגדרות גז וכתובות של חוזי תלות (USDC, WETH). כך, פריסה לרשת חדשה מתווספת עם רשומה אחת.
export interface NetworkConfig {
chainId: number;
rpcUrl: string;
deployer: string;
gasPrice?: bigint;
confirmations: number;
verifier?: "etherscan" | "blockscout" | "none";
verifierUrl?: string;
nativeCurrency: string;
contracts: {
usdc?: string;
weth?: string;
uniswapRouter?: string;
};
}
export const networks: Record<string, NetworkConfig> = {
arbitrum: {
chainId: 42161,
rpcUrl: process.env.ARBITRUM_RPC!,
deployer: DEPLOYER_ADDRESS,
confirmations: 1,
verifier: "etherscan",
nativeCurrency: "ETH",
contracts: {
usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
weth: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
},
},
base: {
chainId: 8453,
rpcUrl: process.env.BASE_RPC!,
deployer: DEPLOYER_ADDRESS,
confirmations: 1,
verifier: "blockscout",
verifierUrl: "https://base.blockscout.com/api",
nativeCurrency: "ETH",
contracts: {
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
weth: "0x4200000000000000000000000000000000000006",
},
},
}; ניהול Artifacts ומצב
לאחר כל פריסה, אנו שומרים כתובות ב-deploy.config.ts. קובץ זה נשמר במאגר ומשמש כמקור אמת יחיד. CI/CD מעדכן אותו אוטומטית.
סקריפט פריסה עם ניסיון חוזר ואימות
import { createPublicClient, createWalletClient, http } from "viem";
async function deployWithRetry(
network: NetworkConfig,
contractName: string,
deployFn: () => Promise<`0x${string}`>,
maxRetries = 3
): Promise<`0x${string}`> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const address = await deployFn();
const client = createPublicClient({ transport: http(network.rpcUrl) });
await client.waitForTransactionReceipt({
hash: address,
confirmations: network.confirmations,
});
console.log(`✓ ${contractName} on ${network.chainId}: ${address}`);
return address;
} catch (err) {
if (attempt === maxRetries - 1) throw err;
console.log(`Retry ${attempt + 1}/${maxRetries}: ${err.message}`);
await sleep(2000 * (attempt + 1));
}
}
throw new Error("unreachable");
}אימות החוזה מתבצע מיד לאחר הפריסה דרך Etherscan או Blockscout. זהו שלב חובה לאמון המשתמשים.
צינור CI/CD
פריסה אוטומטית בעת תיוג גרסה באמצעות GitHub Actions. העבודה רצה ברצף עבור כל רשת כדי למנוע התנגשויות בעדכון export interface NetworkConfig { chainId: number; rpcUrl: string; deployer: string; gasPrice?: bigint; confirmations: number; verifier?: "etherscan" | "blockscout" | "none"; verifierUrl?: string; nativeCurrency: string; contracts: { usdc?: string; weth?: string; uniswapRouter?: string; }; } export const networks: Record<string, NetworkConfig> = { arbitrum: { chainId: 42161, rpcUrl: process.env.ARBITRUM_RPC!, deployer: DEPLOYER_ADDRESS, confirmations: 1, verifier: "etherscan", nativeCurrency: "ETH", contracts: { usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", weth: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", }, }, base: { chainId: 8453, rpcUrl: process.env.BASE_RPC!, deployer: DEPLOYER_ADDRESS, confirmations: 1, verifier: "blockscout", verifierUrl: "https://base.blockscout.com/api", nativeCurrency: "ETH", contracts: { usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", weth: "0x4200000000000000000000000000000000000006", }, }, }; .
---
name: Deploy Protocol
on:
push:
tags:
- "v*"
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
network: [arbitrum, base, optimism]
max-parallel: 1
steps:
- uses: actions/checkout@v4
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
- name: Run tests
run: forge test --fork-url ${{ secrets.MAINNET_RPC }}
- name: Deploy to ${{ matrix.network }}
env:
DEPLOYER_PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }}
RPC_URL: ${{ secrets[format('{0}_RPC', matrix.network)] }}
run: |
forge script script/Deploy.s.sol \
--rpc-url $RPC_URL \
--private-key $DEPLOYER_PRIVATE_KEY \
--broadcast \
--verify
- name: Update deployments.json
run: node scripts/update-deployments.js ${{ matrix.network }}
- name: Commit deployments
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore: update deployments for ${{ matrix.network }} @ ${{ github.ref_name }}"
file_pattern: deployments.json
---
השוואה: פריסה ידנית לעומת אוטומטית
| פרמטר | פריסה ידנית | פריסה אוטומטית |
|---|---|---|
| זמן ל-5 רשתות | 2–3 ימים | שעה אחת |
| הסתברות לשגיאה | גבוהה (קבועים, כתובות) | מינימלית (CI/CD) |
| אימות | באופן אישי | אוטומטית |
| מעקב אחר כתובות | הערות מפוזרות | קובץ deployments.json יחיד |
אוטומציה מאיצה את הפריסה פי 20 בהשוואה לתהליך ידני. העלויות מופחתות ב-70%. זמן השחרור יורד מימים לשעות. לצוות שלנו יש ניסיון של 5+ שנים בחוזים חכמים והוא יישם מערכות כאלה עבור פרוטוקולי DeFi מובילים.
מה כלול בפיתוח המערכת?
- עיצוב ארכיטקטורת פריסה (CREATE2, הגדרות)
- כתיבת סקריפטים לפריסה עם ניסיון חוזר ורישום
- אינטגרציית CI/CD (GitHub Actions / GitLab CI)
- הגדרת אימות עבור Etherscan, Blockscout
- יצירה ותמיכה ב-
deployments.json - תיעוד והדרכת צוות
- אופציונלי: ניטור חוזים דרך Tenderly
איך אנחנו עובדים
- ביקורת (1-2 ימים). בחינת מספר הרשתות, הפרוסים, אחסון המפתחות.
- עיצוב (2-3 ימים). בחירת מחסנית: Foundry או Hardhat. שרטוט סכמת CI/CD.
- פיתוח (5-10 ימים). כתיבת סקריפטים לפריסה. הגדרת לוגיקת ניסיון חוזר. יצירת הגדרות רשת.
- בדיקות (2-3 ימים). פריסה ל-testnets. בדיקת כתובות ואימות.
- השקה (יום אחד). פריסה ל-mainnet. חיבור ניטור. מסירת תיעוד.
טעויות אופייניות בפריסה רב-רשתית
- אחסון מפתח פרטי של הפרוס ב-CI כ-hex פשוט — סיכון לפגיעה. השתמשו ב-AWS KMS או בארנק חומרה.
- הבדלים ב-bytecode בין רשתות עקב chainId מקודד — כתובות CREATE2 יהיו שונות. הוציאו את ה-chainId להגדרות זמן ריצה.
- חוסר בקובץ הגדרות יחיד — מוביל לבלבול בכתובות.
לוחות זמנים ועלות
פיתוח מערכת לרשתות EVM אורך 1 עד 2 שבועות. מערכת בסיסית (3-5 רשתות, ללא ניטור) — מ-3,000 דולר. חבילה מלאה (10+ רשתות, CI/CD, ניטור דרך Tenderly) — מ-8,000 דולר. הוספת רשתות שאינן EVM (Solana, TON) דורשת ערכת כלים נפרדת ונדונה בנפרד. העלות מחושבת עבור הפרויקט שלכם — קבלו ייעוץ.
שאלות נפוצות
האם יש צורך בארנק פרוס נפרד? כן. אנו יוצרים פרוס ייעודי עם הרשאות מינימליות. המפתחות נשמרים ב-AWS KMS או ב-HashiCorp Vault. זה אמין ובטוח יותר מאשר hex פשוט ב-CI.
אם מתרחש כשל באמצע הפריסה — הסקריפט שומר את המצב לאחר כל רשת. הפעלה מחדש ממשיכה מהנקודה האחרונה. הנתונים לא אובדים.
מוכנים לפשט את פריסת הפרוטוקול שלכם? הזמינו פיתוח של מערכת פריסה אוטומטית — אנו נעריך את הפרויקט. צרו קשר לדיון בפרטים.







