Blockchain combat: Issuing its first NFT (Non-Fungible Token) digital collection

Note: This project is only for learning blockchain knowledge, not as any investment advice. Market risk, the investment need to be cautious.

Portal: Introduction to Blockchain: Develop your own encrypted digital currency (Token) in the local network - FoolCoin (FoolCoin)

The project code of this article:

GitHub - hicoldcat/nft-web3-example: NFT smart contract development and deployment example, based on solidity, hardhat, openzeppelin...

NFT concept

NFT (Non-Fungible Token), the official scientific name of non-homogeneous tokens, is different from homogeneous tokens and represents indivisible digital assets. In layman's terms, $10 can be divided into 10 parts, each part is $1, and each part (1 dollar) is equivalent. This is a homogeneous token. The analogy is that a painting is worth $10, but it cannot be split into 10 parts, each of which is $1. So NFT is an indivisible digital asset that can be used as a token for value exchange, but it can only exist as an independent whole.

Initialize the project

Hardhat is a development environment for compiling, deploying, testing and debugging Ethereum applications. It helps developers manage and automate the repetitive tasks inherent in building smart contracts and DApps, and easily introduce more functionality around this workflow. This means that the core of hardhat is compiling, running and testing smart contracts.

Create a folder eg nft-web3-example, and npm initinitialize the project with it.

Add hardhat dependency,

npm install --save-dev hardhat
npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers

Use hardhat to initialize the project, select Create a basic sample project, and generate the project structure file template

npx hardhat

development contract

Solidity is a contract-oriented, high-level programming language created to implement smart contracts.

openzeppelin is a library for secure smart contract development, with built-in standard implementations of many commonly used contracts.

Before development, first install the openzeppelin contract library, which contains many smart contract implementations and some commonly used tool codes.

npm install @openzeppelin/contracts

Then, contractscreate FOOL_NFT.solfiles under the directory. The content is as follows:

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

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

/// @custom:security-contact [email protected]
contract FoolNFT is ERC721, ERC721URIStorage, ERC721Burnable, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    constructor() ERC721("FoolNFT", "FOOL") {}

    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 _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 currentCounter() public view returns (uint256) {
        return _tokenIdCounter.current();
    }

    function freeMint(address to, string memory nftTokenURI) public {
        _safeMint(to, _tokenIdCounter.current());
        _setTokenURI(_tokenIdCounter.current(), nftTokenURI);
        _tokenIdCounter.increment();
    }
}

node configuration

For the difference between node service providers infura and Alchemy , you can read my other article " ALCHEMY VS. INFURA: Which is the best blockchain node service provider?" "

This time, instead of running an Ethereum node locally to deploy the contract, we will use the services provided by the infura node service provider.

Create a project on infura to get the API call address. In the Ethereum test network, we choose rinkeby as our test network. Therefore, the API address network of this project should be selected as rinkeby.

 

Then, because we need to use one to deploy the contract Account, including the public key and private key of the account, so, for safety, we use dotenv to store the data needed to deploy the contract and interact with the contract. In this way, it will not be written into the code.

In the project root directory, execute the following command:

//安装dotenv npm包
npm install dotenv

//根目录下创建.env文件
touch .env

In .envthe file, add the following content,

PUBLIC_KEY=XXXXX
PRIVATE_KEY=XXXXX

API=https://rinkeby.infura.io/v3/XXXXX
API_KEY=XXXXX

NETWORK=rinkeby

PUBLIC_KEYand PRIVATE_KEYcan be found from matemask, or create a new Account by yourself. It is strongly recommended to create a new one to use as a test account. The private key can be exported according to the path: open wallet-account details->export private key.

APIand API_KEYis the API address and PROJECT ID created on infura above.

NETWORKUse rinkeby.

Compile the contract

Command-line completion | Ethereum development environment for professionals by Nomic Foundation is an NPM package that installs a globally accessible binary named hh that runs the project's locally installed hardhat and supports shell autocompletion of tasks.

For the convenience of development, hardhat shorthand can be used, and the installation method is as follows:

