快速实现一个标准的NFT合约(实操篇)

  • 木西
  • 发布于 12小时前
  • 阅读 78

前言本文借助openzeppelin库编写一个标准的NFT合约,从开发,测试,到部署上链全部流程。注意:ERC20标准的同质化代币和ERC721标准的非同质化代币的区别需要metadata,要把信息上传到ipfs上,文中会有详细操作的步骤;同质化代币和非同质化代币程序层面的区别ERC2

前言

本文借助openzeppelin库编写一个标准的NFT合约,从开发,测试,到部署上链全部流程。注意:ERC20标准的同质化代币和ERC721标准的非同质化代币的区别 需要metadata,要把信息上传到ipfs上,文中会有详细操作的步骤;

同质化代币和非同质化代币程序层面的区别

  • ERC20:mapping(address=>uint)//地址指向余额
  • ERC721:mapping(uint=>address)//id指向地址

开发

合约功能说明:铸造,销毁,权限控制:只有项目方可铸造,

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

import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {ERC721Burnable} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol";
import {ERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import {ERC721URIStorage} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

contract BoykaNFT is ERC721, ERC721Enumerable, ERC721URIStorage, ERC721Burnable, Ownable {
    uint256 private _nextTokenId;

    constructor(address initialOwner)
        ERC721("BoykaNFT", "BNFT")
        Ownable(initialOwner)
    {}
    //铸造nft 说明:地址,metadata
    function safeMint(address to, string memory uri) public onlyOwner {
        uint256 tokenId = _nextTokenId++;
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // The following functions are overrides required by Solidity.

    function _update(address to, uint256 tokenId, address auth)
        internal
        override(ERC721, ERC721Enumerable)
        returns (address)
    {
        return super._update(to, tokenId, auth);
    }

    function _increaseBalance(address account, uint128 value)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._increaseBalance(account, value);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

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

测试

步骤:

  1. 在beforeEach中部署合约
  2. 拿到合约实例在describe调用相关方法验证

<!---->

const {ethers,getNamedAccounts,deployments} = require("hardhat");
const { assert,expect } = require("chai");
describe("NFT",async()=>{
    let NFT;//合约
    let firstAccount//第一个账户
    let secondAccount//第二个账户;
    //关于makedata的获取通过filebase工具生成的
    let mekadata='https://zygomorphic-magenta-bobolink.myfilebase.com/ipfs/QmQT8VpmWQVhUhoDCEK1mdHXaFaJ3KawkRxHm96GUhrXLB';
    beforeEach(async()=>{
        await deployments.fixture(["nft"]);
        firstAccount=(await getNamedAccounts()).firstAccount;
        secondAccount=(await getNamedAccounts()).secondAccount;
        const nftDeployment = await deployments.get("BoykaNFT");
        NFT = await ethers.getContractAt("BoykaNFT",nftDeployment.address);//已经部署的合约交互
    })
    describe("NFT",async()=>{
        it("铸造一个nft",async()=>{
            //const owner = await NFT.owner();
            await NFT.safeMint(firstAccount,mekadate);
        })
    })
})
//执行指令:部署nft合约到sepolia链上并铸造一个nft,成功后可以在opensea测试网上预览部署的nft
npx hardhat test --network sepolia

部署

  1. 通过部署插件部署合约
  2. 拿到合约地址,通过verify来验证合约是否部署成功

<!---->

module.exports=async ({getNamedAccounts,deployments})=>{
     let confirmations=5;//等待的区块数
    const {deploy,log} = deployments;
    const {firstAccount,secondAccount} = await getNamedAccounts();
    const BoykaNFT=await deploy("BoykaNFT",{
        from:firstAccount,
        args: [firstAccount],//参数:权限账号
        log: true,
        waitConfirmations: confirmations,//等待的区块数
    })
    //打印部署合约的地址
    console.log('nft合约',BoykaNFT.address)
    await hre.run("verify:verify", {
            address: BoykaNFT.address,
            constructorArguments: [firstAccount],
            });
};
module.exports.tags = ["all", "nft"];
//执行指令:把合约部署到sepolia链上
npx hardhat deploy --network sepolia

文件配置

  • 定义网络节点
  • 通过定义别名获取钱包地址

<!---->

# 在hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
require("@nomicfoundation/hardhat-verify");//验证合约
require('hardhat-deploy');//部署插件
require("dotenv").config();//使用此包读取.env文件的常量
const ALCHEMY_API_KEY=process.env.ALCHEMY_API_KEY;//在infura中创建项目会自动生成
const PRIVATE_KEY=process.env.PRIVATE_KEY;
const PRIVATE_KEY_1=process.env.PRIVATE_KEY_1;
const ETHERSCAN_API_KEY=process.env.ETHERSCAN_API_KEY//ETHERSCAN_API可以在ETHERSCAN浏览器网站注册
module.exports = {
solidity: "0.8.28",
networks:{
    localhost: {
      url:'http://127.0.0.1:8545/',
      chainId: 31337,
    },
    sepolia:{
      url: `https://sepolia.infura.io/v3/${ALCHEMY_API_KEY}`,//使用infura 创建项目会生成对应的api_key
      accounts: [PRIVATE_KEY,PRIVATE_KEY_1]//对应的账号的私钥,账号1和账号2
      chainId: 11155111
    }
  },
  etherscan: {
apiKey: {
  sepolia: ETHERSCAN_API_KEY
}

}, namedAccounts: { firstAccount: { default: 0 }, secondAccount: { default: 1 } },//可以用别名获取账号节点 } # 详细的配置可以在hardhat官网查看

关于铸造NFT时metadata生成

使用filebase网站把matadata存储到ipfs上<br> 把cattle.jpg和cattle.json文件上传到ipfs上<br> 关于cattle.json 实例:

{
    "description": "This is the zodiac sign chicken",//关于nft的描述
    "external_url":"https://openseacreatures.io/3",
    "image":"ipfs://QmWH3hY6J31Hcf61aM6A9cCVArKSfh8E4mYbL9yg68kUx8",//指向的图片的url
    "name":"chicken",//nft的名字
    "attributes": [
       {
          "trait_type": "Background",
          "value": "Blue"
       },
       {
          "trait_type": "Eyes",
          "value": "Black"
       },
       {
          "trait_type": "Mouth",
          "value": "Smile"
       }
    ]//关于属性的设置
 }

如图所示

屏幕截图 2024-12-28 001841.png

工具

使用openSea网站查看合约是否部署成功<br> 在openSea测试网上链接自己的钱包,可以查看到自己铸造的所有的项目<br> 部署成功如图:

屏幕截图 2024-12-27 235740.png

总结

以上就是从开发测试到部署的全流程,NFT和同质化代币最主要的区别就是多了metadata上传到ipfs上的操作;

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
木西
木西
江湖只有他的大名,没有他的介绍。