Разработка 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 между ними без потери metadata и ownership истории.
Как ONFT работает на уровне протокола
LayerZero: endpoints и Ultra Light Node
LayerZero не является отдельным блокчейном. Это messaging protocol с 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 содержит дополнительную логику.
Проблемы, которые нужно решить при разработке
Синхронизация metadata при кросс-чейн трансфере
Metadata NFT хранится на IPFS или Arweave — это не проблема, URI одинаков на всех чейнах. Проблема с динамической metadata: если NFT имеет on-chain attributes (уровень персонажа в игре, накопленные очки), эти данные хранятся в storage контракта. При переносе на другой чейн on-chain state не переносится автоматически.
Решение: включить state в 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 scripts для параллельного деплоя на несколько чейнов + скрипт установки 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 недели. Стоимость рассчитывается индивидуально.







