Сравнение стандартов NFT — ERC-721, ERC-721a и ERC-1155

Итак, вы начинаете новый проект NFT и хотите соответствовать современным передовым практикам. С чего начать?
Давайте взглянем на некоторые из текущих стандартов NFT, которые у нас есть:
Вы можете проверить репозиторий NFT-Standards Github, чтобы следовать образцам кода и попробовать код локально, чтобы создать свои собственные NFT в тестовой сети.

К концу этой статьи и семинара вы сможете создавать NFT с использованием нескольких стандартов и видеть созданные NFT в OpenSea в тестовой сети Rinkeby со всеми соответствующими метаданными.

Стандарт невзаимозаменяемых токенов ERC-721

_ERC-721 defines a minimum interface a smart contract must implement to allow unique tokens to be managed, owned, and traded. It does not mandate a standard for token metadata or restrict adding supplemental functions._

ERC-721 — одна из наиболее распространенных реализаций NFT. Очень распространенный способ реализации смарт-контракта ERC-721 — расширение реализации OpenZeppelin .

Спецификация допускает следующее поведение:

  • Перенос NFT между аккаунтами
  • Контроль доступа, ограничивающий, кто может передавать NFT
  • Обменяйте NFT на другие валюты
  • Определите общее количество NFT в сети.
  • Запрос владельцев конкретного актива

Спецификация также может быть расширена следующим необязательным поведением:

Некоторые ограничения спецификации:

  • При передаче нескольких NFT для каждого требуется отдельная транзакция.
  • Каждый контракт может представлять только один тип NFT.
  • Смарт-контракт должен реализовать IERC721Receiver для получения NFT.
  • Нет поддержки полузаменимых токенов
Основные шаги для реализации смарт-контракта NFT и создания NFT на основе изображений в тестовой сети следующие:
  • Создайте смарт-контракт
  • Разверните смарт-контракт
  • Загрузить и закрепить данные изображения NFT в IPFS
  • Создание метаданных JSON для NFT
  • Вызов смарт-контракта для создания NFT
  • Проверьте NFT в Opensea и Etherscan в тестовой сети.
Вы можете создать бесплатную учетную запись на Infura , чтобы хранить данные изображения в IPFS!

Создание смарт-контракта, совместимого с ERC-721

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

Базовая реализация Пример

Установите пакет OpenZeppelin с помощью npm (при необходимости):

npm i @openzeppelin/contracts

Ознакомьтесь с исходным кодом контракта Infura721NFT.sol на Github или просмотрите код локально после клонирования репозитория NFT-Standards Github.


pragma solidity ^0.8.13;

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

contract Infura721NFT is ERC721URIStorage, Ownable {
   using Counters for Counters.Counter;
   Counters.Counter private _tokenIds;

   constructor() ERC721("Infura721NFT", "INFURA721") {}

   function mintNFT(address recipient)
       public
       returns (uint256)
   {
       _tokenIds.increment();

       uint256 newItemId = _tokenIds.current();
       _safeMint(recipient, newItemId);

       return newItemId;
   }

   function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
       _requireMinted(tokenId);

       return "https://raw.githubusercontent.com/anataliocs/NFT-Standards/main/metadata/InfuraNFT.json";
   }

   function contractURI() public view returns (string memory) {
       return "https://raw.githubusercontent.com/anataliocs/NFT-Standards/main/metadata/opensea-contract-721.json";
   }

   function _requireMinted(uint256 tokenId) internal view virtual {
       require(_exists(tokenId), "ERC721: invalid token ID");
   }
}

Этот базовый пример расширяет ERC721URIStorage и Ownable.

Эта базовая реализация контракта имеет следующие особенности:

  • Расширяет реализацию OpenZeppelin ERC721.
  • Отслеживает itemId NFT с помощью счетчика OpenZeppelin.
  • Предоставляет функцию для создания нового NFT
  • Сохраняет URI в метаданные JSON.
  • Переопределение функции tokenURI() помогает корректно отображать метаданные NFT в OpenSea.
  • Переопределение функции contractURI() помогает отображать данные уровня магазина в OpenSea.

