Как создать веб-сайт NFT

В этой статье вы узнаете, как создать веб-сайт для майнинга NFT, аналогичный ExoBitsNFT.com. Предполагается, что у вас есть некоторые знания о невзаимозаменяемых токенах, криптовалюте, смарт-контрактах и ​​горячих кошельках, таких как MetaMask. Если вы не знакомы с этими технологиями, этот учебник даст вам все необходимое для начала работы. Однако настоятельно рекомендуется получить хорошее представление об этих концепциях, прежде чем приступать к собственному проекту NFT.

Почему НФТ?

Когда я объясняю людям мир невзаимозаменяемых токенов, реакция неизменно представляет собой какой-то растерянный взгляд, за которым следует «Я не понимаю. Это просто JPEG». Я должен признать, что у меня схожие чувства по отношению к современной культуре аватаров для курения сигарет и абстрактного искусства. Ценность, которую я вижу в технологии NFT, заключается в ее способности создавать собственность на цифровые товары. Цифровые продукты, которые могут повышаться в цене и передаваться таким же образом, как ювелирные изделия или коллекционные книги. В настоящее время электронная книга не имеет внутренней ценности. Ваша копия по-прежнему принадлежит издателю, она никогда не может быть перепродана. Конечно, его можно копировать бесконечное количество раз, но эти копии никому не принадлежат, поэтому они не имеют никакой ценности. NFT этой электронной книги является «вашей копией» и может быть продан кому-либо, чтобы стать «их копией». Вот что придает ему ценность.

Мой интерес к NFT заключается не в том, чтобы перевернуть следующий Crypto Punk. Речь идет об изучении технологии, которая, как я считаю, является будущим всего DRM. Я программист по профессии, но я всегда включал искусство и дизайн (и, конечно же, инопланетян) в свою работу. Изучение технологии NFT позволяет мне делать и то, и другое наиболее привлекательным способом.

Когда я приступил к созданию сайта для своего первого NFT-проекта ExoBits, мне пришлось собирать информацию из разрозненных источников. Эта статья служит для закрепления этих знаний, иллюстрируя, как развернуть смарт-контракт и написать JavaScript, необходимый для взаимодействия с ним. Если вам понравилась эта статья, перейдите на ExoBitsNFT.com и создайте свой собственный ExoBit NFT. Эфир, полученный от ExoBits, идет на финансирование будущих проектов NFT и учебных пособий, подобных этому.

Демонстрационный проект

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

Чтобы получить проект ReExoBits, перейдите на https://github.com/mwilber/nft-minting-website-example и загрузите или клонируйте копию репозитория.

Развертывание контракта

В этом разделе описаны шаги, необходимые для развертывания смарт-контракта ReExoBits.sol в тестовой сети Rinkeby. Если вы уже знакомы с развертыванием контракта Ethereum, вы можете использовать предпочитаемый метод и перейти к следующему разделу.

В этой части руководства мы собираемся использовать Remix, бесплатную браузерную IDE, которая предоставляет все необходимое для разработки, компиляции и развертывания смарт-контракта Ethereum. Чтобы начать использовать Remix, просто перейдите на https://remix.ethereum.org/, и все готово.

Когда вы начнете, вы увидите файловый браузер на левой боковой панели. Там будет папка «контракты» с примерами контрактов. Если вы изучаете смарт-контракты и язык Solidity, это полезные примеры. Игнорируйте их пока, потому что мы собираемся использовать подготовленный контракт из примера проекта.

Выделив папку с контрактами, нажмите кнопку «Создать новый файл» выше. Назовите новый файл «ReExoBits.sol». Если файл не открывается автоматически в редакторе кода, дважды щелкните его, чтобы открыть. Вернувшись к примеру проекта, найдите файл «Contract/ReExoBits.sol». Откройте этот файл в своем любимом текстовом редакторе и скопируйте его содержимое в файл «ReExoBits.sol» в Remix.

