Розробка контракту токен-мосту
Токен-міст — інфраструктура для переміщення активів між несумісними блокчейнами. З точки зору користувача все виглядає просто: заблокував токени на Ethereum, отримав їх на Arbitrum.
За цією простотою ховається один із найбільш уразливих класів смарт-контрактів: Ronin, Wormhole, Nomad — усі зломи сталися саме через мости.
Ми розробляємо кастомні мости, що виключають типові уразливості, і гарантуємо безпеку вашого токена на всіх етапах міграції.
Це не випадковість. Міст за визначенням керує заблокованими активами на одному ланцюгу та випускає синтетичні на іншому. Зламати bridge = вкрасти всі locked funds одразу.
Складність посилюється тим, що безпека системи визначається надійністю cross-chain повідомлень — задача, яку ми вирішуємо через комбінацію перевірених архітектурних патернів і формальної верифікації.
Оцінимо ваш проєкт за 2–3 дні. Пишіть — обговоримо деталі.
Чому бриджі такі вразливі?
Бридж керує ліквідністю на кількох мережах. Атакувальники експлуатують компрометацію валідаторів (Ronin), помилки в логіці оскарження (Nomad) або replay-атаки через неправильний підпис повідомлення.
Кожен із цих векторів перекривається на етапі проєктування: ми використовуємо threshold signature scheme (TSS) для захисту ключів, nonce з blockhash фідом для унікальності та обов'язкові аудити як умову деплою.
Архітектурні патерни: порівняння моделей
Lock-and-Mint vs Burn-and-Release vs Liquidity Pool — розробка системи токен
Lock-and-Mint: токен блокується на source chain, на destination chain мінтиться wrapped версія. Приклад: WBTC — BTC заблокований у кастодіана, ERC-20 WBTC замінчений на Ethereum. Перевага: оригінальний токен не потребує функції burn. Недолік: фрагментація ліквідності — wrapped токен на кожному ланцюгу окремий.
Burn-and-Release: нативний токен спалюється на source chain, розблоковується на destination chain. Потребує cross-chain-aware логіки. Circle CCTP для USDC використовує саме цю модель. Для кастомного проєкту, де ваш токен і ви контролюєте контракт, Burn-and-Release простіше і безпечніше (немає locked funds як цілі атаки).
Liquidity pool модель (хаб-і-спок): на кожному ланцюгу пул ліквідності нативного токена. Користувач депонує на одній стороні, отримує з пулу на іншій. Так працюють Hop Protocol і Across Protocol. Перевага: нативні токени на обох сторонах. Недолік: потрібна ліквідність у пулах.
| Модель | Вимога до токена | Ризик locked funds | Використання в production |
|---|---|---|---|
| Lock-and-Mint | Будь-який ERC-20 | Високий | WBTC, Polygon Bridge |
| Burn-and-Release | Наявність burn() | Низький | Circle CCTP, Arbitrum BRIDGE |
| Liquidity Pool | Будь-який ERC-20 | Середній (ліквідність) | Hop, Across |
Як забезпечити безпеку верифікації повідомлень?
Верифікація — ключовий архітектурний вибір. Як destination chain дізнається, що подія на source chain справді відбулася? Використовується один із підходів:
Optimistic верифікація (Nomad, Across): повідомлення вважається валідним, якщо ніхто не оспорив його протягом періоду (30 хвилин — кілька годин). Недолік: latency. Перевага: дешевше в експлуатації. Nomad був зламаний через помилку в логіці оспорювання.
Multisig верифікація (більшість production-мостів): N з M валідаторів підписують підтвердження. Wormhole використовував 19 guardians. Уразливість: компрометація порогової кількості ключів. Ми використовуємо TSS для виключення єдиної точки відмови.
Light client верифікація (zkBridge, IBC): destination chain верифікує consensus proof source chain. Найбезпечніше, але дорого за газом. ZK-based (Succinct, =nil; Foundation) стискає proof до практичного розміру.
Native bridges (Arbitrum, Optimism canonical bridge): використовують rollup's own fraud proof. Максимально безпечно, але лише для конкретної пари L1-L2 і з 7-денним withdrawal period.
| Модель верифікації | Безпека | Витрати газу | Latency | Приклад |
|---|---|---|---|---|
| Optimistic | Середня | Низькі | 30 хв — 2 год | Across |
| Multisig | Висока (за TSS) | Середні | Хвилини | Wormhole |
| Light client | Дуже висока | Високі | Хвилини | zkBridge |
| Native L2 | Максимальна | Низькі | ~7 днів | Arbitrum Bridge |
Детальна реалізація Lock-and-Mint бриджа
Контракт вихідного ланцюга (Locker)
contract BridgeLocker {
mapping(uint32 => bool) public supportedChains;
mapping(bytes32 => bool) public processedNonces;
event TokensLocked(
address indexed token,
address indexed sender,
address indexed recipient,
uint256 amount,
uint32 destinationChain,
bytes32 nonce
);
function lock(
address token,
uint256 amount,
address recipient,
uint32 destinationChain
) external nonReentrant {
require(supportedChains[destinationChain], "Chain not supported");
require(amount > 0, "Zero amount");
// Генеруємо унікальний nonce для цього transfer
bytes32 nonce = keccak256(abi.encodePacked(
block.chainid,
destinationChain,
msg.sender,
recipient,
token,
amount,
block.timestamp,
blockhash(block.number - 1)
));
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
emit TokensLocked(token, msg.sender, recipient, amount, destinationChain, nonce);
}
function release(
address token,
address recipient,
uint256 amount,
bytes32 nonce,
bytes[] calldata signatures
) external {
require(!processedNonces[nonce], "Already processed");
require(_verifySignatures(token, recipient, amount, nonce, signatures), "Invalid signatures");
processedNonces[nonce] = true;
IERC20(token).safeTransfer(recipient, amount);
}
}
Контракт цільового ланцюга (Minter)
contract BridgeMinter {
mapping(address => address) public wrappedTokens; // original → wrapped
mapping(bytes32 => bool) public mintedNonces;
function mint(
address originalToken,
address recipient,
uint256 amount,
bytes32 nonce,
bytes[] calldata signatures
) external {
require(!mintedNonces[nonce], "Already minted");
require(_verifySignatures(originalToken, recipient, amount, nonce, signatures), "Invalid");
mintedNonces[nonce] = true;
address wrapped = wrappedTokens[originalToken];
if (wrapped == address(0)) {
wrapped = _deployWrappedToken(originalToken);
wrappedTokens[originalToken] = wrapped;
}
IWrappedToken(wrapped).mint(recipient, amount);
emit TokensMinted(originalToken, wrapped, recipient, amount, nonce);
}
function burn(
address wrappedToken,
uint256 amount,
address recipient,
uint32 destinationChain
) external nonReentrant {
IWrappedToken(wrappedToken).burnFrom(msg.sender, amount);
// emit подію для relayer-ів
emit TokensBurned(wrappedToken, msg.sender, recipient, amount, destinationChain);
}
}
Верифікація підписів валідаторів
function _verifySignatures(
address token,
address recipient,
uint256 amount,
bytes32 nonce,
bytes[] calldata signatures
) internal view returns (bool) {
require(signatures.length >= threshold, "Not enough signatures");
bytes32 messageHash = keccak256(
abi.encodePacked(
block.chainid,
token,
recipient,
amount,
nonce
)
);
bytes32 ethSignedHash = MessageHashUtils.toEthSignedMessageHash(messageHash);
address lastSigner = address(0);
for (uint256 i = 0; i < signatures.length; i++) {
address signer = ECDSA.recover(ethSignedHash, signatures[i]);
require(isValidator[signer], "Not a validator");
require(signer > lastSigner, "Duplicate signer");
lastSigner = signer;
}
return true;
}
Інфраструктура Relayer та моніторинг
Relayer — off-chain сервіс, що моніторить події на source chain і ініціює транзакції на destination chain. Ми реалізуємо його на TypeScript + viem + BullMQ для черг. Критичний параметр — finality. Ethereum вимагає 12 блоків (~2.5 хвилини), Polygon — 128 блоків. Якщо relayer надсилає mint до finality, reorg на source chain створює дисбаланс: mint відбувся, а lock — ні. Наш relayer очікує checkpoint finality і використовує idempotency через nonce.
Додатково налаштовуємо Tenderly для алертів і Grafana для метрик (затримки, помилки, кількість pending).
Що входить у роботу
- Проєктування архітектури (вибір моделі верифікації, chain pair)
- Розробка контрактів Locker/Minter з тестами (unit + fork на Foundry)
- Розробка Relayer-сервісу з retry та моніторингом
- Розгортання та конфігурація валідаторів (TSS або multisig)
- Інтеграція з гаманцями та фронтендом (ethers.js, RainbowKit)
- Зовнішній аудит контрактів (організація та виправлення знахідок)
- Документація та навчання команди
- Гарантія на контракти після аудиту
Терміни та вартість
| Компонент | Складність | Термін |
|---|---|---|
| Locker + Minter контракти | Висока | 2–3 тижні |
| Signature verification | Середня | 1 тиждень |
| Relayer сервіс | Висока | 2–3 тижні |
| Wrapped token factory | Низька | 3–5 днів |
| Тести (unit + fork) | Висока | 2 тижні |
| Аудит (зовнішній) | — | 3–6 тижнів |
Загальний термін від kick-off до mainnet: 3–5 місяців з урахуванням аудиту. Вартість розраховується індивідуально після уточнення chain пар, моделі верифікації та вимог до decentralization. Оцінимо ваш проєкт за 2–3 дні — зв'яжіться з нами.
Процес роботи
- Аналіз вимог — фіксуємо ланцюги, токени, модель управління валідаторами.
- Проєктування — обираємо архітектуру (lock-and-mint, burn-and-release, або LP) та механізм верифікації.
- Розробка — пишемо контракти та relayer, пишемо fork-тести з реальним mainnet state.
- Тестування — run тестів з форками mainnet, симуляція атак (replay, malleability, reentrancy).
- Аудит — передача коду зовнішнім аудиторам, усунення знахідок.
- Деплой — розгортання на mainnet, моніторинг перших транзакцій.
Замовте розробку моста під ключ — ми проведемо весь цикл від ідеї до mainnet.







