{"content":{"title":"如何在 Solana 上使用 Metaplex 创建可替代的 SPL 代币","body":"## 概述 \r\n\r\n你是在为即将到来的 NFT 发行创建白名单代币吗？还是想为下一个出色的 dApp 启动一个可替代代币？Solana 和 Metaplex 让你可以直接通过终端轻松实现这一目标！\r\n\r\n> 想要视频演示吗？观看 Sahil 的演示，了解[如何在 Solana 上创建可替代代币](https://www.youtube.com/watch?v=dmH2oPBk97w)。\r\n\r\n\r\n> 2022年6月20日，Solana 停止维护 Solana Token List，这是一个包含所有可替代 SPL 代币元数据的存储库。此列表已被 [Metaplex 的可替代代币标准](https://docs.metaplex.com/programs/token-metadata/token-standard#the-fungible-standard) 所取代。如果你熟悉旧标准或只是刚开始你的第一个 SPL 代币，本指南适合你。\r\n\r\n#### **你将做什么** \r\n\r\n在本指南中，你将创建一个钱包（并空投一些 SOL），使用 Metaplex 标准创建可替代代币元数据，将代币元数据上传到 IPFS，并使用 Metaplex umi 客户端在 Solana devnet 上铸造一个新的可替代 SPL 代币。\r\n\r\n#### **你将需要准备哪些设备** \r\n\r\n- 安装 [Nodejs](https://nodejs.org/en/)（版本 16.15 或更高）\r\n- 安装 npm 或 yarn（我们会使用 yarn 来初始化我们的项目并安装必要的包。如果你更喜欢使用 npm，也可以随意使用 npm。）\r\n- 具有 TypeScript 经验和安装 [ts-node](https://www.npmjs.com/package/ts-node)\r\n- [Solana Web3](https://www.npmjs.com/package/@solana/web3.js)\r\n- [Metaplex Foundation umi JS 客户端](https://www.npmjs.com/package/@metaplex-foundation/umi)\r\n- [Metaplex Foundation umi 默认插件包](https://www.npmjs.com/package/@metaplex-foundation/umi-bundle-defaults)\r\n- [Metaplex Foundation MPL 代币元数据库](https://www.npmjs.com/package/@metaplex-foundation/mpl-token-metadata)\r\n\r\n## 设置你的环境 \r\n\r\n在终端中创建一个新的项目目录：\r\n\r\n```\r\nmkdir mint-fungible-spl\r\ncd mint-fungible-spl\r\n\r\n```\r\n \r\n\r\n创建两个文件，**wallet.ts** 和 **mint.ts**。我们将使用 **wallet.ts** 来创建一个新的开发钱包并空投一些 Solana 用于测试。我们将使用 **mint.ts** 来铸造一个新的 SPL 代币并上传我们的代币元数据。\r\n\r\n```\r\necho > {wallet,mint}.ts\r\n\r\n```\r\n\r\n使用 \"yes\" 标志初始化你的项目，以使用默认值为你的新包：\r\n\r\n```\r\nyarn init --yes\r\n#or\r\nnpm init --yes\r\n\r\n```\r\n\r\n创建 **tsconfig.json** 文件：\r\n\r\n```\r\ntsc --init\r\n\r\n```\r\n\r\n打开 **tsconfig.json** 并取消注释（或添加）如下内容：\r\n\r\n```\r\n\"resolveJsonModule\": true\r\n\r\n```\r\n\r\n这将允许我们将 _.json_ 文件导入到我们的存储库中，稍后我们在想要从 _PrivateKey_ 生成 _Keypair_ 时将会很重要。\r\n\r\n还要再次检查 **esModuleInterop** 是否设置为 **true** 以允许我们使用导入。\r\n\r\n#### **安装 Solana Web3 依赖项：** \r\n\r\n```\r\nyarn add @solana/web3.js@1 @metaplex-foundation/umi @metaplex-foundation/mpl-token-metadata @metaplex-foundation/umi-bundle-defaults bs58\r\n#or\r\nnpm i @solana/web3.js@1 @metaplex-foundation/umi @metaplex-foundation/mpl-token-metadata @metaplex-foundation/umi-bundle-defaults bs58\r\n\r\n```\r\n\r\n你的环境应该看起来像这样：\r\n\r\n![准备好的 Solana 环境](https://img.learnblockchain.cn/2025/02/24/0-6f42066b3b57d24fd63ac4a6c6e60ee6.png)\r\n\r\n好的！我们准备好了。\r\n\r\n## 设置你的 QuickNode 端点 \r\n\r\n要在 Solana 上构建，你需要一个 API 端点与网络连接。你可以使用公共节点或部署和管理自己的基础设施；然而，如果你希望获得 8 倍的响应时间，你可以将繁重的任务交给我们。\r\n\r\n 我们将使用 Solana Devnet 节点。复制 HTTP 提供程序链接：\r\n\r\n![新的 Solana 端点](https://img.learnblockchain.cn/2025/02/24/1-fe93ea27d17679c16c968b195af78420.png)\r\n\r\n## 创建钱包并空投 SOL \r\n\r\n为了铸造可替代 SPL 代币，我们首先需要创建一个 Devnet 钱包并空投 SOL。如果你已经拥有纸质钱包，请将其保存到你的项目目录中，命名为 **guideSecret.json**。如果它需要一些 devnet SOL，你可以通过下面的表格申请一些，或从 [QuickNode 多链水龙头](https://faucet.quicknode.com/drip?utm_source=internal&utm_campaign=guides&utm_content=how-to-create-a-fungible-spl-token-with-the-new-metaplex-token-standard) 获取：\r\n\r\n🪂请求 Devnet SOL\r\n\r\n空投 1 SOL（Devnet）\r\n\r\n如果你没有纸质钱包，我们将程序性地生成一个新的。打开 **wallet.ts** 并粘贴以下代码。我们将在下一部分中逐条解释。\r\n\r\nwallet.ts\r\n\r\n```\r\nimport { Keypair, LAMPORTS_PER_SOL, Connection } from \"@solana/web3.js\";\r\nimport * as fs from 'fs';\r\nimport bs58 from 'bs58';\r\n\r\n//第 1 步 - 连接到 Solana 网络\r\nconst endpoint = 'https://example.solana-devnet.quiknode.pro/000000/'; //将其替换为你的 QuickNode RPC 端点\r\nconst solanaConnection = new Connection(endpoint);\r\n\r\n//第 2 步 - 生成一个新的 Solana 钱包\r\nconst keypair = Keypair.generate();\r\nconsole.log(`生成的新 KeyPair. 钱包公钥: `, keypair.publicKey.toString());\r\n\r\n//第 3 步 - 将私钥转换为 Base58\r\nconst privateKey = bs58.encode(keypair.secretKey);\r\nconsole.log(`钱包私钥:`, privateKey);\r\n\r\n//第 4 步 - 将钱包秘密密钥写入 .JSON 文件\r\nconst secret_array = keypair.secretKey\r\n    .toString() //将秘密密钥转换为字符串\r\n    .split(',') //以逗号分隔字符串并转换为字符串数组\r\n    .map(value=>Number(value)); //将数组中的字符串值转换为数字\r\n\r\nconst secret = JSON.stringify(secret_array); //转换为 JSON 字符串\r\n\r\nfs.writeFile('guideSecret.json', secret, 'utf8', function(err) {\r\n    if (err) throw err;\r\n    console.log('将秘密密钥写入 guideSecret.json。');\r\n});\r\n\r\n//第 5 步 - 向新钱包空投 1 SOL\r\n(async()=>{\r\n    const airdropSignature = solanaConnection.requestAirdrop(\r\n        keypair.publicKey,\r\n        LAMPORTS_PER_SOL,\r\n    );\r\n    try{\r\n        const txId = await airdropSignature;\r\n        console.log(`空投交易 ID: ${txId}`);\r\n        console.log(`https://explorer.solana.com/tx/${txId}?cluster=devnet`)\r\n    }\r\n    catch(err){\r\n        console.log(err);\r\n    }\r\n})()\r\n\r\n```\r\n\r\n该脚本将执行 5 个任务：\r\n\r\n1. 连接到 Solana 网络（确保在 **第 6 行** 的示例 URL 中替换为你实际保存的 QuickNode 端点 URL）。\r\n\r\n2. 生成一个新的钱包 Keypair。\r\n\r\n3. 将私钥转换为 Base58 并将其打印到控制台。\r\n\r\n4. 将秘密密钥写入一个 _.json_ 文件，我们将在下一步中使用。第 **16-22** 行是格式化密钥为数字数组所需的。第 **24-27** 行使用 _fs_ 将数组导出到 _.json_ 文件中。\r\n\r\n5. 向新钱包空投 1 SOL。\r\n\r\n继续运行此脚本以创建一个新钱包并空投 1 SOL：\r\n\r\n```\r\nts-node wallet.ts\r\n\r\n```\r\n\r\n你应该会在项目文件夹中看到一个新的文件 **guideSecret.json**，终端日志类似于：\r\n\r\n```\r\n生成的新 KeyPair. 钱包公钥:  G7ugoBpckgiL13KZMzWgQ751G27moDR9yVckQERrNnvj\r\n钱包私钥: 3A4b34ob9hqUaTjR52eBLjQCLova1jqAG1zT59ypXSJvjh1cQzEExpBBbQLWT7gfbcS4KuYddBDiAaYrFCPE55Tu\r\n将秘密密钥写入 guideSecret.json。\r\n空投交易 ID: 58uYUd8PeimjWxf12dZRdqmURoMg1Q15SaaWwEET8U4VXU2pystyUsL9s2sq3cp2JTsUugPY7SUwW82S71SUo6Sj\r\nhttps://explorer.solana.com/tx/58uYUd8PeimjWxf12dZRdqmURoMg1Q15SaaWwEET8U4VXU2pystyUsL9s2sq3cp2JTsUugPY7SUwW82S71SUo6Sj?cluster=devnet\r\n\r\n```\r\n\r\n现在，让我们将私钥添加到我们选择的钱包中（在这种情况下为 Phantom）：\r\n\r\n不要分享私钥\r\n\r\n我们打印私钥只是为了方便我们在 Phantom 中添加新钱包。绝不要与任何人分享你的私钥；在使用代码时始终安全地存储它们，并在环境文件（.env）中使用这些文件，并且在发布到 GitHub 之前，一定要将环境文件添加到 .gitignore 里。\r\n\r\n![在 Phantom 中添加钱包](https://img.learnblockchain.cn/2025/02/24/phantom-22161aca3b9cc87f6f3ed50f472c6e73.gif)\r\n\r\n让我们上传代币图标和元数据。\r\n\r\n## 上传代币图标和元数据 \r\n\r\n我们需要一个代币图标（图像）和可以公开访问的元数据（JSON 文件），以便我们的代币元数据能够在区块链浏览器、钱包和交易所上正确显示。\r\n\r\n为了保持去中心化的理念，我们将使用 [IPFS](https://www.quicknode.com/guides/quicknode-products/ipfs/what-is-ipfs) 上传我们的代币图标和元数据。\r\n\r\n虽然我们建议使用 QuickNode 的 IPFS 工具来固定和提供数据，但我们将在下面演示两种方法（例如 1. 使用 QuickNode 的托管 IPFS 服务；2. 运行本地 IPFS 节点）。\r\n\r\n- QuickNode IPFS\r\n- 本地 IPFS 节点\r\n\r\n> 提示\r\n\r\n要在 QuickNode 上使用 IPFS，需要 Build 计划或更高版本。查看我们的 [定价页面](https://www.quicknode.com/pricing#ipfs?utm_source=internal&utm_campaign=guides&utm_content=how-to-create-a-fungible-spl-token-with-the-new-metaplex-token-standard) 获取按计划划分的功能概述。\r\n\r\n导航到 [QuickNode 仪表板](https://dashboard.quicknode.com/endpoints?utm_source=internal&utm_campaign=guides&utm_content=how-to-create-a-fungible-spl-token-with-the-new-metaplex-token-standard)，然后点击左侧边栏的 **IPFS** 标签。\r\n\r\n接着，点击 **Files** 标签，点击 **New** 按钮并选择 _上传文件_，或简单地拖放你要固定的文件。我们会先上传下面的图像，然后是元数据 JSON 文件。\r\n\r\n![代币图标](https://img.learnblockchain.cn/2025/02/24/BTE-634682de7f75c12eaa660f9288642fde.png)\r\n\r\n图像上传后，点击文件旁边的省略号菜单，然后点击 `复制 IPFS URL`。我们需要在元数据文件中添加此内容。\r\n\r\n现在让我们定义我们的元数据。Solana 最近采用了 [Metaplex 的可替代代币标准](https://docs.metaplex.com/programs/token-metadata/token-standard#the-fungible-standard)，它需要一个 **name**、**symbol**、**description** 和 **image**（均为字符串值）。使用该标准铸造代币将使 Phantom 钱包或 Solana Explorer 等主要平台能够轻松识别你的代币，并使其能够被用户查看。创建一个新的元数据 JSON 文件（在这里命名为 token.json）。\r\n\r\ntoken.json\r\n\r\n```\r\n{\r\n    \"name\": \"最佳代币\",\r\n    \"symbol\": \"BTE\",\r\n    \"description\": \"这是最好的代币！\",\r\n    \"image\": \"IPFS_URL_OF_IMAGE\"\r\n}\r\n\r\n```\r\n\r\n用我们之前获得的图像的 IPFS URL 更新 **IPFS_URL_OF_IMAGE**，保存文件并上传它。固定后，你将看到文件名称以及其他数据，如文件的 CID、是否固定以及固定日期。请同样复制元数据文件的 IPFS URL，我们将预计在稍后铸造代币时需要这个 URL。\r\n\r\n![带有代币图标和元数据上传到 IPFS 的仪表板屏幕截图](https://img.learnblockchain.cn/2025/02/24/2-9bbf9abcd6801096d15235661108c053.png)\r\n\r\n这很简单！此外，我们无需担心运行和管理自己的 IPFS 节点。你可以点击文件旁边的省略号，然后点击 _复制 IPFS URL_ 以获取你的文件所在的 URL。此外，你可以在此省略号菜单下重新下载或查看你的文件详细信息。花点时间试试吧。\r\n\r\n现在我们在 QuickNode 上通过 IPFS 固定了我们的文件。让我们铸造我们的代币吧！\r\n\r\n## 构建铸造工具 \r\n\r\n#### **导入依赖关系** \r\n\r\n打开 **mint.ts** 文件并在 **第 1 行** 导入以下依赖项：\r\n\r\n```\r\nimport { percentAmount, generateSigner, signerIdentity, createSignerFromKeypair } from '@metaplex-foundation/umi'\r\nimport { TokenStandard, createAndMint, mplTokenMetadata } from '@metaplex-foundation/mpl-token-metadata'\r\nimport { createUmi } from '@metaplex-foundation/umi-bundle-defaults';\r\nimport secret from './guideSecret.json';\r\n\r\n```\r\n\r\n我们将在指南中逐步介绍这些内容，但我们想提醒一下最后一项导入，即导入我们在早期步骤中创建的 _.json_。\r\n\r\n#### **建立 Solana 连接** \r\n\r\n通过在下面的代码中用你的 QuickNode 端点 URL 替换示例 URL，在导入下方粘贴：\r\n\r\n```\r\nconst umi = createUmi('https://example.solana-devnet.quiknode.pro/000000/'); //将其替换为你的 QuickNode RPC 端点\r\n\r\n```\r\n\r\n#### **初始化签名钱包** \r\n\r\n我们正从 **secret** 中初始化钱包，并使其成为交易的签名者。\r\n\r\n```\r\nconst userWallet = umi.eddsa.createKeypairFromSecretKey(new Uint8Array(secret));\r\nconst userWalletSigner = createSignerFromKeypair(umi, userWallet);\r\n\r\n```\r\n\r\n\\*注意：将 numDecimals 设置为 0 会导致代币无法被细分。这可能与会员或白名单铸造代币相关。\r\n\r\n#### **创建元数据变量** \r\n\r\n创建一个如下所示的元数据变量，并填写代币详细信息。将 `IPFS_URL_OF_METADATA` 替换为你的元数据（token.json）文件的实际 IPFS URL。\r\n\r\n```\r\nconst metadata = {\r\n    name: \"最佳代币\",\r\n    symbol: \"BTE\",\r\n    uri: \"IPFS_URL_OF_METADATA\",\r\n};\r\n\r\n```\r\n\r\n#### **创建铸造 PDA** \r\n\r\n我们需要为我们的代币创建一个铸造程序派生地址（Mint PDA）。了解 [Solana 中 PDAs 的概念](https://learnblockchain.cn/article/10740#overview-of-pdas) 和 [What is Mint PDA for Tokens on Solana](https://developers.metaplex.com/token-metadata/mint#creating-accounts) 。\r\n\r\n在下面，我们创建一个新的铸造 PDA，并要求 umi 客户端使用我们之前根据秘密初始化的钱包作为签名者，并使用 MPL 代币元来铸造代币元数据。\r\n\r\n```\r\nconst mint = generateSigner(umi);\r\numi.use(signerIdentity(userWalletSigner));\r\numi.use(mplTokenMetadata())\r\n\r\n```\r\n\r\n#### **生成 Mint PDA 并铸造代币的函数** \r\n\r\n在下面的函数中，我们发送一笔交易以部署 Mint PDA 并铸造 100 万个代币。你可以根据适合你的代币和控制台消息更改代币数量和信息。\r\n\r\n```\r\ncreateAndMint(umi, {\r\n    mint,\r\n    authority: umi.identity,\r\n    name: metadata.name,\r\n    symbol: metadata.symbol,\r\n    uri: metadata.uri,\r\n    sellerFeeBasisPoints: percentAmount(0),\r\n    decimals: 8,\r\n    amount: 1000000_00000000,\r\n    tokenOwner: userWallet.publicKey,\r\n    tokenStandard: TokenStandard.Fungible,\r\n}).sendAndConfirm(umi)\r\n    .then(() => {\r\n        console.log(\"成功铸造 100 万个代币 (\", mint.publicKey, \")\");\r\n    })\r\n    .catch((err) => {\r\n        console.error(\"铸造代币时出错:\", err);\r\n    });\r\n\r\n```\r\n\r\n为什么要添加小数？\r\n\r\n添加小数是为了使代币可以分割到最小部分，以便简化交易并提高流动性。例如，在上述示例中，我们表示我们的代币的小数是 8，这意味着我们的一个代币可以分割为第 8 位小数。因此，在铸造 100 万个代币时，我们必须在数字 1000000 前添加八个零（1000000\\_00000000）。\r\n\r\n#### **整合所有内容** \r\n\r\n好了，完整的 **mint.ts** 应该看起来像这样：\r\n\r\nmint.ts\r\n\r\n```\r\nimport { percentAmount, generateSigner, signerIdentity, createSignerFromKeypair } from '@metaplex-foundation/umi'\r\nimport { TokenStandard, createAndMint, mplTokenMetadata } from '@metaplex-foundation/mpl-token-metadata'\r\nimport { createUmi } from '@metaplex-foundation/umi-bundle-defaults';\r\nimport secret from './guideSecret.json';\r\n\r\nconst umi = createUmi('https://example.solana-devnet.quiknode.pro/000000/'); //将其替换为你的 QuickNode RPC 端点\r\n\r\nconst userWallet = umi.eddsa.createKeypairFromSecretKey(new Uint8Array(secret));\r\nconst userWalletSigner = createSignerFromKeypair(umi, userWallet);\r\n\r\nconst metadata = {\r\n    name: \"最佳代币\",\r\n    symbol: \"BTE\",\r\n    uri: \"IPFS_URL_OF_METADATA\",\r\n};\r\n\r\nconst mint = generateSigner(umi);\r\numi.use(signerIdentity(userWalletSigner));\r\numi.use(mplTokenMetadata())\r\n\r\ncreateAndMint(umi, {\r\n    mint,\r\n    authority: umi.identity,\r\n    name: metadata.name,\r\n    symbol: metadata.symbol,\r\n    uri: metadata.uri,\r\n    sellerFeeBasisPoints: percentAmount(0),\r\n    decimals: 8,\r\n    amount: 1000000_00000000,\r\n    tokenOwner: userWallet.publicKey,\r\n    tokenStandard: TokenStandard.Fungible,\r\n}).sendAndConfirm(umi)\r\n    .then(() => {\r\n        console.log(\"成功铸造 100 万个代币 (\", mint.publicKey, \")\");\r\n    })\r\n    .catch((err) => {\r\n        console.error(\"铸造代币时出错:\", err);\r\n    });\r\n\r\n```\r\n\r\n## 运行你的代码 \r\n\r\n在终端中，输入：\r\n\r\n```\r\nts-node mint.ts\r\n\r\n```\r\n\r\n执行成功后，你应该会看到如下输出：\r\n\r\n![最终结果终端输出](<Base64-Image-Removed>)\r\n\r\n你可以在 [Solana Devnet Explorer](https://explorer.solana.com/?cluster=devnet) 中查看你的代币，也可以在你的 Phantom 钱包中查看：\r\n\r\n![在 Solana devnet explorer 上查看的代币账户](https://img.learnblockchain.cn/2025/02/24/5-cca2848814b972b5202cddf4800e484d.png)![在 Phantom 钱包中显示的代币](https://img.learnblockchain.cn/2025/02/24/phantom-2-26c1e521dd071aad3b864c1d3554d6eb.png)\r\n\r\n> 注意：如果你之前铸造过可替代代币，你可能已将你的代币提交到 Solana 代币程序注册表。该注册表现已不再需要你执行的步骤。你已经在链上上传了元数据，因此你可以继续！\r\n\r\n## 聚会时间！ 🎉 \r\n\r\n恭喜你！你刚刚在 Solana 上使用新的 Metaplex 可替代代币标准和 umi 铸造了自己的代币。享受这一过程——我们期待看到你创建的代币！要了解更多信息，请查看我们其他的 Solana 教程 [这里](https://learnblockchain.cn/quicknode)。\r\n\r\n \r\n\r\n>- 原文链接： [quicknode.com/guides/sol...](https://www.quicknode.com/guides/solana-development/spl-tokens/how-to-create-a-fungible-spl-token-with-the-new-metaplex-token-standard)\r\n>- 登链社区 AI 助手，为大家转译优秀英文文章，如有翻译不通的地方，还请包涵～"},"author":{"user":"https://learnblockchain.cn/people/25306","address":null},"history":null,"timestamp":1740450257,"version":1}