Пример JSON метаданных NFT

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

{
 "description": "Infura NFT 721 Demo",
 "external_url": "ipfs://QmaBm1F7vUujz5ZURDXS3yqeTsMFUEVW5M7bGQbTQr7vJK",
 "image": "ipfs://QmaBm1F7vUujz5ZURDXS3yqeTsMFUEVW5M7bGQbTQr7vJK",
 "name": "Infura721NFT",
 "background_color": "#FFF"
}

Пример JSON метаданных на уровне контракта

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

{
 "name": "Infura 721 NFT Items",
 "description": "Infura 721 NFT Items used in workshops, tutorials and reference applications.",
 "image": "https://raw.githubusercontent.com/anataliocs/NFT-Standards/main/images/infura_lockup_red.png",
 "external_link": "https://infura.io/",
 "seller_fee_basis_points": 0
}

Вызов развернутого смарт-контракта, совместимого с ERC-721

После развертывания этого контракта в тестовой сети или локально вы можете вызвать функцию mintNFT() с помощью следующего скрипта:

mintSingleNFT.js


require("dotenv").config();

const { CONTRACT_ADDRESS, PUBLIC_ADDRESS } = process.env;

// Loading the compiled contract Json
const contractJson = require("../build/contracts/Infura721NFT.json");

module.exports = async function (callback) {
 // web3 is injected by Truffle
 const contract = new web3.eth.Contract(
   contractJson.abi,
   CONTRACT_ADDRESS // this is the address generated when running migrate
 );

 // get the current network name to display in the log
 const network = await web3.eth.net.getNetworkType();

 // Generate a transaction to calls the `mintNFT` method
 const tx = contract.methods.mintNFT(PUBLIC_ADDRESS);
 // Send the transaction to the network
 const receipt = await tx
   .send({
     from: (await web3.eth.getAccounts())[0], // uses the first account in the HD wallet
     gas: await tx.estimateGas(),
   })
   .on("transactionHash", (txhash) => {
     console.log(`Mining ERC-721 transaction for a single NFT ...`);
     console.log(`https://${network}.etherscan.io/tx/${txhash}`);
   })
   .on("error", function (error) {
     console.error(`An error happened: ${error}`);
     callback();
   })
   .then(function (receipt) {
     let batchGasCost = receipt.gasUsed * 3;
     // Success, you've minted the NFT. The transaction is now on chain!
     console.log(
       `Success: The single ERC-721 NFT has been minted and mined in block ${receipt.blockNumber} which cost ${receipt.gasUsed} gas`
         + `\n If you were to execute this script 3 times to perform a batch mint, the gas cost would be ${batchGasCost} \n`
     );
     callback();
   });
};

Этот скрипт делает следующее:

  • Извлекает развернутый адрес контракта и целевой кошелек из файла .env.
  • Использует web3.js для загрузки существующего развернутого смарт-контракта.
  • Вызывает функцию контракта mintNFT(), передавая адрес кошелька получателя.
  • Несколько прослушивателей подключены для обработки случаев успеха и ошибок.
  • В консоль выводится ссылка на транзакцию на Etherscan
  • Затем номер блока и стоимость газа выводятся на консоль.
Этот сценарий использует библиотеку HDWalletProvider , использующую узел Infura Ethereum Rinkeby Testnet и мнемонику, хранящуюся в вашем локальном файле .env, для подписи транзакции.

Конфигурация сети в truffle-config.js


...

rinkeby: {
 provider: () => new HDWalletProvider(mnemonic, infuraURL),
 network_id: 4, // Rinkeby's id
 gas: 15500000, // Rinkeby has a lower block limit than mainnet
 confirmations: 2, // # of confs to wait between deployments. (default: 0)
 timeoutBlocks: 200, // # of blocks before a deployment times out  (minimum/default: 50)
 skipDryRun: true, // Skip dry run before migrations? (default: false for public nets )
},

