Минт NFT из Ethereum или OP Mainnet
April 16th, 2024

В этом гостевом посте от Kiwi News обсуждается, как получить доступ к ликвидности Ethereum, сделав чеканку NFT доступной как на L1, так и на L2.

Основная сеть Ethereum по-прежнему обладает наибольшей ликвидностью ETH — на момент написания статьи примерно в 100 раз больше, чем OP . В то же время мы все хотим, чтобы пользователи взаимодействовали со смарт-контрактами L2, чтобы им не приходилось платить комиссию в размере 50 долларов США за каждое простое действие.

Итак, как мы можем использовать ликвидность Mainnet, используя контракты L2 для чеканки NFT?

Именно с такой дилеммой мы столкнулись с нашим проектом — Kiwi News . Это агрегатор ссылок, похожий на Hacker News, ориентированный на криптотехнологии, продукты и культурный контент.

Kiwi — это и приложение, и протокол, построенный на алгоритме, аналогичном алгоритму Farcaster (он называется « согласование наборов »). Это означает, что каждый может разветвить сеть Kiwi и запустить собственное приложение, получая доступ к нашему контенту с помощью различных алгоритмов, модерации и так далее.

Но чтобы на самом деле использовать приложение для голосования, отправки ссылок и комментариев, наши пользователи должны сначала купить наш NFT. Точно так же, как вам нужно заплатить газ для отправки транзакции Ethereum или вам нужно заплатить при создании учетной записи Farcaster на Kiwi News, вам необходимо создать OP NFT для участия.

Но на самом деле мы не начинали продажу NFT в OP Mainnet. Наш NFT сначала был доступен только на Ethereum, но, как оказалось, во время мании $PEPE, чеканка нашего NFT стоимостью 15 долларов стоила 19 долларов . Поэтому мы решили перейти на OP Mainnet.

И это действительно казалось беспроигрышным вариантом: наши пользователи платили меньше, а мы зарабатывали столько же и технически могли даже повысить цены.

Но мы быстро поняли, что у большинства людей — даже давних пользователей Ethereum — не было денег, подключенных к OP Mainnet. Конечно, они могли бы перейти на мост, но их удаление с нашего сайта означало бы общее снижение конверсии продаж. И, как гласит старая мантра распределения, вы должны «ловить рыбу там, где она есть».

Итак, мы решили решить эту проблему, сделав добычу NFT доступной как на L1, так и на L2.

Технические детали

Вот в чем проблема: наш NFT-контракт находится на уровне L2, и есть только функция Zora mintWithRewards(...), вызываемая для создания NFT. Тем не менее, новый пользователь Kiwi News может иметь средства только в основной сети Ethereum.

Если у них есть средства в основной сети OP, то это здорово; они могут просто покупать и выпускать NFT напрямую.

Однако, если у них есть средства в основной сети Ethereum, нам придется сначала заставить их соединить эти средства с основной сетью OP и, в идеале, купить NFT во время процесса соединения.

Мы ни при каких обстоятельствах не хотим, чтобы пользователь подписывал несколько транзакций, поскольку это приведет к увеличению оттока клиентов и сделает весь процесс более громоздким. Необходимость подтверждения нескольких транзакций подряд означает более высокую вероятность того, что что-то пойдет не так, поэтому мы начали искать способы объединить мост и атомарную покупку NFT.

Итак, давайте теперь углубимся в реальный код.

Прежде всего, вы должны понимать, что система Optimism имеет API как на L1, так и на L2. Например, вы можете вызвать контракт на L2, чтобы вывести свои средства в L1. Или вы можете вызвать функцию на L1, чтобы внести средства непосредственно на L2.

Эти интерфейсы особенно полезны в нашем случае, поскольку OptimismPortal ( Etherscan , исходный код ) в основной сети Ethereum имеет функцию под названием OptimismPortal.depositTransaction(...):

/// @notice Accepts deposits of ETH and data, and emits a TransactionDeposited event for use in
///         deriving deposit transactions. Note that if a deposit is made by a contract, its
///         address will be aliased when retrieved using `tx.origin` or `msg.sender`. Consider
///         using the CrossDomainMessenger contracts for a simpler developer experience.

/// @param _to         Target address on L2.
/// @param _value      ETH value to send to the recipient.
/// @param _gasLimit   Amount of L2 gas to purchase by burning gas on L1.
/// @param _isCreation Whether or not the transaction is a contract creation.
/// @param _data       Data to trigger the recipient with.

