Программная загрузка NFT в OpenSea.io с помощью JavaScript

В следующем посте я расскажу, как я использовал простой скрипт для программного создания набора уникальных NFT на OpenSea.io. Обратите внимание, что это просто демонстрация того, как использовать dAppeteer/Puppeteer и JavaScript (точнее, TypeScript) для автоматизации веб-сайтов. Я не рекомендую использовать любой из приведенных ниже кодов для взаимодействия с OpenSea.io или любыми другими сторонними веб-сайтами. Пожалуйста, обязательно ознакомьтесь с соответствующими условиями обслуживания веб-сайта, с которым вы взаимодействуете, чтобы избежать постоянного запрета и удаления вашего контента. Пожалуйста, не используйте этот скрипт для создания 100 000 токенов на OpenSea.io — это был просто яркий заголовок, пытающийся привлечь ваше внимание ¯\_(ツ)_/¯. При этом использование сценариев для автоматизации ручных задач — отличный способ изучить JavaScript, HTML и способы программного взаимодействия со сторонними веб-сайтами.

Почему OpenSea.io?

После тщательной обработки изображений NFT вы, вероятно, захотите опубликовать и предложить их на рынке прямо сейчас. Обычно на этом этапе вы обнаружите, что чеканка большого количества NFT на самом деле может быть самой сложной задачей. Если вы не хотите заключать контракт Solidity, заводить аккаунты в Pinata и Alchemy и тратить сотни долларов на оплату газа, то OpenSea.io предлагает удобную бесплатную альтернативу, которая не требует дополнительных затрат. даже требуют смарт-контракта. Да, минтинг на OpenSea.io абсолютно бесплатный и очень простой!

К сожалению, OpenSea.io не предлагает API для загрузки/чеканки ваших токенов. Создание нового токена с помощью их веб-сайта — это быстрый, но все же ручной и утомительный процесс. Создание сотен NFT вручную, безусловно, заняло бы некоторое время… Итак, я попытался выяснить, как я мог бы вместо этого автоматизировать эти шаги.

Предпосылки

Вам понадобится мнемоническая фраза для вашего кошелька MetaMask (также известная как Секретная фраза восстановления), которая должна получать новоиспечённые NFT. Код ниже будет использовать фразу для автоматической настройки и подключения вашего кошелька. Тем не менее, эта фраза является секретом, поэтому не делитесь ею ни с кем и никогда не отправляйте ее в git.

Также потребуется учетная запись OpenSea.io, подключив тот же кошелек MetaMask и создав новую пустую коллекцию. Обратите внимание на имя и URL-адрес коллекции. Он понадобится вам ниже.

Наконец, и это само собой разумеется, вам также понадобится набор изображений (или видео/аудиофайлов), которые вы хотите превратить в коллекцию NFT.

Установите пакеты

Давайте сначала создадим новый каталог, в котором будут храниться код и изображения, которые мы хотим превратить в NFT. Перейдите в новую папку и установите первый набор пакетов NPM:

npm install esbuild esbuild-register dotenv

Это устанавливает ESBuild, чрезвычайно быстрый сборщик JS со встроенной поддержкой TypeScript, а также DotEnv для инициализации переменных среды, чтобы избежать жесткого кодирования каких-либо секретов.

Для запуска автоматизации мы используем Puppeteer, который управляет Chome или Chromium и может автоматизировать почти все на веб-сайте, который вы можете себе представить. dAppeteer — это специальная библиотека для тестирования и автоматизации web3, построенная поверх этого:

npm install @chainsafe/[email protected] puppeteer

Обратите внимание, что мы явно указываем версию 2.3.0 dAppeteer, поскольку на момент написания этой статьи у последней были некоторые проблемы совместимости с MetaMask. Вы должны быть очень конкретными, когда речь идет о версиях dAppeteer и MetaMask, чтобы обеспечить совместимость и избежать установки потенциально неподдерживаемых или ненадежных версий.

Настройте свою конфигурацию

Создайте новый файл .env и добавьте содержимое ниже. Замените значения любым именем вашей коллекции OpenSea, добавьте описание и URL-адрес, а также свою секретную фразу восстановления MetaMask:

METAMASK_MNEMONIC_PHRASE="add your twelve word secret metamask recovery phrase here"
COLLECTION_NAME="my-first-collection"
DESCRIPTION="My first NFT created in OpenSea"
URL=https://example.com

