Розробка системи автоматичного деплою на кілька мереж

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка системи автоматичного деплою на кілька мереж
Середній
~3-5 днів
Часті запитання

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

Етапи блокчейн-розробки

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

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

Розробка системи автоматичного деплоя на несколько сетей

Проблема виникає на третьому чи четвертому деплоі одного й того ж протоколу в різні сети: хтось задеплоив на Arbitrum з іншим значенням константи, на Base використовувалася інша версія OpenZeppelin, адреси прокси не збереглися нормально, і тепер невизначено що де задеплоено. Multichain auto-deploy вирішує саме це — відтворюваність та трекінг деплоев.

Детерміновані адреси через CREATE2

Головна вимога до мультичейн деплоя: одинакові адреси на всіх сетях. Це спрощує користувацький досвід, документацію та кросс-чейн інтеграції.

CREATE2 дозволяє обчислити адресу контракту до деплоя:

address = keccak256(0xff ++ deployerAddress ++ salt ++ keccak256(bytecode))[12:]

Якщо deployer має одинаковий адресу на всіх EVM-сетях (через Nick's Factory або власний deployer через deterministicDeploy), байткод одинаків, salt одинаків — адреса буде одинакова скрізь.

Foundry підтримує це нативно:

// deploy script
function run() external {
    bytes32 salt = keccak256("MyProtocol_v1.0.0");
    
    vm.broadcast();
    address proxy = factory.deployProxy(
        address(implementation),
        salt,
        abi.encodeCall(MyContract.initialize, (owner, params))
    );
    
    console.log("Deployed at:", proxy);
    console.log("Chain:", block.chainid);
}
# Деплой на несколько сетей
forge script script/Deploy.s.sol --rpc-url arbitrum --broadcast
forge script script/Deploy.s.sol --rpc-url base --broadcast
forge script script/Deploy.s.sol --rpc-url optimism --broadcast

Проблема: якщо байткод відрізняється між сетями (наприклад, hardcoded chainId або address), CREATE2-адреса буде іншою. Потрібно уникати compile-time константних адрес у байткоді.

Структура деплой-системи

Конфігурація сетей

Єдиний джерело істини для всіх сетей в deploy.config.ts:

export interface NetworkConfig {
  chainId: number;
  rpcUrl: string;
  deployer: string;         // адрес деплоера
  gasPrice?: bigint;        // override для сетей з нестабільним gas
  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 та управління станом

Після кожного деплоя потрібно зберегти адреси контрактів. Foundry зберігає в broadcast/ директорію, але це неудобно для multichain трекінгу.

Кращий підхід — генерувати deployments.json:

interface DeploymentRecord {
  network: string;
  chainId: number;
  contracts: Record<string, {
    address: string;
    implementationAddress?: string;
    abi: string;            // IPFS хеш або шлях
    deployedAt: number;     // block number
    txHash: string;
    version: string;        // semver
  }>;
  deployedBy: string;
  timestamp: string;
}

Цей файл коммітиться в репозиторій — єдиний джерело істини про адреси. CI/CD обновляє його після кожного деплоя.

Деплой скрипт з retry та verification

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");
}

Автоматична верифікація контрактів

Після деплоя контракти потрібно верифікувати на block explorer — обов'язково для протоколів, які хочуть довіри користувачів.

Foundry:

forge verify-contract \
  --chain-id 42161 \
  --num-of-optimizations 200 \
  --compiler-version v0.8.24 \
  $CONTRACT_ADDRESS \
  src/MyContract.sol:MyContract \
  --etherscan-api-key $ARBITRUM_ETHERSCAN_KEY

Для Blockscout (Base, деякі L2):

forge verify-contract \
  --verifier blockscout \
  --verifier-url https://base.blockscout.com/api \
  $CONTRACT_ADDRESS \
  src/MyContract.sol:MyContract

Автоматизація в скрипті: верифікувати одразу після деплоя, не чекати окремого кроку. Зберегти статус верифікації в deployments.json.

CI/CD pipeline

GitHub Actions workflow

name: Deploy Protocol

on:
  push:
    tags:
      - "v*"  # деплой при тегіруванні релізу

jobs:
  deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        network: [arbitrum, base, optimism]
      max-parallel: 1  # послідовно, щоб не було race conditions у deployments.json
    
    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

Управління приватними ключами в CI

Ніколи не зберігайте приватний ключ деплоера в CI secrets як hex-строку без додаткового захисту. Краще:

  • AWS KMS + custom signer: ключ в KMS, signing через API. Утечка CI secrets не компрометує ключ.
  • Hardware wallet через Frame + WalletConnect: деплой з ручним підтвердженням для mainnet.
  • Окремий деплой-гаманець з мінімальним балансом (лише на газ), не пов'язаний з treasury.

Мультичейн адресація в протоколі

Якщо контракти взаємодіють між сетями (cross-chain вызови), потрібно управляти адресами peer-контрактів:

contract CrossChainRegistry {
    mapping(uint256 => address) public peers; // chainId => peer address
    
    function setPeer(uint256 chainId, address peer) external onlyOwner {
        peers[chainId] = peer;
        emit PeerSet(chainId, peer);
    }
    
    function _validateCrossChainSender(uint256 srcChainId, address sender) internal view {
        require(peers[srcChainId] != address(0), "Unknown chain");
        require(peers[srcChainId] == sender, "Invalid peer");
    }
}

Якщо використовується LayerZero або Wormhole — вони надають власні registry механізми, але базова логіка та ж.

Моніторинг задеплоєних контрактів

Після деплоя на 5+ сетей потрібно мониторити всі інстанси:

// Агрегований моніторинг через события
const deployments = require("./deployments.json");

for (const [network, data] of Object.entries(deployments)) {
  const client = createPublicClient({ transport: http(data.rpcUrl) });
  
  // Слідимо за критичними событіями на всіх сетях
  client.watchContractEvent({
    address: data.contracts.core.address,
    abi: CORE_ABI,
    eventName: "EmergencyPause",
    onLogs: (logs) => alertOps(`PAUSE on ${network}`, logs),
  });
}

Стек та інструменти

Компонент Інструмент
Compile & test Foundry (forge, cast, anvil)
Deploy scripts TypeScript + viem або Foundry scripts
State tracking deployments.json у git
CI/CD GitHub Actions / GitLab CI
Key management AWS KMS або Ledger для mainnet
Verification Etherscan API / Blockscout API
Monitoring Tenderly + власні алерти

Розробка системи деплоя: 1–2 тижні для EVM-сумісних сетей. Додавання non-EVM сетей (Solana, TON) потребує окремого toolchain та значно складніше.