Теперь, когда у вас есть исходный код «ReExoBits.sol» в Remix, пришло время скомпилировать контракт. Нажмите кнопку «Компилятор» в боковой панели навигации, чтобы открыть параметры компилятора. Нажмите большую синюю кнопку «Скомпилировать ReExoBits.sol». Кнопка «Компилятор» в боковой навигации будет отмечена зеленой галочкой, когда ваш контракт будет скомпилирован.

На этом этапе вам понадобится установленный и настроенный в вашем браузере кошелек MetaMask. Если он у вас еще не установлен, перейдите на веб-сайт MetaMask, чтобы установить и настроить новый адрес кошелька. Нажмите на значок Metamask Fox и подождите секунду или две, пока не появится интерфейс кошелька. В меню выбора вверху, скорее всего, будет написано Ethereum Mainnet, щелкните по нему и выберите Rinkeby Test Network.

Теперь нам нужно получить немного свободных денег для тестирования. Нажмите кнопку «Купить», и откроется всплывающее окно. Прокрутите вниз и нажмите «Получить эфир» в разделе «Проверить сборщик». Откроется домашняя страница Ринкеби. Нажмите кнопку «Crypto Faucet» и следуйте инструкциям на появившейся странице, чтобы получить эфир в свой кошелек. Если вы никогда раньше не работали с тестовой сетью, важно понимать, что этот эфир работает точно так же, как и в основной сети, только исключительно в этой тестовой сети Ринкеби. Его нельзя обменять на эфир в основной сети или другие валюты. ПРИМЕЧАНИЕ. На момент написания этой статьи сборщик, связанный с MetaMask, получает огромное количество трафика. Если ваша первая попытка не удалась, повторите попытку позже или найдите альтернативный «Кран Ринкеби».

Вернувшись к Remix, нажмите кнопку «Развернуть» в боковой панели навигации, чтобы открыть панель развертывания слева. Здесь вы публикуете свой контракт в блокчейне. Самое верхнее поле выбора, помеченное как «Среда», позволяет указать, где развернут ваш контракт. Выберите «Введенный Web3». Это связывает Remix с вашим кошельком MetaMask. Под выбором должна быть указана сеть, к которой подключен ваш кошелек, в данном случае «Rinkeby». Чуть ниже вы увидите поле выбора с надписью «Контракт». Убедитесь, что выбран «ReExoBits.sol». Наконец, нажмите кнопку «Развернуть», чтобы развернуть ваш контракт в тестовой сети Ринкеби. Вскоре после этого вы должны увидеть его на панели под «Развернутыми контрактами».

Оставьте Remix открытым, потому что мы собираемся вернуться к нему через мгновение.

Контрактные функции

Контракт в этом примере является базовым контрактом ERC721 NFT. Он основан на библиотеке OpenZeplin, наборе контрактов Ethereum с открытым исходным кодом, которые проверяются на безопасность и точность. OpenZeplin дает нам все необходимое, чтобы начать чеканить токены прямо из коробки. В стандарте ERC721 довольно много, но это руководство будет сосредоточено только на нескольких функциях, необходимых для примера веб-сайта:

  • tokenUri — стандартная функция ERC721, которая возвращает URI, прикрепленный к токену. Он принимает один целочисленный параметр для идентификатора токена.
  • totalSupply — стандартная функция ERC721, которая возвращает положительное целое число, представляющее общее количество токенов, хранящихся в контракте.
  • CustomMint — пользовательская функция, которая будет вызывать функцию safemint OpenZeplin для создания нашего NFT. Он принимает один строковый параметр, который является общедоступным URI, который нужно прикрепить к токену. Как правило, это указывает на сервер ipfs, но в этом примере это указывает на наш сервер разработки.
  • tokenByUri — пользовательская функция, которая возвращает идентификатор токена, связанный с предоставленным URI, или 0, если он не найден.
  • tokensOfOwner — возвращает массив идентификаторов токенов, принадлежащих указанному адресу кошелька.

