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

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка cross-chain моста
Сложная
от 2 недель до 3 месяцев
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1258
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1170
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    873
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1092
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    563
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    830

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

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

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

Lock-and-Mint (wrapped tokens)

Классическая схема:

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

Риски: весь залог сосредоточен в одном контракте на Ethereum. Взлом = потеря всего locked TVL. Wormhole потерял $320M именно так.

Burn-and-Mint (native tokens)

Применяется для токенов с cross-chain minting capability (USDC через Circle CCTP, USDT через Tether bridge):

  1. Burn USDC на Ethereum (Circle уничтожает обеспечение)
  2. Mint нативный USDC на Arbitrum (Circle выпускает новое обеспечение)

Преимущество: нет locked TVL = нет single point of failure. Но требует контроля над token contract на всех цепях.

Liquidity Pool (liquidity network)

Используется в Stargate, Hop Protocol:

  1. На каждой цепи пул ликвидности
  2. Пользователь вносит USDC на Ethereum пул → получает USDC из Arbitrum пула
  3. LP провайдеры получают комиссии за обеспечение ликвидности

Преимущество: быстрое исполнение без ожидания finality. Риск: imbalanced pools (больше изъятий с одной стороны чем пополнений).

Native verification (light client bridges)

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

Примеры: ICS-23 (Cosmos IBC), Rainbow Bridge (NEAR → Ethereum). Сложность: высокая gas стоимость верификации, особенно для PoW (Ethereum pre-merge).

Optimistic bridges

Оптимистичная верификация: сообщения принимаются как валидные, но есть период (обычно 30 мин — 7 дней) в течение которого watcher может оспорить и заблокировать мошенническую транзакцию. Используется в 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 check
        require(
            tokenCurrentTVL[token] + amount <= tokenTVLLimits[token],
            "TVL limit exceeded"
        );
        
        // Daily limit check
        _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 attack protection. Одна и та же подписанная транзакция не должна быть исполнена дважды или на другой цепи. Решение: включать chainId и уникальный nonce в подписываемые данные.

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

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

TVL caps. Ограничение суммарного TVL на контракте снижает максимальный ущерб от взлома. Если cap $10M — максимальный loss $10M, не $500M.

Timelock для upgrades. Изменения в bridge контракте должны иметь timelock (минимум 48 часов). Это даёт пользователям время вывести средства если изменение кажется подозрительным.

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

Мониторинг

Мост без мониторинга — это не production. Минимальный набор алертов:

  • TVL резкое снижение (>10% за 5 минут)
  • Аномально крупные транзакции (>1% от TVL)
  • Несоответствие между locked и minted (invariant check)
  • Validator downtime (нет подписей от валидатора >N минут)

Выбор существующего решения vs собственный мост

Собственный мост нужен если:

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

Использовать существующий (LayerZero, Axelar, CCIP) если:

  • Стандартный ERC-20 bridging
  • Нужна скорость запуска
  • Нет ресурсов на security audit кастомного bridge

Стоимость аудита кастомного bridge — $50-200k. Это обязательная статья, не опциональная.

Стек

Компонент Технология
Smart contracts Solidity 0.8.x + OpenZeppelin + Foundry
Validator network Node.js + TypeScript + ethers.js
Relayer Node.js + Bull queue + Redis
Monitoring Grafana + Prometheus + PagerDuty
Frontend React + wagmi + viem
Infrastructure AWS ECS + RDS + CloudWatch

Сроки

  • Базовый lock-and-mint (2 цепи, ERC-20): 6-8 недель
  • Multi-chain поддержка (+3 цепи): +4-6 недель
  • Security audit: обязателен, 4-8 недель
  • Production hardening + мониторинг: +3-4 недели
  • Итого: 4-5 месяцев