前言

熟悉hardhat,并发行一个NFT

目标

  • 了解hardhat
  • 了解solidity
  • 使用harhat发行nft
  • 使用ipfs存储你的nft

hardhat是什么

Hardhat是一个便于在以太坊上进行构建的开发环境。它帮助开发人员管理和自动化构建智能合约和dApp的过程中固有的重复任务,以及轻松地围绕此工作流程引入更多功能,并且内置了开发专用以太坊网络,这意味着从根本上进行编译和测试。

前期准备

初始化工程

1
2
3
4
5
6
7
8
9
10
mkdir  nft-public

cd nft-public

npm init

npm install --save-dev hardhat

npx hardhat

运行成功之后会生成如下的文件夹

1
2
3
4
5
6
7
8
9
10
11
.
├── README.md
├── contracts //存放职能合约的目录
│ └── Greeter.sol
├── hardhat.config.js // hardhar的配置目录,例如solidity的版本等
├── package.json
├── scripts // 执行部署合约的脚本
│ └── sample-script.js
├── test // 测试代码的目录
│ └── sample-test.js
└── yarn.lock

申请alchemy

![[alchemyapi.io#申请 alchemy 节点]]

注意我们申请的是他的测试网络

获得rinkeby的eth测试代币

FaucETH

Faucets | Chainlink

合约编写

使用openzeppelin

1
npm install @openzeppelin/contracts

openzeppelin 是什么?

![[openZeppelin#openZeppelin 是什么]]

合约代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

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

contract NFT_WEB3_EXPLORER is ERC721, ERC721Enumerable, Ownable {
string private _baseURIextended;
//我们设置最多可以mint1000个
uint256 public constant MAX_SUPPLY = 5000;
//每个mint的价格是0EHT
uint256 public constant PRICE_PER_TOKEN = 0 ether;
// 设置nft的名字和 symbol
constructor() ERC721("quantclass_nft", "QUANTCLASS_NFT") {
}

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

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

function setBaseURI(string memory baseURI_) external onlyOwner() {
_baseURIextended = baseURI_;
}

function _baseURI() internal view virtual override returns (string memory) {
return _baseURIextended;
}

function mint(uint numberOfTokens) public payable {
uint256 ts = totalSupply();
require(ts + numberOfTokens <= MAX_SUPPLY, "Purchase would exceed max tokens");
require(PRICE_PER_TOKEN * numberOfTokens <= msg.value, "Ether value sent is not correct");

for (uint256 i = 0; i < numberOfTokens; i++) {
_safeMint(msg.sender, ts + i);
}
}

function withdraw() public onlyOwner {
uint balance = address(this).balance;
payable(msg.sender).transfer(balance);
}
}

编译合约

1
npx hardhat compile

部署合约

在script目录下面复制一份sample-script.js 并重命名为quant-script.js用来部署我们的合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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 QuantclassNFT = await hre.ethers.getContractFactory("QUANTCLASS_NFT");
const quantclassNFT = await QuantclassNFT.deploy();

await quantclassNFT.deployed();

console.log("Greeter deployed to:", quantclassNFT.address);
}

// 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);
});

配置hardhat

安装env包

1
npm install dotenv

在根目录创建 .env 文件

1
2
3
PRIVATE_KEY = wallet_private_key

API = https://eth-rinkeby.alchemyapi.io/v2/YOUR_API_KEY

打开 hardhat.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
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}`]
}
},
};

运行部署

1
2
3
4
5
6
7
# 运行部署
npx hardhat --network rinkeby run scripts/quant-script.js

# 部署成功会得到部署成功的合约地址

Greeter deployed to: 0xe7953b8dc8Bb35C8f94F6E906d9269A92bBa8984

0xe7953b8dc8Bb35C8f94F6E906d9269A92bBa8984 | etherscan

就可以在测试网络看到我部署的合约了

EtherScan 认证 (选项)

hardhat-etherscan | Ethereum development environment for professionals by Nomic Foundation

1
npm install --save-dev @nomiclabs/hardhat-etherscan

申请enterscanAPI

配置配置文件

1
2
3
4
5
6
7
8
9
10
module.exports = {
networks: {
mainnet: { ... }
},
etherscan: {
// Your API key for Etherscan
// Obtain one at https://etherscan.io/
apiKey: "YOUR_ETHERSCAN_API_KEY"
}
};
1
2
npx hardhat verify --network mainnet DEPLOYED_CONTRACT_ADDRESS

部署成功之后,就能在0xe7953b8dc8Bb35C8f94F6E906d9269A92bBa8984 | etherscan 看到你自己的合约的各种方法了,以上为了测试方便,我已经将合约认证和上传了。

设置NFT资源

之前部署合约,我们预留了接口,可以让我们设置nft的资源。

查看ERC721的资源URL设置,是以资源的 baseURL + tokenId 进行拼接

配置资源

在目录下面创建文件夹

1
2
3
4
mkdir res
cd res
mkdir img
mkdir metadata

上传资源到ipfs

如果不想配置ipfs可以直接使用
Pinmanager | Pinata 进行上传,上传之后获得图片地址
然后拼接成 metadata的文件并上传
文件如下

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "quantclass-nft",
"attributes": [
{
"trait_type": "tokenID",
"value": "0"
}
],
"description": "quantclass image",
// image url
"image": "ipfs://QmQMsCrchFpHZvVk1v4S11damsqw29seiZWy3m9BYcqfEu/0.png"
}

metadata文件

设置合约NFT图片信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
require("dotenv").config()
const hre = require("hardhat");
const PRIVATE_KEY = process.env.PRIVATE_KEY
const API = process.env.API


const provider = new hre.ethers.providers.JsonRpcProvider(API);
//编译完成合约会自动生成
const abi = require("../artifacts/contracts/QuantclassNFT.sol/QUANTCLASS_NFT.json").abi
const contractAddress = "0xe7953b8dc8Bb35C8f94F6E906d9269A92bBa8984"
const contract = new hre.ethers.Contract(contractAddress, abi, provider)
const wallet = new hre.ethers.Wallet(PRIVATE_KEY, provider)
// metadata 地址
const baseURI = "ipfs://Qmart71tekfPvNdAUSDzffKSU2cnLqnCqbuZdfBSdJdPWq/0"

async function main() {
const contractWithSigner = contract.connect(wallet);
//调用setBaseURI方法
const tx = await contractWithSigner.setBaseURI(baseURI)
console.log(tx.hash);
await tx.wait();
console.log("setBaseURL success");
}

main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

测试mintNFT

合约地址 QUANTCLASS_NFT | Address 0xe7953b8dc8Bb35C8f94F6E906d9269A92bBa8984 | Etherscan

使用小狐狸进行mint

查看NFT

参考

如何发行一款NFT(上) — explorer_of_web3

tags