Розробка omnichain-NFT (ONFT)
NFT, прив'язаний до одного чейна — це актив з обмеженою ліквідністю. Колекція на Ethereum має доступ до OpenSea та Blur, але відрізана від екосистем Polygon, Arbitrum, Solana. Власник, який хоче використовувати NFT в грі на Immutable X або як collateral в DeFi-протоколі на Arbitrum — просто не може.
ONFT (Omnichain Non-Fungible Token) — стандарт LayerZero для NFT з нативним кросс-чейн трансфером. Не бридж з lock-and-mint ризиками, а єдиний контракт, розгорнутий на кількох чейнах, який атомарно переміщує NFT між ними без втрати метаданих та історії власності.
Як ONFT працює на рівні протоколу
LayerZero: endpoints та Ultra Light Node
LayerZero не є окремим блокчейном. Це messaging протокол з Endpoint контрактами на кожному підтримуваному чейні (~50+: Ethereum, Polygon, Arbitrum, Optimism, BSC, Solana, Aptos та інші).
Коли NFT відправляється з Ethereum в Arbitrum:
-
sendFrom()на Ethereum викликаєEndpoint.send()з encoded payload (tokenId, recipient) - LayerZero Oracle (Chainlink, Sequencer або Google Cloud) фіксує block header на Arbitrum
- LayerZero Relayer передає proof транзакції
-
Endpointна Arbitrum верифікує proof через Ultra Light Node (ULN) — не повна верифікація блока, тільки потрібний storage proof -
lzReceive()на ONFT контракті Arbitrum викликається з payload, мінтить NFT отримувачу
На вихідному чейні NFT сжигается (або лочиться в залежності від реалізації). На цільовому — мінтиться. Загальний supply не змінюється.
ONFT721 vs. власна реалізація
LayerZero надає ONFT721 base contract у @layerzerolabs/solidity-examples. Це ERC-721 з доданими функціями sendFrom та lzReceive. Найпростіша ONFT реалізація — спадкування від ONFT721 з додаванням кастомної логіки.
Ключові параметри при деплої:
constructor(
string memory name,
string memory symbol,
uint256 _minGasToTransfer, // мінімальний газ для lzReceive на destination
address _lzEndpoint // LayerZero Endpoint адреса для цього чейну
) ONFT721(name, symbol, _minGasToTransfer, _lzEndpoint) {}
_minGasToTransfer критичен: якщо вказати занадто мало — lzReceive на destination ревертиться через out-of-gas, NFT "застряє" між чейнами. Рекомендація LayerZero: 200 000 gas для базового ONFT721, більше якщо lzReceive містить додаткову логіку.
Проблеми, які потрібно вирішити при розробці
Синхронізація метаданих при кросс-чейн трансфері
Метаданні NFT зберігаються на IPFS або Arweave — це не проблема, URI однаковий на всіх чейнах. Проблема з динамічною метаданими: якщо NFT має on-chain атрибути (рівень персонажа в грі, накопичені очки), ці дані зберігаються в storage контракту. При перенесенні на інший чейн on-chain стан не переносити автоматично.
Рішення: включити стан в LayerZero payload. Кастомна _debitFrom на source упаковує state, кастомна _creditTo на destination відновлює. Це збільшує gas вартість трансфера, але зберігає повне стан.
function _debitFrom(address _from, uint16, bytes memory, uint _tokenId)
internal override returns(bytes memory) {
// Збираємо state токену
TokenState memory state = tokenStates[_tokenId];
_burn(_tokenId); // або lock
return abi.encode(_tokenId, state); // включаємо в payload
}
function _creditTo(uint16, address _toAddress, bytes memory _payload)
internal override returns(uint) {
(uint tokenId, TokenState memory state) = abi.decode(_payload, (uint, TokenState));
_mint(_toAddress, tokenId);
tokenStates[tokenId] = state; // відновлюємо state
return tokenId;
}
Оцінка та оплата LayerZero fee
Трансфер через LayerZero не безплатний: користувач платить нативною валютою source чейна за:
- Gas на source чейні (
Endpoint.send) - Оракул та relayer fee (йде в LayerZero)
- Оцінку газу на destination чейні (prepaid)
Клієнтський код обов'язаний викликати estimateSendFee() перед трансфером і передати результат як msg.value. Якщо msg.value менше оцінки — транзакція ревертиться.
function estimateSendFee(
uint16 _dstChainId,
bytes calldata _toAddress,
uint _tokenId,
bool _useZro,
bytes calldata _adapterParams
) public view returns (uint nativeFee, uint zroFee);
Типова вартість трансфера ETH → Arbitrum: $0.50–2.00 в ETH в залежності від congestion.
Конфігурація Trusted Remote
Кожен ONFT контракт на кожному чейні має знати адреси своїх "собратів" на інших чейнах. Це trustedRemote — авторизований список. Без цього будь-який контракт мав би мінтити ONFT через LayerZero message.
// Виконується після деплою на кожному чейні
function setTrustedRemoteAddress(
uint16 _remoteChainId, // LayerZero chain ID
bytes calldata _remoteAddress
) external onlyOwner;
Помилка: забути встановити trusted remote bidirectionally. Трансфер Ethereum→Polygon працює, Polygon→Ethereum ні — тому що Polygon контракт не додав Ethereum в trusted remote.
nonce та ordering гарантії
LayerZero v1 гарантує ordered delivery: повідомлення між двома чейнами доставляються в порядку відправки. Якщо транзакція з nonce N застряла (релейер не доставив) — усі наступні з nonce N+1, N+2 чекають. Це може заблокувати всі трансферки з конкретного чейну.
LayerZero v2 (2024) переходить на unordered delivery з application-level ordering — більш гнучка модель, не блокує чергу.
Стек для повноцінного ONFT проекту
Контракти: Solidity 0.8.x, @layerzerolabs/lz-evm-oapp-v2 (для LZ v2) або @layerzerolabs/solidity-examples (LZ v1), OpenZeppelin ERC721.
Тестування: Foundry з LZEndpointMock — mock LayerZero endpoint для локального тестування кросс-чейн викликів без реального оракула. Тест: відправити з chain A, перевірити mint на chain B.
Frontend: wagmi/viem для мультичейн підтримки, переключення мережі при трансфері, відображення estimated fee через estimateSendFee.
Деплой: Foundry скрипти для паралельного деплою на кілька чейнів + скрипт встановлення trustedRemote для всіх пар.
Процес та сроки
Проектування (1 день): список цільових чейнів, наявність on-chain state для синхронізації, кастомна логіка в _debitFrom/_creditTo.
Розробка контрактів (2–3 дні): ONFT721 з кастомною логікою, тесты через LZEndpointMock.
Frontend компонент (1 день): бридж-інтерфейс з вибором destination chain, fee estimation, статус трансфера.
Деплой та конфігурація (0.5 дня): деплой на всі чейни, встановлення trustedRemote.
Всього: 3–5 днів для базового ONFT без on-chain state. З синхронізацією складного state — 1–2 тижні. Вартість розраховується індивідуально.







