用React+Hardhat快速构建本地Dapp-NFT发行系统

  • A+
所属分类:分享

chatGPT账号

一、环境准备

1. 技术栈
  • node.js 18.18.0
  • react 18.2.0
  • hardhat 2.13.0
  • openzeppelin 4.9.6
  • IPFS Desktop
  • solidity ^0.8.20
  • vscode 1.87.2
  • ethers.js 6.11.1
2. 主要技术简介
  • hardhat
    Hardhat是一个编译、部署、测试和调试以太坊应用的开发环境。而且Hardhat内置了Hardhat网络,这是一个专为开发设计的本地以太坊网络。主要功能有Solidity调试,跟踪调用堆栈、交易失败时的明确错误信息提示等。
  • ethers.js
    ethers.js库旨在为以太坊区块链及其生态系统提供一个小而完整的 JavaScript API 库。
    相较于web3.js,ethers.js更轻量更灵活,能提升dapp的性能。
    ethers.js有四大核心模块:Provider、Signer、Contract、utils。其中Provider用来读取区块链网络和获取链上状态,Signer主要是进行签名进行以太币交易,Contract是合约的抽象,可以用来操作合约,utils主要是进行格式化钱包余额等数据处理。

二、构建步骤

1.用react快速创建web3应用程序

首先,用npx命令创建一个typescript React应用程序。
npx create-react-app geno-nft-dapp --template typescript

用React+Hardhat快速构建本地Dapp-NFT发行系统

接下来,进入到新目录,此时创建的项目是一个普通的前端应用程序。

用React+Hardhat快速构建本地Dapp-NFT发行系统

接下来安装hardhat并集成到项目中
npm install --save-dev hardhat

用React+Hardhat快速构建本地Dapp-NFT发行系统

删除 目录下README.md 和 tsconfig.json文件,防止与下述步骤命令执行产生冲突
npx hardhat init

用React+Hardhat快速构建本地Dapp-NFT发行系统

用React+Hardhat快速构建本地Dapp-NFT发行系统

此命令是初始化hardhat,将其注入集成至项目结构中

安装hardhat-toolbox
npm install --save-dev @nomicfoundation/hardhat-toolbox

用React+Hardhat快速构建本地Dapp-NFT发行系统

此时项目结构如下:

用React+Hardhat快速构建本地Dapp-NFT发行系统

注意,"@types/node"版本需在18.0.0以上,不然会与hardhat-toolbox产生依赖冲突。

最后,修改tsconfig.json文件如下

{
  "compilerOptions": {
    "jsx": "preserve",
    "target": "es2020",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "resolveJsonModule": true
  },
  "include": [
    "src//*"                             // 包含src目录下的所有文件
  ],
  "exclude": [
    "node_modules",                        // 排除node_modules目录
    "/*.spec.ts"                         // 排除所有的测试文件
  ]
}

此时运行npm start就可以启动项目

2.安装OpenZeppelin

首先安装OpenZeppelin。
OpenZeppelin Contracts 是一个用于安全智能合约开发的库。,它提供了 ERC20 和 ERC721 等标准的实现
npm install @openzeppelin/contracts

用React+Hardhat快速构建本地Dapp-NFT发行系统

安装完成后,可以在项目node_modules目录下看到Openzeppeline结构如下:

用React+Hardhat快速构建本地Dapp-NFT发行系统

access: 此目录提供了限制谁可以访问合同功能或何时可以访问的方法
crosschain:此目录提供了提高智能合约跨链意识的构建块
finance:此目录包括金融系统的基元:
PaymentSplitter允许在一组账户中分割以太币和ERC20付款。发送方不需要知道资产将以这种方式分割,因为它是由合同透明地处理的。分割可以是相等的部分,也可以是任何其他任意的比例。
VestingWallet为特定受益人处理以太币和ERC20代币的归属。可以将多个代币的托管权交给本合同,该合同将按照给定的、可定制的行权时间表将代币发放给受益人。
goverance:This directory includes primitives for on-chain governance 此目录包括链上治理的基元
interfaces:这些接口可用作.sol文件,也可用作编译器.json ABI文件(通过npm包)。这些对于与实施它们的第三方合同进行交互非常有用
metax: 这是一组实现不同代理模式的低级别合约,可升级也可不升级。有关此模式的深入概述,请查看代理升级模式页面。
proxy:
security:这些合同旨在涵盖常见的安全做法。
PullPayment:一种可以用来避免重入攻击的模式。
重入保护:一个可以在某些函数中防止重入的修饰符。
可暂停:一种常见的应急响应机制,可以在补救挂起时暂停功能
token:
utils:包含实用程序函数的杂项约定和库,可用于提高安全性、使用新的数据类型或安全地使用低级基元。
Address、Arrays、Base64和Strings库提供了更多与这些本机数据类型相关的操作,而SafeCast添加了在不同的有符号和无符号数字类型之间安全转换的方法。Multicall提供了一个函数,用于在单个外部调用中将多个调用批处理在一起。
对于新的数据类型:
计数器:获取只能递增、递减或重置的计数器的简单方法。对于ID生成、计算合同活动等非常有用。
EnumerableMap:类似于Solidity的映射类型,但具有键值枚举:这将让您知道映射有多少个条目,并对它们进行迭代(这在映射中是不可能的)。
EnumerableSet:类似于EnumerableMap,但适用于集合。可用于存储特权帐户、已颁发的ID等。
ERC721合约继承关系图结构
[图片] ERC721URIStorage:是 tokenId 到 tokenURI 映射关系的实际持有者
ERC721Enumrable:提供遍历代币的能力
ERC721Metadata: Metadata 层面的通用属性,包括token名称/存储地址(metadata URI)等
ERC165:检查一个智能合约是不是支持ERC721