Сохраните файл, но никогда не добавляйте его в git! На всякий случай добавьте его в свой .gitignore. Мы будем использовать фразу восстановления для автоматизации установки MetaMask в нашем сценарии ниже. Это совершенно безопасно и никогда не покинет вашу локальную машину. Тем не менее, я рекомендую использовать дополнительный кошелек, чтобы случайно не скомпрометировать ваш основной во время экспериментов и тестирования.

Создать сценарий

Я разделю сценарий на несколько частей, чтобы было проще объяснить логику. Сначала создайте новый файл в папке проекта и назовите егоindex.ts. Затем давайте создадим заголовок нашего скрипта:

import puppeteer, { Page } from "puppeteer";
import * as dappeteer from "@chainsafe/dappeteer";
import * as DotEnv from "dotenv";
import fs from "fs";
import path from "path";
DotEnv.config();
const seed = process.env.METAMASK_MNEMONIC_PHRASE;
const collectionName = process.env.COLLECTION_NAME;
const createAssetURL = `https://opensea.io/collection/${collectionName}/assets/create`;
const description = process.env.DESCRIPTION;
const link = process.env.URL;
const imageDir = path.join(__dirname, "images");

Здесь мы импортируем Puppeteer, dAppeteer, DotEnv и некоторые помощники файловой системы. Вызов DotEnv.config() автоматически загрузит содержимое файла .env и инициализирует наши переменные среды.

После инициализации зависимостей задаем пару статических переменных: секретную фразу, название коллекции, описание и ссылку, которая будет добавляться в каждый отчеканенный NFT. Все это основано на файле среды .env, созданном ранее. imageDir — это папка, которая будет содержать все наши изображения для загрузки.

Затем мы определяем несколько вспомогательных функций, которые будем использовать позже:

async function connectWallet(page: Page, metamask: dappeteer.Dappeteer) {
  // OpenSea gives us a list of different wallet options. MetaMask is the first one.
  console.log("Connecting to Metamask...");
  // Force list of wallets to refresh as otherwise OpenSea sometimes doesn't detect MetaMask properly
  const moreButton = await page.$x('//button[contains(.,"Show more options")]');
  await moreButton[0].click();
  await page.waitForTimeout(1000);
  // Find the MetaMask button and click it
  const metaMaskButton = await page.$x(
    '//button[.//span[contains(text(),"MetaMask")]]'
  );
  await metaMaskButton[0].click();
  await page.waitForTimeout(2000);
  await metamask.approve();
  console.log("Wallet connected");
}

connectWallet ищет кнопку «MetaMask» на текущем page, нажимает ее и подключает ваш кошелек MetaMask. Во время тестирования я обнаружил одно предостережение: иногда OpenSea не распознает плагин MetaMask должным образом. Поэтому мы сначала инициируем еще один щелчок по списку кошельков, чтобы убедиться, что он правильно обновляет все параметры.

async function uploadImage(page: Page, filepath: string) {
  const elementHandle = await page.$("#media");
  await elementHandle.uploadFile(filepath);
}

uploadImage выполняет фактическую загрузку файла. Он принимает текущее page, а также изображение filepath в качестве входных параметров.

async function fillFields(
  page: Page,
  name: string,
  description: string,
  link: string
) {
  // Get and fill in the input name
  await page.focus("#name");
  await page.keyboard.type(name, { delay: 25 });
// Get and fill in the description
  await page.focus("#description");
  await page.keyboard.type(description, { delay: 25 });
await page.focus("#external_link");
  await page.keyboard.type(link, { delay: 25 });
}

И, наконец, fillFields заполняет форму ввода OpenSea, заполняя поля name, description и внешние поля link.

С этим готовым, теперь для основной функции:

