Розробка cross-chain мосту

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

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

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

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

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

Розробка кросс-чейн мосту

Кросс-чейн міст — це система, яка дозволяє переміщувати активи або дані між незалежними блокчейн-мережами. На перший погляд завдання просте: заблокувати токени на чейні A, випустити еквівалент на чейні B. На практиці — це один з найбільш технічно та з точки зору безпеки складних продуктів в Web3. Хаки мостів складають велику частину всіх втрат у крипто ($2B+ втрачено на Ronin, Wormhole, Nomad, Harmony).

Архітектурні паттерни

Lock-and-Mint (wrapped токени)

Класична схема:

  1. Користувач блокує ETH в Lock контракті на Ethereum
  2. Міст випускає wETH (wrapped ETH) на Polygon
  3. При зворотному переводі: спалити wETH на Polygon → розблокувати ETH на Ethereum

Ризики: весь залог зосереджений в одному контракті на Ethereum. Взлом = втрата всього заблокованого TVL. Wormhole втратив $320M саме так.

Burn-and-Mint (нативні токени)

Застосовується для токенів з можливістю кросс-чейн монетизації (USDC через Circle CCTP, USDT через Tether міст):

  1. Спалити USDC на Ethereum (Circle знищує забезпечення)
  2. Выпустити нативний USDC на Arbitrum (Circle видає нове забезпечення)

Перевага: немає заблокованого TVL = немає єдиної точки відмови. Але вимагає контролю над контрактом токена на всіх чейнах.

Liquidity Pool (liquidity network)

Використовується в Stargate, Hop Protocol:

  1. Пул ліквідності на кожному чейні
  2. Користувач вносить USDC на пул Ethereum → отримує USDC з пула Arbitrum
  3. LP-провайдери отримують комісії за надання ліквідності

Перевага: швидке виконання без очікування фіналізації. Ризик: дисбаланс пулів (більше виводів з однієї сторони, ніж внесків).

Native verification (light client мости)

Найбільш децентралізований варіант: смарт-контракт на чейні B верифікує заголовки блоків чейна A через light client. Доводить, що транзакція сталася без полаганння на валідаторів.

Приклади: ICS-23 (Cosmos IBC), Rainbow Bridge (NEAR → Ethereum). Складність: висока вартість газу для верифікації, особливо для PoW (Ethereum до merge).

Optimistic мости

Оптимістична верифікація: повідомлення приймаються як дійсні, але існує період (зазвичай 30 хв — 7 днів), протягом якого спостерігач може оспорити та заблокувати шахрайські транзакції. Використовується в Across Protocol, Connext (частково).

Компроміс: безпека vs швидкість. 7-денний період = повільно, але дуже безпечно.

Деталі реалізації

Lock контракт (Ethereum)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

contract BridgeLock is ReentrancyGuard, Pausable, AccessControl {
    bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
    bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
    
    // Ліміти TVL для захисту
    mapping(address => uint256) public tokenTVLLimits;
    mapping(address => uint256) public tokenCurrentTVL;
    
    // Щоденні ліміти
    mapping(address => uint256) public dailyBridgeLimit;
    mapping(address => uint256) public dailyBridgedAmount;
    mapping(address => uint256) public lastResetTimestamp;
    
    // Nonce для запобігання replay
    mapping(bytes32 => bool) public processedDeposits;
    
    event Deposit(
        bytes32 indexed depositId,
        address indexed sender,
        address indexed token,
        uint256 amount,
        uint256 destinationChainId,
        address recipient
    );
    
    function deposit(
        address token,
        uint256 amount,
        uint256 destinationChainId,
        address recipient
    ) external nonReentrant whenNotPaused returns (bytes32 depositId) {
        require(amount > 0, "Zero amount");
        require(tokenTVLLimits[token] > 0, "Token not supported");
        
        // Перевірка TVL
        require(
            tokenCurrentTVL[token] + amount <= tokenTVLLimits[token],
            "TVL limit exceeded"
        );
        
        // Перевірка щоденного ліміту
        _checkAndUpdateDailyLimit(token, amount);
        
        // Генеруємо унікальний ID депозиту
        depositId = keccak256(abi.encodePacked(
            msg.sender, token, amount, destinationChainId, recipient,
            block.chainid, block.number, block.timestamp
        ));
        
        require(!processedDeposits[depositId], "Duplicate deposit");
        processedDeposits[depositId] = true;
        
        // Переводимо токени
        IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
        tokenCurrentTVL[token] += amount;
        
        emit Deposit(depositId, msg.sender, token, amount, destinationChainId, recipient);
    }
    
    // Тільки RELAYER_ROLE може розблокувати
    function unlock(
        bytes32 depositId,
        address token,
        uint256 amount,
        address recipient,
        bytes calldata proof
    ) external onlyRole(RELAYER_ROLE) nonReentrant {
        // Верифікуємо proof (підписи валідаторів або merkle proof)
        require(_verifyProof(depositId, token, amount, recipient, proof), "Invalid proof");
        
        // Ідемпотентність
        require(!processedUnlocks[depositId], "Already unlocked");
        processedUnlocks[depositId] = true;
        
        tokenCurrentTVL[token] -= amount;
        IERC20(token).safeTransfer(recipient, amount);
        
        emit Unlock(depositId, recipient, token, amount);
    }
}