合约示例

// contracts/GameItem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

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

contract GenoNFT is ERC721URIStorage, ERC721Enumerable {
    
    uint256 private _nextTokenId;
    constructor() ERC721("GenoNFT", "GT") {
       
    }

    function mint(address player, string memory tokenUri)
        public
        returns (uint256)
    {
  
        uint256 tokenId = _nextTokenId++;
        _mint(player, tokenId);
        _setTokenURI(tokenId, tokenUri);

        return tokenId;
    }

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


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


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

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

3.编译合约

在编译之前,我们先对hardhat.config.ts文件做一些修改:


import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";

const config: HardhatUserConfig = {
  solidity: "0.8.24",
  paths:{
    artifacts: "./src/artifacts"
    }
};

export default config;

此配置用来指定合约语言solidity的版本以及合约编译后的输出路径。
接下来在项目目录下执行如下命令对合约进行编译:
npx hardhat compile

用React+Hardhat快速构建本地Dapp-NFT发行系统

合约编译完成后会在上面配置的目录下生成一些文件,
[图片] 这里需要重点关注的是GenoNFT.json文件,它包含了合约的ABI信息。ABI代表应用程序二进制接口,是客户端应用程序和以太坊区块链之间的接口。

4.部署合约

执行npx hardhat node命令来启动harhat自带的本地链

用React+Hardhat快速构建本地Dapp-NFT发行系统

接下来在metamask中配置好本地链网络并随意选中上图中的账户导入到metamask中

用React+Hardhat快速构建本地Dapp-NFT发行系统

复制账户私钥将其导入值metamask中

用React+Hardhat快速构建本地Dapp-NFT发行系统

创建部署脚本文件deploy.ts来部署合约到本地链上,部署脚本如下

用React+Hardhat快速构建本地Dapp-NFT发行系统

执行部署命令
npx hardhat run .\scripts\deploy.ts --network localhost

用React+Hardhat快速构建本地Dapp-NFT发行系统
此时,我们已经成功将合约部署到本地链上,并打印出了合约地址。

三、实现铸币发布NFT功能

1.铸币功能流程

用React+Hardhat快速构建本地Dapp-NFT发行系统

2.相关核心代码
const mint = async () => {
        try {
            const data: NftMeta = { ...meta, imageUri: uri,type:"image" }
            const json = JSON.stringify(data);
            //上传文件到Ipfs(json)
            const metauri = await storeMeta(json); 
            //铸造nft
            const { success, tokenId } = await mintNFT(metauri);
        } catch (error) {
            if (error instanceof Error)
                messageBox("danger", "", error.message)
        }
    }
    
    
    
    export const mintNFT = async (tokenUri: String): Promise<{ success: boolean, tokenId?: number }> => {
  
    //连接钱包
    const { success, address, provider, signer } = await trying();
    if (!success || !signer) {
        return { success: false };
    }
    //构造合约
    let nft = new Contract(configuration().nftAddress, NFT.abi, signer);
    //调用合约铸币方法
    let transaction = await nft.mint(address, tokenUri);
    let tx = await transaction.wait(1);

    let logs = tx.logs[1];
    let nftIndex = parseInt(logs.data, 16);
    let tokenId = Number(nftIndex);
    alert(nftIndex);
    return { success: true, tokenId };
}

免责声明

发文时比特币价格:$71249

当前比特币价格:[crypto coins=”BTC” type=”text” show=”price”]

当前比特币涨幅:[crypto coins=”BTC” type=”text” show=”percent”]

免责声明:

本文不代表路远网立场,且不构成投资建议,请谨慎对待。用户由此造成的损失由用户自行承担,与路远网没有任何关系;

路远网不对网站所发布内容的准确性,真实性等任何方面做任何形式的承诺和保障;

网站内所有涉及到的区块链(衍生)项目,路远网对项目的真实性,准确性等任何方面均不做任何形式的承诺和保障;

网站内所有涉及到的区块链(衍生)项目,路远网不对其构成任何投资建议,用户由此造成的损失由用户自行承担,与路远网没有任何关系;

路远区块链研究院声明:路远区块链研究院内容由路远网发布,部分来源于互联网和行业分析师投稿收录,内容为路远区块链研究院加盟专职分析师独立观点,不代表路远网立场。

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的电报
  • 这是我的电报扫一扫
  • weinxin
chatGPT账号
区块逐字

发表评论

您必须登录才能发表评论!