quentangle

Posted on Jan 06, 2022Read on Mirror.xyz

教程:用React构建一个NFT的前端

原文链接:Tutorial: Building a web3 frontend with React

简介

之前的教程中,我们介绍了如何从头开始创建和部署一个NFT藏品智能合约。我们还探讨了如何在etherscan上验证我们的合约,并使自己和用户能够直接从合约的etherscan页面调用合约功能。

然而,大多数严肃的项目倾向于部署他们自己的网站,并允许用户直接从网站上铸造。

这正是我们将在本教程中涉及的内容。更具体地说,本教程将告诉你如何:

  1. 让用户将他们的Metamask钱包连接到你的网站上
  2. 允许用户调用合约函数,进行支付,并从合约中铸造一个NFT。

在本教程结束时,你将拥有一个用React构建的功能齐全的web3前端。你也将获得构建任何通用的web3前端所需的基础知识(不仅是铸造NFT)。

前提条件

React 官方教程

本教程假定你已经开发并部署了智能合约到Rinkeby测试网络。如果你还没有,我们强烈建议你学习这个教程。为了继续学习本教程,你将需要以下东西:

  1. 你的智能合约的ABI文件(在你的智能合约项目的artifacts文件夹中可以找到)。
  2. 你的智能合约的地址。

我们还假设你有使用React和Javascript的经验。如果没有,我们强烈建议你先看一下React网站上的官方教程

设置项目

让我们从使用 create-react-app创建一个React项目开始。打开你的终端,运行以下命令:

npx create-react-app nft-collectible-frontend

安装过程将需要2–10分钟。完成之后,通过运行以下程序检查一切是否正常:

cd nft-collectible-frontend
npm start

如果一切顺利,你应该看到你的浏览器在http://localhost:3000打开一个新的标签,屏幕如下。很标准的React。

现在让我们做一点清理工作。

进入public/index.html,修改你网站的标题和meta描述。这一步是可选的。

接下来,进入src文件夹,删除App.test.jslogo.svgsetupTests.js文件。在本教程中,我们将不需要这些文件。

转到App.js文件,用以下模板替换其内容。

import './App.css';function App() {
    return (
        <h1>Hello World</h1>
    );
}export default App;

删除App.css的所有内容。但是不要删除这个文件。在后面的章节中,我们将提供一些基本的风格设计,对于这个演示项目来说应该是足够的。

如果你回到localhost,你应该看到一个屏幕,上面写着Hello World。我们现在有了一个基本的React项目,可以开始了。

获得合同ABI和地址

为了使我们的 React 前端能够与我们的智能合约连接和通信,它需要合约的 ABI 和地址。

ABI(或Application Binary Interface)是一个JSON文件,在合约编译期间自动生成。区块链以字节码的形式存储我们的智能合约。为了在其上调用函数,传递正确的参数,并使用高级语言解析返回值,我们需要向我们的前端指定有关函数和合约的细节(如名称、参数、类型等)。这正是ABI文件的作用。为了了解更多关于ABI的信息,建议去看看这篇优秀的文章

要找到ABI文件,请进入hardhat项目并导找到artifacts/contracts/NFTCollectible.sol/NFTCollectible.json

现在需要把JSON文件复制到我们的React项目中。在 src 文件夹中创建一个名为 contracts 的新文件夹,并粘贴 NFTCollectible.json 文件。

你应该已经有了你部署的智能合约的地址。(如果你没有,只需再次将其部署到Rinkeby,并获得最新的地址和ABI文件)。

我们在上一个教程中的合约地址是0x355638a4eCcb777794257f22f50c289d4189F245。(译注:你部署的合约地址会有所不同)。我们在本教程中也将使用这个合约。

现在让我们导入合同ABI并在App.js文件中定义合约地址。

设置模板式HTML、CSS和JS

我们的网站将是非常简单的。只有一个标题和一个连接钱包的按钮。钱包连接后,连接钱包按钮将被一个Mint NFT按钮所取代。

我们不打算费力地创建单独的组件文件。相反,我们将在App.js中编写所有的HTML和逻辑,在App.css中编写所有的CSS。

将以下Github gist的内容复制到你的App.js文件。

(记得在第5行设置正确的合约地址)

