Разработка omnichain-токена (OFT)

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка omnichain-токена (OFT)
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1062
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка omnichain-токена (OFT)

Стандартный подход к multi-chain токену: wrap/bridge модель. Оригинальный токен на Ethereum, на каждой другой цепи — wrapped версия через bridge. Проблема: ликвидность фрагментирована, canonical supply неочевидна, пользователь держит «USDC-Polygon» отдельно от «USDC-Arbitrum», bridge риски мультиплицируются с количеством chain.

OFT (Omnichain Fungible Token) от LayerZero решает это иначе: один контракт на каждой цепи, единый supply, перевод между цепями работает через burn-and-mint без wrapping. Токен на Arbitrum — это тот же токен, не wrapped копия.

Как работает LayerZero OFT

Burn-and-mint механизм

Transfer: Arbitrum → Optimism
1. Пользователь вызывает oft.send() на Arbitrum
2. OFT контракт сжигает X токенов на Arbitrum (уменьшает supply)
3. LayerZero relayer передаёт сообщение на Optimism
4. OFT контракт на Optimism минтит X токенов (увеличивает supply)
5. Суммарный supply across chains не изменился

vs Bridge (wrap) модель:
1. Lock X токенов на source в bridge контракте
2. Mint X wrapped токенов на destination
Проблема: bridge контракт — single point of failure для всего locked supply

В OFT нет центрального bridge контракта с locked активами. Нет «TVL в bridge» который можно взломать. Взломать можно только LayerZero messaging layer — это отдельный риск, но не «всё сразу».

Имплементация OFT v2

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

import { OFT } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFT.sol";

contract MyOFT is OFT {
    constructor(
        string memory _name,
        string memory _symbol,
        address _lzEndpoint,   // LayerZero endpoint для этой цепи
        address _delegate      // owner/admin
    ) OFT(_name, _symbol, _lzEndpoint, _delegate) {}
    
    // Минт только на home chain (обычно)
    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }
}

Деплой одного и того же контракта на каждой целевой цепи. Разница только в _lzEndpoint — адрес LayerZero endpoint специфичен для каждой цепи.

// Deployment script (Hardhat/Foundry)
const LZ_ENDPOINTS = {
  ethereum:  "0x1a44076050125825900e736c501f859c50fE728c",
  arbitrum:  "0x1a44076050125825900e736c501f859c50fE728c", // V2 одинаков
  optimism:  "0x1a44076050125825900e736c501f859c50fE728c",
  polygon:   "0x1a44076050125825900e736c501f859c50fE728c",
  base:      "0x1a44076050125825900e736c501f859c50fE728c",
}

// LayerZero V2: эндпоинт одинаковый на всех EVM-сетях
// Но EID (Endpoint ID) уникален для каждой цепи
const LZ_EIDS = {
  ethereum:  30101,
  arbitrum:  30110,
  optimism:  30111,
  polygon:   30109,
  base:      30184,
}

Конфигурация peers (wire-up)

После деплоя на все цепи — связываем контракты между собой. Каждый OFT должен знать адреса своих peer'ов.

import { ethers } from "ethers"
import { Options } from "@layerzerolabs/lz-v2-utilities"

// На Arbitrum: установить peer для Optimism
const oftArbitrum = new ethers.Contract(OFT_ARBITRUM, OFT_ABI, signerArbitrum)

// Адрес peer должен быть bytes32-encoded (левый padding нулями)
const optimismPeerBytes32 = ethers.zeroPadValue(OFT_OPTIMISM, 32)

await oftArbitrum.setPeer(
  LZ_EIDS.optimism,       // destination EID
  optimismPeerBytes32      // адрес peer на destination
)

// Аналогично на Optimism: установить peer для Arbitrum
const oftOptimism = new ethers.Contract(OFT_OPTIMISM, OFT_ABI, signerOptimism)
await oftOptimism.setPeer(LZ_EIDS.arbitrum, ethers.zeroPadValue(OFT_ARBITRUM, 32))

Это не автоматически — нужно вызвать setPeer для каждой пары цепей. При N цепях: N*(N-1) вызовов (каждая цепь знает всех остальных).

Отправка токенов между цепями

// Пользователь: отправить 100 MTK с Arbitrum на Optimism
const oft = new ethers.Contract(OFT_ARBITRUM, OFT_ABI, signer)

// 1. Получить quote (сколько нативного газа нужно заплатить)
const sendParam = {
  dstEid: LZ_EIDS.optimism,
  to: ethers.zeroPadValue(recipientAddress, 32),
  amountLD: ethers.parseEther("100"),    // amount in local decimals
  minAmountLD: ethers.parseEther("99"),  // 1% slippage tolerance
  extraOptions: "0x",
  composeMsg: "0x",
  oftCmd: "0x",
}

