Обучение
July 15, 2022

Как разработать смарт-контракт NFT (ERC721) с помощью Alchemy, первая неделя

Ну что же, приступим к челленджу первой недели курса - разработка смарт-контракта на Solidity и его развертывание на блокчейне.

Поначалу эти задачи могут показаться сложными: solidity, безопасность, оптимизация, среда разработки и плата за газ - это лишь некоторые из вещей, через которые вам нужно будет пройти, чтобы разместить свой первый код на блокчейне.

К счастью, за последние несколько месяцев для разработчиков было выпущено много инструментов, чтобы значительно упростить разработку смарт-контрактов.

Такие инструменты, как OpenZeppelin Wizard, который предлагает разработчикам функции click and write для создания компонуемых и безопасных смарт-контрактов за пару кликов, используемые с инструментами разработчика Web3, такими как Alchemy, делают опыт написания кода на блокчейне простым, быстрым и надежным, как никогда раньше.

В этом уроке вы узнаете, как разработать и развернуть смарт-контракт ERC721 (NFT) с помощью Alchemy, OpenZeppelin, Remix и Ethereum Rinkeby.

Вот краткая выжимка того, вы узнаете:

  • Как написать и изменить смарт-контракт с помощью OpenZeppelin и Remix
  • Как получить бесплатно Rinkeby ETH с помощью rinkebyfaucet.com
  • Как развернуть смарт-контракт на блокчейне тестовой сети Ethereum Rinkeby, чтобы сэкономить на сборах за газ
  • Как разместить метаданные токенов NFT на IPFS с помощью Filebase
  • Как сминтить NFT и визуализировать его на OpenSea

Давайте начнем с создания смарт-контракта.

Разрабатываем ERC721 смарт-контракт с OpenZeppelin

В этом уроке мы будем использовать мастер OpenZeppelin для создания смарт-контракта.

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

Вы не хотите, чтобы кто-то украл все ваши драгоценные криптовалюты или NFT, как только вы развернете их на блокчейне, верно?

Open Zeppelin служит этой цели, являясь одним из крупнейших разработчиков стандартов смарт-контрактов (ERC 20, ERC 721 и т.д.), позволяет разработчикам использовать тщательно проверенный код для разработки надежных контрактов.

Первое, что вам нужно сделать, чтобы разработать наш смарт-контракт ERC721 NFT, это перейти на страницу мастера смарт-контрактов Open Zeppelin.

Оказавшись на странице, вы увидите следующий редактор:

Открываем редактор OpenZeppelin

Нажмите кнопку ERC721 в верхнем левом углу, чтобы выбрать тип стандарта ERC для использования и тип контракта, который вы хотите написать:

Выбираем стандарт контракта

Теперь, когда мы выбрали стандарт контракта, в меню слева вы должны увидеть несколько вариантов:

Изменяем настройки под себя

Давайте начнем с выбора имени и символа наших токенов. Нажмите на текстовое поле с "MyToken" и дайте ему имя, сделайте то же самое с символом и оставьте поле Base URI пустым (имя токена будет использоваться OpenSea и Rarible в качестве названия коллекции).

Выбираем функции токена NFT (ERC721)

Теперь нам нужно будет выбрать функции, которые мы хотим интегрировать в наш смарт-контракт, сразу после раздела «Settings»  расположен раздел «Features», где мы можем выбрать различные модули для включения в свой смарт-контракт.

В нашем случае мы выберем следующие модули для иморта:

  • Mintable - создаст функцию минта, вызываемую только привилегированными учетными записями
  • Autoincrement IDs - автоматически присваивают инкрементные идентификаторы вашим NFT
  • Enumerable - предоставит вам доступ к перечислению токенов в цепочке и функциям, таким как «totalSupply», которых нет в хранилище URI интеграции ERC721 по умолчанию, для связывания метаданных и изображений с каждым из ваших NFT
  • URI Storage - чтобы иметь возможность связывать URI с нашими NFT
Выбираем импортируемые модули

В этом уроке мы не будем создавать какую-либо токеномику наших NFT, поэтому оставим следующие модули неактивными:

  • Burnable - для сжигания токенов
  • Pausable - для приостановки перевода токенов, продажи и т. д.
  • Votes - для доступа к функциям управления, таким как делегации и голосования