...

Дополнительные сведения о спецификации ERC-721

Переводы могут быть инициированы только (по умолчанию):

  • Владелец NFT.
  • Утвержденный адрес NFT
  • Уполномоченный оператор текущего владельца NFT

Спецификация очень открытая, беспристрастная и допускает гибкое поведение, например:

  • Реестры NFT только для чтения
  • Запретить переводы, если контракт приостановлен
  • Черный список определенных адресов от получения NFT
  • Взимание комиссии с обеих сторон сделки
Кроме того, принимающие контракты должны реализовывать IERC721Receiver для безопасного получения ERC721 NFT. Обязательно используйте функцию safeTransferFrom() , чтобы убедиться, что адрес получателя может обрабатывать ERC721 NFT, реализуя функцию onERC721Received().

Стандарт невзаимозаменяемых токенов ERC-721a с пакетной чеканкой

ERC-721a является расширением ERC-721, которое обеспечивает значительную экономию газа при пакетной чеканке NFT, но при этом соответствует стандарту ERC-721.

Это достигается в основном за счет 3 оптимизаций:

  • Удаление повторяющихся хранилищ из OpenZeppelin (OZ) ERC721Enumerable
  • Обновление баланса владельца один раз для каждого запроса на пакетную чеканку, а не для каждой чеканки NFT.
  • Обновление данных владельца один раз для запроса пакетной чеканки, а не для каждой чеканки NFT.

ERC-721a Характеристики:

  • Переносит инициализацию владения токеном со стадии чеканки на стадию передачи.
  • Сильно оптимизирован для коллекций генеративных иллюстраций NFT.
  • Увеличение экономии газа для более популярных проектов
  • Лучше всего использовать для NFT с активной фазой монетного двора
  • Приоритет отдается экономии газа на этапе чеканки.

Ограничения ERC-721a:

  • Дороже для одиночных монетных дворов NFT
  • Не идеально подходит для чисто утилитарных NFT, у которых нет активной фазы монетного двора.
  • Каждый контракт может представлять только один тип NFT.
  • Нет поддержки полузаменимых токенов

Создание смарт-контракта, совместимого с ERC-721a

Установите пакет Azuki ERC-721a с помощью npm (при необходимости):

npm install --save-dev erc721a

Базовая реализация Пример

Ознакомьтесь с исходным кодом Infura721aNFT.sol** ** на Github или просмотрите код локально после клонирования репозитория NFT-Standards Github.

Infura721aNFT.sol

Этот базовый пример расширяет ERC721A и Ownable .

pragma solidity ^0.8.13;

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

contract Infura721aNFT is ERC721A, Ownable {

   event Mint(uint256 _value, string tokenURI);

   constructor() ERC721A("Infura721aNFT", "INFURA721a") {}

   function mint(address recipient, uint256 quantity) public returns (uint256) {

       _safeMint(recipient, quantity);

       return 0;
   }

   function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {

       return "https://raw.githubusercontent.com/anataliocs/NFT-Standards/main/metadata/InfuraNFT.json";
   }

   function contractURI() public view returns (string memory) {
       return "https://raw.githubusercontent.com/anataliocs/NFT-Standards/main/metadata/opensea-contract-721a.json";
   }
}

Этот договор имеет следующие особенности:

  • Расширяет реализацию Azuki ERC721A.
  • Предоставляет функцию mint() для чеканки партии NFT.
  • Поддерживает внутреннее сопоставление балансов адресов.
  • Переопределение функции tokenURI() помогает корректно отображать метаданные NFT в OpenSea.
  • Переопределение функции contractURI() помогает отображать данные на уровне витрины в OpenSea.

Вызов развернутого смарт-контракта, совместимого с ERC-721a

После развертывания этого контракта в тестовой сети или локально вы можете вызвать функцию mint() для создания нескольких NFT с помощью следующего сценария:

mintBatchNFT.js


require("dotenv").config();

const {PUBLIC_ADDRESS, CONTRACT_ADDRESS_721A} = process.env;