npm i -g hardhat-shorthand

hardhat-completion install

After that, run the following command:

//合约编译
hh compile

The generated artifactsfolder is the compiled file.

deploy contract

Create it under scriptsthe directory deploy.jswith the following contents:

// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
const hre = require("hardhat");

async function main() {
    // Hardhat always runs the compile task when running scripts with its command
    // line interface.
    //
    // If this script is run directly using `node` you may want to call compile
    // manually to make sure everything is compiled
    // await hre.run('compile');

    // We get the contract to deploy
    const FoolNFT = await hre.ethers.getContractFactory("FoolNFT");
    const fool = await FoolNFT.deploy();

    await fool.deployed();

    console.log("FoolNFT deployed to:", fool.address);
    console.log("owner", await logo.owner());

}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
    .then(() => process.exit(0))
    .catch((error) => {
        console.error(error);
        process.exit(1);
    });

Then, modify hardhat.config.jsthe content,

require("@nomiclabs/hardhat-waffle");

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

require('dotenv').config();

const { API, PRIVATE_KEY } = process.env;

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.8.4",
  defaultNetwork: "rinkeby",
  networks: {
    hardhat: {},
    rinkeby: {
      url: API,
      accounts: [`0x${PRIVATE_KEY}`]
    }
  },
};

Because ETH needs to be consumed when the contract is deployed to the rinkeby network, so before deployment, we need to obtain some ETH for testing to the test account we created earlier. Open FaucETH  and select the rinkeby network, fill in our wallet address to get it.

After that, switch to the rinkeby test network in the metamask wallet, and you can see that the ETH we received has entered the account.

Then, we can execute the deployment of the contract,

hh run scripts/deploy.js --network rinkeby

//执行完成后得到提示,代表合约部署完成
FoolNFT deployed to: 0x1F8fa1e7C29968b25C3a5129365Da8BC3990856b
owner 0xD936DEa2791e76F20A643d4149747e8598E51D8c

After the deployment is complete, we can  search our contract address at https://rinkeby.etherscan.io/ to find our contract information.

In addition, we can also https://rinkeby.etherscan.io/address/AccountOwnerKeyquery the asset consumption in the address, and the address in the address AccountOwnerKeyshould be replaced with the above owner address, which is what we .envconfigured in it PUBLIC_KEY.

For example, in this example, https://rinkeby.etherscan.io/address/0xD936DEa2791e76F20A643d4149747e8598E51D8c received three test tokens of 0.42 ETH, and deployed the contract consumed 0.004464784529 ETH.

 

Minting NFTs

NFT.Storage - Free decentralized storage and bandwidth for NFTs on IPFS & Filecoin. It is an open source storage service based on IPFS and Filecoin.

Before casting NFT, we need to register an account at NFT.Storage - Free decentralized storage and bandwidth for NFTs on IPFS & Filecoin. , and then get API Token. For how to obtain it, please refer to the document: NFT.Storage Docs .

 

After getting it, you can save this Token in .env. .envAdd the following to :

// nft.storage的api token
NFT_STORAGE_TOKEN=XXX...

// 之前合约部署成功后的地址,本例中是0x1F8fa1e7C29968b25C3a5129365Da8BC3990856b
NFT_CONTRACT_ADDRESS=0x1F8fa1e7C29968b25C3a5129365Da8BC3990856b

After that, install the npm package according to the JavaScript client library documentation NFT.Storage Docs on nft.storage nft.storage .

npm install nft.storage

scriptsCreate mint.jsthe file below as follows:

// We require the Hardhat Runtime Environment explicitly here. This is optional
// but useful for running the script in a standalone fashion through `node <script>`.
//
// When running the script with `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.

require("@nomiclabs/hardhat-ethers");
const hre = require("hardhat");
const path = require("path");
const fs = require("fs");
const NFTS = require("nft.storage");

// 调用dotenv配置方法
require('dotenv').config();

// NFT.storage 获取的API Token
const storageToken = process.env.NFT_STORAGE_TOKEN || "";

// NFTStorage 客户端实例
const client = new NFTS.NFTStorage({ token: storageToken });