Если вы хотите узнать больше об этих модулях, ознакомьтесь с официальной документацией OpenZeppelin о стандарте ERC721.

Теперь, когда мы выбрали нужные функции, OpenZeppelin Wizard сгенерирует код смарт-контракта, он должен выглядеть следующим образом:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract Web3shit is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    constructor() ERC721("web3shit", "W3S") {}

    function safeMint(address to, string memory uri) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // The following functions are overrides required by Solidity.

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

Пришло время скопировать наш код и перенести его в Remix IDE, чтобы изменить и развернуть его на блокчейне.

Изменяем и разворачиваем свой ERC721 контракт с REMIX IDE

Теперь, когда у вас есть смарт-контракт ERC721, давайте изменим и развернем его в тестовой сети Rinkeby. Для этого мы будем использовать Remix IDE, бесплатную интегрированную веб-среду разработки, специально разработанную для разработки смарт-контрактов на Solidity.

Прежде всего, как вы, возможно, заметили, в редакторе OpenZeppelin Wizard есть кнопка "Open in Remix", нажатие на нее откроет REMIX IDE в новой вкладке вашего браузера:

Открываем код смарт-контракта в REMIX IDE

Контракт начинается со строки "SPDX-License-Identifier", которая определяет тип лицензии, под которой будет опубликован наш код - в приложениях web3 рекомендуется сохранять код как open source, поскольку это обеспечит надежность.

// SPDX-License-Identifier: MIT

Дальше идет pragma - версия компилятора, которую мы хотим использовать для компиляции кода смарт-контракта. Маленький символ "^" сообщает компилятору, что каждая версия от 0.8.0 до 0.8.9 подходит для компиляции нашего кода.

pragma solidity ^0.8.4;

Затем мы импортируем кучу библиотек и инициализируем смарт-контракт.

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

Затем мы инициализируем контракт, наследуя все стандарты, которые мы импортируем из репозитория Open Zeppelin:

contract Web3shit is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {...}

Как вы можете заметить, функция safeMint имеет модификатор “only owner” - это позволит только владельцу смарт-контракта (адрес кошелька, на котором был развернут смарт-контракт) минтить NFT. Скорее всего, вы захотите, чтобы кто-нибудь кроме вас также мог минтить NFT, для этого вам нужно будет удалить модификатор onlyOwner из основной функции.