// Loading the compiled contract Json
const contract721aJson = require("../build/contracts/Infura721aNFT.json");

module.exports = async function (callback) {
   // web3 is injected by Truffle
   const contract721a = new web3.eth.Contract(
       contract721aJson.abi,
       CONTRACT_ADDRESS_721A // this is the address generated when running migrate
   );

   // get the current network name to display in the log
   const network = await web3.eth.net.getNetworkType();

   // Generate a transaction to calls the `mint` function
   const tx721a = contract721a.methods.mint(PUBLIC_ADDRESS, 3);
   // Send the transaction to the network
   await tx721a
       .send({
           from: (await web3.eth.getAccounts())[0], // uses the first account in the HD wallet
           gas: await tx721a.estimateGas(),
       })
       .on("transactionHash", (txhash) => {
           console.log(`Mining ERC-721a transaction for a batch of 3 NFTs ...`);
           console.log(`https://${network}.etherscan.io/tx/${txhash}`);
       })
       .on("error", function (error) {
           console.error(`An error happened: ${error}`);
           callback();
       })
       .then(function (receipt) {
           // Success, you've minted the NFT. The transaction is now on chain!
           console.log(
               `\n Success: 3 ERC-721a NFTs have been minted and mined in block ${receipt.blockNumber} which cost ${receipt.gasUsed} gas \n`
           );
           callback();
       });
};

Этот скрипт делает следующее:

  • Извлекает развернутый адрес контракта и целевой кошелек из файла .env.
  • Использует web3.js для загрузки существующего развернутого смарт-контракта.
  • Вызывает функцию mint() контракта, передавая адрес кошелька получателя и количество NFT для чеканки.
  • Несколько прослушивателей подключены для обработки случаев успеха и ошибок.
  • В консоль выводится ссылка на транзакцию на Etherscan
  • Затем номер блока и стоимость газа выводятся на консоль.

Дополнительные ресурсы по спецификации ERC-721a:

Стандарт мультитокена ERC-1155

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

Характеристики ERC-1155:

  • Может передавать несколько NFT в одной транзакции ( функция safeBatchTransferFrom() )
  • Контракт может представлять несколько типов не зависящих от взаимозаменяемости NFT.
  • Поддержка полузаменимых токенов
  • Обычно требуется меньше места для хранения, но хранится менее надежная информация
  • Возможна многоязычная локализация текста
  • Получите баланс нескольких активов за один звонок
  • Разрешить пакетное утверждение для адреса ( функция setApprovalForAll() )
  • Поддержка EIP-165 : объявить поддерживаемые интерфейсы

Ограничения ERC-1155:

  • Невозможно легко запросить владельца NFT в сети
  • Невозможно легко перечислить токены в цепочке
  • Сторонняя поддержка обычно не так надежна, как поддержка ERC-721 (по состоянию на июль 2022 г.)
  • Состояние контракта содержит данные о владении для каждого идентификатора токена, а не токен, существующий или не существующий в кошельке.
  • Оффчейн-сервисам не так просто определить право собственности на конкретный NFT.

Создание смарт-контракта, совместимого с ERC-1155

Базовая реализация Пример

Ознакомьтесь с исходным кодом Infura1155NFT.sol на Github или просмотрите код локально после клонирования репозитория NFT-Standards Github.

Infura1155NFT.sol

Этот базовый пример расширяет ERC1155 и Ownable.

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

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

contract Infura1155NFT is ERC1155, Ownable {
   uint256 public constant GOLD = 0;
   uint256 public constant RARE_ITEM = 1;
   uint256 public constant EPIC_ITEM = 2;

   constructor() ERC1155("https://raw.githubusercontent.com/anataliocs/NFT-Standards/main/metadata/Infura1155NFT-type{id}.json") {}

   function mint(address recipient) public returns (uint256)
   {
       _mint(recipient, GOLD, 10, "");
       _mint(recipient, RARE_ITEM, 1, "");
       _mint(recipient, EPIC_ITEM, 1, "");

       return 0;
   }

   function contractURI() public view returns (string memory) {
       return "https://raw.githubusercontent.com/anataliocs/NFT-Standards/main/metadata/opensea-contract-1155.json";
   }
}

