用Hardhat在zkEVM上部署一个动态NFT

如何使用Hardhat将ERC721 NFT合约部署到zkEVM Testnet上

img

使用Hardhat在zkEVM Testnet上部署一个Onchain的SVG动画NFT

什么是zkEVM

简而言之,Polygon zkEVM是一个二级扩容解决方案,旨在实现EVM-equivalence(等效),并允许你使用现有的Solidity代码更便宜、更快、更安全地部署合约到它。

想进一步深入了解它吗?请看我的另一篇文章:如何将ERC20代币合约部署到zkEVM Testnet

准备网络与Token

建议使用Polygon zkEVM Bridge 站点,将网络添加到我们的钱包,并将 Goerli Token 桥接到 zkEVM Testnet:

img

https://public.zkevm-test.net/

环境要求

在我们开始之前,请确保你的电脑上已经安装了以下依赖:NVM或Node v18.15.0,并且已经配置了钱包,并且goerli 测试网代币被桥接到了zkEVM Testnet。如果你想了解如何做到这一点,请查看我的另一篇文章如何将ERC20合约部署到zkEVM Testnet

用Hardhat部署合约

让我们建立一个ERC721 NFT,并通过Hardhat将其部署到zkEVM。

为了确保我们从新开始,我们将利用Hardhat模板生成一个基本的TypeScript项目。

# Create our project folder
mkdir zkevm-erc721-hardhat;
cd zkevm-erc721-hardhat;

npx hardhat;

# Expected Output:
# Ok to proceed? (y) y
# 888    888                      888 888               888
# 888    888                      888 888               888
# 888    888                      888 888               888
# 8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
# 888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
# 888    888 .d888888 888    888  888 888  888 .d888888 888
# 888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
# 888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888
#
# 👷 Welcome to Hardhat v2.13.0 👷‍
#
# ? What do you want to do? …
#   Create a JavaScript project
# ❯ Create a TypeScript project
#   Create an empty hardhat.config.js
#   Quit
#
# ✔ What do you want to do? · Create a TypeScript project
# ✔ Hardhat project root: · /path/to/zkevm-erc721-hardhat
# ✔ Do you want to add a .gitignore? (Y/n) · y
# ✔ Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox)? (Y/n) · y

接下来,让我们安装依赖项:

npme install;
npm install @openzeppelin/contracts dotenv;

创建我们的NFT

我们这里的NFT,将通过引入一个动画的SVG来使它突出一点。

首先删除默认生成的模板化合约,并在其目录上创建一个新的合约。

rm contracts/Lock.sol;
touch contracts/zkEVMNFT.sol;

在该文件中,复制并粘贴以下NFT solidity代码。

文件: ./contracts/zkEVMNFT.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

// Imports
// ========================================================
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Base64.sol";

// Contract
// ========================================================
contract ZkEVMNFT is ERC721 {
    // Extending functionality
    using Strings for uint256;

    /**
     * Main constructor
     */
    constructor() ERC721("zkEVMNFT", "zkNFT") {}

    /**
     * Main minting function
     */
    function safeMint(address to, uint256 tokenId) public {
        _safeMint(to, tokenId);
    }

    // The following functions are overrides required by Solidity.
    /**
     * @dev See {ERC721}
     */
    function _burn(uint256 tokenId) internal override(ERC721) {
        super._burn(tokenId);
    }

    /**
     * Public function or burning
     */
    function burn (uint256 tokenId) public {
        _burn(tokenId);
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721)
        returns (string memory)
    {
        // Validation
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        // SVG Image
        bytes memory imageSVG = abi.encodePacked(
            "<svg width=\"256\" height=\"256\" viewBox=\"0 0 256 256\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">",
            "<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>",
            "<g clip-path=\"url(#clip0_108_2)\">",
            "<rect id=\"background\" width=\"256\" height=\"256\" fill=\"#ff0000\"/>",
            "<rect x=\"28\" y=\"28\" width=\"200\" height=\"200\" fill=\"white\"/>",
            "</g>",
            "<defs>",
            "<clipPath id=\"clip0_108_2\">",
            "<rect width=\"256\" height=\"256\" fill=\"white\"/>",
            "</clipPath>",
            "</defs>",
            "<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>",
            "</svg>"
        );

        // JSON
        bytes memory dataURI = abi.encodePacked(
            "{",
                "\"name\": \"NUMSVG #", tokenId.toString(), "\",",
                "\"image\": \"data:image/svg+xml;base64,", Base64.encode(bytes(imageSVG)), "\"",
            "}"
        );

        // Returned JSON
        return string(
            abi.encodePacked(
                "data:application/json;base64,",
                Base64.encode(dataURI)
            )
        );
    }
}

配置 Hardhat

接下来我们将配置Hardhat配置文件,使环境变量能够被加载,配置对网络的支持,并调整优化。

文件: ./hardhat.config.ts

// Imports
// ========================================================
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import dotenv from "dotenv";

