{"content":{"title":"如何创建NFT并OpenSea上展示《alchemy How to Develop an NFT Smart Contract》译","body":"翻译：1_bit\r\n原文：[https://docs.alchemy.com/docs/how-to-develop-an-nft-smart-contract-erc721-with-alchemy](https://docs.alchemy.com/docs/how-to-develop-an-nft-smart-contract-erc721-with-alchemy)\r\n\r\n注：英文不好各位多多担待\r\n\r\n# 1.如何使用 Alchemy 开发一个 NFT 智能合约\r\n在你第一次使用 solidity 时开发一个智能合约部署在区块链上，你可能会觉得有点难。有关更多的合约安全、节省 gas 这些你都会在开发时经历。\r\n\r\n幸运的是，在过去的几个月中，开发人员构建了许多智能合约开发的工具使得开发更加简单。\r\n\r\n这些工具例如 [OpenZeppelin](https://learnblockchain.cn/article/727) Wizard （智能合约向导），可以通过鼠标点击后生成一个安全、可靠的智能合约，通过智能合约向导与 Alchemy 和一些 web3 的开发者工具将会使合约开发变得前所未有的简单、快速和可靠。\r\n\r\n在本教程中，你将会学习如何使用 Alchemy 、OpenZeppelin Wizard （智能合约向导）、Remix 、goerli网络开发和部署 ERC721 的智能合约。\r\n\r\n准确的说，你将会学习以下内容：\r\n\r\n - 如何使用 OpenZeppelin 和 Remix 编写和修改智能合约\r\n - 获取免费的 Goerli ETH\r\n   [https://goerlifaucet.com/](https://goerlifaucet.com/)\r\n - 在 Goerli 测试网络上部署便宜的 gas 合约\r\n - 在 FileBase 上对 NFT token 的元数据进行托管\r\n - 铸造 NFT 以及在 OpenSea 进行查看\r\n\r\n在 youtube 上有对应的视频教程：[https://youtu.be/veBu03A6ptw](https://youtu.be/veBu03A6ptw)\r\n\r\n首先我们从创建智能合约开始。\r\n\r\n注：下面标题序号为了读者看的更清楚所以是自己所标注的\r\n\r\n## 1.2 使用 OpenZeppelin 开发 ERC721 标准的智能合约\r\n在之前说过，本教程中，你将会使用 OpenZeppelin 去此案一个只能合约，这么做有两个重要的原因：\r\n\r\n - 将会使你的合约安全\r\n - 他将会使你的合约符合标准（遵循标准意思是自动化了不需要自己写）\r\n\r\n当你编写一个智能合约时，安全是很关键的，有很多智能合约由于安全性太差，导致数亿美元被恶意盗窃的例子。\r\n\r\n你也不想在你合约部署在区块链网络上后就会被窃取吧？\r\n\r\nOpenZeppelin 就是由此而生，是最大的智能合约标准维护者之一，允许开发人员使用已经被 OpenZeppelin 进行代码审计后的可靠合约代码。\r\n\r\n接下来你需要做的第一件事就是打开这个链接 [https://docs.openzeppelin.com/contracts/4.x/wizard](https://docs.openzeppelin.com/contracts/4.x/wizard) 去创建安全的合约代码。\r\n\r\n当你进入页面后，你将会看到以下的编辑器：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/b15018704eb447569d8626c8b6dbf778.png)\r\n点击在左上角（往右边数第二个）的 ERC721 按钮，选择你要使用的 ERC 标准：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/636eae7348ff442f8634bd0ba40c8b1f.png)\r\n现在，你已经选择你合约的标准，在左侧的菜单中，你可以看到一些选项。\r\n\r\n让我们从选择 token 的名称和符号开始。点击在文本框中的  “MyToken”  并且给他一个名称，对应的 Symbol 类型的文本框也可以改成你想要的名字，不过在 Base URI 文本框中我们可以留空，这个可以给用户进行传递，因为 IPFS 的元数据我们将会存储在 OpenSea 中。\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/344f36c38bbd4af68b8db82580782d43.png)\r\n\r\n## 1.3 选择 NFT token 的功能\r\n\r\n现在需要你去选择一些你想要添加到合约中的功能（剩下的一句没翻译，感觉啰嗦了）：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/96bb37794b1f4f14b5477031df90a71c.png)\r\n在本节中，你需要集成以下所列出的功能：\r\n\r\n - **Mintable** （铸币）你将会创建一个 mint 方法并且只有特殊账户可使用（一般是 owner）\r\n - **Autoincrement IDs**（ tokenid 自动加1）这个功能将会自动的为你的 NFT 的 ID 自动分配增量 ID\r\n - **Enumerable**（枚举）能够访问链上的 token 枚举 以及 totalSupply 之类的功能，像 ERC721 的 URI 默认情况下是不存在的，需要将元数据和对应的图片进行关联。（其实这里我不是很清楚啥意思）\r\n - **URI Storage** 存储一个 URI 与 NFT 关联（这个需要我们传入的）\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/cee95fb8ed9f41f2905be06de36462ca.png)\r\n\r\n在当前教程中，你应该不想创建一个 NFT 还有一些增发、销毁、暂停、投票之类需要“经济学”支持的NFT，那么就不要勾选 **Burnable**、**Pausable**、**Votes**：\r\n\r\n - Burnable -  销毁 token\r\n - Pausable -  token 转移、销售等\r\n - Votes -投票类\r\n\r\n如果你想去学习更多看这里：[https://docs.openzeppelin.com/contracts/4.x/api/token/erc721](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721)\r\n\r\n现在你已经有了需要的功能，OpenZeppelin 将会填充剩下的合约代码，此时你应该在合约向导中看到跟下面差不多的代码：\r\n\r\n```javascript\r\n// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.4;\r\n\r\nimport \"@openzeppelin/contracts/token/ERC721/ERC721.sol\";\r\nimport \"@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol\";\r\nimport \"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol\";\r\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\r\n\r\ncontract Alchemy is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {\r\n    constructor() ERC721(\"Alchemy\", \"ALC\") {}\r\n\r\n    function safeMint(address to, uint256 tokenId, string memory uri)\r\n        public\r\n        onlyOwner\r\n    {\r\n        _safeMint(to, tokenId);\r\n        _setTokenURI(tokenId, uri);\r\n    }\r\n\r\n    // The following functions are overrides required by Solidity.\r\n\r\n    function _beforeTokenTransfer(address from, address to, uint256 tokenId)\r\n        internal\r\n        override(ERC721, ERC721Enumerable)\r\n    {\r\n        super._beforeTokenTransfer(from, to, tokenId);\r\n    }\r\n\r\n    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {\r\n        super._burn(tokenId);\r\n    }\r\n\r\n    function tokenURI(uint256 tokenId)\r\n        public\r\n        view\r\n        override(ERC721, ERC721URIStorage)\r\n        returns (string memory)\r\n    {\r\n        return super.tokenURI(tokenId);\r\n    }\r\n\r\n    function supportsInterface(bytes4 interfaceId)\r\n        public\r\n        view\r\n        override(ERC721, ERC721Enumerable)\r\n        returns (bool)\r\n    {\r\n        return super.supportsInterface(interfaceId);\r\n    }\r\n}\r\n```\r\n\r\n是时候复制我们的代码去 Remix 上修改和部署了。\r\n\r\n## 1.4 在 REMIX 上修改和部署你的 ERC721 合约\r\n现在你已经有了一个 ERC721 的智能合约，现在开始让我们去修改和部署它到 Goerli 测试网络上。你将会使用 Remix IDE，它是一个为 solidity 设计的免费的智能合约 web 端的开发环境。\r\n\r\n首先，你可能注意到了，在 OpenZeppelin Wizard 编辑器的顶部，有一个 “Open in Remix” 的按钮：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/3728ec10cd3541288d41230d362ab024.png)\r\n点击按钮后将会在浏览器中打开一个新的页面。\r\n\r\n## 1.5 使用 Remix 修改你的NFT 智能合约\r\n从合约代码的头部开始， “SPDX-License-Identifier”  是你的开源标准协议，在 web3 应用中开源是可以保持项目的可信度的，是需要去做的。\r\n\r\n```javascript\r\n// SPDX-License-Identifier: MIT\r\n```\r\n\r\n接着是 pragma，这是用于指定当前智能合约的编译版本，如果你使用 ^ 符号就表示当前合约代码使用在 0.8.0 到 0.8.9 之间适用。\r\n\r\n```javascript\r\npragma solidity ^0.8.4;\r\n```\r\n\r\n然后我们导入库并且初始化（还没到）我们的智能合约：\r\n\r\n```javascript\r\nimport \"@openzeppelin/contracts/token/ERC721/ERC721.sol\";\r\nimport \"@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol\";\r\nimport \"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol\";\r\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\r\nimport \"@openzeppelin/contracts/utils/Counters.sol\";\r\n```\r\n\r\n接着开始初始化合约，继承这些导入的库标准：\r\n\r\n```javascript\r\n contract Alchemy is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {...}\r\n```\r\n\r\n你可以注意到，这个 safemint 方法有一个  “only owner”  的修饰，这个修饰只允许当前合约的所有者调用这个方法去铸造 NFT，如果你想所有人都可以使用 mint 方法，那么\r\n你可以删除  Mint 方法的 onlyOwner 修饰：\r\n\r\n```javascript\r\nfunction safeMint(address to, string memory uri) public {\r\n    uint256 tokenId = _tokenIdCounter.current();\r\n    _tokenIdCounter.increment();\r\n    _safeMint(to, tokenId);\r\n    _setTokenURI(tokenId, uri);\r\n}\r\n```\r\n\r\n你也可以去删除在合约引入说明中的 Ownable：\r\n\r\n```javascript\r\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\r\n```\r\n现在每个人都可以去使用 mint 创建 NFT 了，但你需要避免用户 mint 过多的 NFT，那么就需要制定一个 mint NFT 的最大上限。\r\n\r\n比如说你想要用户创造上限为 10000 的 NFT，那么新建一个 uint256 的变量，这个变量名叫做 MAX_SUPPLY，给与值为 10000：\r\n\r\n\r\n```javascript\r\nCounters.Counter private _tokenIdCounter;\r\n    uint256 MAX_SUPPLY = 100000;\r\n\r\n    constructor() ERC721(\"Alchemy\", \"ALCH\") {}\r\n```\r\n\r\n接着，让我们进入 safeMint  方法中增加 require 判断当前对应的 NFT 数量：\r\n\r\n```javascript\r\nrequire(_tokenIdCounter.current() <= MAX_SUPPLY, \"I'm sorry we reached the cap\");\r\n```\r\n\r\n现在，你一讲限制了所提供最大的 NFT 数，那么此时就编译智能合约在 Goerli 测试网上部署吧。你需要创建一个免费的 [Alchemy](http://Alchemy.com) 账户（主要是提供了RPC）。\r\n\r\n\r\n## 1.6 创建免费 Alchemy 账户\r\n\r\n首先，让我们去 [alchemy.com](https://alchemy.com) 导航上点击 Login 创建一个新的账户：\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/8f1f8c98d4c14038abdae0fa0578e4b0.png)\r\n选择 ethereum ：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/01dafc0021a443d9938b1ac8f58cfb70.png)\r\n给你的应用和你的团队命名，选择 goerli 网络，最后点击“create app”创建项目：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/dd8eab1feaaf4eb6adf5a28f76a79132.png)\r\n当你完成上面的流程后，我们将会跳转到控制面板，点击你刚刚命名的应用进入，此时，点击在右上角的  “VIEW KEY”  按钮可以查看 APIKEY 和 HTTPURL 等信息：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/23b470ff9bfc44c288f8bcce4f8933c4.png)\r\n\r\n下一步，你需要去添加 Alchemy 的 Goerli 的 RPC Provider 到 Metamask ，如果你没有安装 metamask，请确保安装，若没有 wallet 请先按照以下教程添加一个 wallet 到你的浏览器中，点击“add network”：\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/162350fc59e646d9bfd9080a666144d1.png)\r\n你将会跳转到以下页面，你需要填写 goerli 网络和 RPC URL 信息：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/231a15638a7746e2a063525be85885da.png)\r\n添加以下信息到表单中：\r\n\r\n - **Network name**: Alchemy Goerli 网络名\r\n - **New RPC URL**: the HTTP URL of the Goerli Alchemy Application 你刚刚在 alchemy 上的 http 的 rpc url\r\n - **Chain ID**: 5\r\n - **Currency Symbol**: GoerliETH 网络标识\r\n - **Block Explorer**: https://goerli.etherscan.io\r\n\r\n非常棒，你刚刚已经把 alchemy 的 goerli 网络添加到了 metamask 。\r\n\r\n现在就准备开始在 goerli 中部署我们的智能合约吧，但是我们需要 get some goerli test eth（文字限定所以就不翻译了，此处获取 goerli test eth 可以查找对应的 faucet，such as goerlifaucet.com）。\r\n\r\n\r\n## 1.7 编译和部署 NFT 智能合约在 goerli 测试网络上\r\n返回 remix，让我们点击网页左侧菜单中的蓝色 compiler 按钮进行编译：\r\n \r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/8e89031b614446519b79240966990335.png)\r\n此时点击 部署按钮进入到 “Deploy and Run Transactions\" 菜单中，点击 环境 Environment 在下拉菜单中选择 “injected Web3”。\r\n\r\n确保 metamask wallet 已经连接了 goerli network，在 Contract 的下拉菜单中 选择 NFT 智能合约（你要编译的）没然后点击 Deploy部署（一定要先编译）：\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/4f29d462f1cf4c9cbbeb3c13f18ca5d5.png)\r\n此时 metamask 将会弹出一个窗口，点击 sign，并且继续支付 gas 费用。\r\n\r\n如果一切工作都很顺利，那么在 10秒之后，你可以看到这个合约列表下将会出现已经部署的合约：\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/16e1a044231d4ea693598257110cfddf.png)\r\n\r\n## 1.8 什么是 NFT 元数据\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/f57b7f9c400e4323aa3cd924fdb36531.png)\r\n为了让合约返回一个 OpenSea 的元数据，我们需要设置一个 URI，ERC721 的 tokenUri 方法会返回一个 HTTP 或 IPFS 的URL，例如 ipfs://bafkreig4rdq3nvyg2yra5x363gdo4xtbcfjlhshw63we7vtlldyyvwagbq ，查询时，这个 URL 将会返回一个 JSON 数据，其中包括了你的 token 的元数据。\r\n\r\n你可以查看更多官方所提供的元数据标准 [https://docs.opensea.io/docs/metadata-standards](https://docs.opensea.io/docs/metadata-standards)。\r\n\r\n## 1.8 怎么样去格式化你的 NFT 元数据\r\n根据 OpenSea 的文档，一个 NFT 的元数据应该是存储在 json 文件中，这个文件接口如下：\r\n\r\n```javascript\r\n{ \r\n  \"description\": \"YOUR DESCRIPTION\",\r\n  \"external_url\": \"YOUR URL\",\r\n  \"image\": \"IMAGE URL\",\r\n  \"name\": \"TITLE\", \r\n  \"attributes\": [\r\n    {\r\n      \"trait_type\": \"Base\", \r\n      \"value\": \"Starfish\"\r\n    }, \r\n    {\r\n      \"trait_type\": \"Eyes\", \r\n      \"value\": \"Big\"\r\n    }, \r\n    {\r\n      \"trait_type\": \"Mouth\", \r\n      \"value\": \"Surprised\"\r\n    }, \r\n    {\r\n      \"trait_type\": \"Level\", \r\n      \"value\": 5\r\n    }, \r\n    {\r\n      \"trait_type\": \"Stamina\", \r\n      \"value\": 1.4\r\n    }, \r\n    {\r\n      \"trait_type\": \"Personality\", \r\n      \"value\": \"Sad\"\r\n    }, \r\n    {\r\n      \"display_type\": \"boost_number\", \r\n      \"trait_type\": \"Aqua Power\", \r\n      \"value\": 40\r\n    }, \r\n    {\r\n      \"display_type\": \"boost_percentage\", \r\n      \"trait_type\": \"Stamina Increase\", \r\n      \"value\": 10\r\n    }, \r\n    {\r\n      \"display_type\": \"number\", \r\n      \"trait_type\": \"Generation\", \r\n      \"value\": 2\r\n    }]\r\n  }\r\n```\r\n\r\n一下是一个有关这些属性的简短说明：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/11a306140d0a4127a1fdf2cd595ae1ca.png)\r\n\r\n|属性|说明  |\r\n|--|--|\r\n|image | 这是图片的 URL， 可以是任何的图像，可以是 IPFS 上存储的 URL 路径，建议使用 350+350 的大小|\r\n|image_data | SVG图像，如果你想使用动态图像（不建议），只有在你数据中不包括 image 时可以使用|\r\n|external_url | 这个 URL 是显示在 OpenSea 资源下凡的图片URL，你可以在 OpenSea 之外的站点上查看|\r\n|description | 这个NFT 的描述|\r\n|name | 这个NFT 的名称|\r\n|attributes | 属性，将会显示在 OpenSea 页面上|\r\n|background_color | OpenSea 上项目的背景色，必须是十六进制不需要添加“#”之类的前置|\r\n|animation_url | 多媒体 url|\r\n\r\n\r\n了解了一些元数据内容后，学习如何存储元数据在 IPFS上。\r\n\r\n## 1.9 在 IPFS 存储元数据\r\n首先，导航去 [fillebase](https://filebase.com/) 创建一个账户。\r\n\r\n登录后，点击左侧菜单的 bucket 按钮 创建一个新的 bucket：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/2b9eb7a89b2b4130a7802591d480b533.png)\r\n进入 bucket，点击上传按钮，上传你想要作为 NFT 使用的图片。\r\n\r\n上传你完毕后，复制 IPFS GateWay 网关的 URL：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/c595de7cb5e54a77ad84dc7404a7e4f9.png)\r\n使用一个文本编辑器，复制下面的 json code：\r\n\r\n```javascript\r\n{ \r\n  \"description\": \"This NFT proves I've created and deployed my first ERC20 smart contract on Goerli with Alchemy Road to Web3\",\r\n  \"external_url\": \"Alchemy.com/?a=roadtoweb3weekone\",\r\n  \"image\": \"https://ipfs.filebase.io/ipfs/bafybeihyvhgbcov2nmvbnveunoodokme5eb42uekrqowxdennt2qyeculm\",\r\n  \"name\": \"A cool NFT\", \r\n  \"attributes\": [\r\n    {\r\n      \"trait_type\": \"Base\", \r\n      \"value\": \"Starfish\"\r\n    }, \r\n    {\r\n      \"trait_type\": \"Eyes\", \r\n      \"value\": \"Big\"\r\n    }, \r\n    {\r\n      \"trait_type\": \"Mouth\", \r\n      \"value\": \"Surprised\"\r\n    }, \r\n    {\r\n      \"trait_type\": \"Level\", \r\n      \"value\": 5\r\n    }, \r\n    {\r\n      \"trait_type\": \"Stamina\", \r\n      \"value\": 1.4\r\n    }, \r\n    {\r\n      \"trait_type\": \"Personality\", \r\n      \"value\": \"Sad\"\r\n    }, \r\n    {\r\n      \"display_type\": \"boost_number\", \r\n      \"trait_type\": \"Aqua Power\", \r\n      \"value\": 40\r\n    }, \r\n    {\r\n      \"display_type\": \"boost_percentage\", \r\n      \"trait_type\": \"Stamina Increase\", \r\n      \"value\": 10\r\n    }, \r\n    {\r\n      \"display_type\": \"number\", \r\n      \"trait_type\": \"Generation\", \r\n      \"value\": 2\r\n    }]\r\n  }\r\n```\r\n\r\n随后保存文件为  \"metadata.json\"，返回到 bucket 中上传 metadata.json 文件：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/204ff6a722e542a5b3f4834596495492.png)\r\n最后，点击 CID 并且进行赋值，你将需要这个在铸造 NFT 时 使用到：\r\n\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/3efe92c9858a4a408b59f843f4fb225a.png)\r\n\r\n## 1.10 铸造你的 NFT\r\n返回到 remix 中，在你部署的合约之下，找到对应的方法列表：\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/09dd63099c42423784dac5adeb2d65d0.png)\r\n橙色方法时写入区块链的方法，蓝色方法时读取区块链内容的方法。\r\n\r\n点击 safeMint 方法的下拉选项，复制你的地址，并且添加对应的 URI字段，这个字段格式如下：\r\n\r\n```javascript\r\nipfs://\\<your\\_metadata\\_cid>\r\n```\r\n\r\n单击交易后弹出一个 metamask 的窗口，支付 gas。\r\n\r\n点击 sign 后将会铸造你的第一个 nft。\r\n\r\n接着转移到 OpenSea 检查你的元数据是否被正确读取到。\r\n\r\n## 1.11 在 OpenSea 上展现你的 NFT\r\n\r\n进入 OpenSEA 的测试网络 [https://testnets.opensea.io/zh-CN](https://testnets.opensea.io/zh-CN) 并使用钱包登录，此时点击你的头像，你可以看到一个新铸造的 NFT，如果你的图片没有显示，点击 refresh metadata 按钮进行刷新：\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/38a34959884a40e5a381276488be68d0.png)\r\n有时候 OpenSea 需要一段时间 6h 后显示这个 NFT：\r\n![在这里插入图片描述](https://img-blog.csdnimg.cn/8f9fe7f56cf543088ebbe2a44bec3f28.png)"},"author":{"user":"https://learnblockchain.cn/people/11898","address":null},"history":"QmTGAJbTYhfNLEEWt7ZwirhUtXJoJnPb1w4RNHGbW7ffSA","timestamp":1667459313,"version":1}