
Если вы уже читали мою статью про архитектуру LayerZero v2, то следующий логичный шаг — попробовать протокол в деле. В этой статье расскажу, как развернуть простой OApp в Remix IDE: настроим контракты, отправим сообщение между двумя сетями и разберёмся, как это работает на практике.
Терминология:
-
Исходнаясеть — блокчейн, отправляющий данные в другую сеть.
-
Сеть назначения — блокчейн, принимающий данные из исходной сети.
-
OApp (Omnichain Application) — оминчейн приложение, имеющее все необходимые интерфейсы для отправки и получения сообщений.
-
EID — Endpoint ID. Endpoint — это смарт‑контракт, который обрабатывает все входящие и исходящие сообщения в любой сети.
-
ZRO — utility‑токен платформы LayerZero, а также токен голосования.
-
Executor — он же исполнитель, смарт‑контракт который исполняет транзакцию по доставке сообщения в сети назначения.
Создание простого OApp
В документации предлагается развернуть два смарт‑контракта: SourceOApp и DestinationOApp, которые отправляют сообщения между блокчейнами, используя Remix (см. раздел «Example Omnichain Application»). Эти контракты уже написаны — остается только их задеплоить и настроить.
На всякий случай я продублирую ссылки на Remix сюда, т. к. документацию часто переписывают:
Это два очень простых смарт-контракта. SourceOApp наследуется от OAppSender, а DestinationOApp от OAppReceiver.
Наша задача — переслать простое сообщение (строку) из одного блокчейна в другой.
Что нам понадобится? На самом деле, не так много:
-
ДваOApp‑приложения, задеплоенные в разных блокчейнах (
SourceOAppиDestinationOApp). -
Адреса Endpoint в обеих сетях, а также их EID (Endpoint ID). Их можно найти в этой таблице.
Source OApp
Посмотрите на код SourceOApp (в Remix) — он довольно простой. Нас интересуют две функции:
-
SourceOApp::quote— позволяет рассчитать, сколько нативных токенов нужно передать в сеть назначения, чтобы хватило на выполнение транзакции и проверок в стеке безопасности. Параметр_payInLzTokenустанавливается, если оплата будет производиться не в нативном токене сети (а например с помощью ERC20-токена протокола ZRO).
function quote( uint32 _dstEid, string memory _message, bool _payInLzToken ) public view returns (MessagingFee memory fee) { bytes memory payload = abi.encode(_message); fee = _quote(_dstEid, payload, _options, _payInLzToken); }
-
SourceOApp::send— отправляет сообщение и передает нативные токены для оплаты газа в сети назначения.
function send( uint32 _dstEid, string memory _message ) external payable { // Кодируем сообщение перед вызовом _lzSend. bytes memory _encodedMessage = abi.encode(_message); _lzSend( _dstEid, _encodedMessage, _options, // Комиссия в нативном токене (или токене ZRO). MessagingFee(msg.value, 0), // Адрес отправителя в исходной сети (на случай возврата комиссии). payable(msg.sender) ); emit MessageSent(_message, _dstEid); }
Базовая настройка OApp
Адрес Endpoint задается при деплое контракта. После развертывания нужно также связать смарт-контракты между собой вызвав SourceOApp::setPeer(uint32 eid, bytes32 peer) в обоих контрактах (в исходной сети и в сети назначения).
Примечание: Peer в данном случае это OApp. Два OApp могут обмениваться сообщениями только если у них есть связь peer-to-peer через eid и адрес контракта. Функция setPeer находится в смарт-контракте OAppCore.
Важно! SourceOApp::setPeer принимает адрес в формате bytes32. Это сделано намеренно, так как форматы адресов в разных блокчейнах могут различаться (например, в EVM это 20 байт, а в Solana — 32 байта).
Чтобы перевести EVM-адрес в bytes32, можно воспользоваться следующей функцией:
function addressToBytes32(address _addr) public pure returns (bytes32) { return bytes32(uint256(uint160(_addr))); }
После выполнения транзакции можно проверить, что peer установлен верно.
Примечание: EID необходим, потому что не у всех блокчейнов есть chain ID.
Destination OApp
В OApp-сети назначения нас интересует одна ключевая функция — _lzReceive. Она переопределяется так, чтобы обрабатывать определенные типы сообщений. В нашем случае это обычная строка.
function _lzReceive( Origin calldata _origin, bytes32 /*_guid*/, bytes calldata message, address /*executor*/, // Адрес исполнителя, указанный для OApp bytes calldata /*_extraData*/ // Любые дополнительные данные или опции ) internal override { // Декодируем полезную нагрузку, чтобы получить сообщение data = abi.decode(message, (string)); emit MessageReceived(data, _origin.srcEid, _origin.sender, _origin.nonce); }
Практическая часть
Теперь выполним все шаги из документации по развертыванию приложений и отправке сообщения. Для большего погружения рекомендую использовать другие блокчейны из этого списка вместо тех, что предложены в документации.
Примечание: копируйте адреса контрактов сразу после деплоя, а также делайте «Pin contract for current workspace» — нажать соответствующий значок рядом с адресом контракта, потому что в Remix, если переключиться на другую сеть, развернутые контракты сбрасываются (даже если вкладка остается открытой).
Напомню: чтобы в Remix получить доступ к функциям уже развернутого контракта, выберите этот контракт во вкладке Contract и вставьте его адрес в At Address.
Порядок действий:
-
Выбираем два блокчейна. Проверяем, что у нас достаточно нативных токенов для оплаты газа.
-
Переходим в документацию LayerZero → Chains и находим адреса Endpoint и соответствующие EID. Сохраняем их — они понадобятся дальше.
-
Деплоим контракт
SourceOAppв исходную сеть. Для этого потребуется адрес Endpoint в этой сети. -
Деплоим контракт
DestinationOAppв сеть назначения, также используя соответствующий Endpoint. Сохраняем адрес. -
Переключаемся обратно на исходную сеть, переводим адреса в формат
bytes32. Для этого вызываемSource::addressToBytes32(как для OApp в исходной сети, так и для OApp в сети назначения). -
Связываем OApps через
setPeer, выполнив две транзакции:-
Source::setPeer(uint32 eid, bytes32 peer) -
Destination::setPeer(uint32 eid, bytes32 peer)
-
-
Рассчитываем, сколько нативных токенов потребуется для оплаты газа в сети назначения. Для этого используем
Source::quote(uint32 dstEid, string message, bool payLzToken). ПараметрpayLzTokenможно указатьfalse.
Расчет стоимости выполнения транзакции в сети назначения через quote -
Вставляем рассчитанный
fee(количество Wei) в поле Value в Remix и отправляем сообщение черезSource::send(uint32 dstEid, string message).
Поле для вставки количества нативных токенов в Wei, которое будет отправлено в сеть назначения
Вызов send для отправки сообщения -
Проверяем транзакцию на testnet.layerzeroscan.com. Ищем по хешу транзакции (его можно взять в логах Remix).
Логи Remix, здесь можно взять хеш транзакции
Отображение транзакции в layerzeroscan -
Сразу после совершения транзакции статус в сканере будет Inflight. Через несколько минут, если все прошло успешно, статус изменится на Delivered. В сети назначения можно проверить полученное сообщение через
Destination::data.
Проверка в сети назначения, что сообщение доставлено
Примечание: если у вас есть немного ETH в Arbitrum и POL в Polygon, можно воспользоваться развернутыми мной смарт-контрактами и проверить, как это работает в мейннете. Это обойдется всего в несколько центов, но иногда проще заплатить, чем искать тестовые токены.
Для этого:
-
Откройте Remix.
-
Подключитесь к контрактам по указанным ниже адресам (через At Address).
-
Отправьте текстовое сообщение.
Важно: работает только в одном направлении — Arbitrum → Polygon.
Смарт-контракты:
-
Arbitrum:
-
SourceOApp: 0x6b5ebc0bdb2572533f1cbe09026663a524421594
-
EID: 30110
-
-
Polygon:
-
DestinationOApp: 0x0c02cb84eef5f3ea61be9dfec7f884dffc1fa6c0
-
EID: 30109
-
Примеры транзакций:
Когда работаешь в мейннете, волей-неволей начинаешь все делать внимательнее и глубже вникаешь в детали. Хотя даже это не спасает от ошибок 😁. Я, например, несколько раз накосячил — указывал неверный EID и забывал отправить нативные токены. Так что рекомендую попробовать!
Мы с коллегами периодически пишем в нашем Telegram-канале. Иногда это просто мысли вслух, иногда какие-то наблюдения с проектной практики. Не всегда всё оформляем в статьи, иногда проще написать пост в телегу. Так что, если интересно, что у нас в работе и что обсуждаем, можете заглянуть.
Заключение
Теперь можно задеплоить DestinationOApp в любой другой блокчейн (включая не-EVM сети), добавить новый peer в SourceOApp и отправлять туда сообщения.
Это была своего рода разминка. Возможно вам этого будет достаточно, но если есть желание разобраться в OApp глубже, я написал отдельную статью по созданию OFT (Omnichain Fungible Token). OFT — это тоже OApp, только с дополнительной логикой для пересылки токена между сетями. Об этом будет часть 2.
Ссылки
ссылка на оригинал статьи https://habr.com/ru/articles/899020/
Добавить комментарий