// Config
// ========================================================
dotenv.config();
const config: HardhatUserConfig = {
  solidity: {
    version: "0.8.18",
    settings: {
      optimizer: {
        enabled: true,
        runs: 200,
      }
    }
  },
  networks: {
    mumbai: {
      url: `${process.env.RPC_MUMBAI_URL || ''}`,
      accounts: process.env.WALLET_PRIVATE_KEY
        ? [`0x${process.env.WALLET_PRIVATE_KEY}`]
        : [],
    },
    zkevmTestnet: {
      url: `${process.env.RPC_ZKEVM_URL || ''}`,
      accounts: process.env.WALLET_PRIVATE_KEY
        ? [`0x${process.env.WALLET_PRIVATE_KEY}`]
        : [],
    }
  },
};

// Exports
// ========================================================
export default config;

为Hardhat添加环境变量

一旦我们完成了Hardhat的配置,我们将创建一个环境变量文件来存储我们的关键值。

touch .env;

注意:记得保护你的私钥不被人窥视。

文件: ./env

RPC_MUMBAI_URL=https://rpc.ankr.com/polygon_mumbai
RPC_ZKEVM_URL=https://rpc.public.zkevm-test.net
WALLET_PRIVATE_KEY=<YOUR-WALLET-PRIVATE-KEY>

最后,让我们修改我们的部署脚本以指向正确的合约。

文件: ./scripts/deploy.ts

// Imports
// ========================================================
import { ethers } from "hardhat";

// Main Deployment Script
// ========================================================
async function main() {
  // Make sure in the contract factory that it mateches the contract name in the solidity file
  // Ex: contract zkEVMNFT
  const zkERC721Contract = await ethers.getContractFactory("zkEVMNFT");
  const contract = await zkERC721Contract.deploy();

  await contract.deployed();

  console.log(`zkEVMNFT deployed to ${contract.address}`);
};

// Init
// ========================================================
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

部署出NFT合约

一旦一切就绪,我们现在可以将我们的NFT合约部署到zkEVM Testnet。

# FROM: ./zkevm-erc721-hardhat

npx hardhat run scripts/deploy.ts --network zkevmTestnet;

# Expected Output:
# zkEVMNFT deployed to 0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7

如果我们在资源管理器上进入合约,我们可以看到以下结果:

img

https://explorer.public.zkevm-test.net/address/0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7

在zkEVM区块浏览器上验证我们的NFT合约

我们可以直接通过Hardhat与合约交互,进行铸币和读取,但我们将使用区块链浏览器来连接和铸币一个新的NFT。

在我们进入验证过程之前,我们将通过标准输入JSON进行验证,我们需要一个文件来上传。

如果我们看一下我们的build-info文件夹,我们可以找到一个JSON文件,我们只需要得到input部分。复制整个部分并创建一个名为verify.json的新文件,并将JSON数据粘贴在那里。

记住,你需要事先编译合约,用npx hardhat compile生成这些文件。

文件: ./artifacts/build-info/your-build-file.json

img

创建 JSON 文件

img

我们新的标准输入JSON文件为verify.json

接下来在区块资源管理器上访问该合约,并开始验证过程。

img

zkEVM Testnet 区块浏览器验证和发布合约

img

zkEVM Testnet Block Explorer 通过标准输入 JSON 验证

img

zkEVM Testnet Block Explorer验证Conifgure verify.json

一旦验证通过,我们就可以在zkEVM Testnet block explorer上看到我们合约的完整代码。

img

https://explorer.public.zkevm-test.net/address/0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7/contracts#address-tabs

在 zkEVM 测试完上铸造 NFT

现在我们的合约已经被验证了,我们可以通过区块浏览器和我们的钱包与它交互。

为了本教程的目的,我们将铸造一个NFT tokenID为4,以纪念距离zkEVM主网测试版发布的天数--2023年3月27日。

img

https://explorer.public.zkevm-test.net/address/0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7/write-contract#address-tabs

img

在zkEVM Testnet上铸造NFT

img

确认在zkEVM Testnet上成功铸造了一个NFT

查看我们的zkEVM NFT

我们在zkEVM Testnet上铸造了NFT,现在我们希望能够直观地看到这个链上SVG NFT。

img

zkEVM Testnet区块浏览器读取NFT

复制tokenURI查询的数据部分,在Chrome浏览器中,将其粘贴到地址栏。

img

zkEVM Testnet NFT tokenURI JSON数据

从JSON中复制image值,在Chrome中,将其粘贴在地址栏中。

img

zkEVM Testnet NFT SVG

🎉 我们成功地部署了合约,验证了合约,并且我们能够看到铸造的NFT。

完整的zkEVM NFT代码库

点击这里查看本教程的完整代码库.

下一步是什么?

请关注更多关于zkEVM的教程。

如果你想更深入地了解zkEVM,请查看Polygon University

--

感谢 Chaintool 对本翻译的支持, Chaintool 是一个为区块链开发者准备的开源工具箱

点赞 3
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
翻译小组
翻译小组
0x9e64...7c84
大家看到好的文章可以在 GitHub 提 Issue: https://github.com/lbc-team/Pioneer/issues 欢迎关注我的 Twitter: https://twitter.com/UpchainDAO