Mint контракт (цільовий чейн)

contract BridgeMint is ERC20, AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    
    // Маппінг originTxHash → вже оброблено
    mapping(bytes32 => bool) public mintedDeposits;
    
    function mint(
        bytes32 depositId,
        address recipient,
        uint256 amount,
        bytes calldata validatorSignatures
    ) external onlyRole(MINTER_ROLE) {
        require(!mintedDeposits[depositId], "Already minted");
        
        // Верифікуємо M-of-N підписи від валідаторів
        _verifyValidatorSignatures(depositId, recipient, amount, validatorSignatures);
        
        mintedDeposits[depositId] = true;
        _mint(recipient, amount);
        
        emit Minted(depositId, recipient, amount);
    }
    
    function burn(uint256 amount, uint256 targetChainId, address recipient) external {
        _burn(msg.sender, amount);
        emit Burned(msg.sender, amount, targetChainId, recipient);
    }
}

Валідатор / Relayer

Off-chain компонент, який моніторить події на вихідному чейні та ініціює mint/unlock на цільовому:

class BridgeRelayer {
  private validators: Signer[];
  private threshold: number;
  
  async watchSourceChain() {
    this.sourceBridge.on("Deposit", async (depositId, sender, token, amount, destChain, recipient, event) => {
      // Чекаємо достатньої кількості підтверджень
      await this.waitForConfirmations(event.blockNumber, REQUIRED_CONFIRMATIONS);
      
      // Кожен валідатор підписує дані депозиту
      const signatures = await this.collectValidatorSignatures(
        depositId, token, amount, destChain, recipient
      );
      
      if (signatures.length >= this.threshold) {
        await this.executeMint(destChain, depositId, recipient, amount, signatures);
      }
    });
  }
  
  private async collectValidatorSignatures(
    depositId: string,
    token: string,
    amount: bigint,
    destChain: number,
    recipient: string
  ): Promise<string[]> {
    const messageHash = ethers.solidityPackedKeccak256(
      ["bytes32", "address", "uint256", "uint256", "address"],
      [depositId, token, amount, destChain, recipient]
    );
    
    const signatures = await Promise.all(
      this.validators.map(v => v.signMessage(ethers.getBytes(messageHash)))
    );
    
    return signatures;
  }
}

Безпека — критичні моменти

Захист від replay атак. Одна і та ж підписана транзакція не повинна виконуватися двічі або на іншому чейні. Рішення: включати chainId і унікальний nonce в підписані дані.

Signature malleability. ECDSA підписи математично malleable — з однієї можна отримати іншу дійсну. OpenZeppelin ECDSA.recover вирішує це, але важливо його використовувати, а не raw ecrecover.

Reentrancy. Функції unlock/mint мають оновлювати стан ДО зовнішніх викликів (transfer). Паттерн checks-effects-interactions або модифікатор nonReentrant.

TVL caps. Обмеження загального TVL контракту знижує максимальний шкоду від хаку. Якщо ліміт $10M — максимальна втрата $10M, не $500M.

Timelock для оновлень. Зміни в bridge контракті повинні мати timelock (мінімум 48 годин). Це дає користувачам час вивести кошти, якщо зміна здається підозрілою.

Emergency pause. Guardian role може зупинити всі вхідні/вихідні передачі при виявленні аномалій. Автоматично через circuit breaker (аномально великий withdraw).

Моніторинг

Міст без моніторингу не є production. Мінімальний набір алертів:

  • Раптове зниження TVL (>10% за 5 хвилин)
  • Аномально великі транзакції (>1% від TVL)
  • Невідповідність між заблокованими та выпущеними (проверка інваріанта)
  • Downtime валідаторів (немає підписів від валідатора >N хвилин)

Вибір існуючого рішення vs власний міст

Будувати власний міст потрібно, якщо:

  • Кастомна токен-економіка (не стандартний ERC-20)
  • Специфічні вимоги безпеки
  • Непідтримувані чейни в існуючих протоколах
  • Повний контроль над структурою комісій

Використовувати існуючий (LayerZero, Axelar, CCIP), якщо:

  • Стандартний ERC-20 bridging
  • Потрібна швидкість запуску
  • Немає ресурсів на аудит безпеки кастомного мосту

Вартість аудиту кастомного мосту — $50-200k. Це обов'язкова стаття, не опціональна.

Stack

Компонент Технологія
Смарт-контракти Solidity 0.8.x + OpenZeppelin + Foundry
Validator network Node.js + TypeScript + ethers.js
Relayer Node.js + Bull queue + Redis
Моніторинг Grafana + Prometheus + PagerDuty
Frontend React + wagmi + viem
Інфраструктура AWS ECS + RDS + CloudWatch

Таймлайни

  • Базовий lock-and-mint (2 чейни, ERC-20): 6-8 тижнів
  • Мультичейн підтримка (+3 чейни): +4-6 тижнів
  • Аудит безпеки: обов'язковий, 4-8 тижнів
  • Production hardening + моніторинг: +3-4 тижні
  • Всього: 4-5 місяців