请注意,我们已经定义了几个函数,这些函数目前没有什么作用。我们将在下文中解释它们的用途,并加入逻辑。

我们也有少量的CSS供你使用。将以下内容复制到你的App.css文件中。

你的网站看起来应该长这样:

前端页面的最终外观

通过添加更多的样式和静态元素(图片、页眉、页脚、社交媒体链接等),可以自由地定制网站的外观。

我们已经把这个项目的大部分基础模块放在一起。我们现在来解决本教程的第一个主要目标之一:允许用户将他们的钱包连接到我们的网站。

连接Metamask钱包

为了使用户能够从合约中调用方法,用户需要将他们的钱包连接到网站。钱包将使用户能够支付gas和销售价格,从而从我们的藏品中铸造一个NFT。

在本教程中,我们将专门使用Metamask钱包和它的API套件。像Moralisweb3modal这样现成的解决方案,可以让你用很少的代码添加对多个钱包的支持。但在这个项目中,我们将专注于从头开始实现连接钱包功能。我们将在后面的教程中介绍Moralis等解决方案。

我们假设你已经在浏览器中安装了Metamask钱包扩展。如果你有,Metamask会将一个ethereum对象注入你的浏览器的全局window对象中。我们将访问window.ethereum来执行我们的大部分功能。

检查Metamask钱包是否存在

用户不能在我们的网站上铸造NFT,除非他们有一个Metamask钱包。在App component中填充checkWalletIsConnected函数,检查Metamask钱包是否存在。

注意,我们还定义了useEffect hook,当App组件加载时检查Metamask的存在。

在应用程序的localhost页面上打开控制台。如果你已经安装了Metamask,应该看到一条消息,说Wallet exists! We’re ready to go!

Chrome Console Output

在程序中连接Metamask

仅仅安装了Metamask扩展,并不意味着Metamask会自动连接到我们访问的每个网站。我们需要提示Metamask,要求用户链接到网站。

这就是 Connect Wallet功能的作用。它相当于web3的一个登录按钮。它允许用户通过网站前台连接并发送合约函数调用请求。

Metamask通过window.ethereum.request方法使这个过程非常简单。

让我们首先在App()中用useState定义一个变量,它将跟踪用户的钱包地址。(不要忘记从React中导入useState!)。

const [currentAccount, setCurrentAccount] = useState(null);

现在我们来定义connectWalletHandler函数。

让我们简单地看一下这个函数是做什么的。

  1. 它检查你是否安装了Metamask。如果没有,网站会显示一个弹出窗口,要求你安装Metamask。
  2. 它要求Metamask提供用户的钱包地址。
  3. 用户同意与网站连接,它就会获取第一个可用的钱包地址,并将其作为currentAccount变量的值。
  4. 如果出了问题(比如用户拒绝连接),它就会失败,并在控制台打印出错误信息。

现在,如果你在你的网站上打开Metamask扩展,它会告诉你,你没有连接。

现在是关键时刻的时候了。点击你网站上的Connect Wallet按钮。Metamask将提示你与网站连接。同意之后,你的扩展屏幕将看起来像这样。

恭喜你!我们已经成功地将钱包连接到我们的网站。

钱包连接后,我们用Mint NFT按钮代替Connect Wallet的按钮。在App的返回值中,用一个条件渲染来替换连接钱包按钮的渲染。

{currentAccount ? mintNftButton() : connectWalletButton()}

网站现在应该是这样的:

刷新页面并检查扩展。你会看到,Metamask告诉我们,我们仍然连接到网站,但我们的网站仍然显示一个 Connect Wallet的按钮。

如果你熟悉React,应该很清楚为什么会发生这种情况。因为我们只是在connectWallet函数中设置currentAccount state。

理想的情况是,网站应该在每次加载App组件时(即每次刷新时)检查钱包是否已连接。

让我们扩展checkWalletIsConnected函数,在网站加载时立即检查账户,如果钱包已经连接,则设置currentAccount

(注意,我们已经将这个函数标记为异步)。让我们简单地谈谈这个函数的作用:

  1. 检查Metamask是否被安装,并将结果输出到控制台。
  2. 为已连接的账户请求Metamask。
  3. 如果Metamask已经连接了,它就会给函数一个账户列表。如果没有,则返回一个空列表。
  4. 如果列表不是空的,该函数会选择Metamask发送的第一个账户并将其设置为当前账户。