const [nativeFee, lzTokenFee] = await oft.quoteSend(sendParam, false)
console.log(`Fee: ${ethers.formatEther(nativeFee)} ETH`)

// 2. Отправить (msg.value = nativeFee для оплаты LayerZero)
const tx = await oft.send(
  sendParam,
  { nativeFee, lzTokenFee: 0n },
  signer.address, // refund address (если overpay)
  { value: nativeFee }
)

const receipt = await tx.wait()
// Токены на Arbitrum сожжены
// Через ~15-60 секунд появятся на Optimism

Decimals: потенциальная ловушка

Разные цепи могут иметь разные native decimals. Solana использует 6 decimals (lamports), EVM — 18. OFT v2 решает это через shared decimals: все chains работают с меньшим числом (обычно 6), конвертируя при отправке.

// OFT v2: sharedDecimals по умолчанию 6
// При отправке с EVM (18 decimals) → dust removed

// Пример: отправка 1.000000000000000001 MTK (18 decimals)
// Reальная сумма которая прибудет: 1.000000 MTK (6 shared decimals)
// 0.000000000000000001 MTK остаётся у отправителя как "dust"

// Проверка всегда: quoteSend → amountReceivedLD
// amountReceivedLD = фактически полученная сумма с учётом конвертации

Это важно для UI: показывать пользователю amountReceivedLD, а не исходную сумму.

OFTAdapter: для существующих токенов

Если токен уже существует на Ethereum и его контракт нельзя изменить (нет mint/burn функций в нужном контексте) — используем OFTAdapter. На Ethereum: Lock&Release (токены locked в adapter). На других цепях: OFT с burn&mint.

import { OFTAdapter } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/OFTAdapter.sol";

contract MyTokenAdapter is OFTAdapter {
    constructor(
        address _token,        // существующий ERC-20 токен
        address _lzEndpoint,
        address _delegate
    ) OFTAdapter(_token, _lzEndpoint, _delegate) {}
}

На других цепях деплоим обычный OFT (с mint/burn). Только Ethereum использует lock модель — остальные цепи используют burn/mint.

Компромисс OFTAdapter: Ethereum-locked токены — это снова bridge риск. Если adapter взломан — supply на других цепях не обеспечен. Для критических проектов: multisig или timelock на adapter, cap на locked amount.

DVN: настройка безопасности

LayerZero V2 позволяет настраивать DVN (Decentralized Verifier Network) — кто верифицирует cross-chain сообщения.

// Кастомная DVN конфигурация
// По умолчанию: LayerZero DVN (один верификатор)
// Усиленная: требуем 2-of-3 верификацию

const enforcedOptions = [
  {
    eid: LZ_EIDS.optimism,
    msgType: 1,
    options: Options.newOptions()
      .addExecutorLzReceiveOption(200_000, 0) // 200k gas на destination
      .toHex()
  }
]

// Установка через OApp config
await oft.setEnforcedOptions(enforcedOptions)

Для production OFT с большим TVL: настраивать минимум два независимых DVN (LayerZero + Google Cloud DVN или Polyhedra). Компромисс: больше DVN = дороже fee, но выше безопасность.

Мониторинг и обработка failed messages

Сообщение может зависнуть если destination chain momentarily недоступен или gas недостаточен. LayerZero V2 хранит failed messages, их можно retry.

// LayerZero Scan API для мониторинга
const response = await fetch(
  `https://scan.layerzero-api.com/v1/messages/tx/${txHash}`
)
const { data } = await response.json()

if (data.status === 'FAILED') {
  console.log(`Message failed: ${data.failReason}`)
  // Вызвать lzEndpoint.retryMessage() или
  // lzEndpoint.forceResumeReceive() если нужно пропустить
}

Для UX: показывать пользователю статус cross-chain транзакции через LayerZero Scan embeddable widget или кастомный polling.

Процесс работы

Подготовка (3-5 дней). Список целевых цепей, tokenomics (где минтить initial supply, нужен ли adapter для существующего токена), требования к security (DVN конфигурация, multisig).

Разработка (1-2 недели). OFT контракты → Foundry тесты для каждого сценария (send, receive, failed message retry) → deployment scripts для всех цепей → wire-up (setPeer на каждой).

Тестирование (1 неделя). Testnet деплой на все целевые цепи → функциональное тестирование cross-chain transfers → проверка decimals conversion → нагрузочный тест с concurrent messages.

Mainnet деплой и верификация. Верификация контрактов на Etherscan/explorer каждой цепи. Тестовая small-amount транзакция перед анонсом.

OFT для 3-5 цепей с базовым функционалом — 3-4 недели. С OFTAdapter для существующего токена, кастомными DVN и мониторинг дашбордом — 5-7 недель.