教程:用React构建一个NFT的前端
Tutorial: Building a web3 frontend with React
原文链接:Tutorial: Building a web3 frontend with React
简介
在之前的教程中,我们介绍了如何从头开始创建和部署一个NFT藏品智能合约。我们还探讨了如何在etherscan上验证我们的合约,并使自己和用户能够直接从合约的etherscan页面调用合约功能。
然而,大多数严肃的项目倾向于部署他们自己的网站,并允许用户直接从网站上铸造。
这正是我们将在本教程中涉及的内容。更具体地说,本教程将告诉你如何:
- 让用户将他们的Metamask钱包连接到你的网站上
- 允许用户调用合约函数,进行支付,并从合约中铸造一个NFT。
在本教程结束时,你将拥有一个用React构建的功能齐全的web3前端。你也将获得构建任何通用的web3前端所需的基础知识(不仅是铸造NFT)。
前提条件
本教程假定你已经开发并部署了智能合约到Rinkeby测试网络。如果你还没有,我们强烈建议你学习这个教程。为了继续学习本教程,你将需要以下东西:
- 你的智能合约的ABI文件(在你的智能合约项目的artifacts文件夹中可以找到)。
- 你的智能合约的地址。
我们还假设你有使用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.js
、logo.svg
和setupTests.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套件。像Moralis和web3modal这样现成的解决方案,可以让你用很少的代码添加对多个钱包的支持。但在这个项目中,我们将专注于从头开始实现连接钱包功能。我们将在后面的教程中介绍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!
在程序中连接Metamask
仅仅安装了Metamask扩展,并不意味着Metamask会自动连接到我们访问的每个网站。我们需要提示Metamask,要求用户链接到网站。
这就是 Connect Wallet功能的作用。它相当于web3的一个登录按钮。它允许用户通过网站前台连接并发送合约函数调用请求。
Metamask通过window.ethereum.request
方法使这个过程非常简单。
让我们首先在App()
中用useState
定义一个变量,它将跟踪用户的钱包地址。(不要忘记从React中导入useState
!)。const [currentAccount, setCurrentAccount] = useState(null);
现在我们来定义connectWalletHandler
函数。
让我们简单地看一下这个函数是做什么的。
- 它检查你是否安装了Metamask。如果没有,网站会显示一个弹出窗口,要求你安装Metamask。
- 它要求Metamask提供用户的钱包地址。
- 用户同意与网站连接,它就会获取第一个可用的钱包地址,并将其作为
currentAccount
变量的值。 - 如果出了问题(比如用户拒绝连接),它就会失败,并在控制台打印出错误信息。
现在,如果你在你的网站上打开Metamask扩展,它会告诉你,你没有连接。
现在是关键时刻的时候了。点击你网站上的Connect Wallet按钮。Metamask将提示你与网站连接。同意之后,你的扩展屏幕将看起来像这样。
恭喜你!我们已经成功地将钱包连接到我们的网站。
钱包连接后,我们用Mint NFT按钮代替Connect Wallet的按钮。在App的返回值中,用一个条件渲染来替换连接钱包按钮的渲染。{currentAccount ? mintNftButton() : connectWalletButton()}
网站现在应该是这样的:
刷新页面并检查扩展。你会看到,Metamask告诉我们,我们仍然连接到网站,但我们的网站仍然显示一个 Connect Wallet的按钮。
如果你熟悉React,应该很清楚为什么会发生这种情况。因为我们只是在connectWallet
函数中设置currentAccount
state。
理想的情况是,网站应该在每次加载App
组件时(即每次刷新时)检查钱包是否已连接。
让我们扩展checkWalletIsConnected
函数,在网站加载时立即检查账户,如果钱包已经连接,则设置currentAccount
。
(注意,我们已经将这个函数标记为异步)。让我们简单地谈谈这个函数的作用:
- 检查Metamask是否被安装,并将结果输出到控制台。
- 为已连接的账户请求Metamask。
- 如果Metamask已经连接了,它就会给函数一个账户列表。如果没有,则返回一个空列表。
- 如果列表不是空的,该函数会选择Metamask发送的第一个账户并将其设置为当前账户。
如果你现在刷新页面,你会看到网站确实按规定显示了Mint NFT按钮。
从网站上Mint NFTs
现在让我们来实现我们网站的核心功能。当用户点击Mint NFT按钮时,我们希望发生以下情况。
- Metamask提示用户支付NFT的价格+gas。
- 用户确认,Metamask代表用户调用我们合约中的
mintNFT
功能。 - 交易完成,它会通知用户交易的成功/失败。
要做到这一点,我们将需要智能合约项目中引入ethers
库。在终端运行以下命令:npm install ethers
在App.js
中引入:import { ethers } from 'ethers';
最后,让我们填写mintNftHandler
函数。
(别忘了把这个函数标记为异步)
让我们来谈谈这个函数做什么。
- 访问由Metamask注入的
ethereum
对象。 - 如果
ethereum
存在,它将Metamask设置为RPC提供者。这意味着,你将使用Metamask钱包向矿工发出请求。 - 为了发出请求,用户将需要使用他们的私钥签署交易。因此需要使用
Signer
。 - 然后,使用部署的合约地址、合约ABI和signer启动一个ethers合约实例。
- 现在可以通过上述的合同对象来调用合约的函数。调用
mintNFT
函数,要求Metamask发送0.01 ETH(这是我们为NFT设定的价格)。 - 等待交易被处理,处理完毕后,我们将交易哈希值输出到控制台。
- 如果有任何失败(错误的函数调用,错误的参数传递,<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网络。但实际情况可能并不总是如此。
你能不能实现在用户没有连接到Rinkeby时提醒他网络错误的功能(就像OpenSea那样)?另外,确保用户在连接到错误的网络时不能看到Mint NFT按钮。
显示交易状态
目前,我们的网站将交易状态打印到控制台。在一个真实的项目中,你不能指望你的用户在与网站互动的同时打开他们的控制台。
你能实现跟踪交易状态并实时反馈给用户吗?当交易正在处理时,它应该显示一个加载器,如果交易失败则通知用户,如果交易成功则显示交易哈希/Opensea链接。
即使资金不存在,也要提示Metamask
如果Metamask钱包中没有任何ETH,点击Mint NFT将完全不会提示Metamask。事实上,用户将不会收到任何反馈。
你能确保在用户资金不足的情况下也能提示Metamask吗?最好是由Metamask来通知用户需要多少ETH以及他还差多少。
其他产品质量方面的改进
这里有一些其他的产品质量的改进,你可以考虑。
- 允许用户在同一时间铸造超过1个NFT。
- 从NFT藏品中添加一些艺术作品的样本。
- 在Opensea上添加一个链接到你的藏品。
- 添加经过验证的智能合约地址,以便用户可以仔细检查幕后真正发生的事情。
- 添加Twitter、IG和Discord的链接。
我们的NFT沙盒项目,Rinkeby Squirrels,实现了这里提到的大部分用户体验升级。试着在这里mint一个,看看你是否能注意到它和我们建立的网站之间的区别。
最终代码库:https://github.com/rounakbanik/nft-collectible-frontend