{"content":{"title":"如何在 Solana 上使用 Typescript 铸造 NFT","body":"#### 在开始本指南之前 \r\n\r\n本指南将深入讲解如何通过一系列技术步骤使用 TypeScript 在 Solana 上铸造 NFT。如果你想更快地完成此任务，而将繁重的工作留给我们，我们建议使用 [Crossmint NFT Mint API \\[主网\\]](https://marketplace.quicknode.com/add-on/crossmint-nft-mint-api-mainnet) 附加组件。通过使用 Crossmint NFT Mint API \\[主网\\]，你可以通过一个 POST 请求向 Solana 用户和电子邮件地址空投 NFT。使用 NFT Mint API 可以轻松创建集合并铸造 NFT！\r\n\r\n[尝试 Crossmint NFT Mint API \\[主网\\]](https://marketplace.quicknode.com/add-on/crossmint-nft-mint-api-mainnet)\r\n\r\n## 概述 \r\n\r\n使用 [Candy Machine](https://www.quicknode.com/guides/solana-development/how-to-deploy-an-nft-collection-on-solana-using-sugar-candy-machine) 铸造 10,000 个 NFT 非常有趣，但在许多情况下我们可能需要铸造单个 NFT（例如，1 of 1 艺术、音乐等）。Metaplex 是创建 Solana 元数据标准的组织，最近开发了一些方便的 JS SDK，使从终端创建 NFT 变得简单。\r\n\r\n#### 你将要做的事情 \r\n\r\n在本指南中，你将使用 Metaplex Umi JS 库从终端向 Solana 的 devnet 铸造带有元数据的 NFT。我们将涵盖 NFT 铸造的三个关键步骤：\r\n\r\n1. 上传图像\r\n2. 上传元数据\r\n3. 铸造 NFT\r\n\r\n#### 你将需要的工具 \r\n\r\n- 已安装 [Nodejs](https://nodejs.org/en/)（版本 16.15 或更高）\r\n- 有 Typescript 经验并安装 [ts-node](https://www.npmjs.com/package/ts-node)\r\n\r\n## Solana NFT 背景信息 \r\n\r\n在构建我们的应用程序之前，让我们谈谈 NFT 和 Solana 的 NFT 架构。那么，NFT到底是什么？非同质化代币实质上是在链上存储的一组唯一数据，和其他任何代币都不相同。简而言之，只有一个。在 Solana 上，我们通过使用 SPL Token Program 并创建一个供给为 1 的代币来实现这一点。此外，NFT 不能被拆分，这意味着你不能拥有 NFT 的三分之一（**注：有一些关于分数化的高级 NFT 协议，但通常情况下，NFT 不可分割**）。在 Solana 上，我们还使用 SPL Token Program 将 decimals 设置为 0（这意味着它不能被分割）。\r\n\r\n尽管创建供给为 1 和 decimals 为 0 的 SPL 代币在技术上是通过其独特的 Mint ID 定义的 NFT，但 SPL token 程序在某些类型和可以存储的数据量方面是有限的。进入 Metaplex。Metaplex 元数据程序为链上和链下元数据设定了标准。通过与 Mint 账户相关联的程序派生地址，Metaplex 允许用户为铸造分配更详细的元数据（例如名称、描述、可变性、卖方费用等）。Metaplex 元数据中一个显著的字段称为 \"URI\"。这是指向特定链下元数据的地址。由于租赁费用和速度限制，Metaplex 预计某些数据存储在链下（例如图像/图像 URI、NFT 特征/属性）。以下是与这些字段关系可视化的 Metaplex 图示：\r\n\r\n![](https://img.learnblockchain.cn/2025/02/24/0-f64c5bf9ff737640465cb35f7c9e8001.png)\r\n\r\n_来源：_[\\*https://docs.metaplex.com/programs/token-metadata/overview\\\\\r\n\\*](https://docs.metaplex.com/programs/token-metadata/overview)\r\n\r\n在本指南中，我们将首先将我们的图像上传到 Arweave 并获取其 URI，然后我们将创建链下元数据并上传到 Arweave，最后我们将铸造我们的代币并定义指向链下元数据的链上元数据。\r\n\r\n## 设置你的项目 \r\n\r\n在终端创建一个新的项目目录：\r\n\r\n```\r\nmkdir mint-nft\r\ncd mint-nft\r\n\r\n```\r\n\r\n为你的应用程序创建一个文件，**app.ts**：\r\n\r\n```\r\necho > app.ts\r\n\r\n```\r\n\r\n使用“yes”标志初始化你的项目，以使用默认值创建新包：\r\n\r\n```\r\nyarn init --yes\r\n#或\r\nnpm init --yes\r\n\r\n```\r\n\r\n创建一个启用 .json 导入的 **tsconfig.json** 文件：\r\n\r\n```\r\ntsc -init --resolveJsonModule true\r\n\r\n```\r\n\r\n最后，创建一个用于上传的文件夹：\r\n\r\n```\r\nmkdir uploads\r\n\r\n```\r\n\r\n将图像（.png 或 .jpg）保存到此文件夹中，命名为 **image.png**。我们将使用这张像素化的美图：\r\n\r\n![](<Base64-Image-Removed>)\r\n\r\n#### 安装 Solana Web3 依赖 \r\n\r\n我们需要为本练习添加 Solana Web3 和 SPL Token 库。此外，我们将使用 Metaplex 的 JS SDK 和 MPL Token Metadata 库。在你的终端中输入：\r\n\r\n```\r\nyarn add @metaplex-foundation/umi @metaplex-foundation/umi-bundle-defaults @metaplex-foundation/mpl-token-metadata @metaplex-foundation/umi-storage-mock\r\n#或\r\nnpm install @metaplex-foundation/umi @metaplex-foundation/umi-bundle-defaults @metaplex-foundation/mpl-token-metadata @metaplex-foundation/umi-storage-mock\r\n\r\n```\r\n\r\n#### 创建钱包并空投 SOL \r\n\r\n你需要创建一个 [Solana 文件系统钱包](https://docs.solana.com/wallet-guide/file-system-wallet)（密钥对将写入 **wallet.json** 文件）并向其空投一些 SOL。从你的终端，输入：\r\n\r\n```\r\nsolana-keygen new --silent --no-bip39-passphrase --outfile wallet.json\r\n\r\n```\r\n\r\n这将创建一个新钱包并将其保存到名为 **wallet.json** 的文件中。然后，你可以使用 [QuickNode Faucet](https://faucet.quicknode.com/solana/devnet) 或 [Solana Faucet](https://faucet.solana.com/) 向该钱包空投一些 SOL。你可以通过输入以下命令获得钱包地址：\r\n\r\n```\r\nsolana address -k wallet.json\r\n\r\n```\r\n\r\n完成后，你应能够查看你的钱包余额：\r\n\r\n```\r\nsolana balance -ud -k wallet.json\r\n\r\n```\r\n\r\n如果余额依然为 0，请再次尝试使用水龙头。你将在继续下一个步骤之前需要大约 0.1 Devnet SOL。\r\n\r\n## 设置你的应用 \r\n\r\n#### 导入必要的依赖 \r\n\r\n打开 **app.ts**，并在 _第 1 行_ 粘贴以下导入：\r\n\r\n```\r\nimport { createNft, mplTokenMetadata } from '@metaplex-foundation/mpl-token-metadata'\r\nimport { createUmi } from '@metaplex-foundation/umi-bundle-defaults'\r\nimport { createGenericFile, createSignerFromKeypair, generateSigner, keypairIdentity, percentAmount, sol } from '@metaplex-foundation/umi';\r\nimport { mockStorage } from '@metaplex-foundation/umi-storage-mock';\r\nimport * as fs from 'fs';\r\nimport secret from './wallet.json';\r\n\r\n```\r\n\r\n \r\n\r\n除了我们在前一步中创建的钱包外，我们还从 Metaplex Umi 库中导入了一些必要的方法和类。此外，由于我们将从系统上传文件，因此需要导入 _fs_ 文件系统库。\r\n\r\n#### 设置你的 QuickNode 端点 \r\n\r\n要在 Solana 上构建，你需要一个 API 端点来连接到网络。你可以使用公共节点，也可以部署和管理自己的基础设施；然而，如果你希望获得 8 倍的响应速度，你可以将繁重的工作留给我们。\r\n\r\n \r\n\r\n![](https://img.learnblockchain.cn/2025/02/24/3-32e2e904682695d2fd322a17df638d05.png)\r\n\r\n在 **app.ts** 中的导入语句下声明你的 RPC，并通过初始化新的 Umi 实例与 Solana 进行连接：\r\n\r\n```\r\nconst QUICKNODE_RPC = 'https://example.solana-devnet.quiknode.pro/0123456/'; //用你的 QuickNode RPC 端点替换\r\nconst umi = createUmi(QUICKNODE_RPC);\r\n\r\n```\r\n\r\n#### 声明变量 \r\n\r\n你需要在运行脚本之前准备几样东西：\r\n\r\n- 定义你的 Umi 兼容创建者 _Signer_\r\n\r\n- 配置你的 Umi 实例\r\n\r\n- 定义你的 NFT 元数据\r\n\r\n\r\n在 Umi 声明下方添加以下声明，以定义你的创建者并配置你的 Umi 实例：\r\n\r\n```\r\nconst creatorWallet = umi.eddsa.createKeypairFromSecretKey(new Uint8Array(secret));\r\nconst creator = createSignerFromKeypair(umi, creatorWallet);\r\numi.use(keypairIdentity(creator));\r\numi.use(mplTokenMetadata());\r\numi.use(mockStorage());\r\n\r\n```\r\n\r\n#### 定义 NFT 特征 \r\n\r\n我们将创建一个 `nftDetail` 对象，包含我们希望包含在 NFT 中的一些元数据。创建一个新的常量 `nftDetail`，并包括以下属性：\r\n\r\n```\r\nconst nftDetail = {\r\n    name: \"QuickNode Pixel\",\r\n    symbol: \"QP\",\r\n    uri: \"IPFS_URL_OF_METADATA\",\r\n    royalties: 5.5,\r\n    description: '面向所有人的像素基础设施！',\r\n    imgType: 'image/png',\r\n    attributes: [\\\r\n        { trait_type: '速度', value: '快速' },\\\r\n    ]\r\n};\r\n\r\n```\r\n\r\n请随意在这里使用你自己的值。\r\n\r\n## 上传你的图像 \r\n\r\n在开始之前，我们需要将用于 NFT 的图像上传到一个去中心化存储平台。这是因为我们必须将 NFT 图像的 URI 传递到 NFT 的元数据中。如果你已经有一个拥有 URI 的图像，随意在 `main` 函数中定义该值并跳过此步骤。\r\n\r\n为了上传我们的图像，我们将使用 Umi 上载器。Umi 上载器是一个简单的方法，可将文件上传到 Arweave、IPFS 或其他去中心化存储平台。我们将使用 `fs` 创建图像缓冲区，然后使用 Umi 上载器进行上传。将以下函数添加到你的 **app.ts** 文件：\r\n\r\n```\r\nasync function uploadImage(): Promise<string> {\r\n    try {\r\n        const imgDirectory = './uploads';\r\n        const imgName = 'image.png'\r\n        const filePath = `${imgDirectory}/${imgName}`;\r\n        const fileBuffer = fs.readFileSync(filePath);\r\n        const image = createGenericFile(\r\n            fileBuffer,\r\n            imgName,\r\n            {\r\n                uniqueName: nftDetail.name,\r\n                contentType: nftDetail.imgType\r\n            }\r\n        );\r\n        const [imgUri] = await umi.uploader.upload([image]);\r\n        console.log('已上传图像:', imgUri);\r\n        return imgUri;\r\n    } catch (e) {\r\n        throw e;\r\n    }\r\n\r\n}\r\n\r\n```\r\n\r\nUmi 上载器接口\r\n\r\n如果你密切关注我们之前的依赖安装和导入，你可能会注意到我们正在使用一个名为 `@metaplex-foundation/umi-storage-mock` 的包。正如其名，这是一种模拟存储系统，允许我们在不实际上传文件到去中心化存储平台的情况下测试我们的代码。这对于测试和调试非常有用（使用也不花费任何钱！）。Umi 的一个伟大方面是它为存储（以及其他常见操作）提供了原始的通用函数，这些可以轻松替换为真实的存储系统，例如 Arweave 或 IPFS。这是通过 [`UploaderInterface`](https://umi-docs.vercel.app/interfaces/umi.UploaderInterface.html) 实现的。这意味着你可以通过简单的导入和配置更改，将模拟存储替换为不同的插件。以下是当前替代 **UploaderInterface** 选项的 [列表](https://github.com/metaplex-foundation/umi/blob/main/docs/implementations.md#uploader-interface)。你需要做的就是导入相关包，并将 `umi.use(mockStorage())` 这一行更改为 `umi.use(newStoragePlugin())`。\r\n\r\n非常酷！\r\n\r\n## 上传元数据 \r\n\r\n元数据或多或少就是使你的 NFT 特别的东西。它包括图像、任何定义特征、将其分配给集合等。我们将使用与上一步上传图像类似的方法，但有一种特殊的方法来处理 _.json_ 文件。将以下函数添加到你的 **app.ts** 文件：\r\n\r\n```\r\nasync function uploadMetadata(imageUri: string): Promise<string> {\r\n    try {\r\n        const metadata = {\r\n            name: nftDetail.name,\r\n            description: nftDetail.description,\r\n            image: imageUri,\r\n            attributes: nftDetail.attributes,\r\n            properties: {\r\n                files: [\\\r\n                    {\\\r\n                        type: nftDetail.imgType,\\\r\n                        uri: imageUri,\\\r\n                    },\\\r\n                ]\r\n            }\r\n        };\r\n        const metadataUri = await umi.uploader.uploadJson(metadata);\r\n        console.log('已上传元数据:', metadataUri);\r\n        return metadataUri;\r\n    } catch (e) {\r\n        throw e;\r\n    }\r\n}\r\n\r\n```\r\n\r\n在这里，我们创建了一个 JSON 对象，包含我们希望包含在 NFT 中的元数据。然后我们使用 Umi 上载器的 `uploadJson` 方法上传这个 JSON 对象。元数据对象包括名称、描述、图像 URI、属性和 NFT 的属性。你可以稍微调整一下，但请确保符合 [Metaplex 元数据标准](https://developers.metaplex.com/token-metadata/token-standard#the-non-fungible-standard)。\r\n\r\n## 铸造 NFT \r\n\r\n与我们之前的函数一样，Metaplex API 简化了这个过程，让我们能够通过一个方法 **createNft** 铸造 NFT。与上一步中的元数据不同，在最后这一步中，我们必须传入一些将直接存储在 Solana 链上的元数据。\r\n\r\n在你的 **uploadMetadata()** 函数之后和 **main()** 之前，创建一个新的异步函数 **mintNft()**：\r\n\r\n```\r\nasync function mintNft(metadataUri: string) {\r\n    try {\r\n        const mint = generateSigner(umi);\r\n        await createNft(umi, {\r\n            mint,\r\n            name: nftDetail.name,\r\n            symbol: nftDetail.symbol,\r\n            uri: metadataUri,\r\n            sellerFeeBasisPoints: percentAmount(nftDetail.royalties),\r\n            creators: [{ address: creator.publicKey, verified: true, share: 100 }],\r\n        }).sendAndConfirm(umi)\r\n        console.log(`创建的 NFT: ${mint.publicKey.toString()}`)\r\n    } catch (e) {\r\n        throw e;\r\n    }\r\n}\r\n\r\n```\r\n\r\n此函数将在链上铸造一个代币，链接到我们的元数据，使用 Metaplex 元数据程序。\r\n\r\n## 创建和调用主函数 \r\n\r\n创建一个异步函数 **main**，用于执行我们代码的所有步骤：\r\n\r\n```\r\nasync function main() {\r\n    const imageUri = await uploadImage();\r\n    const metadataUri = await uploadMetadata(imageUri);\r\n    await mintNft(metadataUri);\r\n}\r\n\r\nmain();\r\n\r\n```\r\n\r\n如你所见，我们需要定义三个函数：**uploadImage**、**uploadMetadata** 和 **mintNft**。我们将在 **main** 函数中调用这些函数。让我们从 **uploadImage** 开始。\r\n\r\n现在，你只需运行你的代码！\r\n\r\n## 运行你的代码💨 \r\n\r\n你已经准备好了！当你准备好时，在终端中输入：\r\n\r\n```\r\nts-node app\r\n\r\n```\r\n\r\n是的！你看到这样的输出了吗？\r\n\r\n```\r\nts-node app\r\n已上传图像: https://mockstorage.example.com/QuickNode Pixel\r\n已上传元数据: https://mockstorage.example.com/Km5iewgZoHUE1dIQJZx7\r\n创建的 NFT: 2dgA2xB4MebkQTMZeucoN1crDrPcLPaBryWChaUBcU6x\r\n\r\n```\r\n\r\n做得很好。\r\n\r\n## 接下来的步骤和总结 \r\n\r\n你现在拥有一个方便且可重用的脚本，可以直接从终端铸造 NFT！这很方便，对吧？想继续构建吗？以下是一些可以扩展你刚学到的内容的想法：\r\n\r\n1. 使用 SPL 代币转让将此 NFT 发送到另一个钱包。（ [指南：如何在 Solana 上转移 SPL 代币](https://www.quicknode.com/guides/solana-development/how-to-transfer-spl-tokens-on-solana)）\r\n2. 使用 Solana dApp Scaffold 创建一个 NFT 铸造小部件。（ [指南：如何使用 Solana Wallet Adapter 和 Scaffold 将用户连接到你的 dApp](https://www.quicknode.com/guides/solana-development/how-to-connect-users-to-your-dapp-with-the-solana-wallet-adapter-and-scaffold)） **注：你将不得不使用钱包适配器身份，而不是** **keypairIdentity** **以在带有 Solana Wallet Adapter 的前端创建 Metaplex 的实例。**\r\n\r\n \r\n\r\n>- 原文链接： [quicknode.com/guides/sol...](https://learnblockchain.cn/article/11175-using-typescript)\r\n>- 登链社区 AI 助手，为大家转译优秀英文文章，如有翻译不通的地方，还请包涵～"},"author":{"user":"https://learnblockchain.cn/people/25306","address":null},"history":null,"timestamp":1740450305,"version":1}