本教程介绍了如何构建一个 NFT 铸造器(NFT minter),通过将智能合约连接到 React 前端,使用 Metamask 和 Web3 工具,来创建一个完整的 dApp。内容涵盖连接 Metamask、调用智能合约方法、签署交易等步骤,并详细解释了如何使用 Pinata 将 NFT 元数据存储到 IPFS。
作者:Alchemy Team
审核人:Brady Werkheiser
发布于 2021 年 8 月 30 日,阅读需要 14 分钟
在本教程中,你将构建一个 NFT minter,并学习如何通过使用 Metamask 和 Web3 工具将你的智能合约连接到 React 前端来创建一个完整的堆栈 dApp。
另外,请务必查看我们的 NFT 教程系列的其他内容:
对于来自 Web2 背景的开发人员来说,最大的挑战之一是弄清楚如何将你的智能合约连接到前端项目并与之交互。
通过构建一个 NFT minter——一个简单的 UI,你可以在其中输入指向你的数字资产的链接、标题和描述——你将学习如何:
通过你的前端项目连接到 Metamask
从你的前端调用智能合约方法
使用 Metamask 签署交易
在本教程中,我们将使用 React 作为我们的前端框架。由于本教程主要侧重于 Web3 开发,因此我们不会花费太多时间来分解 React 的基础知识。相反,我们将专注于为我们的项目带来功能。
作为先决条件,你应该具有 React 的初级理解水平——了解组件、props、useState/useEffect 和基本函数调用的工作方式。 如果你以前从未听说过这些术语中的任何一个,你可能需要查看此 React 入门教程。 对于更多视觉学习者,我们强烈推荐 Net Ninja 提供的这个出色的 完整现代 React 教程 视频系列。
事不宜迟,让我们开始吧!
在我们开始查看任何代码之前,重要的是要了解制作 NFT 的工作原理。 它涉及两个步骤:
1) 在以太坊区块链上发布一个 NFT 智能合约。通常这是一个 ERC-721 或 ERC-1155 智能合约。
两种 NFT 智能合约标准的最大区别在于,ERC-1155 是一种多代币标准,包括批处理功能,而 ERC-721 是一种单一代币标准,因此仅支持一次转移一个代币。
2) 调用该 NFT 智能合约上的铸造函数来铸造 NFT。铸造 只是在区块链上发布你的非同质化代币的唯一实例的行为。
通常,此铸造函数要求你传入两个变量作为参数,首先是接收者,它指定将收到你新铸造的 NFT 的地址,其次是 NFT 的 tokenURI ,一个解析为描述 NFT 元数据的 JSON 文档的字符串。
NFT 的元数据真正赋予了它生命力,使其具有属性,例如名称、描述、图像(或不同的数字资产)和其他属性。 这是一个 tokenURI 示例,其中包含 NFT 的元数据。
在本教程中,我们将重点关注第 2 部分,即使用我们的 React UI 调用现有 NFT 的智能合约铸造函数。
这是一个链接 到我们将在本教程中调用的 ERC-721 NFT 智能合约。 如果你想了解我们是如何制作的,我们强烈建议你查看我们的另一个教程“ 如何创建 NFT”。
太棒了,现在我们了解了制作 NFT 的工作原理,让我们克隆我们的入门文件吧!
首先,转到 nft-minter-tutorial github 存储库以获取此项目的入门文件。 将此存储库克隆到你的本地环境。
不知道如何克隆存储库? 查看 Github 的 本指南。
当你打开这个克隆的 nft-minter-tutorial 存储库时,你会注意到它包含两个文件夹:minter-starter-files 和 nft-minter。
minter-starter-files 包含此项目的入门文件(本质上是 React UI)。 在本教程中,我们将在这个目录中工作,因为你将学习如何通过将其连接到你的以太坊钱包和一个 NFT 智能合约来使这个 UI 焕发生机。
nft-minter 包含完整的已完成教程,并且在你遇到困难时可以作为参考。
接下来,在你的首选代码编辑器中打开你的 minter-starter-files 的副本(在 Alchemy,我们是 VSCode 的忠实粉丝),然后导航到你的 src 文件夹:
我们将在“src”文件夹中工作
在你的选择的编辑器中打开 minter-starter-files
我们将编写的所有代码都将位于 src 文件夹下。 我们将编辑 Minter.js 组件并编写额外的 javascript 文件,以使我们的项目具有 Web3 功能。
在开始编码之前,重要的是要查看入门文件中已为我们提供的功能。
让我们首先在我们的浏览器中运行 React 项目。 React 的优点在于,一旦我们的项目在我们的浏览器中运行,我们保存的任何更改都将在我们的浏览器中实时更新。
要运行该项目,请导航到 minter-starter-files 文件夹的根目录,然后在你的终端中运行 npm install
以安装项目的依赖项:
已复制
cd minter-starter-files npm install
一旦这些安装完成,在你的终端中运行 npm start
:
已复制
npm start
这样做应该会在你的浏览器中打开 http://localhost:3000/,在那里你将看到我们项目的前端。 它应该包含 3 个字段:一个输入你的 NFT 资产链接的地方、输入你的 NFT 名称的地方以及提供描述的地方。
你的项目 UI 应该是什么样子
如果你尝试单击“连接钱包”或“铸造 NFT”按钮,你会注意到它们不起作用——那是因为我们仍然需要对它们的功能进行编程! :)
注意:确保你在 minter-starter-files 文件夹中,而不是 nft-minter 文件夹中!
让我们回到我们的编辑器中的 src 文件夹并打开 Minter.js 文件。 了解此文件中的所有内容非常重要,因为它是我们将要处理的主要 React 组件。
在我们的这个文件的顶部,我们有我们的状态变量,我们将在特定事件后更新它们。
已复制
//状态变量 const [walletAddress, setWallet] = useState(""); const [status, setStatus] = useState(""); const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [url, setURL] = useState("");
从未听说过 React 状态变量或状态Hook? 查看 这些 文档。
以下是每个变量的表示:
walletAddress - 一个存储用户钱包地址的字符串
status - 一个包含要在 UI 底部显示的消息的字符串
name - 一个存储 NFT 名称的字符串
description - 一个存储 NFT 描述的字符串
url - 一个指向 NFT 数字资产的链接的字符串
在状态变量之后,你将看到三个未实现的函数:useEffect
、connectWalletPressed
和 onMintPressed
。 你会注意到所有这些函数都是 async,这是因为我们将在其中进行异步 API 调用! 它们的名称与它们的功能相同:
已复制
` useEffect(async () => { //TODO:实现
}, []); const connectWalletPressed = async () => { //TODO:实现
}; const onMintPressed = async () => { //TODO:实现
}; `
useEffect- 这是一个在你的组件渲染后调用的 React Hook。 因为它有一个空数组 [] prop 传递给它(参见第 3 行),它只会在组件的首次渲染时被调用。 在这里,我们将调用我们的钱包监听器和另一个钱包函数来更新我们的 UI,以反映是否已连接钱包。
connectWalletPressed - 将调用此函数以将用户的 Metamask 钱包连接到我们的 dApp。
onMintPressed - 将调用此函数以铸造用户的 NFT。
在此文件末尾附近,我们有我们组件的 UI。 如果你仔细扫描这段代码,你会注意到当其相应文本字段中的输入发生更改时,我们会更新我们的 url
、name
和 description
状态变量。
你还会看到,当单击 ID 为 mintButton 和 walletButton 的按钮时,分别调用 connectWalletPressed
和 onMintPressed
。
最后,让我们说明在哪里添加了这个 Minter 组件。
如果你转到 App.js 文件,这是 React 中的主要组件,充当所有其他组件的容器,你将看到我们的 Minter 组件在第 7 行注入。
在本教程中,我们将只编辑 Minter.js 文件并在我们的 src 文件夹中添加文件。
现在我们了解了我们正在使用什么,让我们设置我们的以太坊钱包!
为了让用户能够与你的智能合约交互,他们需要将他们的以太坊钱包连接到你的 dApp。
对于本教程,我们将使用 Metamask,这是一个浏览器中的虚拟钱包,用于管理你的以太坊帐户地址。 如果你想了解更多关于以太坊上的交易如何运作的信息,请查看以太坊基金会的 此页面。
你可以在 这里 免费下载并创建一个 Metamask 帐户。 当你创建一个帐户时,或者如果你已经有一个帐户,请确保在右上角切换到“Ropsten 测试网络”(这样我们就不会处理真钱)。
Metamask 钱包示例
为了铸造我们的 NFT(或签署以太坊区块链上的任何交易),我们将需要一些假的 Eth。 要获得 Eth,你可以转到 Ropsten 水龙头 并输入你的 Ropsten 帐户地址,然后单击“发送 Ropsten Eth”。 你应该很快在你的 Metamask 帐户中看到 Eth!
为了再次确认我们的余额是否存在,让我们使用 Alchemy 的 composer 工具 发出 eth_getBalance
请求。 这将返回我们钱包中的 Eth 数量。 在你输入你的 Metamask 帐户地址并单击“发送请求”后,你应该会看到如下所示的响应:
已复制
{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}
注意:此结果以 wei 而不是 eth 为单位。 Wei 用作以太币的最小面额。 从 wei 到 eth 的转换是:1 eth = 10¹⁸ wei。 因此,如果我们将 0xde0b6b3a7640000 转换为十进制,我们得到 1*10¹⁸,等于 1 eth。
唷! 我们的假钱都在那里! 🤑
现在我们的 Metamask 钱包已设置好,让我们将我们的 dApp 连接到它!
因为我们想遵守 M-V-C 范例,我们将创建一个单独的文件,其中包含我们的函数来管理我们的 dApp 的逻辑、数据和规则,然后将这些函数传递到我们的前端(我们的 Minter.js 组件)。
为此,让我们在你的 src 目录中创建一个名为 utils 的新文件夹,并在其中添加一个名为 interact.js 的文件,其中将包含我们所有的钱包和智能合约交互函数。
在我们的 interact.js 文件中,我们将编写一个 connectWallet
函数,然后我们将导入并在我们的 Minter.js 组件中调用它。
在你的 interact.js 文件中,添加以下内容
让我们分解一下这段代码的作用:
首先,我们的函数检查你的浏览器中是否启用了 window.ethereum
。
window.ethereum 是 Metamask 和其他钱包提供商注入的全局 API,允许网站请求用户的以太坊帐户。 如果获得批准,它可以从用户连接到的区块链读取数据,并建议用户签署消息和交易。 查看 Metamask 文档 以获取更多信息!
如果 window.ethereum
不存在,则表示未安装 Metamask。 这导致返回一个 JSON 对象,其中返回的 address 是一个空字符串,并且 status JSX 对象表明用户必须安装 Metamask。
我们编写的大多数函数都将返回 JSON 对象,我们可以使用它们来更新我们的状态变量和 UI。
现在,如果 window.ethereum
存在,那么事情就会变得有趣。
使用 try/catch 循环,我们将尝试通过调用 window.ethereum.request({ method: "eth_requestAccounts" });
连接到 Metamask。 调用此函数将在浏览器中打开 Metamask,用户将被提示将其钱包连接到你的 dApp。
如果用户选择连接,则 method: "eth_requestAccounts"
将返回一个数组,其中包含所有已连接到 dApp 的用户的帐户地址。 总而言之,我们的 connectWallet
函数将返回一个 JSON 对象,其中包含此数组中的第一个 address
(参见第 9 行)和一个 status
消息,提示用户向智能合约编写消息。
如果用户拒绝连接,则 JSON 对象将包含一个空字符串作为返回的 address
和一个反映用户拒绝连接的 status
消息。
现在我们已经编写了这个 connectWallet
函数,让我们将其连接到我们的 Minter.js. 组件。
首先,我们必须通过添加 import { connectWallet } from "./utils/interact.js"
; 到 Minter.js 文件的顶部来将我们的函数导入到我们的 Minter.js 文件。 你的 Minter.js 的前 11 行现在应该如下所示:
已复制
import { useEffect, useState } from "react"; import { connectWallet } from "./utils/interact.js"; const Minter = (props) => { //状态变量 const [walletAddress, setWallet] = useState(""); const [status, setStatus] = useState(""); const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [url, setURL] = useState("");
然后,在我们的 connectWalletPressed
函数中,我们将调用我们导入的 connectWallet
函数,如下所示:
已复制
const connectWalletPressed = async () => { const walletResponse = await connectWallet(); setStatus(walletResponse.status); setWallet(walletResponse.address); };
请注意,我们的大部分功能都从 interact.js 文件中的 Minter.js 组件中抽象出来了吗? 这是为了让我们遵守 M-V-C 范例!
在 connectWalletPressed
中,我们只是对我们导入的 connectWallet
函数进行 await 调用,并使用它的响应,我们通过它们的状态Hook更新我们的 status
和 walletAddress
变量。
现在,让我们保存这两个文件(Minter.js 和 interact.js)并测试我们到目前为止的 UI。
在 http://localhost:3000/ 页面上打开你的浏览器,然后按页面右上角的“连接钱包”按钮。
如果你安装了 Metamask,你将被提示将你的钱包连接到你的 dApp。 接受连接邀请。
你应该看到钱包按钮现在反映你的地址已连接! 太棒了 🔥
接下来,尝试刷新页面...这很奇怪。 我们的钱包按钮提示我们连接 Metamask,即使它已经连接了...
页面重新加载时出现问题
不过,别担心! 我们可以通过实现一个名为 getCurrentWalletConnected
的函数轻松修复它,该函数将检查是否已有地址连接到我们的 dApp 并相应地更新我们的 UI!
在你的 interact.js 文件中,添加以下 getCurrentWalletConnected
函数:
此代码与我们之前编写的 connectWallet
函数非常相似。
主要的区别在于,我们没有调用方法 eth_requestAccounts
,该方法打开 Metamask 以供用户连接他们的钱包,而是调用方法 eth_accounts
,该方法只是返回一个包含当前连接到我们的 dApp 的 Metamask 地址的数组。
要查看此函数的实际效果,让我们在我们的 Minter.js 组件的 useEffect
函数中调用它。
就像我们对 connectWallet
所做的那样,我们必须像这样从我们的 interact.js 文件中将此函数导入到我们的 Minter.js 文件中:
已复制
import { useEffect, useState } from "react"; import { connectWallet, getCurrentWalletConnected //在此处导入 } from "./utils/interact.js";
现在,我们只需在我们的 useEffect
函数中调用它:
已复制
useEffect(async () => { const {address, status} = await getCurrentWalletConnected(); setWallet(address) setStatus(status); }, []);
请注意,我们使用我们对 getCurrentWalletConnected
的调用的响应来更新我们的 walletAddress
和 status
状态变量。
添加此代码后,尝试刷新我们的浏览器窗口。 按钮应该说你已连接,并显示你连接的钱包地址的预览 - 即使在你刷新后! 😅
我们 dApp 钱包设置的最后一步是实现钱包监听器,以便我们的 UI 在我们钱包的状态发生变化时更新,例如当用户断开连接或切换帐户时。
在你的 Minter.js 文件中,添加一个 addWalletListener
函数,如下所示:
让我们快速分解一下这里发生的事情:
首先,我们的函数检查是否启用了 window.ethereum
(即安装了 Metamask)。
如果没有,我们只需将我们的 status
状态变量设置为一个 JSX 字符串,提示用户安装 Metamask。
如果已启用,我们在第 3 行设置监听器 window.ethereum.on("accountsChanged")
,该监听器监听 Metamask 钱包中的状态变化,其中包括用户将其他帐户连接到 dApp、切换帐户或断开帐户连接时。 如果至少连接了一个帐户,则 walletAddress
状态变量将更新为监听器返回的 accounts
数组中的第一个帐户。 否则,walletAddress
将设置为空字符串。
最后,我们必须在我们的 useEffect
函数中调用它:
已复制
`useEffect(async () => { const {address, status} = await getCurrentWalletConnected(); setWallet(address) setStatus(status);
addWalletListener(); }, []); `
万岁! 我们已经完成了我们所有钱包功能的编程! 现在我们的钱包已设置好,让我们弄清楚如何铸造我们的 NFT!
所以请记住我们刚刚在本教程的步骤 0 中讨论过的 NFT 元数据——它赋予了 NFT 生命力,允许它具有属性,例如数字资产、名称、描述和其他属性。
我们需要将此元数据配置为 JSON 对象并存储它,以便我们可以在调用我们智能合约的 mintNFT
函数时将其作为 tokenURI
参数传入。
“资产链接”、“名称”、“描述”字段中的文本将构成我们 NFT 元数据的不同属性。 我们将把此元数据格式化为 JSON 对象,但我们可以选择将其存储在以下位置:
我们可以将其存储在以太坊区块链上; 但是,由于以太坊的性质,这样做会非常昂贵(我们说的是数百美元)。 ❌
我们可以将其存储在中心化服务器上,例如 AWS 或 Firebase。 但这将违背我们的去中心化精神。 ❌
我们可以使用 IPFS,一种用于在分布式文件系统中存储和共享数据的去中心化协议和点对点网络。 由于此协议是去中心化的且免费,因此它是我们的最佳选择! ✅
为了将我们的元数据存储在 IPFS 上,我们将使用 Pinata,一个方便的 IPFS API 和工具包。 在下一步中,我们将准确地解释如何做到这一点!
如果你没有 Pinata 帐户,请在此处注册一个免费帐户 here 并完成验证你的电子邮件和帐户的步骤。
导航到 https://pinata.cloud/keys 页面,然后选择顶部的“新密钥”按钮,将管理员小部件设置为启用,然后命名你的密钥。
创建你的 Pinata API 密钥
然后你将看到一个弹出窗口,其中包含你的 API 信息。 确保将其放在安全的地方。
确保将你的 API 密钥和密钥保存在安全的地方
现在我们的密钥已设置好,让我们将其添加到我们的项目中,以便我们可以使用它。
我们可以安全地将我们的 Pinata 密钥和密钥存储在环境文件中。 让我们在你的项目目录中安装 dotenv 包。
在你的终端中打开一个新选项卡(与正在运行本地主机的选项卡分开),并确保你在 minter-starter-files 文件夹中,然后在你的终端中运行以下命令:
已复制
npm install dotenv --save
接下来,通过在你的命令行上输入以下内容,在你的 minter-starter-files 的根目录中创建一个 .env 文件:
已复制
vim .env
这将在 vim(一个文本编辑器)中弹出你的 .env 文件。 要保存它,请在键盘上按“esc”+“:”+“q”,依次进行。
接下来,在 VSCode 中,导航到你的 .env 文件并将你的 Pinata API 密钥和 API 密钥添加到其中,如下所示:
已复制
REACT_APP_PINATA_KEY = REACT_APP_PINATA_SECRET =
保存该文件,然后你就可以开始编写将你的 JSON 元数据上传到 IPFS 的函数了!
幸运的是,Pinata 有一个 专门用于将 JSON 数据上传到 IPFS 的 API 和一个方便的带有 axios 示例的 JavaScript,我们可以使用它,并进行一些细微的修改。
在你的 utils 文件夹中,让我们创建另一个名为 pinata.js 的文件,然后像这样从 .env 文件中导入我们的 Pinata 密钥和密钥:
已复制
require('dotenv').config(); const key = process.env.REACT_APP_PINATA_KEY; const secret = process.env.REACT_APP_PINATA_SECRET;
接下来,将以下代码中的附加代码粘贴到你的 pinata.js 文件中。 别担心,我们将分解一切的含义!
已复制
`require('dotenv').config(); const key = process.env.REACT_APP_PINATA_KEY; const secret = process.env.REACT_APP_PINATA_SECRET; const axios = require('axios'); export const pinJSONToIPFS = async(JSONBody) => { const url =
https://api.pinata.cloud/pinning/pinJSONToIPFS`;
//向 Pinata 发出 axios POST 请求 ⬇️
return axios
.post(url, JSONBody, {
headers: {
pinata_api_key: key,
pinata_secret_api_key: secret,
}
})
.then(function (response) {
return {
success: true,
pinataUrl: "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash
};
})
.catch(function (error) {
console.log(error)
return {
success: false,
message: error.message,
}
}); }; ``
所以这段代码到底做了什么?
首先,它导入 axios,这是一个基于 promise 的 HTTP 客户端,适用于浏览器和 node.js,我们将使用它来向 Pinata 发出请求。
然后我们有我们的异步函数 pinJSONToIPFS
,它将 JSONBody
作为其输入,并将 Pinata api 密钥和密钥放在其标头中,所有这些都是为了向他们的 pinJSONToIPFS
API 发出 POST 请求。
如果此 POST 请求成功,则我们的函数将返回一个 JSON 对象,其中 success
布尔值为 true,pinataUrl
为固定元数据的位置。 我们将使用返回的此 pinataUrl
作为我们智能合约的 mint 函数的 tokenURI
输入。
如果此 post 请求失败,则我们的函数将返回一个 JSON 对象,其中 success
布尔值为 false,message
字符串传递我们的错误。
与我们的 connectWallet
函数返回类型一样,我们返回 JSON 对象,以便我们可以使用它们的参数来更新我们的状态变量和 UI。
既然我们可以通过我们的 pinJSONToIPFS 函数将我们的 NFT 元数据上传到 IPFS,我们需要一种方法来加载我们的智能合约的实例,以便我们可以调用它的 mintNFT 函数。
正如我们之前提到的,在本教程中,我们将使用 此现有的 NFT 智能合约; 但是,如果你想了解我们是如何制作它的,或者自己制作一个,我们强烈建议你查看我们的另一个教程,“如何创建 NFT”。
如果你仔细检查我们的文件,你会注意到在我们的 src 目录中,有一个 contract-abi.json 文件。 ABI 对于指定合约将调用的哪个函数以及确保该函数将以你期望的格式返回数据是必需的。
我们还需要一个炼金术 API 密钥和炼金术 Web3 API 来连接到以太坊区块链并加载我们的智能合约。
如果你还没有 Alchemy 帐户,请在此处 免费注册。
创建 Alchemy 帐户后,你可以通过创建一个应用程序来生成 API 密钥。 这将允许我们向 Ropsten 测试网络发出请求。
通过将鼠标悬停在导航栏中的“应用程序”上并单击“创建应用程序”,导航到你的 Alchemy 仪表板中的“创建应用程序”页面
创建一个新应用程序
为你的应用程序命名(我们选择了“My First NFT!”),提供一个简短的描述,为环境选择“分期”(用于你的应用程序记账),并为你的网络选择“Ropsten”。
配置你的应用程序详细信息
单击“创建应用程序”,就这样! 你的应用程序应该出现在下面的表格中。
太棒了,现在我们已经创建了我们的 HTTP Alchemy API URL,像这样将其复制到你的剪贴板……
复制你的 Alchemy API 密钥
……然后让我们将其添加到我们的 .env 文件中。 总而言之,你的 .env 文件应如下所示:
已复制
REACT_APP_PINATA_KEY = REACT_APP_PINATA_SECRET = REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/
现在我们有了我们的合约 ABI 和我们的 Alchemy API 密钥,我们准备好使用 Alchemy Web3 加载我们的智能合约。
首先,如果你还没有安装 Alchemy Web3,你需要通过导航到主目录来安装它:终端中的 nft-minter-tutorial:
已复制
cd .. npm install @alch/alchemy-web3
接下来让我们回到我们的 interact.js 文件。在文件的顶部,添加以下代码以从你的 .env 文件中导入你的 Alchemy 密钥并设置你的 Alchemy Web3 端点:
已复制
require('dotenv').config(); const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY; const { createAlchemyWeb3 } = require("@alch/alchemy-web3"); const web3 = createAlchemyWeb3(alchemyKey);
Alchemy Web3 是 Web3.js 的一个包装器,它提供了增强的 API 方法和其他关键优势,使你作为 web3 开发人员的生活更轻松。 它旨在需要最少的配置,因此你可以立即开始在你的应用程序中使用它!
接下来,让我们将我们的合约 ABI 和合约地址添加到我们的文件中。
已复制
require('dotenv').config(); const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY; const { createAlchemyWeb3 } = require("@alch/alchemy-web3"); const web3 = createAlchemyWeb3(alchemyKey); const contractABI = require('../contract-abi.json') const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE";
一旦我们有了这两个,我们就准备好开始编写我们的 mint 函数了!
在你的 interact.js 文件中,让我们定义我们的函数 mintNFT,它将以同名方式铸造我们的 NFT。
因为我们将进行大量的异步调用(到 Pinata 以将我们的元数据固定到 IPFS,Alchemy Web3 以加载我们的智能合约,以及 Metamask 以签署我们的交易),我们的函数也将是异步的。``` export const mintNFT = async(url, name, description) => { //error handling // 错误处理 if (url.trim() == "" || (name.trim() == "" || description.trim() == "")) { return { success: false, status: "❗Please make sure all fields are completed before minting.", // ❗请确保在铸造之前完成所有字段。 } }
//make metadata // 创建元数据 const metadata = new Object(); metadata.name = name; metadata.image = url; metadata.description = description;
//make pinata call // 发起Pinata调用 const pinataResponse = await pinJSONToIPFS(metadata); if (!pinataResponse.success) { return { success: false, status: "😢 Something went wrong while uploading your tokenURI.", // 😢 上传你的tokenURI时出错。 } } const tokenURI = pinataResponse.pinataUrl; }
请注意,我们将调用 `pinJSONToIPFS(metadata)` 的响应存储在 `pinataResponse` 对象中。然后,我们解析此对象以查找任何错误。
如果存在错误,我们将返回一个 JSON 对象,其中 `success` 布尔值为 false,并且我们的 `status` 字符串传达我们的调用失败。否则,我们从 `pinataResponse` 中提取 `pinataURL` 并将其存储为我们的 `tokenURIvariable`。
现在是时候使用我们在文件顶部初始化的 Alchemy Web3 API 加载我们的智能合约了。将以下代码行添加到 `mintNFT` 函数的底部,以在 `window.contract` 全局变量处设置合约:
window.contract = await new web3.eth.Contract(contractABI, contractAddress);
最后要添加到我们的 `mintNFT` 函数中的是我们的以太坊交易:
//set up your Ethereum transaction // 设置你的以太坊交易 const transactionParameters = { to: contractAddress, // Required except during contract publications. // 除了合约发布期间,其他时候都需要。 from: window.ethereum.selectedAddress, // must match user's active address. // 必须与用户的活动地址匹配。 'data': window.contract.methods.mintNFT(window.ethereum.selectedAddress, tokenURI).encodeABI()//make call to NFT smart contract // 调用NFT智能合约 };
//sign the transaction via Metamask // 通过Metamask签署交易 try { const txHash = await window.ethereum .request({ method: 'eth_sendTransaction', params: [transactionParameters], }); return { success: true, status: "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" + txHash // ✅ 在Etherscan上查看你的交易:https://ropsten.etherscan.io/tx/ } } catch (error) { return { success: false, status: "😥 Something went wrong: " + error.message // 😥 出了点问题:" + error.message } }
如果你已经熟悉以太坊交易,你会注意到该结构与你所见过的非常相似。
首先,我们设置我们的交易参数。
- `to` 指定接收者地址(我们的智能合约)
- `from` 指定交易的签名者(用户连接到 Metamask 的地址: `window.ethereum.selectedAddress` `)`
- `data` 包含对我们的智能合约 `mintNFT` 方法的调用,该方法接收我们的 `tokenURI` 和用户的钱包地址 `window.ethereum.selectedAddress` 作为输入
然后,我们进行一个等待调用 `window.ethereum.request`,我们在此处要求 Metamask 签署交易。请注意,在此请求中,我们指定了我们的 eth 方法 ( `eth_SentTransaction` ) 并传入我们的 `transactionParameters`。此时,Metamask 将在浏览器中打开,并提示用户签署或拒绝交易。
- 如果交易成功,该函数将返回一个 JSON 对象,其中布尔值 `success` 设置为 true,并且 `status` 字符串提示用户查看 Etherscan 以获取有关其交易的更多信息。
- 如果交易失败,该函数将返回一个 JSON 对象,其中布尔值 `success` 设置为 false,并且 `status` 字符串传递错误消息。
总而言之,我们的 `mintNFT` 函数应该如下所示:
export const mintNFT = async(url, name, description) => {
//error handling // 错误处理 if (url.trim() == "" || (name.trim() == "" || description.trim() == "")) { return { success: false, status: "❗Please make sure all fields are completed before minting.", // ❗请确保在铸造之前完成所有字段。 } }
//make metadata // 创建元数据 const metadata = new Object(); metadata.name = name; metadata.image = url; metadata.description = description; //pinata pin request // pinata pin请求 const pinataResponse = await pinJSONToIPFS(metadata); if (!pinataResponse.success) { return { success: false, status: "😢 Something went wrong while uploading your tokenURI.", // 😢 上传你的tokenURI时出错。 } } const tokenURI = pinataResponse.pinataUrl; //load smart contract // 加载智能合约 window.contract = await new web3.eth.Contract(contractABI, contractAddress);//loadContract(); //set up your Ethereum transaction // 设置你的以太坊交易 const transactionParameters = { to: contractAddress, // Required except during contract publications. // 除了合约发布期间,其他时候都需要。 from: window.ethereum.selectedAddress, // must match user's active address. // 必须与用户的活动地址匹配。 'data': window.contract.methods.mintNFT(window.ethereum.selectedAddress, tokenURI).encodeABI() //make call to NFT smart contract // 调用NFT智能合约 };
//sign transaction via Metamask // 通过Metamask签署交易 try { const txHash = await window.ethereum .request({ method: 'eth_sendTransaction', params: [transactionParameters], }); return { success: true, status: "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" + txHash // ✅ 在Etherscan上查看你的交易:https://ropsten.etherscan.io/tx/ } } catch (error) { return { success: false, status: "😥 Something went wrong: " + error.message // 😥 出了点问题:" + error.message } } }
这是一个巨大的功能!现在,我们只需要将我们的 `mintNFT` 函数连接到我们的 **Minter.js** 组件...
### 第 9 步:将 mintNFT 连接到我们的 Minter.js 前端
打开你的 **Minter.js** 文件并更新顶部的 `import { connectWallet } from "./utils/interact.js"`; 行,如下所示:
import { connectWallet, mintNFT } from "./utils/interact.js";
最后,实现 `onMintPressed` 函数以对你导入的 `mintNFT` 函数进行等待调用,并更新 `status` 状态变量以反映我们的交易是成功还是失败:
const onMintPressed = async () => { const { status } = await mintNFT(url, name, description); setStatus(status); };
### 第 10 步:将你的 NFT 部署到实时网站
准备好让你的项目上线供用户交互了吗?查看此 [教程](https://app.gitbook.com/@alchemyapi/s/alchemy/tutorials/nft-minter/how-do-i-deploy-nfts-online) 以将你的 Minter 部署到实时网站:
### 第 11 步:以迅雷不及掩耳之势席卷区块链世界🚀
开个玩笑,你已经到了本教程的结尾!回顾一下,通过构建 NFT minter,你成功地学习了如何:
- 通过你的前端项目连接到 Metamask
- 从你的前端调用智能合约方法
- 使用 Metamask 签署交易
据推测,你希望能够在你的钱包中炫耀通过你的 dApp 铸造的 NFT - 因此请务必查看我们的快速教程 [如何在你的钱包中查看你的 NFT](https://docs.alchemyapi.io/alchemy/tutorials/how-to-write-and-deploy-a-nft-smart-contract/how-to-view-your-nft-in-your-wallet) !
而且,与往常一样,如果你有任何问题,我们随时在 [Alchemy Discord](https://discord.gg/gWuC7zB) 中提供帮助。我们迫不及待地想看看你如何将本教程中的概念应用于你未来的项目! 🧙♂️
Supercharged \| Alchemy \| Substack
[](https://alchemysupercharged.substack.com/)
## Supercharged
web3 领先的开发者平台的最新消息
作者:Alchemy
· 超过 110,000 名订阅者
订阅
通过订阅,你同意 [Substack 的使用条款](https://alchemysupercharged.substack.com/tos?utm_source=embed_publication)、[我们的隐私政策](https://alchemysupercharged.substack.com/privacy?utm_source=embed_publication) 和 [我们的信息收集通知](https://substack.com/ccpa?utm_source=embed_publication#personal-data-collected)
[](https://substack.com/?utm_source=embed&utm_content=alchemysupercharged)
使用 Alchemy 的 NFT API 免费开始构建你可以想象的任何 NFT 应用程序 [获取你的 API 密钥](https://dashboard.alchemy.com/signup/?a=blog%252Fnft-minter-tutorial-how-to-create-a-full-stack-dapp)
📚 目录
* * *
- 第 0 步:NFT 101 制作
- 第 1 步:克隆启动器文件
- 第 2 步:查看我们的启动器文件
- 第 3 步:设置你的以太坊钱包
- 第 4 步:将 Metamask 连接到你的 UI
- 第 5 步:NFT 元数据 101
- 第 6 步:使用 Pinata 将你的元数据固定到 IPFS
- 第 7 步:加载你的智能合约
- 第 8 步:实现 mintNFT 函数
- 第 9 步:将 mintNFT 连接到我们的 Minter.js 前端
- 第 10 步:将你的 NFT 部署到实时网站
- 第 11 步:以迅雷不及掩耳之势席卷区块链世界🚀
分享:

### 相关文章
[\\
\\
NFTs\\
\\
**Spearmint, the Free and Automated Allowlist Platform** \\
\\
使用 Spearmint(免费的自动化允许列表平台)提升你的 NFT 游戏体验。管理 NFT 铸造、培养社区参与度并避免代价高昂的 Gas 战争。](https://www.alchemy.com/blog/spearmint) [\\
\\
NFTs\\
\\
**TheFungiNFT Partners with Alchemy to Leverage NFT Drops for Social Good** \\
\\
TheFungiNFT 与 Alchemy 合作,进行有影响力的 NFT 投放,并帮助提高人们对心理健康的认识。](https://www.alchemy.com/blog/thefunginft-partner-with-alchemy-to-leverage-nft) [\\
\\
NFTs\\
\\
**Your Guide to ERC-1155: Comparing ERC-721 to ERC-1155** \\
\\
了解 ERC-1155 代币标准并比较 ERC-721 与 ERC-1155,以便你可以了解差异以及何时使用每种标准。](https://learnblockchain.cn/article/12903)

### 构建区块链魔法
Alchemy 将最强大的 web3 开发者产品和工具与资源、社区和传奇支持相结合。
[获取你的 API 密钥](https://dashboard.alchemy.com/signup/?a=blog%252Fnft-minter-tutorial-how-to-create-a-full-stack-dapp)
>- 原文链接: [alchemy.com/blog/nft-min...](https://www.alchemy.com/blog/nft-minter-tutorial-how-to-create-a-full-stack-dapp)
>- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!