{"content":{"title":"用Hardhat在zkEVM上部署一个动态NFT","body":"> * 原文链接：https://codingwithmanny.medium.com/deploy-an-animated-nft-to-zkevm-with-hardhat-1c580e4a4465 \r\n> * 译文出自：[登链翻译计划](https://github.com/lbc-team/Pioneer)\r\n> * 译者：[翻译小组](https://learnblockchain.cn/people/412)  校对：[Tiny 熊](https://learnblockchain.cn/people/15)\r\n> * 本文永久链接：[learnblockchain.cn/article…](https://learnblockchain.cn/article/5579)\r\n\r\n\r\n\r\n\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/56316.png)\r\n\r\n使用Hardhat在zkEVM Testnet上部署一个Onchain的SVG动画NFT\r\n\r\n## 什么是zkEVM\r\n\r\n简而言之，[Polygon zkEVM](https://polygon.technology/polygon-zkevm)是一个二级扩容解决方案，旨在实现[EVM-equivalence（等效）](https://vitalik.ca/general/2022/08/04/zkevm.html)，并允许你使用现有的Solidity代码更便宜、更快、更安全地部署合约到它。\r\n\r\n想进一步深入了解它吗？请看我的另一篇文章：[如何将ERC20代币合约部署到zkEVM Testnet](https://learnblockchain.cn/article/5349)。\r\n\r\n## 准备网络与Token\r\n\r\n建议使用[Polygon zkEVM Bridge 站点](https://public.zkevm-test.net/)，将网络添加到我们的钱包，并将 Goerli Token 桥接到 zkEVM Testnet：\r\n\r\n  ![img](https://img.learnblockchain.cn/2023/03/25/84527.png)\r\n\r\n>  https://public.zkevm-test.net/\r\n\r\n## 环境要求\r\n\r\n在我们开始之前，请确保你的电脑上已经安装了以下依赖：NVM或Node `v18.15.0`，并且已经配置了钱包，并且goerli 测试网代币被桥接到了zkEVM Testnet。如果你想了解如何做到这一点，请查看我的另一篇文章[如何将ERC20合约部署到zkEVM Testnet](https://learnblockchain.cn/article/5349)。\r\n\r\n\r\n\r\n## 用Hardhat部署合约\r\n\r\n让我们建立一个ERC721 NFT，并通过Hardhat将其部署到zkEVM。\r\n\r\n为了确保我们从新开始，我们将利用Hardhat模板生成一个基本的TypeScript项目。\r\n\r\n```\r\n# Create our project folder\r\nmkdir zkevm-erc721-hardhat;\r\ncd zkevm-erc721-hardhat;\r\n\r\nnpx hardhat;\r\n\r\n# Expected Output:\r\n# Ok to proceed? (y) y\r\n# 888    888                      888 888               888\r\n# 888    888                      888 888               888\r\n# 888    888                      888 888               888\r\n# 8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888\r\n# 888    888     \"88b 888P\"  d88\" 888 888 \"88b     \"88b 888\r\n# 888    888 .d888888 888    888  888 888  888 .d888888 888\r\n# 888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.\r\n# 888    888 \"Y888888 888     \"Y88888 888  888 \"Y888888  \"Y888\r\n#\r\n# 👷 Welcome to Hardhat v2.13.0 👷‍\r\n#\r\n# ? What do you want to do? …\r\n#   Create a JavaScript project\r\n# ❯ Create a TypeScript project\r\n#   Create an empty hardhat.config.js\r\n#   Quit\r\n#\r\n# ✔ What do you want to do? · Create a TypeScript project\r\n# ✔ Hardhat project root: · /path/to/zkevm-erc721-hardhat\r\n# ✔ Do you want to add a .gitignore? (Y/n) · y\r\n# ✔ Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)? (Y/n) · y\r\n```\r\n\r\n接下来，让我们安装依赖项:\r\n\r\n```bash\r\nnpme install;\r\nnpm install @openzeppelin/contracts dotenv;\r\n```\r\n\r\n## 创建我们的NFT\r\n\r\n我们这里的NFT，将通过引入一个动画的SVG来使它突出一点。\r\n\r\n首先删除默认生成的模板化合约，并在其目录上创建一个新的合约。\r\n\r\n```\r\nrm contracts/Lock.sol;\r\ntouch contracts/zkEVMNFT.sol;\r\n```\r\n\r\n在该文件中，复制并粘贴以下NFT solidity代码。\r\n\r\n**文件：** `./contracts/zkEVMNFT.sol`。\r\n\r\n```solidity\r\n// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.9;\r\n\r\n// Imports\r\n// ========================================================\r\nimport \"@openzeppelin/contracts/token/ERC721/ERC721.sol\";\r\nimport \"@openzeppelin/contracts/utils/Strings.sol\";\r\nimport \"@openzeppelin/contracts/utils/Base64.sol\";\r\n\r\n// Contract\r\n// ========================================================\r\ncontract ZkEVMNFT is ERC721 {\r\n    // Extending functionality\r\n    using Strings for uint256;\r\n\r\n    /**\r\n     * Main constructor\r\n     */\r\n    constructor() ERC721(\"zkEVMNFT\", \"zkNFT\") {}\r\n\r\n    /**\r\n     * Main minting function\r\n     */\r\n    function safeMint(address to, uint256 tokenId) public {\r\n        _safeMint(to, tokenId);\r\n    }\r\n\r\n    // The following functions are overrides required by Solidity.\r\n    /**\r\n     * @dev See {ERC721}\r\n     */\r\n    function _burn(uint256 tokenId) internal override(ERC721) {\r\n        super._burn(tokenId);\r\n    }\r\n\r\n    /**\r\n     * Public function or burning\r\n     */\r\n    function burn (uint256 tokenId) public {\r\n        _burn(tokenId);\r\n    }\r\n\r\n    /**\r\n     * @dev See {IERC721Metadata-tokenURI}.\r\n     */\r\n    function tokenURI(uint256 tokenId)\r\n        public\r\n        view\r\n        override(ERC721)\r\n        returns (string memory)\r\n    {\r\n        // Validation\r\n        require(_exists(tokenId), \"ERC721Metadata: URI query for nonexistent token\");\r\n        \r\n        // SVG Image\r\n        bytes memory imageSVG = abi.encodePacked(\r\n            \"<svg width=\\\"256\\\" height=\\\"256\\\" viewBox=\\\"0 0 256 256\\\" fill=\\\"none\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\">\",\r\n            \"<style xmlns=\\\"http://www.w3.org/2000/svg\\\">@keyframes rainbow-background { 0% { fill: #ff0000; } 8.333% { fill: #ff8000; } 16.667% { fill: #ffff00; } 25.000% { fill: #80ff00; } 33.333% { fill: #00ff00; } 41.667% { fill: #00ff80; } 50.000% { fill: #00ffff; } 58.333% { fill: #0080ff; } 66.667% { fill: #0000ff; } 75.000% { fill: #8000ff; } 83.333% { fill: #ff00ff; } 91.667% { fill: #ff0080; } 100.00% { fill: #ff0000; }} #background { animation: rainbow-background 5s infinite; } #text { font-family: \\\"Helvetica\\\", \\\"Arial\\\", sans-serif; font-weight: bold; font-size: 72px; }</style>\",\r\n            \"<g clip-path=\\\"url(#clip0_108_2)\\\">\",\r\n            \"<rect id=\\\"background\\\" width=\\\"256\\\" height=\\\"256\\\" fill=\\\"#ff0000\\\"/>\",\r\n            \"<rect x=\\\"28\\\" y=\\\"28\\\" width=\\\"200\\\" height=\\\"200\\\" fill=\\\"white\\\"/>\",\r\n            \"</g>\",\r\n            \"<defs>\",\r\n            \"<clipPath id=\\\"clip0_108_2\\\">\",\r\n            \"<rect width=\\\"256\\\" height=\\\"256\\\" fill=\\\"white\\\"/>\",\r\n            \"</clipPath>\",\r\n            \"</defs>\",\r\n            \"<text xmlns=\\\"http://www.w3.org/2000/svg\\\" id=\\\"text\\\" x=\\\"128\\\" y=\\\"150\\\" fill=\\\"black\\\" style=\\\"width: 256px; display: block; text-align: center;\\\" text-anchor=\\\"middle\\\">\", tokenId.toString(), \"</text>\",\r\n            \"</svg>\"\r\n        );\r\n\r\n        // JSON\r\n        bytes memory dataURI = abi.encodePacked(\r\n            \"{\",\r\n                \"\\\"name\\\": \\\"NUMSVG #\", tokenId.toString(), \"\\\",\",\r\n                \"\\\"image\\\": \\\"data:image/svg+xml;base64,\", Base64.encode(bytes(imageSVG)), \"\\\"\",\r\n            \"}\"\r\n        );\r\n\r\n        // Returned JSON\r\n        return string(\r\n            abi.encodePacked(\r\n                \"data:application/json;base64,\",\r\n                Base64.encode(dataURI)\r\n            )\r\n        );\r\n    }\r\n}\r\n```\r\n\r\n## 配置 Hardhat\r\n\r\n接下来我们将配置Hardhat配置文件，使环境变量能够被加载，配置对网络的支持，并调整优化。\r\n\r\n**文件：** `./hardhat.config.ts`。\r\n\r\n```typescript\r\n// Imports\r\n// ========================================================\r\nimport { HardhatUserConfig } from \"hardhat/config\";\r\nimport \"@nomicfoundation/hardhat-toolbox\";\r\nimport dotenv from \"dotenv\";\r\n\r\n// Config\r\n// ========================================================\r\ndotenv.config();\r\nconst config: HardhatUserConfig = {\r\n  solidity: {\r\n    version: \"0.8.18\",\r\n    settings: {\r\n      optimizer: {\r\n        enabled: true,\r\n        runs: 200,\r\n      }\r\n    }\r\n  },\r\n  networks: {\r\n    mumbai: {\r\n      url: `${process.env.RPC_MUMBAI_URL || ''}`,\r\n      accounts: process.env.WALLET_PRIVATE_KEY\r\n        ? [`0x${process.env.WALLET_PRIVATE_KEY}`]\r\n        : [],\r\n    },\r\n    zkevmTestnet: {\r\n      url: `${process.env.RPC_ZKEVM_URL || ''}`,\r\n      accounts: process.env.WALLET_PRIVATE_KEY\r\n        ? [`0x${process.env.WALLET_PRIVATE_KEY}`]\r\n        : [],\r\n    }\r\n  },\r\n};\r\n\r\n// Exports\r\n// ========================================================\r\nexport default config;\r\n```\r\n\r\n## 为Hardhat添加环境变量\r\n\r\n一旦我们完成了Hardhat的配置，我们将创建一个环境变量文件来存储我们的关键值。\r\n\r\n```\r\ntouch .env;\r\n```\r\n\r\n**注意：**记得保护你的私钥不被人窥视。\r\n\r\n**文件：** `./env`。\r\n\r\n```toml\r\nRPC_MUMBAI_URL=https://rpc.ankr.com/polygon_mumbai\r\nRPC_ZKEVM_URL=https://rpc.public.zkevm-test.net\r\nWALLET_PRIVATE_KEY=<YOUR-WALLET-PRIVATE-KEY>\r\n```\r\n\r\n最后，让我们修改我们的部署脚本以指向正确的合约。\r\n\r\n**文件：** `./scripts/deploy.ts`。\r\n\r\n```typescript\r\n// Imports\r\n// ========================================================\r\nimport { ethers } from \"hardhat\";\r\n\r\n// Main Deployment Script\r\n// ========================================================\r\nasync function main() {\r\n  // Make sure in the contract factory that it mateches the contract name in the solidity file\r\n  // Ex: contract zkEVMNFT\r\n  const zkERC721Contract = await ethers.getContractFactory(\"zkEVMNFT\");\r\n  const contract = await zkERC721Contract.deploy();\r\n\r\n  await contract.deployed();\r\n\r\n  console.log(`zkEVMNFT deployed to ${contract.address}`);\r\n};\r\n\r\n// Init\r\n// ========================================================\r\n// We recommend this pattern to be able to use async/await everywhere\r\n// and properly handle errors.\r\nmain().catch((error) => {\r\n  console.error(error);\r\n  process.exitCode = 1;\r\n});\r\n```\r\n\r\n## 部署出NFT合约\r\n\r\n一旦一切就绪，我们现在可以将我们的NFT合约部署到zkEVM Testnet。\r\n\r\n```bash\r\n# FROM: ./zkevm-erc721-hardhat\r\n\r\nnpx hardhat run scripts/deploy.ts --network zkevmTestnet;\r\n\r\n# Expected Output:\r\n# zkEVMNFT deployed to 0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7\r\n```\r\n\r\n如果我们在资源管理器上进入合约，我们可以看到以下结果：\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/83065.png)\r\n\r\n>  https://explorer.public.zkevm-test.net/address/0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7\r\n\r\n## 在zkEVM区块浏览器上验证我们的NFT合约\r\n\r\n我们可以直接通过Hardhat与合约交互，进行铸币和读取，但我们将使用区块链浏览器来连接和铸币一个新的NFT。\r\n\r\n在我们进入验证过程之前，我们将通过标准输入JSON进行验证，我们需要一个文件来上传。\r\n\r\n如果我们看一下我们的`build-info`文件夹，我们可以找到一个JSON文件，我们只需要得到input部分。复制整个部分并创建一个名为`verify.json`的新文件，并将JSON数据粘贴在那里。\r\n\r\n记住，你需要事先编译合约，用`npx hardhat compile`生成这些文件。\r\n\r\n**文件：** `./artifacts/build-info/your-build-file.json`。\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/32522.png)\r\n\r\n创建 JSON 文件\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/82300.png)\r\n\r\n我们新的标准输入JSON文件为verify.json\r\n\r\n接下来在区块资源管理器上访问该合约，并开始验证过程。\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/28227.png)\r\n\r\nzkEVM Testnet  区块浏览器验证和发布合约\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/93173.png)\r\n\r\nzkEVM Testnet Block Explorer 通过标准输入 JSON 验证\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/19446.png)\r\n\r\nzkEVM Testnet Block Explorer验证Conifgure verify.json\r\n\r\n一旦验证通过，我们就可以在zkEVM Testnet block explorer上看到我们合约的完整代码。\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/77829.png)\r\n\r\n>  https://explorer.public.zkevm-test.net/address/0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7/contracts#address-tabs\r\n\r\n## 在 zkEVM 测试完上铸造 NFT \r\n\r\n现在我们的合约已经被验证了，我们可以通过区块浏览器和我们的钱包与它交互。\r\n\r\n\r\n\r\n为了本教程的目的，我们将铸造一个NFT tokenID为4，以纪念距离zkEVM主网测试版发布的天数--2023年3月27日。\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/68005.png)\r\n\r\n> https://explorer.public.zkevm-test.net/address/0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7/write-contract#address-tabs\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/8921.png)\r\n\r\n在zkEVM Testnet上铸造NFT\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/17187.png)\r\n\r\n> 确认在zkEVM Testnet上成功铸造了一个NFT\r\n\r\n## 查看我们的zkEVM NFT\r\n\r\n我们在zkEVM Testnet上铸造了NFT，现在我们希望能够直观地看到这个链上SVG NFT。\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/40087.png)\r\n\r\nzkEVM Testnet区块浏览器读取NFT\r\n\r\n复制`tokenURI`查询的数据部分，在Chrome浏览器中，将其粘贴到地址栏。\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/71194.png)\r\n\r\n> zkEVM Testnet NFT tokenURI JSON数据\r\n\r\n从JSON中复制`image`值，在Chrome中，将其粘贴在地址栏中。\r\n\r\n![img](https://img.learnblockchain.cn/2023/03/25/10590.png)\r\n\r\n> zkEVM Testnet NFT SVG\r\n\r\n🎉  我们成功地部署了合约，验证了合约，并且我们能够看到铸造的NFT。\r\n\r\n## 完整的zkEVM NFT代码库\r\n\r\n点击[这里](https://github.com/codingwithmanny/zkevm-erc721-hardhat)查看本教程的完整代码库.\r\n\r\n\r\n\r\n## 下一步是什么？\r\n\r\n请关注更多关于zkEVM的教程。\r\n\r\n如果你想更深入地了解zkEVM，请查看[Polygon University](https://university.polygon.technology/)。\r\n\r\n--\r\n\r\n感谢 [Chaintool](https://chaintool.tech/) 对本翻译的支持， Chaintool 是一个为区块链开发者准备的**开源**工具箱"},"author":{"user":"https://learnblockchain.cn/people/412","address":null},"history":null,"timestamp":1679745629,"version":1}