---
title: "如何创建 NFT"
description: "使用 Metamask、Solidity、Hardhat、Pinata 和 Alchemy，在 Ethereum 区块链上创建并部署 NFT 的分步指南。"
---

# 如何创建 NFT

本教程将带你使用 Ethereum 和 Inter Planetary File System（IPFS）编写并部署一个非同质化（ERC721）代币智能合约。

_完成本指南预计耗时：约 15 分钟_

即使你一直与世隔绝，也一定注意到各大新闻媒体都在报道 NFT 的兴起。

随着 NFT 进入大众视野，现在正是亲自了解这股热潮的好时机——在 Ethereum 区块链上发布你自己的 NFT（ERC-721 代币）！

在本教程中，我们将使用 [Metamask](https://metamask.io/)、[Solidity](https://docs.soliditylang.org/en/v0.8.0/)、[Hardhat](https://hardhat.org/)、[Pinata](https://pinata.cloud/) 和 [Alchemy](https://alchemy.com/?r=affiliate:68212b46-a5c5-4f4c-bc8b-73f50536fcaf) 在 Sepolia 测试网络上创建并部署一个 ERC-721 智能合约（如果你现在还不理解这些术语，不用担心，我们会解释）。在本教程的第二部分，我们将介绍如何使用智能合约[铸造一个 NFT](/blog/how-to-mint-an-nft-using-web3-js)；第三部分将介绍如何[在 Metamask 中查看你的 NFT](/blog/how-to-view-your-nft-in-your-mobile-wallet)。

当然，如果你在任何环节遇到问题，可以访问 [Alchemy 支持中心](https://www.alchemy.com/support)！

## 第 1 步：连接到 Ethereum 网络

向 Ethereum 区块链发起请求的方式有很多，但为了尽量简化流程，我们将使用 [Alchemy](https://alchemy.com/?r=affiliate:68212b46-a5c5-4f4c-bc8b-73f50536fcaf) 的免费账户。Alchemy 是一个区块链开发者平台和 API，让我们无需运行自己的节点即可与 Ethereum 链通信。

在本教程中，我们还会用到 Alchemy 的开发者工具进行监控和分析，以了解智能合约部署过程中的底层情况。如果你还没有 Alchemy 账户，可以在[这里](https://alchemy.com/?r=affiliate:68212b46-a5c5-4f4c-bc8b-73f50536fcaf)免费注册。

## 第 2 步：创建你的应用（以及 API key）

创建好 Alchemy 账户后，你可以通过创建一个应用来生成 API key。这将使我们能够向 Sepolia 测试网络发起请求。如果你想进一步了解测试网络，可以查看[这篇指南](https://www.alchemy.com/docs/choosing-a-web3-network)。

1. 将鼠标悬停在导航栏的 “Apps” 上，点击 “Create App”，进入 Alchemy Dashboard 中的 “Create App” 页面

<ImageBlock
  src="https://media.alchemy.com/1704631152-create-a-new-app.jpg"
  alt="Alchemy 控制台:从 Apps 菜单创建新应用"
  width={2880}
  height={1640}
  caption="创建新应用"
/>

2. 为你的应用命名（我们选择了 “My First NFT!”），填写简短描述，Environment 选择 “Staging”（用于应用的记录管理），网络选择 “Sepolia”。

<ImageBlock
  src="https://media.alchemy.com/1704802198-name-your-app.png"
  alt="带测试网络选择器的旧版 Alchemy 控制台应用创建界面"
  width={1518}
  height={912}
  caption="为应用命名并添加描述"
/>

3. 点击 “Create app”，就完成了！你的应用会出现在下方的表格中。

## 第 3 步：创建 Ethereum 账户（地址）

我们需要一个 Ethereum 账户来发送和接收交易。本教程中，我们将使用 Metamask——一个浏览器中的虚拟钱包，用来管理你的 Ethereum 账户地址。如果你想更深入了解 Ethereum 上的交易是如何运作的，可以查看 Ethereum 基金会的[这个页面](https://ethereum.org/en/developers/docs/transactions/)。

你可以在[这里](https://metamask.io/download.html)免费下载并创建 Metamask 账户。在创建账户时，或者如果你已经有账户，请务必切换到 Sepolia 测试网络（这样我们处理的就不是真实资金）。

<ImageBlock
  src="https://media.alchemy.com/1704802303-download-and-create-a-metamask-account.png"
  alt="带测试网络选择器的旧版 MetaMask 钱包设置界面"
  width={712}
  height={1194}
  caption="下载并创建 Metamask 账户"
/>

## 第 4 步：从水龙头获取 ether

要将我们的智能合约部署到测试网络，我们需要一些虚拟 Eth。要获取 Eth，你可以前往 [Sepolia 水龙头](/faucets/ethereum-sepolia)，输入你的 Sepolia 账户地址，申请 Sepolia Eth。不久之后你就能在 Metamask 账户中看到 Eth 了！

## 第 5 步：检查你的余额

为了再次确认余额已到账，让我们使用 [Alchemy 的 composer 工具](https://www.alchemy.com/composer/?composer_state=%7B%22network%22%3A0%2C%22methodName%22%3A%22eth_getBalance%22%2C%22paramValues%22%3A%5B%22%22%2C%22latest%22%5D%7D)发起一个 `eth_getBalance` 请求。这会返回我们钱包中的 Eth 数量。输入你的 Metamask 账户地址并点击 “Send Request” 后，你应该会看到类似这样的响应：

\{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"\}

**注意：** 这个结果的单位是 wei，不是 eth。wei 是 ether 的最小计量单位。wei 与 eth 的换算关系是：1 eth = 10¹⁸ wei。所以如果我们把 0xde0b6b3a7640000 转换成十进制，得到的是 1×10¹⁸，也就是 1 eth。

太好了！我们的虚拟资金都到账了！🤑

## 第 6 步：初始化我们的项目

首先，我们需要为项目创建一个文件夹。打开[命令行](https://www.computerhope.com/jargon/c/commandi.htm)并输入：

进入项目文件夹后，我们使用 npm init 来初始化项目。如果你还没有安装 npm，请按照[这些说明](https://nodejs.org/en/download/)操作（我们还需要 [Node.js](https://nodejs.org/en/download/)，所以也请下载它！）。

安装过程中的问题怎么回答其实并不重要，以下是我们的回答方式，供参考：

确认 package.json，就完成了！

第 7 步：下载 [Hardhat](https://hardhat.org/getting-started/#overview)

Hardhat 是一个用于编译、部署、测试和调试 Ethereum 软件的开发环境。它可以帮助开发者在将智能合约和[应用](https://www.alchemy.com/dapps/top/defi-dapps)部署到主链之前在本地进行构建。

打开终端，确保你在 my-nft 项目文件夹内，然后运行：

查看这个页面以获取更多[安装说明](https://hardhat.org/getting-started/#overview)的细节。

## 第 8 步：创建 Hardhat 项目

在项目文件夹内运行：

你会看到一条欢迎信息，并可以选择想要执行的操作。选择 “create an empty hardhat.config.js”：

这会为我们生成一个 **hardhat.config.js** 文件，我们将在其中指定项目的所有配置（在第 13 步中进行）。

## 第 9 步：添加项目文件夹

为了让项目保持整洁，我们将创建两个新文件夹。在命令行中进入项目的根目录并输入：

**contracts/** 用来存放我们的 NFT 智能合约代码

scripts/ 用来存放部署及与智能合约交互的脚本

## 第 10 步：编写我们的合约

现在环境已经搭建好，接下来是更有意思的部分：编写智能合约代码！

在你喜欢的编辑器中打开 my-nft 项目（我们喜欢用 [VSCode](https://code.visualstudio.com)）。智能合约使用一种叫做 [Solidity](https://www.alchemy.com/overviews/solidity) 的语言编写，我们也将用它来编写 MyNFT.sol 智能合约。‌

1. 进入 “contracts” 文件夹，新建一个名为 MyNFT.sol 的文件

2. 下面是我们的 NFT 智能合约代码，它基于 [OpenZeppelin](https://docs.openzeppelin.com/contracts/3.x/erc721) 库的 ERC721 实现。将下面的内容复制粘贴到你的 MyNFT.sol 文件中。

<CalloutBlock>

注意：如果你想通过智能合约为 NFT 设定价格，可以查看[这篇教程](https://www.alchemy.com/docs/reference/nft-api-endpoints)。

</CalloutBlock>

1. 由于我们要继承 OpenZepplin 合约库中的类，请在命令行中运行以下命令，将该库安装到我们的文件夹中：

**textnpm install @openzeppelin/contracts@3.1.0-solc-0.7**

那么，这段代码具体做了什么？让我们逐行分析。

在第 5-7 行，我们的代码继承了三个 [OpenZepplin](https://openzeppelin.com) 智能合约类：

- @[openzeppelin](https://www.alchemy.com/dapps/openzeppelin)/contracts/token/ERC721/ERC721.sol 包含了 ERC721 标准的实现，我们的 NFT 智能合约将继承它（要成为有效的 NFT，智能合约必须实现 ERC721 标准的所有方法）。要了解更多关于继承的 ERC721 函数的信息，可以查看[这里](https://eips.ethereum.org/EIPS/eip-721)的接口定义。

- @openzeppelin/contracts/utils/Counters.sol 提供了只能按 1 递增或递减的计数器。我们的智能合约使用一个计数器来跟踪已铸造的 NFT 总数，并为新的 NFT 设置唯一 ID。每个通过智能合约铸造的 NFT 都必须分配一个唯一 ID——这里的唯一 ID 就是由已存在的 NFT 总数决定的。例如，我们用智能合约铸造的第一个 NFT 的 ID 是 “1”，第二个 NFT 的 ID 是 “2”，以此类推。

- @openzeppelin/contracts/access/Ownable.sol 在我们的智能合约上设置了[访问控制](https://docs.openzeppelin.com/contracts/3.x/access-control)，这样只有智能合约的所有者（也就是你）才能铸造 NFT。注意，是否包含访问控制完全取决于你的偏好。如果你希望任何人都能使用你的智能合约铸造 NFT，删除第 10 行的 Ownable 和第 17 行的 onlyOwner 即可。

在第 10-28 行，是我们自定义的 NFT 智能合约，它其实非常短——只包含一个计数器、一个构造函数和一个函数！这要归功于我们继承的 OpenZepplin 合约，它们已经实现了创建 NFT 所需的大部分方法，比如 ownerOf（返回 NFT 的所有者）和 transferFrom（转移 NFT 的所有权）。

在第 14 行，你会注意到我们向 ERC721 构造函数传入了两个字符串 “MyNFT” 和 “NFT”。第一个变量是智能合约的名称，第二个是它的符号。这两个变量你可以随意命名！

最后，从第 16 行开始，是我们的函数 mintNFT\(\)，它让我们能够铸造一个 NFT！你会注意到这个函数接收两个变量：

- address recipient 指定将接收新铸造的 NFT 的地址

- string memory tokenURI 是一个字符串，它应该指向一个描述 NFT 元数据的 JSON 文档。NFT 的元数据才是真正赋予其生命的部分，它让 NFT 拥有额外的属性，比如名称、描述、图片以及其他特征。在本教程的第二部分，我们会介绍如何配置这个元数据。

**mintNFT** 调用了继承的 ERC721 库中的一些方法，最终返回一个数字，表示新铸造的 NFT 的 ID。

## 第 11 步：将 MetaMask 和 Alchemy 连接到你的项目

现在我们已经创建了 Metamask 钱包、Alchemy 账户，并编写好了智能合约，是时候把三者连接起来了。

从你的虚拟钱包发出的每一笔交易都需要使用你唯一的私钥进行签名。为了让我们的程序获得这个权限，我们可以将私钥（以及 Alchemy API key）安全地存储在一个环境文件中。

要了解更多关于发送交易的信息，可以查看[这篇教程](https://docs.alchemy.com/docs/how-to-send-transactions-on-ethereum)，了解如何使用 web3 发送交易。

首先，在项目目录中安装 dotenv 包：

然后，在项目根目录下创建一个 **.env** 文件，并将你的 Metamask 私钥和 HTTP Alchemy API URL 添加进去。

注意：你的 .env 文件必须命名为 .env！不要将文件名改为 xx.env

- 按照[这些说明](https://metamask.zendesk.com/hc/en-us/articles/360015289632-How-to-Export-an-Account-Private-Key)从 Metamask 导出你的私钥

- 参照下方步骤获取 HTTP Alchemy API URL 并复制到剪贴板

<ImageBlock
  src="https://media.alchemy.com/1704631290-copy-your-alchemy-api-key.gif"
  alt="Alchemy 控制台:复制应用的 HTTP API URL 以用于 .env 文件"
  width={843}
  height={480}
  caption="复制你的 Alchemy API 密钥"
/>

你的 **.env** 文件应该是这样的：

## 第 12 步：安装 Ethers.js

Ethers.js 是一个库，它通过将[标准 JSON-RPC 方法](https://www.alchemy.com/docs)封装为更易用的方法，让与 Ethereum 交互和发起请求变得更容易。

Hardhat 可以非常方便地集成[插件](https://hardhat.org/plugins/)以获得额外的工具和扩展功能。我们将使用 [Ethers 插件](https://hardhat.org/plugins/nomiclabs-hardhat-ethers.html)来部署合约（[Ethers.js](https://github.com/ethers-io/ethers.js/) 提供了一些非常简洁的合约部署方法）。

在你的项目目录中输入：

我们在下一步的 **hardhat.config.js** 中也需要用到 ethers。

## 第 13 步：更新 hardhat.config.js

到目前为止我们已经添加了几个依赖和插件，现在我们需要更新 **hardhat.config.js**，让我们的项目知道这些内容。

将你的 **hardhat.config.js** 更新为如下内容：

## 第 14 步：编译我们的合约

为了确保目前一切正常，让我们编译一下合约。compile 任务是 hardhat 内置任务之一。

在命令行中运行：

你可能会看到一个关于源文件中未提供 SPDX 许可证标识符的警告，不用担心这个——只要其他部分看起来没问题就行！如果有问题，你可以从 Alchemy Dashboard 提交一个支持工单。

## 第 15 步：编写我们的部署脚本

现在合约已经写好，配置文件也准备就绪，是时候编写合约的部署脚本了。

进入 **scripts/** 文件夹，新建一个名为 **deploy.js** 的文件，并添加以下内容：

Hardhat 在他们的 [Contracts 教程](https://hardhat.org/tutorial/testing-contracts.html#writing-tests)中出色地解释了每一行代码的作用，我们在这里采用了他们的解释。

[ethers.js](https://www.alchemy.com/dapps/ethers-js) 中的 **ContractFactory** 是用于部署新智能合约的抽象，因此这里的 MyNFT 就是我们 NFT 合约实例的工厂。使用 **hardhat-ethers** 插件时，**ContractFactory** 和 **Contract** 实例默认连接到第一个 signer。

在 **ContractFactory** 上调用 **deploy\(\)** 会启动部署，并返回一个解析为 **Contract** 的 **Promise**。这个对象包含了我们智能合约每个函数对应的方法。

## 第 16 步：部署我们的合约

我们终于准备好部署智能合约了！回到项目目录的根目录，在命令行中运行：

然后你应该会看到类似这样的内容：

如果我们前往 [Sepolia Etherscan](https://sepolia.etherscan.io) 搜索我们的合约地址，应该能看到它已经成功部署。这笔交易看起来会像这样：

<ImageBlock
  src="https://media.alchemy.com/1704803201-ropsten-etherscan.png"
  alt="Etherscan 交易视图确认 NFT 智能合约部署成功"
  width={2880}
  height={1646}
  caption="地址部署成功"
/>

**From** 地址应该与你的 Metamask 账户地址一致，而 To 地址会显示 “Contract Creation”。如果我们点进这笔交易，会在 **To** 字段中看到我们的合约地址：

<ImageBlock
  src="https://media.alchemy.com/1704803309-nft-deployed-successfully.png"
  alt="太棒了!你刚刚把 NFT 智能合约部署到了 Ethereum 链上 🎉"
  width={2880}
  height={1538}
  caption="太棒了!你刚刚把 NFT 智能合约部署到了 Ethereum 链上 🎉"
/>

为了了解底层发生了什么，让我们前往 [Alchemy dashboard](https://dashboard.alchemy.com/signup?referral=affiliate:68212b46-a5c5-4f4c-bc8b-73f50536fcaf) 中的 Explorer 标签页。如果你有多个 Alchemy 应用，请确保按应用筛选并选择 “MyNFT”。

<ImageBlock
  src="https://media.alchemy.com/1704803382-navigate-to-the-explorer-tab-in-our-alchemy-dashboard.jpg"
  alt="Alchemy 控制台 Explorer 标签页,列出合约部署期间发起的 JSON-RPC 调用"
  width={2880}
  height={1636}
  caption="前往 Alchemy 控制台中的 Explorer 标签页"
/>

在这里你会看到一系列 JSON-RPC 调用，这些都是我们调用 **.deploy\(\)** 函数时，Hardhat/Ethers 在底层为我们发起的。

这里有两个值得关注的重要调用：**`eth_sendRawTransaction`**，这是真正将我们的智能合约写入 Sepolia 链的请求；以及 **`eth_getTransactionByHash`**，它是根据交易哈希读取交易信息的请求（这是发送交易时的一个典型模式）。

要了解更多关于发送交易的信息，可以查看这篇关于[使用 Web3 发送交易](https://docs.alchemy.com/docs/how-to-send-transactions-on-ethereum)的教程。

以上就是本教程第一部分的全部内容。在第二部分中，我们将通过[铸造一个 NFT](/blog/how-to-mint-an-nft-using-web3-js)来实际与我们的智能合约进行交互；第三部分将介绍如何[在 Metamask 中查看你的 NFT](/blog/how-to-view-your-nft-in-your-mobile-wallet)！🤑
