Професійна розробка cross-chain мостів під ключ
Ми стикалися з проєктами, де команди втрачали мільйони через помилки в bridge-контрактах — злами Wormhole, Ronin, Nomad забрали понад $2 млрд. За останні роки через мости було викрадено понад $2 млрд. Розробка надійного cross-chain мосту вимагає глибокого розуміння криптографії, консенсусів та вразливостей. Наш досвід 7+ років та 20+ реалізованих bridge-рішень дозволяє будувати мости, які витримують атаки та працюють роками без інцидентів.
Cross-chain міст — це система, яка переміщує активи або дані між блокчейнами. На перший погляд задача проста: заблокувати токени на ланцюгу A, випустити еквівалент на ланцюгу B. На практиці — це один із найбільш технічно та security-складних продуктів у Web3. Ми використовуємо перевірені архітектурні патерни та проводимо багатошаровий аудит. Замовте розробку мосту — оцінимо ваш проєкт за 1-2 дні.
Чому безпека — головний пріоритет?
Міст — найбільш атакована частина будь-якої DeFi-інфраструктури. Якщо у звичайному DEX злам призводить тільки до втрати пулу ліквідності, то злам bridge може обнулити всі заблоковані активи. Ми застосовуємо наступні заходи:
- Replay attack protection. Унікальний nonce, chainId та depositId у підписуваних даних виключають подвійне виконання на різних ланцюгах.
- Signature malleability. Використовуємо OpenZeppelin ECDSA.recover замість raw ecrecover — це усуває математичну malleability підписів.
- Reentrancy. Unlock та mint функції оновлюють стан до зовнішніх викликів (checks-effects-interactions) та використовують nonReentrant модифікатор.
- TVL caps. Обмеження сумарного TVL на контракті знижує максимальний збиток від зламу. Якщо cap $10M — максимальний loss $10M, не $500M.
- Timelock для upgrades. Зміни в bridge контракті мають timelock мінімум 48 годин. Це дає користувачам час вивести кошти, якщо зміна здається підозрілою.
- Emergency pause. Guardian role може зупинити всі вхідні/вихідні перекази при виявленні аномалій. Автоматично через circuit breaker (аномально великий withdraw).
Які архітектури мостів існують?
Lock-and-Mint (wrapped tokens)
Класична схема:
- Користувач блокує ETH у Lock контракті на Ethereum.
- Міст випускає wETH (wrapped ETH) на Polygon.
- При зворотному переказі: burn wETH на Polygon → unlock ETH на Ethereum.
Ризики: весь залог зосереджений в одному контракті на Ethereum. Злам = втрата всього locked TVL.
Burn-and-Mint (native tokens)
Застосовується для токенів з cross-chain minting capability (USDC через Circle CCTP):
- Burn USDC на Ethereum (Circle знищує забезпечення).
- Mint нативний USDC на Arbitrum (Circle випускає нове забезпечення).
Перевага: немає locked TVL = немає single point of failure. Але вимагає контролю над token contract на всіх ланцюгах.
Liquidity Pool (liquidity network)
Використовується у Stargate, Hop Protocol:
- На кожному ланцюгу пул ліквідності.
- Користувач вносить USDC на Ethereum пул → отримує USDC з Arbitrum пулу.
- LP провайдери отримують комісії за забезпечення ліквідності.
Перевага: швидке виконання без очікування finality. Ризик: imbalanced pools (більше вилучень з одного боку, ніж поповнень).
Native verification (light client bridges)
Найдецентралізованіший варіант: смарт-контракт на ланцюгу B верифікує заголовки блоків ланцюга A через light client. Доводить, що транзакція відбулася без довіри до валідаторів.
Приклади: ICS-23 (Cosmos IBC), Rainbow Bridge (NEAR → Ethereum). Складність: висока gas вартість верифікації, особливо для PoW.
Optimistic bridges
Оптимістична верифікація: повідомлення приймаються як валідні, але є період (зазвичай 30 хв — 7 днів), протягом якого watcher може оскаржити та заблокувати шахрайську транзакцію.
Трейдоф: безпека 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;
}
}
Порівняння архітектур
| Архітектура | Безпека | Швидкість | Децентралізація | Складність |
|---|---|---|---|---|
| Lock-and-Mint | Середня | Висока | Низька (централізований залог) | Низька |
| Burn-and-Mint | Висока | Висока | Висока (немає locked TVL) | Середня |
| Liquidity Pool | Середня | Дуже висока | Середня | Середня |
| Native verification | Дуже висока | Низька (дорогий gas) | Максимальна | Дуже висока |
| Optimistic | Висока | Низька (затримка) | Висока | Середня |
Моніторинг
Міст без моніторингу — це не production. Мінімальний набір алертів:
- TVL різке зниження (>10% за 5 хвилин)
- Аномально великі транзакції (>1% від TVL)
- Невідповідність між locked та minted (invariant check)
- Validator downtime (немає підписів від валідатора >N хвилин)
Ми інтегруємо Grafana, Prometheus та PagerDuty. Досвід показує: 80% інцидентів виявляються в перші 10 хвилин при правильному моніторингу.
Вибір існуючого рішення vs власний міст
Власний міст потрібен якщо:
- Кастомна токен-економіка (не стандартний ERC-20)
- Специфічні вимоги до безпеки
- Непідтримувані ланцюги в існуючих протоколах
- Повний контроль над fee структурою
Використовувати існуючий (LayerZero, Axelar, CCIP) якщо:
- Стандартний ERC-20 bridging
- Потрібна швидкість запуску
- Немає ресурсів на security audit кастомного bridge
Кастомний міст у 3 рази безпечніший готового за умови якісного аудиту. Однак аудит — обов'язкова стаття витрат, не опціональна.
Що входить у роботу
- Вихідний код смарт-контрактів (Solidity 0.8.x + OpenZeppelin + Foundry)
- Валідатор / relayer на Node.js + TypeScript (ethers.js)
- Моніторинг та алертинг (Grafana, Prometheus, PagerDuty)
- Документація: архітектура, API, deployment guide
- Інструкція з експлуатації та передача прав
- Підтримка після запуску (1 місяць)
- Звіт з аудиту безпеки
Приклад із практики: міст між Ethereum та Polygon
Для одного DeFi-протоколу ми розробили lock-and-mint міст із валідаторами M-of-N (5 із 7). Використовували Solidity 0.8.20, Foundry для тестування, Tenderly для симуляції. Впровадили TVL caps по $5M на токен, timelock 72 години, emergency pause. Аудит (через Certik) показав 0 критичних вразливостей. Міст працює більше 2 років без інцидентів.
| Компонент | Технологія |
|---|---|
| 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 місяців
Зв'яжіться з нами, щоб обговорити ваш проєкт. Ми оцінимо задачу за 1-2 дні та запропонуємо оптимальну архітектуру.