Примечание об этом контракте. Для простоты этот контракт только прикрепляет к токену URI. Хотя стандарт ERC721 требует только присоединения URI, обычно вы должны хранить дополнительную информацию, такую ​​как уникальный идентификатор, связанный с активом. Настоятельно рекомендуется найти дополнительные учебные пособия по написанию контрактов, чтобы увидеть широкий спектр возможностей, доступных в смарт-контрактах.

Адрес контракта и ABI

Есть две критически важные части информации, необходимые для подключения вашего веб-сайта на основе JavaScript к вашему контракту в блокчейне Ethereum: адрес контракта и двоичный интерфейс приложения контракта (ABI). Если вы не знакомы с ABI, подумайте об этом как об исходной карте. Это просто объект JSON с массивом, который определяет все свойства вашего контракта. Он предоставляет всю информацию, необходимую приложению JavaScript для ссылки на них. Вы можете получить как адрес контракта, так и ABI из Remix.

Сначала откройте образец проекта в своем любимом редакторе кода. Откройте файл /src/components/Login.jsx. Найдите строку, в которой определена переменная contractAddress. Удалите существующий адрес в кавычках. Существующий адрес контракта указывает на тестовый контракт, использованный при написании этой статьи, и может быть недоступен к тому времени, когда вы будете это читать. Кроме того, вы захотите использовать свой новый контракт, который начинается заново.

Вернитесь в Remix и найдите название своего контракта в нижней части боковой панели в разделе «Развернутые контракты». Справа от названия есть кнопка «Копировать в буфер обмена». Щелкните ее, чтобы скопировать развернутый адрес контракта. Вставьте этот адрес в файл Login.jsx, чтобы задать для переменной contractAddress адрес вашего контракта.

Снова вернитесь в Remix и нажмите кнопку «Компилятор» в боковой панели навигации, чтобы вернуться на вкладку «Компилятор». В нижней части боковой панели есть поле выбора с надписью «Контракт». Убедитесь, что выбран «ReExoBits.sol». Нажмите на кнопку «Сведения о компиляции». В появившемся всплывающем окне вы увидите элемент с надписью «ABI», нажмите кнопку «Копировать» рядом с ним, чтобы скопировать массив ABI в буфер обмена. Затем вернитесь к исходному коду проекта и откройте файл /src/contract/ReExoBits.json. Полностью сотрите содержимое файла и вставьте только что скопированный массив ABI. В примере контракта этот ABI должен быть точно таким же, как тот, который вы только что удалили. Однако, если вы когда-нибудь внесете какие-либо изменения в контракт, вам нужно будет обновить массив ABI проекта таким образом.

Веб3

Web3.js — это библиотека с открытым исходным кодом, используемая для взаимодействия с блокчейном Ethereum из среды JavaScript. Важно понимать, что Web3.js не имеет прямого доступа к блокчейну. Он должен подключаться к серверу, который предоставляет интерфейс JSON-RPC для узла блокчейна. Сервер может поступать из разных источников. В этом примере ваш кошелек MetaMask предоставит этот сервер.

С помощью Web3.js вы сможете сделать следующее: получить адрес своего общедоступного кошелька, подключиться к смарт-контракту Ethereum, прочитать данные из вашего контракта (например, получить URI токена), вызвать функции в вашем контракте (например, получить список токенов, принадлежащих адресу) и выполнять транзакции в вашем контракте (например, создавать токены). Этот последний пункт, выполнение транзакций в любом контракте, является чем-то вроде особого случая. При чтении данных из контракта вы не меняете его состояние. Эти виды операций бесплатны, поскольку не требуют обновления блокчейна. С другой стороны, транзакция изменяет состояние контракта и поэтому должна быть заминирована для обновления блокчейна. Для транзакций требуется плата за газ. В этом примере мы будем использовать бесплатный тестовый эфир, который мы получили от крана Ринкеби. Но в основном блокчейне вы должны использовать настоящий эфир, который обычно добывается за реальные деньги.