Этот договор имеет следующие особенности:

  • Расширяет реализацию OpenZeppelin ERC1155.
  • Определяет 3 типа токенов в одном контракте
  • Предоставляет функцию mint() для чеканки нескольких типов NFT
  • Выпускает несколько токенов за одну транзакцию
  • Переопределение функции tokenURI() помогает корректно отображать метаданные NFT в OpenSea.
  • Переопределение функции contractURI() помогает отображать данные уровня магазина в OpenSea.

Вызов развернутого смарт-контракта, совместимого с ERC-1155

После развертывания этого контракта в тестовой сети или локально вы можете вызвать функцию mint() для создания нескольких NFT с помощью следующего сценария:

mint1155NFT.js


require("dotenv").config();

const {PUBLIC_ADDRESS, CONTRACT_ADDRESS_1155} = process.env;

// Loading the compiled contract Json
const contract1155Json = require("../build/contracts/Infura1155NFT.json");

module.exports = async function (callback) {
   // web3 is injected by Truffle
   const contract1155 = new web3.eth.Contract(
       contract1155Json.abi,
       CONTRACT_ADDRESS_1155 // this is the address generated when running migrate
   );

   // get the current network name to display in the log
   const network = await web3.eth.net.getNetworkType();

   // Generate a transaction to calls the `mint` function
   const tx1155 = contract1155.methods.mint(PUBLIC_ADDRESS);
   // Send the transaction to the network
   await tx1155
       .send({
           from: (await web3.eth.getAccounts())[0], // uses the first account in the HD wallet
           gas: await tx1155.estimateGas(),
       })
       .on("transactionHash", (txhash) => {
           console.log(`Mining ERC-1155 transaction for 2 NFTs and fungible tokens ...`);
           console.log(`https://${network}.etherscan.io/tx/${txhash}`);
       })
       .on("error", function (error) {
           console.error(`An error happened: ${error}`);
           callback();
       })
       .then(function (receipt) {
           // Success, you've minted the NFT. The transaction is now on chain!
           console.log(
               `\n Success: ERC-1155 NFTs and tokens have been minted and mined in block ${receipt.blockNumber} which cost ${receipt.gasUsed} gas \n`
           );
           callback();
       });
};

Этот скрипт делает следующее:

  • Извлекает развернутый адрес контракта и целевой кошелек из файла .env.
  • Использует web3.js для загрузки существующего развернутого смарт-контракта.
  • Вызывает функцию mint() контракта, передавая адрес кошелька получателя, который чеканит и передает несколько NFT.
  • Несколько прослушивателей подключены для обработки случаев успеха и ошибок.
  • В консоль выводится ссылка на транзакцию на Etherscan
  • Затем номер блока и стоимость газа выводятся на консоль.

Сравнение реализаций

Как правило, реализации ERC-1155 более надежны, многофункциональны и дешевле в развертывании, если у вас есть несколько токенов. Однако ERC-1155 получает эти возможности, делая некоторые компромиссы, сохраняя менее надежную информацию и жертвуя некоторой простотой использования для определенных функций.
Если у вас есть только один NFT и вы никогда не будете добавлять другой тип, реализации ERC-721 и ERC-721a могут быть немного проще в использовании и позволят вам легче запрашивать данные всей коллекции, но ERC-1155, как правило, будет лучший, более перспективный выбор.
Давайте сложим разные реализации рядом:
Features
ERC-721
ERC-721а
ERC-1155
Пакетные переводы
Несколько типов токенов в одном контракте
Легко определить общее количество NFT в сети
Простой запрос владельцев определенного актива
Локализация

Вывод

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

Поддержите блог, перечислив небольшие чаевые

Если у вас нет кошелька MetaMask, вы можете получить его здесь
Made on
Tilda