如果你现在刷新页面,你会看到网站确实按规定显示了Mint NFT按钮。

从网站上Mint NFTs

现在让我们来实现我们网站的核心功能。当用户点击Mint NFT按钮时,我们希望发生以下情况。

  1. Metamask提示用户支付NFT的价格+gas。
  2. 用户确认,Metamask代表用户调用我们合约中的mintNFT功能。
  3. 交易完成,它会通知用户交易的成功/失败。

要做到这一点,我们将需要智能合约项目中引入ethers库。在终端运行以下命令:

npm install ethers

App.js中引入:

import { ethers } from 'ethers';

最后,让我们填写mintNftHandler函数。

(别忘了把这个函数标记为异步)

让我们来谈谈这个函数做什么。

  1. 访问由Metamask注入的ethereum对象。
  2. 如果ethereum存在,它将Metamask设置为RPC提供者。这意味着,你将使用Metamask钱包向矿工发出请求。
  3. 为了发出请求,用户将需要使用他们的私钥签署交易。因此需要使用Signer
  4. 然后,使用部署的合约地址、合约ABI和signer启动一个ethers合约实例。
  5. 现在可以通过上述的合同对象来调用合约的函数。调用mintNFT函数,要求Metamask发送0.01 ETH(这是我们为NFT设定的价格)。
  6. 等待交易被处理,处理完毕后,我们将交易哈希值输出到控制台。
  7. 如果有任何失败(错误的函数调用,错误的参数传递,<0.01 ETH发送,用户拒绝交易,等等),错误信息将打印到控制台。

在网站上打开浏览器的控制台,这样就能实时查看mint状态。

现在,点击Mint NFT按钮。Metamask将提示您支付0.01 ETH + gas。该交易将需要大约15–20秒的时间来处理。完成后,交易将由Metamask的弹出窗口和控制台的输出来确认。

现在也可以在Opensea上查看NFT了。转到你在testnets.opensea.io上的账户,就可以看到你最新的NFT。

用户体验的改进和结论

恭喜!你现在有了一个功能齐全的web3前端,用户可以通过它来铸造NFT。你现在有了一个功能齐全的web3前端,用户可以通过它来铸造NFTs。

然而,你可能已经注意到的,网站的用户体验还有很多需要改进的地方。这里有一些你应该考虑做的改进。

确保用户连接到正确的网络

我们假设用户在与网站互动时,已经连接到Rinkeby网络。但实际情况可能并不总是如此。

网络错误时Opensea发出的警告

你能不能实现在用户没有连接到Rinkeby时提醒他网络错误的功能(就像OpenSea那样)?另外,确保用户在连接到错误的网络时不能看到Mint NFT按钮。

显示交易状态

目前,我们的网站将交易状态打印到控制台。在一个真实的项目中,你不能指望你的用户在与网站互动的同时打开他们的控制台。

你能实现跟踪交易状态并实时反馈给用户吗?当交易正在处理时,它应该显示一个加载器,如果交易失败则通知用户,如果交易成功则显示交易哈希/Opensea链接。

即使资金不存在,也要提示Metamask

如果Metamask钱包中没有任何ETH,点击Mint NFT将完全不会提示Metamask。事实上,用户将不会收到任何反馈。

你能确保在用户资金不足的情况下也能提示Metamask吗?最好是由Metamask来通知用户需要多少ETH以及他还差多少。

其他产品质量方面的改进

这里有一些其他的产品质量的改进,你可以考虑。

  1. 允许用户在同一时间铸造超过1个NFT。
  2. 从NFT藏品中添加一些艺术作品的样本。
  3. 在Opensea上添加一个链接到你的藏品。
  4. 添加经过验证的智能合约地址,以便用户可以仔细检查幕后真正发生的事情。
  5. 添加Twitter、IG和Discord的链接。

我们的NFT沙盒项目,Rinkeby Squirrels,实现了这里提到的大部分用户体验升级。试着在这里mint一个,看看你是否能注意到它和我们建立的网站之间的区别。

最终代码库:https://github.com/rounakbanik/nft-collectible-frontend


翻译:团长(https://twitter.com/quentangle_

NFT