Тестовый забег

На этом этапе вы можете запустить проект и просмотреть веб-сайт с предоставленным сервером разработки. Сначала мы установим модули npm, необходимые для сборки и размещения проекта разработки. Большинство модулей узла, которые мы устанавливаем, предназначены для разработки, но на самом веб-сайте используются два модуля: Web3.js и React. React используется только для упрощения разработки самого веб-сайта. Если вы не знакомы с React или предпочитаете другое решение, весь код проекта, взаимодействующий с блокчейном, представляет собой обычный JavaScript и легко адаптируется к другим средам.

Из папки проекта в командной строке введите:

npm install

Когда установка будет завершена, соберите проект и запустите тестовый сервер:

npm start

После запуска вы можете получить доступ к веб-серверу в своем браузере, используя адрес:

http://локальный:3000

Подключение кошелька

При загрузке на сайте появится большая кнопка «Подключить кошелек». Нажмите эту кнопку, и вы получите приглашение от MetaMask войти в систему и авторизовать веб-сайт. После этого кнопка подключения заменяется меню навигации по сайту. Негласно был получен и адрес вашего кошелька, и ссылка на контракт.

Давайте рассмотрим код, необходимый для этого. Откройте файл /src/components/Login.jsx. Когда вы нажимаете кнопку «Подключить кошелек», вызывается локальная функция DoConnect():

