{"content":{"title":"构建你的第一个Solana NFT dApp","body":">  原文链接： https://medium.com/shyft-to/build-your-first-nft-dapp-cd499445ffa6\r\n\r\n\r\n\r\n> 让你轻松地将NFT、代币、市场等整合到你的应用程序中\r\n\r\n![img](https://img.learnblockchain.cn/2022/10/13/1_prnjh5MhXvNDlDOSodum4g.png!/scale/50)\r\n\r\n在本教程中，我们将建立一个简单的dApp，让你可以用Shyft APIs在[Solana](https://learnblockchain.cn/article/4375)区块链上创建一个NFT。在本教程中铸造的NFT在你的钱包里是可用的。\r\n\r\n## 前提条件\r\n\r\n首先，安装Phantom Wallet的浏览器插件，链接如下：\r\n\r\n- [Chrome/Brave浏览器](https://chrome.google.com/webstore/detail/phantom/bfnaelmomeimhlpmgjnjophhpkkoljpa)\r\n- [火狐浏览器](https://addons.mozilla.org/en-US/firefox/addon/phantom-app/)\r\n\r\n安装后，在浏览器中打开*Phantom*，并按照屏幕上的步骤创建一个新钱包。如果你需要帮助，请参考这个[指南](https://news.coincu.com/2433-the-easiest-guide-to-using-phantom-wallet-on-solana/)。现在，你有一个公钥-私钥对，作为区块链上的一个身份。\r\n\r\n## 安装nodeJs和npx(node包执行器)\r\n\r\n**Mac的步骤：**\r\n\r\n```\r\n//type the following commands on your terminal:\r\n$ brew update\r\n$ brew install node//once completed, check if installed properly:\r\n$ node -v\r\nv18.0.0    // if installed properly, should output the node installed version// install npx:\r\n$ npm i -g npx\r\n```\r\n\r\n## 设置\r\n\r\n现在我们已经准备好创建我们的第一个NFT铸币应用。我们将用 ReactJs 建立一个简单的前端，可以创建一个NFT。我们将使用*VS Code*来完成这个教程。\r\n\r\n**认证：获取 Shyft API 密钥**\r\n\r\n`x-api-key`是一个认证参数，它让你能够访问 SHYFT API，可以从[SHYFT网站](https://shyft.to/)获取API密钥。只需用你的电子邮件ID在[这里](https://shyft.to/get-api-key)注册，就可以免费得到。\r\n\r\n**设置 react 项目**\r\n\r\n```\r\n$ npx create-react-app my-first-nft-dapp\r\n```\r\n\r\n为dapp创建模板代码，文件结构看起来如下：\r\n\r\n![img](https://img.learnblockchain.cn/2022/10/13/1_QmSGc3w6oPwH7sQGXe6Q_Q.png)\r\n\r\n## 让我们深入了解代码\r\n\r\n创建一个新的表单来接受所有的细节（参数），我们需要创建一个新的NFT。已经为这个表单创建了一个新的组件，你也可以直接把这段代码放在App.js下：\r\n\r\n```javascript\r\nconst [file, setfile] = useState();\r\n  const [network, setnetwork] = useState(\"devnet\");\r\n  const [publicKey, setPublicKey] = useState(''); //your wallet's public key\r\n  const [name, setName] = useState();\r\n  const [symbol, setSymbol] = useState();\r\n  const [desc, setDesc] = useState();\r\n  const [attr, setAttr] = useState();\r\n  const [extUrl, setExtUrl] = useState();\r\n  const [maxSup, setMaxSup] = useState(0);\r\n  const [roy, setRoy] = useState(1);\r\n  \r\n  const [status, setStatus] = useState(\"Awaiting Upload\");\r\n  const [dispResponse, setDispResp] = useState(\"\");\r\n  \r\n  return (\r\n    <div className=\"App\">\r\n      <form>\r\n        <label htmlFor=\"file\">Select File</label>\r\n        <input name=\"file\" type=\"file\" onChange={(e) => {\r\n          setfile(e.target.files[0]));\r\n        }} />\r\n        <br /><label htmlFor=\"network\">\r\n          Network <span>(network: string)</span>\r\n        </label>\r\n        <select name=\"network\" onChange={(e) => { setnetwork(e.target.value) }}>\r\n          <option value=\"devnet\">Devnet</option>\r\n          <option value=\"testnet\">Testnet</option>\r\n          <option value=\"mainnet-beta\">Mainnet Beta</option>\r\n        </select>\r\n        <br /><label>Public Key (wallet:string)</label>\r\n        <input type=\"text\" value={publicKey} onChange={(e) => setPublicKey(e.target.value)} required />\r\n        <br /><label htmlFor=\"name\">Name (name:string)</label>\r\n        <input type=\"text\" name=\"name\" value={name} onChange={(e) => setName(e.target.value)} required />\r\n        <br /><label htmlFor=\"symbol\">Symbol (symbol:string)</label>\r\n        <input type=\"text\" name=\"symbol\" value={symbol} onChange={(e) => setSymbol(e.target.value)} required />\r\n        <br /><label htmlFor=\"desc\">Description (description:string)</label>\r\n        <textarea name=\"desc\" value={desc} onChange={(e) => setDesc(e.target.value)} required></textarea>\r\n        <br /><label htmlFor=\"attributes\">Attributes (attributes:string)</label>\r\n        <textarea name=\"attributes\" value={attr} onChange={(e) => setAttr(e.target.value)} required></textarea>\r\n        <br /><label htmlFor=\"external_url\">External Url (external_url:string)</label>\r\n        <input type=\"text\" name=\"external_url\" value={extUrl} onChange={(e) => setExtUrl(e.target.value)} />\r\n        <br /><label htmlFor=\"max_supply\">Max Supply (max_supply:number)</label>\r\n        <input type=\"number\" name=\"max_supply\" value={maxSup} onChange={(e) => { setMaxSup(e.target.value) }} required />\r\n        <br /><label htmlFor=\"royalty\">Royalty (royalty:number)</label>\r\n        <input type=\"number\" name=\"royalty\" value={roy} onChange={(e) => { setRoy(e.target.value) }} required />\r\n        <br /><button type=\"submit\" onClick={mintNow}>\r\n          Submit\r\n        </button>\r\n      </form><textarea\r\n        className=\"form-control\"\r\n        name=\"\"\r\n        value={JSON.stringify(dispResponse)}\r\n        id=\"\"\r\n        cols=\"30\"\r\n        rows=\"10\"\r\n      ></textarea>\r\n    </div>\r\n  );\r\n```\r\n\r\n这是一个简单的表单，需要以下参数：\r\n\r\n1. 选择一个你想铸成NFT的图像文件\r\n2. 选择网络（testnet, devnet, mainnet-beta）。\r\n3. 你 phantom 钱包的公钥\r\n4. NFT名称\r\n5. NFT符号\r\n6. NFT描述\r\n7. External_Url，可以链接到任何网站。这将在 phantom 钱包账户中可见，用于导航。\r\n8. 可以铸造一个特定NFT的版本数量。如果你想创建一个独一无二的NFT，可以设置为零。\r\n9. NFT创建者的版税百分比。可以是0-100之间的任何数值。\r\n10. 与NFT相关的属性。这是一个JSON数组字符串，不要忘记像下面的代码片断那样进行Stringify。\r\n\r\n```\r\nlet attrib = [{\"trait_type\": \"speed\", \"value\": 100},\r\n{\"trait_type\": \"aggression\", \"value\": \"crazy\"},\r\n{\"trait_type\": \"energy\", \"value\": \"very high\"}];let paramsToPass = JSON.stringify(attrib);//Here we have created 3 attributes for our NFT, namely:\r\n//1. speed = 100\r\n//2. aggression = \"crazy\"\r\n//3. energy = \"very high\"\r\n```\r\n\r\n## 进行API调用\r\n\r\n一旦有了数据，我们就可以进行API调用了。在本教程中，我们用了`axios`包来进行API调用，但你也可以用任何其他方法，包括JavaScript自己的`fetch`。\r\n\r\n```\r\n\"dependencies\": {\r\n    ...\r\n    \"axios\": \"^0.27.2\"\r\n    ....\r\n  },\r\n```\r\n\r\n`axios`允许你的react应用程序向`Shyft`服务器发出HTTP请求，并创建NFT。\r\n\r\n现在，让我们创建一个函数，用我们在本文中创建的表单收集的数据进行API调用。\r\n\r\n\r\n```\r\nconst mintNow = (e) => {\r\ne.preventDefault();\r\nsetStatus(\"Loading\");\r\nlet formData = new FormData();\r\nformData.append(\"network\", network);\r\nformData.append(\"wallet\", publicKey);\r\nformData.append(\"name\", name);\r\nformData.append(\"symbol\", symbol);\r\nformData.append(\"description\", desc);\r\nformData.append(\"attributes\", JSON.stringify(attr));\r\nformData.append(\"external_url\", extUrl);\r\nformData.append(\"max_supply\", maxSup);\r\nformData.append(\"royalty\", roy);\r\nformData.append(\"file\", file);axios({\r\n // Endpoint to send files\r\n url: \"https://api.shyft.to/sol/v1/nft/create_detach\",\r\n method: \"POST\",\r\n headers: {\r\n  \"Content-Type\": \"multipart/form-data\",\r\n  \"x-api-key\": \"Your-api-key\",\r\n  Accept: \"*/*\",\r\n  \"Access-Control-Allow-Origin\": \"*\",\r\n },// Attaching the form data\r\n data: formData,\r\n})\r\n // Handle the response from backend here\r\n .then(async (res) => {\r\n  console.log(res);\r\n  if(res.data.success === true)\r\n  {\r\n   setStatus(\"success: Transaction Created. Signing Transactions. Please Wait.\");\r\n   const transaction = res.data.result.encoded_transaction; //encoded transaction\r\n   setSaveMinted(res.data.result.mint);\r\n   const ret_result = await signAndConfirmTransactionFe(network,transaction,callback); //signing the encoded transaction\r\n       console.log(ret_result);\r\n   setDispResp(res.data);\r\n   \r\n  }\r\n })// Catch errors if any\r\n .catch((err) => {\r\n  console.warn(err);\r\n  setStatus(\"success: false\");\r\n });}\r\n```\r\n\r\nAPI端点：\r\n\r\n```\r\nPOST https://api.shyft.to/sol/v1/nft/create_detach\r\n```\r\n\r\n我们需要在这个API调用的标题部分包括从 SHYFT 网站获得的[API key](https://shyft.to/get-api-key)。关于这个API和使用的参数的详细信息，请阅读API[文档](https://docs.shyft.to/start-hacking/nft#create)链接到完整的`App.js`文件，在进行上述修改后：\r\n\r\nhttps://github.com/Shyft-to/example-projects/blob/main/example-projects-on-NFT/my-first-nft-dapp-create-nft/src/create_nft.js\r\n\r\n## 签署编码的交易\r\n\r\n现在你已经准备好铸造你的第一个NFT了。请注意，这个API**不需要私钥，**而是用公钥签名创建操作所需的交易**。\r\n\r\n转到终端，启动react app，运行:\r\n\r\n```\r\nnpm run start\r\n```\r\n\r\n你的基本应用程序将看起来像这样：\r\n\r\n![img](https://img.learnblockchain.cn/2022/10/13/1_vRvi1qag7v2z7x1mV93K2w.png)\r\n\r\n\\-*基本的NFT dApp*-\r\n\r\n现在你要做的就是在输入框中输入信息并点击提交按钮。\r\n\r\n当你点击提交时，请求被发送到 Shyft 服务器，你已经成功创建了一个新的NFT交易。现在，你需要做的就是用你的钱包给这个交易签名，然后就可以了！这个新的NFT将被添加到你的钱包。\r\n\r\n返回:\r\n\r\n```\r\n{\r\n  \"success\": true,\r\n  \"message\": \"NFT create request generated successfully\",\r\n  \"result\": {\r\n    \"encoded_transaction\": \"AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC1sWheB4cuv/Q2+0jo6buiunKcPGhvFP0MkPZKAqEaKsIdRPXcFkiLuiIa/pyUVrKUczLjr1Hs9QG5Y5iDBksMAgAFChjKn1HFRx7tvpclOYMfcFNqxsZNjaEl7aHTEnXr/g8Vvb/IjgDJjQVC1f/ryBRhD4ahT7Q1HBYM6DpJ0WUTWn4RAfrhLTPnJMqAy/QLpEQpZFP+r95vFI1kPxccS3tj+xQxDOha6hFwbvC1+bl1KPipyhIpii09w/bWfkdcNqLoa6h4uighckbsoxU8cDYR7skYHVwb90kZsVS8bPEVzOEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIyXJY9OJInxuz0QKRSODYMLWhOZ2v8QhASOe9jb6fhZC3BlsePRfEU4nVJ/awTDzVi4bHMaoP21SbbRvAP4KUYGp9UXGSxcUSGMyUw9SvF/WNruCJuh/UTj29mKAAAAAAbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpOUrCSjEODK7K+JCY0PfeMucSKkHyBEhkHVSHEcaSI5MGBQIAATQAAAAAYE0WAAAAAABSAAAAAAAAAAbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpCQIBCEMAABjKn1HFRx7tvpclOYMfcFNqxsZNjaEl7aHTEnXr/g8VARjKn1HFRx7tvpclOYMfcFNqxsZNjaEl7aHTEnXr/g8VBgcAAwABBQkIAAkDAQMACQcBAAAAAAAAAAcHAgEAAAAFCJ0BEAkAAABmaXNoIGV5ZXMDAAAARllFWAAAAGh0dHBzOi8vbmZ0c3RvcmFnZS5saW5rL2lwZnMvYmFma3JlaWVmamF6cnhpcGpwYmFwNjZkejQ3ZW80ZWdkdzdscHUzY2tvZW43NXZhdGhvaTRtaWszM2n0AQEBAAAAGMqfUcVHHu2+lyU5gx9wU2rGxk2NoSXtodMSdev+DxUBZAAAAQcJBAEAAAACCQUIChEBAQAAAAAAAAA=\",\r\n    \"mint\": \"Pmhfos1S2sipnz3UPgAjHQeeYzFn1pbEqpdissS2fZbj\"\r\n  }\r\n}\r\n```\r\n\r\n一旦我们收到`encoded_transaction`的返回，最后一步就是签名交易。我们可以通过两种方式签名`encoded_transaction`，要么从前端用钱包，要么用钱包的`private key`从后端。\r\n\r\n\r\n你可以在[这里,](https://docs.shyft.to/tutorials/how-to-sign-transactions-on-solana)阅读更多关于在Solana上签名交易的信息，或者你可以使用我们的在线开发工具来签名这个编码交易，可在以下链接中找到：\r\n\r\nhttps://shyft-insider.vercel.app/\r\n\r\n签名成功后，NFT将被创建并添加到你的钱包里。\r\n\r\n要检查NFT是否在区块链上被创建。\r\n\r\n1. 进入https://explorer.solana.com/ ，选择网络。(*本教程为Devenet*)。\r\n2. 将返回的`mint`值(*代币的链上地址*)粘贴到搜索栏中，应该会得到创建的NFT的详细信息。\r\n3. 在Solana explorer的前一个搜索栏中粘贴返回的`txnId`，可以查看交易的详细信息。\r\n\r\n现在，NFT已经在你的钱包里收到了！！！。进入你的phantom钱包的`Your Collectibles`标签，应该看到新创建的NFT。\r\n\r\n![img](https://img.learnblockchain.cn/2022/10/13/1_LrSOApnpLaw-D7FH5uyfzA.png)\r\n\r\n\\-*新铸造的NFT在你的phantom钱包中可见*-\r\n\r\n![img](https://img.learnblockchain.cn/2022/10/13/1_E7aB8iVZuqB5aG4HnX7pRA.png)\r\n\r\n\\-*在请求中传递的NFT的属性*-\r\n\r\n## 结语\r\n\r\n希望你喜欢这个教程，并对深入研究web3的美妙之处感到兴奋。请继续关注更多此类教程。如果你想获得这个项目的完整代码和漂亮的用户界面，请在这个库中查看/分叉并使用这些代码：\r\n\r\nhttps://github.com/Shyft-to/example-projects/tree/main/example-projects-on-NFT/my-first-nft-dapp-create-nft\r\n\r\n欢迎查看我们的其他教程：[从钱包读取所有NFT](https://docs.shyft.to/tutorials/read-all-nfts-from-a-wallet)和[构建NFT门控DApp](https://docs.shyft.to/tutorials/build-nft-gated-dapp)。\r\n\r\n## 资源\r\n\r\n- [SHYFT API 文档](https://docs.shyft.to/)\r\n- [Shyft网站](https://shyft.to/)\r\n- [获取API密钥](https://shyft.to/get-api-key)\r\n- [GitHub](https://github.com/Shyft-to)\r\n- [Solana交易签名工具](https://shyft-insider.vercel.app/)\r\n- 加入我们的[Discord](https://discord.com/invite/VS5ThWVRMn)\r\n\r\n谢谢你的时间。我们希望你在使用[SHYFT APIs](https://shyft.to/)构建dApps的过程中度过一段美好时光。快乐的黑客。😇"},"author":{"user":"https://learnblockchain.cn/people/3291","address":null},"history":"Qmb2tgHQWtfNr7omCuUsTQphduqYmT2zCXQXiHPrV3EgM3","timestamp":1668137056,"version":1}