Сравнение стандартов NFT — ERC-721, ERC-721a и ERC-1155
К концу этой статьи и семинара вы сможете создавать NFT с использованием нескольких стандартов и видеть созданные NFT в OpenSea в тестовой сети Rinkeby со всеми соответствующими метаданными.
ERC-721 — одна из наиболее распространенных реализаций NFT. Очень распространенный способ реализации смарт-контракта ERC-721 — расширение реализации OpenZeppelin .
Спецификация допускает следующее поведение:
Спецификация также может быть расширена следующим необязательным поведением:
Некоторые ограничения спецификации:
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.
{
"description": "Infura NFT 721 Demo",
"external_url": "ipfs://QmaBm1F7vUujz5ZURDXS3yqeTsMFUEVW5M7bGQbTQr7vJK",
"image": "ipfs://QmaBm1F7vUujz5ZURDXS3yqeTsMFUEVW5M7bGQbTQr7vJK",
"name": "Infura721NFT",
"background_color": "#FFF"
}
{
"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
}
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();
});
};
...
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 )
},
...
npm install --save-dev erc721a
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";
}
}
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();
});
};
// 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";
}
}
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();
});
};