// NFT合约部署成功后的地址
const CONTRACT_ADDRESS = process.env.NFT_CONTRACT_ADDRESS || "";

// 合约部署人
const OWNER = process.env.PUBLIC_KEY || "";

// 合约接口
const contractInterface = require("../artifacts/contracts/FOOL_NFT.sol/FoolNFT.json").abi;

// provider
const provider = new hre.ethers.providers.InfuraProvider(process.env.NETWORK, process.env.API_KEY);

// 钱包实例
const wallet = new hre.ethers.Wallet(`0x${process.env.PRIVATE_KEY}`, provider);

// 合约
const contract = new hre.ethers.Contract(CONTRACT_ADDRESS, contractInterface, provider)
const contractWithSigner = contract.connect(wallet);

// 上传文件到nft.storage
async function uploadNFTFile({ file, name, description }) {
    console.log("Uploading file to nft storage", { file, name, description });
    const metadata = await client.store({
        name,
        description,
        image: file,
    });
    return metadata;
}

// 铸造NFT
async function mintNFT({
    filePath,
    name = "",
    description = "",
}) {
    console.log("要铸造的NFT:", { filePath, name, description });
    const file = fs.readFileSync(filePath);

    const metaData = await uploadNFTFile({
        file: new NFTS.File([file.buffer], name, {
            type: "image/png", // image/png
        }),
        name,
        description,
    });

    console.log("NFT Storage上存储的NFT数据:", metaData);

    const mintTx = await contractWithSigner.safeMint(OWNER, metaData?.url);
    const tx = await mintTx.wait();
    console.log("铸造的NFT区块地址:", tx.blockHash);
}


// 入口函数
async function main() {

    // 读取根目录下assets文件夹下的文件
    const files = fs.readdirSync(path.join(__dirname, "../assets"));

    for (const file of files) {
        const filePath = path.join(__dirname, "../assets", file);
        await mintNFT({
            filePath,
            name: file,
            description: path.join(file),
        });
    }
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
    .then(() => process.exit(0))
    .catch((error) => {
        console.error(error);
        process.exit(1);
    });

Run mint NFT command:

hh run scripts/mint.js --network rinkeby

After waiting for a while, the following information will be printed out:

要铸造的NFT: {
  filePath: 'E:\\MyCode\\nft-web3-example\\assets\\ilona-frey.png',
  name: 'ilona-frey.png',
  description: 'ilona-frey.png'
}
Uploading file to nft storage {
  file: File { _name: 'ilona-frey.png', _lastModified: 1653204844289 },
  name: 'ilona-frey.png',
  description: 'ilona-frey.png'
}
NFT Storage上存储的NFT数据: Token {
  ipnft: 'bafyreihyccauepunk7jgk5chy5u3qoaysrsm5ohwwzjbf7v2iebwbghyjm',
  url: 'ipfs://bafyreihyccauepunk7jgk5chy5u3qoaysrsm5ohwwzjbf7v2iebwbghyjm/metadata.json'
}
铸造的NFT区块地址: 0xd37902100eec5afceb337ca5a9f4e2e667e222696f58cb84f48bd0b8b85a96b5

View NFTs

At this point, log in to NFT markets such as opensea or looksrare, and you can view the NFT we minted. The address is as follows:

Opensea test network address: https://testnets.opensea.io/

Looksrare test network address: https://rinkeby.looksrare.org/

Take looksrare as an example, choose to log in, it will evoke metamask wallet login:

After authorized login, you can see in the personal center, my NFT:

 

 

Opensea view is similar.

Summarize

So far, we have completed the release of our first NFT digital product. After that, you can use some front-end libraries such as, etc., and combine the front-end framework to develop some web pages to display your own NFT website interaction logic, so as to complete a real NFT digital collection trading website use-nft. web3-reactYou can also refer to some articles on the Denglian community , or start-up projects like GitHub - jellydn/nft-app: How to create your own NFT and mint NFT token to learn.

Guess you like

Origin blog.csdn.net/smartContractXH/article/details/126619353