本篇文章次要介绍如何将您的 NFT(ERC-721 Token) 通过智能合约部署到去中心化网络中。
Init Project
// 创立一款 ocean 的 NFT
mkdir nft-ocean
// 进入目录
cd nft-ocean
// 初始化我的项目,依据提醒填写即可,packname 和 description 填写即可
npm init
// 增加 hardhat 依赖
npm install --save-dev hardhat
/* 应用脚手架搭建我的项目, 咱们抉择 Create a basic sample project
能够帮忙咱们创立 1 个 demo 工程并依照所需的依赖 */
npx hardhat
这是初始化实现后的代码构造
- contracts 目录用来寄存咱们的智能合约代码
- scripts 用来寄存咱们的脚本,比方合约部署就会依赖其中的脚本
- test 目录用来寄存咱们为智能合约编写的测试代码
- hardhat.config.js 是对于 hardhat 框架的一些配置,比方 solidity 版本等
- package.json 是 npm 的相干配置
编写合约代码
目前就能够开始写合约代码了,本文次要是介绍 NFT 的发行,咱们简略来实现一份 NFT 智能合约代码。
// 装置 openzeppelin/contracts 依赖,内置较多合约协定的实现以及工具代码
npm install @openzeppelin/contracts
咱们在 contracts 目录下创立 NFT_OCEAN.sol 的文件。
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
contract NFT_OCEAN is ERC721, ERC721Enumerable, Ownable {
string private _baseURIextended;
//Set mint limit
uint256 public constant MAX_SUPPLY = 5;
//0.01eth per mint
uint256 public constant PRICE_PER_TOKEN = 0.01 ether;
constructor() ERC721("nft_ocean", "NFT_OCEAN") { }
function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal override(ERC721, ERC721Enumerable) {super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
}
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);
}
}
该代码不具备上线规范,因为没有白名单。每个地址限度 mint 多少个等,仅供示意。倡议发行 NFT 前须要好好设计合约
合约编译
// 合约编译
npx hardhat compile
合约部署
批改 scripts 目录下主动生成的脚本改名为 deploy.js 并批改外部代码
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 Nft_ocean = await hre.ethers.getContractFactory("NFT_OCEAN");
const nft_ocean = await Nft_ocean.deploy();
await nft_ocean.deployed();
console.log("NFT_OCEAN deployed to:", nft_ocean.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);
});
抉择节点服务代理
有很多办法能够向以太坊区块链发出请求,但为了让事件变得简略,咱们将应用 Alchemy 上的收费帐户,这是一个区块链开发人员平台和 API,容许咱们与以太坊链,无需运行咱们本人的节点。创立过程参考文档,别离创立一个 Ethereum Mainnet 和 Ethereum Sepolia 的 APP
测试网络环境搭建
咱们首先须要在测试网络中部署,咱们抉择 Sepolia 作为咱们的测试网络。
因为在代码执行过程中会用到公钥、私钥、API 等数据,但这些数据又不能编码到代码中,因为如果咱们应用 git 来治理我的项目,信息将会泄露。咱们应用 dotenv 寄存部署合约以及和合约交互须要用到的数据。
// 增加 dotenv 依赖
npm install dotenv
// 工程根目录下创立变量存储文件
touch .env
为.env 减少内容
PRIVATE_KEY= 导出你 metamask 的私钥填在这里
API=Alchemy APP 中的 HTTPS
PUBLIC_KEY= metamask 地址
NETWORK=sepolia
API_KEY=Alchemy 的 API KEY
- metamask 私钥导出: 关上钱包 - 账号详情 -> 导出私钥。
- API、API_KEY:关上 Alchemy 中 Ethereum Sepolia 的 API KEY,复制 HTTPS 和 API_KEY
批改 hardhat.config.js
/**
* @type import('hardhat/config').HardhatUserConfig
*/
require("@nomicfoundation/hardhat-toolbox");
require("@nomiclabs/hardhat-ethers");
//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.19",
defaultNetwork: "sepolia",
networks: {hardhat: {},
sepolia: {
url: API,
accounts: [`0x${PRIVATE_KEY}`]
}
},
};
获取测试网络 ETH 因为合约测试须要耗费 ETH,咱们须要获取一些测试用的 ETH,咱们关上 https://sepoliafaucet.com/,填入咱们的钱包地址即可获取。
此时咱们将钱包切换到 sepolia 测试网络,能够看到钱包中曾经有一些 ETH 了。
执行合约部署
首先装置 ETHERS.JS
npm install --save-dev @nomiclabs/hardhat-ethers ethers@^5.7.2
执行合约部署
npx hardhat --network sepolia run scripts/deploy.js
// 执行实现后失去提醒,代表合约部署实现
NFT_WEB3_EXPOLRER deployed to: {合约地址}
此时咱们能够在 https://sepolia.etherscan.io/ 搜寻咱们的合约地址找到咱们的合约信息
为 NFT 设置资源
当初的 NFT 仅仅蕴含 tokenID,咱们须要为其设置资源, 在 ERC721 的实现中咱们能够看到 tokenURI 办法的实现,各个交易市场就是调用该办法来读取 NFT 资源信息的
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
咱们能够看到这里是将 BaseURI 和 tokenID 拼接来读取 tokenURI 的,所以咱们也依照这种格局来筹备资源。
筹备资源
// 创立资源文件
mkdir res
cd res
// 寄存图片
mkdir nfts
// 寄存 metadata 信息
mkdir metadata
因为本文是示例我的项目,没有找专门的设计师设计图片,咱们网上找了 5 张图片。将 5 张图片放入 img 目录下,顺次命名为 1.jpeg ~ 5.jpeg
咱们资源文件上传到 ipfs 中,这里和以太坊网络一样,因为咱们不想运行本地 ipfs 节点,所以咱们寻找节点服务提供商进行上传。咱们应用 https://dashboard.4everland.org/bucket/storage/ 将 nfts 文件夹上传
上传实现后的图片资源
选中图片文件夹进行 snapshots, 即可生成 root cid, 网关拜访成果如下:
接下来咱们筹备 metadata 文件, 在 metadata 目录下新建 5 个文件顺次命名为 0~4,咱们看下 0 号文件信息
{
"name": "nft-ocean",
"attributes": [
{
"trait_type": "tokenID",
"value": "0"
}
],
"description": "nft-ocean image",
// 填入下面刚刚上传实现的图片地址
"image": "ipfs://bafybeief75v57gu3khb5ftjkgldyzyea4x3r6sesekcia6lk2pijgl5idm/01.jpeg"
}
咱们将 metadata 文件夹上传并 snapshot, 获取文件夹 CID 后进行 publish
设置 BaseURI
咱们须要将刚刚上传后的 metadata 文件地址设置给合约的 baseURI,这样各个平台在应用 tokenURI 获取资源信息才能够获取到。
编写代码与合约交互设置 BaseURI,在 scripts 目录下新建 setBaseURI.js 文件。
setBaseURI.js
require("dotenv").config()
const hre = require("hardhat");
const PRIVATE_KEY = process.env.PRIVATE_KEY
const NETWORK = process.env.NETWORK
const API_KEY = process.env.API_KEY
const provider = new hre.ethers.providers.AlchemyProvider("sepolia",API_KEY);
// 编译实现合约会主动生成
const abi = require("../artifacts/contracts/NFT_OCEAN.sol/NFT_OCEAN.json").abi
const contractAddress = "填合约地址"
const contract = new hre.ethers.Contract(contractAddress, abi, provider)
const wallet = new hre.ethers.Wallet(PRIVATE_KEY, provider)
const baseURI = "ipfs://{metadata 文件的 root cid}/" // 填
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);
});
执行 setBaseURI 脚本
npx hardhat --network sepolia run scripts/setBaseURI.js
Mint 测试
因为只有 mint 过的 tokenID 才会展现在交易市场中,所以咱们须要编写代码进行 mint 测试(理论场景,该操作应该由前端页面调用实现)。
mint.js
require("dotenv").config()
const hre = require("hardhat");
const PRIVATE_KEY = process.env.PRIVATE_KEY
const NETWORK = process.env.NETWORK
const API_KEY = process.env.API_KEY
const provider = new hre.ethers.providers.AlchemyProvider("sepolia",API_KEY);
const abi = require("../artifacts/contracts/NFT_OCEAN.sol/NFT_OCEAN.json").abi
const contractAddress = "0x531D6B8fBe5FAC349D05642E17F5E998A4DfEd68"
const contract = new hre.ethers.Contract(contractAddress, abi, provider)
const wallet = new hre.ethers.Wallet(PRIVATE_KEY, provider)
async function main() {const contractWithSigner = contract.connect(wallet);
// 获取 mint 须要多少 ETH
const price = await contract.PRICE_PER_TOKEN();
console.log("price is" + price);
// 调用 mint 办法,领取 mint 费用
const tx = await contractWithSigner.mint(1, { value: price});
console.log(tx.hash);
await tx.wait();
console.log("mint success");
}
main()
.then(() => process.exit(0))
.catch((error) => {console.error(error);
process.exit(1);
});
执行 mint 脚本
npx hardhat --network sepolia run scripts/mint.js
Mint 胜利
查看 NFT
此时咱们去 opensea 测试网 https://testnets.opensea.io/ 查看咱们 mint 的 NFT 即可。