function depositTransaction(
    address to,
    uint256 value,
    uint64 gasLimit,
    bool isCreation,
    bytes memory data
)
public
payable;

Обратите внимание, насколько depositTransaction(...)аргументы похожи на имя обычной транзакции Ethereum? Есть address touint256 valueи bytes memory dataкодировка деталей вызова функции. uint64 gasLimitЗдесь тоже нет ничего удивительного: определение максимального количества газа, которое может использовать транзакция, и bool isCreationнеобходимости bytes memory dataсоздания нового контракта.

depositTransaction(...)Аргументы аналогичны, поскольку внутри процесса мостового соединения узел основной сети OP отправит транзакцию в основную сеть OP с соответствующими параметрами, когда процесс мостового соединения вызывающего абонента L1 завершится успешно.

Это особенно полезно для нас, когда мы хотим создать NFT с использованием mintWithRewards(...)функции Zora в основной сети OP, а также позволить пользователям вызывать эту функцию из основной сети Ethereum без отдельных транзакций моста и чеканки.

Итак, давайте углубимся в код. Ниже приведен интерфейс нашего контракта на сбор NFT в основной сети OP, развернутого через Zora:

function mintWithRewards(
    address recipient,
    uint256 quantity,
    string calldata comment,
    address mintReferral
) external payable returns (uint256);

Для функции требуется address recipientполучатель NFT, a uint256 quantityдля определения количества NTF, a string calldata commentдля комментариев в цепочке и для address mintReferralуказания реферера за рекомендацию монетного двора.

Итак, если мы возьмем на себя работу внешнего инженера, которому поручено создать кнопку монетного двора NFT, которая работает как в основной сети OP, так и в основной сети ETH, вот пошаговый процесс того, что нам нужно будет вычислить:

  1. Проверьте баланс ETH пользователя в OP Mainnet. Если пользователь может позволить себе чеканить NFT, предложите ему позвонить mintWithRewards(...)непосредственно в Optimism.

  2. Если у пользователя недостаточно ETH в основной сети OP, проверьте баланс пользователя в основной сети Ethereum. Если пользователь не может позволить себе купить NFT в сети ETH, сообщите ему об этом; в противном случае перейдите к шагу 3.

import { fetchBalance, getAccount } from "@wagmi/core";
import { mainnet, optimism } from "wagmi/chains";
Import { utils } from “ethers”;

const { address } = getAccount();
if (!address) {
  throw new Error("Account not available");
}

const balance = {
  mainnet: (await fetchBalance({ address, chainId: mainnet.id })).value,
  optimism: (await fetchBalance({ address, chainId: optimism.id })).value,
};

const mintPriceETH = utils.parseEther(“0.00256”);

if (balance.optimism >= mintPriceETH) {
  // mint on OP mainnet
} else if (balance.mainnet >= mintPriceETH) {
  // mint on ETH mainnet
} else {
  throw new Error(“Insufficient balance”);
}
  1. Теперь, когда мы знаем, что пользователь собирается создать NFT из сети ETH через OptimismPortal, нам нужно подготовить два вызова ETH. Один предназначен для вызова depositTransaction(...)L1, другой — для вызова mintWithRewards(...)L2. Мы передадим второй вызов, предназначенный для выполнения в основной сети OP, в depositTransaction(...)файл bytes memory data. Вот как мы это делаем:

3.1. Мы строим данные вызова функции, mintWithRewards(...)собирая входные данные для mintWithRewards(...)функции на L2. Мы используем эфиры interface.encodeFunctionData(name, [...inputs])для упаковки вызова в шестнадцатеричную строку.

import { getAccount } from "@wagmi/core";
import { Contract } from "@ethersproject/contracts";
import { mainnet, optimism } from "wagmi/chains";

import { getProvider } from “./viem-adapter.mjs”;

const nftAddress = 0xabc…;
const nftABI = [{...}];

function prepareL2Call() {
  const { address } = getAccount();
  const opProvider = getProvider({ chainId: optimism.id });
  const contract = new Contract(nftAddress, nftABI, opProvider);
  const recipient = address;
  const quantity = 1;
  const comment = “minting this from mainnet!”
  const referrer = null;
  return contract.interface.encodeFunctionData("mintWithRewards", [
    recipient,
    quantity,
    comment,
    referrer,
  ]);
}