function safeMint(address to, string memory uri) public {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

Также удалим его из объявления контракта "Ownable", и импортируемой библиотеки "Ownable.sol"

import "@openzeppelin/contracts/access/Ownable.sol";

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

Допустим, вы хотите, чтобы пользователи могли чеканить в общей сложности до 10 000 NFT. Для этого давайте создадим новую переменную uint256, назовем ее MAX_SUPPLY и присвоим ей значение 10000.

    Counters.Counter private _tokenIdCounter;
    uint256 MAX_SUPPLY = 100000;

    constructor() ERC721("web3shit", "w3s") {}

Далее давайте перейдем к функции safeMint и добавим оператор require в строку 18:

require(_tokenIdCounter.current() <= MAX_SUPPLY, "I'm sorry we reached the cap");

Давайте потратим пару слов, чтобы лучше понять, что такое утверждение "require" в Solidity. Вы можете прочитать больше о Solidity "require" statement в официальной документации.

Редактируем код смарт-контракта в REMIX IDE

Итоговый код контракта будет выглядеть так:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract Web3shit is ERC721, ERC721Enumerable, ERC721URIStorage {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;
    uint256 MAX_SUPPLY = 100000;
    
    constructor() ERC721("web3shit", "W3S") {}

    function safeMint(address to, string memory uri) public {
        require(_tokenIdCounter.current() <= MAX_SUPPLY, "I'm sorry we reached the cap");
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // The following functions are overrides required by Solidity.

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}

Теперь, когда мы ограничили максимальное количество наших NFT, пришло время составить смарт-контракт и развернуть его в тестовой сети Rinkeby.

Для этого нам нужно будет создать бесплатную учетную запись на Alchemy.com, добавить ее в качестве поставщика узлов в Metamask и получить бесплатный Rinkeby ETH.

Создаем бесплатную учетную запись Alchemy

Прежде всего, нам нужно перейти к alchemy.com нажмите на кнопку “Войти” и создайте новую учетную запись:

Регистрируем бесплатную учетную запись на Alchemy

Выбираем экосистему Ethereum:

Выбираем экосистему Ethereum

Придумываем название нашему приложению и команде, выбираем сеть Rinkeby и нажимаем "Create App":

Придумываем название команды и приложения

При выборе тарифа выбираем бесплатный, привязку платежных данных скипаем, пропускаем все предложения и нажимаем "Let's Go".

Как только вы завершите процесс регистрации, мы будем перенаправлены на панель мониторинга Alchemy. Нажимаем на приложение с выбранным вами именем, в данном случае “First Week”, нажимаем на кнопку "VIEW KEY" в правом верхнем углу и копируем HTTPS-URL:

Копируем HTTPS URL

Далее нам нужно будет добавить Alchemy в Metamask в качестве RPC Provider. Если у вас не установлен Metamask, обязательно следуйте официальному руководству, чтобы добавить его в свой браузер и создать новый кошелек.

Добавляем Alchemy Rinkeby в свой кошелек Metamask

Как только Metamask будет установлен, нажмите на выпадающее меню "Настройки", далее "Сеть" и "Добавить сеть".

Вы будете перенаправлены на следующую страницу, где вам нужно будет заполнить информацию о сети Rinkeby и URL-адресе RPC.

Добавляем свой RPC для Rinkeby

Добавьте в форму следующую информацию:

  • Название сети: Alchemy Rinkeby
  • Новый URL-адрес RPC: HTTPS-URL приложения Rinkeby Alchemy
  • Идентификатор цепочки: 4
  • Символ валюты: ETH
  • Проводник блоков: https://rinkeby.etherscan.io/

Мы только что добавили Rinkeby в Metamask, используя Alchemy!

Пришло время развернуть наш смарт-контракт на Rinkeby, но сначала нам нужно будет получить немного Rinkeby ETH для тестирования.

Получаем бесплатный тестовый Rinkeby ETH

Получить тестовый Rinkeby ETH очень просто, перейдите к rinkebyfaucet.com, скопируйте адрес кошелька в текстовую строку и нажмите на кнопку "Send Me ETH":

Получаем тестовый эфир

Через 10-20 секунд вы увидите, что Rinkeby ETH появится в кошельке Metamask.
Вы сможете получать до 0,1 ETH каждые 24 часа без входа в систему или 0,5 при логине через учетную запись Alchemy.

Теперь, когда у вас есть тестовый ETH, пришло время скомпилировать и развернуть наш смарт-контракт NFT на блокчейне.

Компилируем и деплоим смарт-контракт NFT в сети Rinkeby

Вернемся к Remix IDE, и перейдем в меню компилятора в левой части страницы и нажмем на синюю кнопку "Compile":

Компилируем смарт-контракт

Затем нажмем на меню "Deploy and Run", и в выпадающем меню "Environment" выбираем "Injected Web3":

Деплоим смарт-контракт

Убедившись, что кошелек Metamask находится в сети Alchemy Rinkeby, выбераем свой смарт-контракт NFT из выпадающего меню "Contract" и жмем "Deploy".

Когда появится всплывающее окно Metamask, нажимаем на кнопку "Подписать" и переходим к оплате комиссии за газ.

Если все сработало как и ожидалось, через 10 секунд вы должны увидеть контракт, указанный в разделе Deployed Contracts.

Теперь, когда смарт-контракт развернут в тестовой сети Rinkeby, пришло время отчеканить наши NFT, но сначала вам нужно будет создать и загрузить метаданные в IPFS.

Давайте разберемся, что мы подразумеваем под термином "метаданные".

Что такое метаданные NFT?

Чтобы OpenSea извлекал метаданные вне цепочки для токенов ERC721, контракту необходимо будет вернуть URI, указывающий на размещенные метаданные.

Пример отображения метаданных на OpenSea

Чтобы найти этот URL-адрес, OpenSea, Rarible и другие популярные торговые площадки будут использовать метод tokenURI, содержащийся в стандарте ERC721Uristorage.

Функция URL-адреса в ERC721 должна возвращать URL-адрес HTTP или IPFS, например:

ipfs://bafkreig4rdq3nvyg2yra5x363gdo4xtbcfjlhshw63we7vtlldyyvwagbq

При запросе этот URL-адрес должен возвращать большой двоичный объект JSON с метаданными для вашего токена.

Вы можете прочитать больше о стандартах метаданных в официальной документации OpenSea.

Как отформатировать ваши метаданные NFT

Согласно документации OpenSea, метаданные NFT должны храниться в файле .json и структурироваться следующим образом:

{ 
  "description": "YOUR DESCRIPTION",
  "external_url": "YOUR URL",
  "image": "IMAGE URL",
  "name": "TITLE", 
  "attributes": [
    {
      "trait_type": "Base", 
      "value": "Starfish"
    }, 
    {
      "trait_type": "Eyes", 
      "value": "Big"
    }, 
    {
      "trait_type": "Mouth", 
      "value": "Surprised"
    }, 
    {
      "trait_type": "Level", 
      "value": 5
    }, 
    {
      "trait_type": "Stamina", 
      "value": 1.4
    }, 
    {
      "trait_type": "Personality", 
      "value": "Sad"
    }, 
    {
      "display_type": "boost_number", 
      "trait_type": "Aqua Power", 
      "value": 40
    }, 
    {
      "display_type": "boost_percentage", 
      "trait_type": "Stamina Increase", 
      "value": 10
    }, 
    {
      "display_type": "number", 
      "trait_type": "Generation", 
      "value": 2
    }
  ]
}

Вот краткое объяснение того, что хранит каждое свойство:


image

Это URL-адрес изображения элемента. Это может быть изображение практически любого типа (включая DVD-диски, которые будут кэшироваться в PNG OpenSea), а также URL-адреса или пути IPFS. Мы рекомендуем использовать изображение размером 350 x 350.


image_data

Необработанные данные изображения SVG, если вы хотите создавать изображения на лету (не рекомендуется). Используйте это только в том случае, если вы не включаете параметр image.


external_url

Это URL-адрес, который появится под изображением ресурса на OpenSea и позволит пользователям покинуть OpenSea и просмотреть товар на вашем сайте.


description

Удобочитаемое описание товара. Поддерживается Markdown.


name

Название товара.


attributes

Это атрибуты для элемента, которые будут отображаться на странице OpenSea для элемента.


background_color

Цвет фона объекта на OpenSea. Должно быть шестнадцатеричным числом из шести символов без предварительного знака #.


animation_url

URL-адрес мультимедийного вложения для элемента. Поддерживаются расширения файлов GLTF, GLB, WEBM, MP4, M4V, OGV и OGG, а также расширения только для аудио MP3, WAV и YOGA. Animation_url также поддерживает HTML-страницы, что позволяет нам создавать богатые возможности и интерактивные NFT с использованием JavaScript canvas, WebGL и других технологий. Теперь поддерживаются скрипты и относительные пути внутри HTML-страницы. Однако доступ к расширениям браузера не поддерживается.


youtube_url

URL-адрес видео на YouTube.


Теперь, когда у нас есть краткое представление о том, что будет содержаться в метаданных ваших токенов, давайте узнаем, как их создать и сохранить в IPFS.

Создание и загрузка метаданных в IPFS

Прежде всего, переходим к filebase.com и создаем новую учетную запись.
После входа в систему нажимаем на кнопку корзины в меню слева и создаем новую корзину:

Создаем корзину на Filebase

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

После загрузки нажимаем на изображение и копируем URL адрес шлюза IPFS:

Копируем URL адрес шлюза IPFS

Используя любой текстовый редактор, вставьте следующий JSON-код:

{ 
  "description": "This NFT proves I've created and deployed my first ERC20 smart contract on Rinkeby with Alchemy Road to Web3",
  "external_url": "Alchemy.com",
  "image": "https://ipfs.filebase.io/ipfs/bafybeihyvhgbcov2nmvbnveunoodokme5eb42uekrqowxdennt2qyeculm",
  "name": "A cool NFT", 
  "attributes": [
    {
      "trait_type": "Base", 
      "value": "Starfish"
    }, 
    {
      "trait_type": "Eyes", 
      "value": "Big"
    }, 
    {
      "trait_type": "Mouth", 
      "value": "Surprised"
    }, 
    {
      "trait_type": "Level", 
      "value": 5
    }, 
    {
      "trait_type": "Stamina", 
      "value": 1.4
    }, 
    {
      "trait_type": "Personality", 
      "value": "Sad"
    }, 
    {
      "display_type": "boost_number", 
      "trait_type": "Aqua Power", 
      "value": 40
    }, 
    {
      "display_type": "boost_percentage", 
      "trait_type": "Stamina Increase", 
      "value": 10
    }, 
    {
      "display_type": "number", 
      "trait_type": "Generation", 
      "value": 2
    }
  ]
}

И сохраните файл как "metadata.json". Вернитесь в Filebase и загрузите файл metadata.json в ту же корзину, куда мы загрузили наше изображение.

Загружаем файл metadata.json в ту же корзину

Наконец, нажмите на CID и скопируйте его (второй столбец таблицы, пример CID QmNvDrfrvVV3jyZZymPW92JrCLPWw7dgvpkqiuz1FMk8k8), он нам понадобится в следующей части, чтобы создать URI токена при минте NFT.

Минтим свой Rinkeby NFT

Вернемся к Remix и в меню "Deploy & Run Transactions" и перейдем в раздел "Deployed Contracts" - и нажмите на контракт, который мы только что развернули, откроется список всех методов, содержащихся в вашем смарт-контакте:

Вставляем адрес вашего кошелька и url CID метаданнных

Оранжевые методы - это методы, которые фактически записываются в блокчейн, Cиние методы - это методы, предназначенные для обучения.

Нажмите на выпадающий список метода safeMint и вставьте адрес своего кошелька в поле address и строку содержащую CID вашего файла метаданных в поле url:


ipfs://<your_metadata_cid>


При нажатии на "transact" появится всплывающее окно Metamask, предлагающее нам оплатить комиссию за газ.

Нажимаем на кнопку "Подписать" и минтим свой первый NFT!

Подождите пару секунд и, чтобы убедиться, что минт прошел успешно, скопируйте и вставьте свой адрес в поле ввода метода balanceOf и запустите его - он должен показать, что у вас есть 1 NFT.

Проделайте то же самое с методом tokenUri, вставив "0" в качестве аргумента id - он должен отображать ваш tokenURI.

Отлично! Вы только что сминтили свой первый NFT!

Теперь пришло время перейти на OpenSea, чтобы проверить, доступны ли наши метаданные для чтения.

Визуализируем свой NFT в OpenSea

Перейдем на testnets.opensea.io и войдем в систему с помощью своего кошелька Metamask. Затем нажмем на фотографию своего профиля, чтобы увидеть там свой новоиспеченный NFT. Если изображение еще не видно, щелкните по нему и нажмите на кнопку "Refresh metadata".

Обновляем метаданные

Иногда OpenSea испытывает трудности с распознаванием метаданных в testnet, и для их просмотра может потребоваться до 6 часов. Через некоторое время ваш NFT должен отобразиться следующим образом:

После подгрузки метаданных

Поздравляем, вы успешно создали, модифицировали и развернули свой первый смарт-контракт. Сминтили свой первый NFT и опубликовал свое изображение в IPFS! 🔥

Следующий шаг? Почему бы вам не изменить свой смарт-контракт, чтобы пользователи могли минтить только до определенного количества NFT? 5 на пользователя должно быть достаточно, или кто-то может начать минтить тысячи NFT!

Чтобы сделать это, загляните в тип отображения, здесь есть удивительное руководство, которое проведет вас по нему.

Заполняем форму

Чтобы получить наш заветный еженедельный PoK (Proof of Knowledge), заполните эту форму, включая адрес развернутого смарт-контракта: https://alchemyapi.typeform.com/roadtoweekone

Примерно через неделю вам отправят PoK, забираем на https://mintkudos.xyz/, вот так он выглядит!

PoK - Alchemy Road to Web3 - Week One
Этот непередаваемый NFT доказывает, что вы официально завершили первую неделю курса Alchemy Road to Web3 Bootcamp. Вы успешно написали и развернули смарт-контракт ERC721 на Rinkeby, используя: OpenZeppelin, Remix, Alchemy, Solidity Fundamentals, Поздравляем!

Ну вот, первая неделя нашей поездки закончена, подписывайтесь на обновления чтобы не пропустить гайд для следующей недели!👇

Третий слой | Telegram| Chat | Twitter