const DoConnect = async () => {
  console.log(‘Connecting….’);
  try {
    const web3 = new Web3(Web3.givenProvider || 
                          “ws://localhost:8545”);
    await window.ethereum.request({ method: ‘eth_requestAccounts’ })
    const accounts = await web3.eth.getAccounts();
    const instance = new web3.eth.Contract(
      ExobitsABI,
      contractAddress
    );
    props.callback({ web3, accounts, contract: instance });
  } catch (error) {
    console.error(“Could not connect to wallet.”, error);
  }
};

Первое, что делает функция, — создает экземпляр нового объекта Web3. В той же строке Web3.givenProvider указывает на сервер JSON-RPC, указанный в MetaMask. Если вы используете другого провайдера, вы должны указать здесь. Адрес localhost является общим запасным вариантом для локальной разработки и здесь не используется.

Следующая строка вызывает window.ethereum.request(), запрашивая доступ к провайдеру и, в данном случае, предлагает MetaMask авторизовать веб-сайт. Свойство Ethereum внедряется в объект window с помощью MetaMask. После авторизации мы можем получить доступ к вашему контракту в блокчейне через MetaMask и его интерфейс JSON-RPC.

Следующая строка извлекает адрес подключенного кошелька, вызывая функцию Web3.js getAccounts(). Функция возвращает массив доступных адресов с активным адресом в позиции 0. Следующая строка создает экземпляр объекта Web3.js Contract, который используется для взаимодействия с контрактом. Конструктор принимает ABI контракта и адрес в качестве параметров.

Адрес кошелька и объект контракта — это все, что вам нужно, чтобы начать взаимодействовать с вашим активным контрактом в блокчейне. В этом примере эти объекты передаются в функцию обратного вызова OnLogin() в /src/App.js, чтобы распространить их на отдельные компоненты React, составляющие Веб-сайт. Этот последний шаг зависит от реализации веб-сайта.

Чеканка токенов

Теперь, когда вы подключены к блокчейну через службу JSON-RPC в MetaMask, вы можете начать взаимодействовать со своим контрактом. Одной из ключевых функций, которые вам понадобятся на вашем веб-сайте NFT, является возможность создавать или чеканить новые токены. Демонстрационный проект поставляется с 4 предопределенными активами, готовыми к работе, вы найдете их в каталоге /public/token_data/. Каждый актив представляет собой файл png с сопровождающим файлом метаданных json. URI для файла json — это то, что вы передадите функции монетного двора. Он содержит данные в стандартном формате, который будут понимать сторонние галереи, такие как OpenSea. Именно через этот файл данных json ваш NFT в конечном итоге указывает на ресурс изображения.

Давайте попробуем отчеканить токен. На веб-сайте-примере нажмите ссылку «Mint» в верхней части навигации. Вам представлены четыре возможных актива для чеканки. Чеканка — это транзакция, и поэтому для ее завершения необходимо заплатить «газом». Кроме того, в нашем примере контракта также взимается комиссия за выпуск 0,001 эфира, выплачиваемая владельцу контракта. Выберите понравившееся изображение и нажмите на него, чтобы отчеканить токен. MetaMask представит окно для подтверждения транзакции. После подтверждения вы можете следить за статусом транзакции в MetaMask. Будьте терпеливы, в зависимости от активности блокчейна чеканка может занять минуты или даже часы.

Создав свой первый токен, давайте взглянем на базовый код. Откройте файл проекта: /src/pages/Mint.jsx Код в этом файле отображает 4 изображения ресурсов. У каждого изображения есть обработчик события клика, который вызывает локальную функцию DoMint, которая, в свою очередь, вызывает функцию CustomMint в вашем контракте. Рассмотрим подробнее функцию DoMint:

const DoMint = async (tokenURI) => {
  try{
    let gasLimit = await props.contract.methods.
      CustomMint(tokenURI).estimateGas({
        from: props.address,
        value: 100000000000000
      });
    let result = await props.contract.methods.
      CustomMint(tokenURI).send({
        from: props.address,
        value: 100000000000000,
        gasLimit: gasLimit
      });
    CheckAssetURIs();
  }catch(e){
    console.error('There was a problem while minting', e);
  }
};

Эта функция состоит из трех основных шагов. На первом этапе извлекается оценка платежа за газ, необходимого для завершения транзакции. Поскольку минтинг — это транзакционный процесс, т. е. обновление состояния вашего контракта, необходима плата за газ. Комиссия платит майнерам за обновление блокчейна, что закрепляет вашу транзакцию в постоянной общедоступной записи. Цены на газ сильно колеблются. Важно знать ожидаемую сумму, чтобы пользователю была представлена ​​точная цена. Вызов метода estimateGas для функции контракта подобен пробному прогону транзакции. Он проверит функцию контракта CustomMint и рассчитает ожидаемую стоимость.

Метод estimateGas принимает объект параметра, соответствующий фактической транзакции. Объект содержит адрес запрашивающей стороны, а также необязательное «значение», которое представляет собой количество эфира, переданного от запрашивающей стороны в контракт. Стоимость указана отдельно от платы за газ. Это может быть любая сумма, включая 0, и в этом примере мы решили взимать 0,001 эфира в качестве платежа владельцу контракта. Значение представлено в «Вэй», что является наименьшей единицей эфира. 0,001 эфира переводится как 100000000000000 вей.

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

Последний шаг в примере вызывает локальную функцию CheckAssetURIs для обновления коллекции. Поскольку мы только что отчеканили токен, актив больше недоступен, поэтому мы хотим удалить его со страницы чеканки. CheckAssetURIs вызывает функцию контракта tokenByUri. tokenByUri возвращает либо идентификатор токена (положительное целое число, начинающееся с 1) для запрошенного актива, либо 0, если этот актив не был создан. В этом случае мы будем отбрасывать любые URI активов, которые возвращают идентификатор токена. Обратите внимание, что здесь используется метод call() функции контракта, а не send(). Это связано с тем, что tokenByUri просто возвращает данные, а не обновляет состояние контракта. Поэтому комиссию за транзакцию отправлять не нужно.

let tokenId = await props.contract.methods.tokenByUri(uri).call();

Создать галерею токенов

Теперь, когда в вашем контракте есть отчеканенный токен, пришло время просмотреть его в галерее веб-сайта. Нажмите на ссылку «Галерея» в верхней части навигации. В галерее отображаются изображения для всех токенов, доступных в контракте. На данный момент есть только один. Вы можете вернуться на страницу «Монет» и отчеканить еще пару, если хотите. Затем вернитесь в галерею, чтобы увидеть новые токены.

Теперь, когда вы научились вызывать функции контракта, получить данные токена довольно просто. Давайте подробнее рассмотрим код, из которого состоит галерея. Откройте файл проекта /src/pages/Gallery.jsx. Когда галерея загружается, первое, что она делает, это вызывает функцию контракта totalSupply. Эта функция вернет общее количество сохраненных токенов.

const totalSupply = 
  await props.contract.methods.totalSupply().call();

Поскольку идентификаторы токенов хранятся в виде возрастающих целых чисел, начиная с 1, мы можем просто выполнить цикл от 1 до общего количества и получить идентификатор каждого токена. Это делается в функции галереи GetTokenURIs:

const GetTokenURIs = async (totalSupply) => {
  if(!totalSupply) return;
  let tokens = [];
  for(let idx=totalSupply; idx>=1; idx — ){
    try{
      let tokenURI = 
        await props.contract.methods.tokenURI(idx).call()
      let response = await fetch(tokenURI);
      let metaData = await response.json();
      if(metaData && metaData.image)
        tokens.push(metaData.image);
    }catch(e){
      console.error(‘Error occurred while fetching metadata.’)
      continue;
    }
  }
  if(tokens.length) setTokenURIs([…tokens]);
};

Эта функция принимает общее количество поставок и использует цикл for для перебора каждого идентификатора токена. Для каждого идентификатора он вызывает функцию контракта tokenURI и получает URI метаданных json для этого конкретного токена. Затем он использует запрос fetch API для извлечения данных json, анализирует их и получает URI изображения для галереи.

Этот проект просто отображает теги ‹img› с полученным URI изображения в качестве источника. Способ отображения NFT в галерее зависит от реализации. Например, Веб-сайт ExoBits отображает NFT в веб-компоненте. Поэтому вместо получения URI изображения он извлекает уникальное значение ключа, хранящееся в контракте, и загружает его в веб-компонент. Изображение, в свою очередь, визуализируется в режиме реального времени. Это позволяет использовать дополнительные функции, такие как смоделированная торговая карточка, по которой вы можете щелкнуть, чтобы увидеть статистику на обратной стороне. Владельцам токенов даже доступны специальные функции, которые больше никому не нравятся: слоу-мо перерисовка, удаление фона и загрузка изображения.

Галерея владельцев

Говоря о владельцах, поскольку коллекции NFT, как правило, исчисляются тысячами, не очень удобно заставлять людей прокручивать всю галерею, чтобы найти свои токены. Мы можем создать частную галерею, чтобы владельцы могли видеть только свои токены. Взгляните на файл проекта: /src/pages/MyTokens.jsx. Вы увидите, что он работает почти так же, как и страница галереи с одним отличием. Вместо получения общего количества токенов вызывается функция контракта tokensOfOwner:

const userTokens = await props.contract.methods.
  tokensOfOwner(props.address).call();

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

Заворачивать

Благодаря этому у вас есть все основные функции, которые нужны каждому веб-сайту NFT. Используйте это как отправную точку. Добавьте к нему функциональные возможности, соответствующие уникальности вашего собственного NFT. Если вы не знакомы с написанием смарт-контрактов, лучше изучить этот аспект этого процесса дальше. Сила NFT заключается в том, как вы представляете данные владельца токена в контракте. Четкое понимание смарт-контрактов позволяет вам наилучшим образом закрепить ключевую информацию вашего NFT в блокчейне.

Если вы нашли эту статью полезной, зайдите на ExoBitsNFT.com и создайте свой собственный ExoBit NFT. Эфир, полученный от ExoBits, идет на финансирование будущих проектов NFT и учебных пособий, подобных этому. Подпишитесь на меня, @greenzeta, в Твиттере, чтобы быть в курсе последних новостей о проектах и ​​обучающих материалах, находящихся в разработке.