3.2. Затем depositTransaction(...)мы выбираем цель вызова функции как address toи устанавливаем uint256 valueзначение ETH, которое хотим передать address to. Что касается uint64 gasLimit, мы должны смоделировать стоимость вызова ETH на Optimism с балансом пользователя. Однако баланс ETH пользователя недостаточен, помните? Таким образом, любой estimateGasвызов с провайдером основной сети OP и адресом пользователя будет ошибочным.

Следовательно, мы предлагаем обнаружить статическую гостевую функцию, например, вызвав mintWithRewards(...)функцию вручную и используя это значение в коде.

Что касается других аргументов, bool isCreationis falseи bytes memory dataнаконец содержит данные вызова, сгенерированные на шаге 3.1.

import { prepareWriteContract } from "@wagmi/core";
import { mainnet } from "wagmi/chains";

const optimismPortalAddress = 0x…;
const optimismPortalABI = [{...}, …];

async function writeToDeposit(nftAddress, price, data) {
  const isCreation = false;
  const gasLimit = 170000;

  return await prepareWriteContract({
    address: optimismPortalAddress,
    abi: optimismPortalABI,
    functionName: "depositTransaction",
    args: [nftAddress, price, gasLimit, isCreation, data],
    value: price,
    chainId: mainnet.id,
  });
}

3.3. Важно отметить, что нам придется внести как минимум сумму, равную uint256 valueили превышающую сумму msg.valueвызова L1. Это гарантирует, что некоторое количество эфира будет помещено в Optimism и доступно для mintWithRewards(...)вызова. Мы делаем это, установив msg.valueзначение price.

3.4. Наконец, собрав все аргументы, мы предлагаем пользователю подписать эту транзакцию для основной сети Ethereum.

import { useContractWrite } from "wagmi";
// …

function prepareL2Call(...) {...}
async function writeToDeposit(...) {...}
// …


const BuyButton = (props) => {
  // …
  const { write } = useContractWrite(config);
  // …

  return (
    <button disabled={!write} onClick={() => write?.()}>
    Buy NFT
    </button>
  );
}

Вот и все! Это то, что вам нужно сделать, если вы хотите атомарно создать NFT на Optimism, обеспечивая вызов непосредственно из основной сети Ethereum.

Хотя фрагменты кода не совсем соответствуют рабочему коду, который мы используем в Kiwi News, они очень близки к коду, исходный код которого мы открыли. Полную последовательность подготовки и многое другое вы можете найти на нашем GitHub . И вы можете попробовать это напрямую, зайдя на нашу страницу минтинга NFT .

Разумеется, использование depositTransaction(...)функции OptimismPortal — не единственный вариант перехода с уровня L1 на уровень L2. На данный момент существует множество бирж, предлагающих аналогичные услуги. Однако особенность портала OP заключается в том, что он сохраняет адрес msg.senderтого адреса, который подписал и отправил транзакцию в основной сети ETH. Это может быть особенно важно, когда ваше децентрализованное приложение полагает, что этот адрес должен быть законным.

Заключение

При переносе нашего NFT из Ethereum в OP Mainnet нам нужен был способ использовать ликвидность L1, в то же время позволяя всем нашим пользователям чеканить его из OP Mainnet.

OptimismPortal предоставляет удобную функциональность для соединения средств и выполнения вызова ETH в одной атомарной транзакции, что обеспечивает высокую вероятность успеха, когда пользователь отправляет вызов.

Мы подробно описали наш код React.js, который проверяет балансы Ethereum и L2, чтобы обеспечить правильный вызов для каждого пользователя. Если у пользователя нет OP ETH, он подключается напрямую через портал.

В будущем мы с нетерпением ждем новых таких интересных технических возможностей. Например, нам бы хотелось иметь возможность атомарно использовать ликвидность цепочки Base с помощью аналогичной транзакции на портале, чтобы сделать наш код более доступным для вызова для всех пользователей Ethereum.

Мы надеемся, что этот пост был полезен вам в вашей работе. В Kiwi News мы всегда работаем над тем, чтобы максимально эффективно обслуживать экосистему — будь то последние новости, сообщения в блогах или репозитории GitHub. Мы будем рады видеть вас среди читателей! Посетите нас на https://kiwinews.xyz .

Перевод оригинальной статьи от 16 апреля 2024

Subscribe to ✨🔴leisan🔴✨
Receive the latest updates directly to your inbox.
Nft graphic
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.
More from ✨🔴leisan🔴✨

Skeleton

Skeleton

Skeleton