async function main() {
  // Launch the browser with MetaMask
  const browser = await dappeteer.launch(puppeteer, {
    metamaskVersion: "v10.1.1",
  });
  const metamask = await dappeteer.setupMetamask(browser, { seed });
  // Open OpenSea.io website in a new tab
  console.log("Launching OpenSea...");
  const page = await browser.newPage();
  await page.goto(createAssetURL, { waitUntil: "networkidle0" });
  await page.bringToFront();
  // Close first empty tab
  const tabs = await browser.pages();
  await tabs[0].close();
  // Connect OpenSea with MetaMask
  await connectWallet(page, metamask);
  await page.waitForTimeout(2000);
  // The first request will need to be signed explicitly
  console.log("Sign initial request...");
  await metamask.sign();
  await page.bringToFront();
  await page.waitForTimeout(2000);
  // Read the contents of the images folder
  const files = fs.readdirSync(imageDir);
// Start the loop on each image of images folder
  for (const file of files) {
    if (file === ".DS_Store") {
      continue; // Skip macOS hidden file
    }
    // On every iteration (re-)open the asset creation page
    console.log("Creating new asset...");
    const filepath = path.join(imageDir, file);
    await page.bringToFront();
    await page.goto(createAssetURL);
    await page.waitForSelector("#media"); // wait for the upload button to be available
    // Upload the current image file
    await uploadImage(page, filepath);
    // Fill the fields using the asset name with the count
    const name = file.split(".")[0];
    await fillFields(page, name, description, link);
    console.log(`Minting NFT: ${name}...`);
    const createButton = await page.$x('//button[contains(., "Create")]');
    await createButton[0].click();
    // Now wait for the success popup to appear
    console.log("Waiting for success popup...");
    await page.waitForSelector(
      "div.AssetSuccessModalContentreact__DivContainer-sc-1vt1rp8-1"
    );
    await page.waitForTimeout(1000);
    // Delete the local files to remember which ones we already uploaded
    fs.rmSync(filepath);
  }
  console.log("Successfully minted all NFTs");
}
main();

Давайте рассмотрим это более подробно:

  1. Сначала запускаем браузер и настраиваем Metamask. Здесь мы используем переменную окружения process.env.METAMASK_MNEMONIC_PHRASE, чтобы избежать жесткого кодирования ее в нашем коде.
  2. Затем мы открываем OpenSea.io в новом окне браузера и подключаем его к нашему кошельку MetaMask.
  3. После подключения кошелька OpenSea автоматически попросит нас подписать первоначальный запрос. Что ж, мы тоже так сделаем.
  4. Теперь мы можем прочитать содержимое нашего imageDir, а затем перебрать каждый файл, чтобы создать соответствующий новый актив NFT.
  5. Для каждого актива мы сначала перезагружаем веб-сайт создания актива, загружаем локальный файл изображения, а затем заполняем оставшуюся часть формы. Имя актива основано на имени файла. Мы просто обрываем расширение файла (т. е. .png или .jpg) и используем остаток в качестве нашего имени. Вы можете адаптировать эту логику ко всему, что имеет для вас смысл.
  6. Наконец, мы используем селекторы XPath, чтобы найти кнопку Создать внизу страницы и нажать на нее. Это инициирует чеканку, которая может занять пару секунд.
  7. После создания актива нам придется подождать, пока OpenSea подтвердит успешную чеканку нашего токена. Мы делаем это, ожидая появления определенного всплывающего окна. Затем мы можем перейти к следующему токену.
  8. После создания нового актива в OpenSea мы удаляем локальный файл изображения, чтобы отслеживать, какой из них мы загрузили ранее, и избежать случайного создания дубликатов.

Между разными этапами есть несколько циклов сна (page.waitForTimeout), в которых мы просто ждем, пока браузер и MetaMask завершат загрузку.

Приведенный выше код должен быть относительно простым для отслеживания и обновления. Если вы хотите внести изменения и добавить дополнительную информацию об активе, вы можете просто расширить функцию fillFields своей собственной логикой.

Выгода!

Теперь, когда наш сценарий готов, пришло время его запустить!

Создайте новый подкаталог images на том же уровне, что и файл index.ts. Затем поместите копию всех ваших изображений, которые вы хотите превратить в NFT, в эту папку. Имя каждого файла (без расширения файла) будет использоваться в качестве имени актива NFT в OpenSea. Сделайте копию исходных изображений, так как скрипт удалит содержимое папки images, чтобы отслеживать, какие из них он уже обработал.

Затем наступает момент истины... Запускаем скрипт:

node -r esbuild-register index.ts

Это будет использовать ESBuild для компиляции нашего скрипта и запуска его с помощью Node.js. Вы увидите, как откроется новое окно браузера, и скрипт выполнит свою магию. Не взаимодействуйте вручную ни с одним из окон, но пусть он работает без помех. В противном случае вы можете прервать поток или прервать процесс. Дайте ему немного времени, пока мы создаем один ресурс за другим, и дайте OpenSea.io достаточно времени для загрузки и обработки, не перегружая его.

Что дальше?

Код выше доступен на GitHub: https://github.com/arabold/opensea-uploader:



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