如何使用Hardhat将ERC721 NFT合约部署到zkEVM Testnet上
使用Hardhat在zkEVM Testnet上部署一个Onchain的SVG动画NFT
简而言之,Polygon zkEVM是一个二级扩容解决方案,旨在实现EVM-equivalence(等效),并允许你使用现有的Solidity代码更便宜、更快、更安全地部署合约到它。
想进一步深入了解它吗?请看我的另一篇文章:如何将ERC20代币合约部署到zkEVM Testnet。
建议使用Polygon zkEVM Bridge 站点,将网络添加到我们的钱包,并将 Goerli Token 桥接到 zkEVM Testnet:
在我们开始之前,请确保你的电脑上已经安装了以下依赖:NVM或Node v18.15.0
,并且已经配置了钱包,并且goerli 测试网代币被桥接到了zkEVM Testnet。如果你想了解如何做到这一点,请查看我的另一篇文章如何将ERC20合约部署到zkEVM Testnet。
让我们建立一个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,将通过引入一个动画的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.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的配置,我们将创建一个环境变量文件来存储我们的关键值。
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合约部署到zkEVM Testnet。
# FROM: ./zkevm-erc721-hardhat
npx hardhat run scripts/deploy.ts --network zkevmTestnet;
# Expected Output:
# zkEVMNFT deployed to 0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7
如果我们在资源管理器上进入合约,我们可以看到以下结果:
https://explorer.public.zkevm-test.net/address/0x7F77cF06C84A7bae6D710eBfc78a48214E4937a7
我们可以直接通过Hardhat与合约交互,进行铸币和读取,但我们将使用区块链浏览器来连接和铸币一个新的NFT。
在我们进入验证过程之前,我们将通过标准输入JSON进行验证,我们需要一个文件来上传。
如果我们看一下我们的build-info
文件夹,我们可以找到一个JSON文件,我们只需要得到input部分。复制整个部分并创建一个名为verify.json
的新文件,并将JSON数据粘贴在那里。
记住,你需要事先编译合约,用npx hardhat compile
生成这些文件。
文件: ./artifacts/build-info/your-build-file.json
。
创建 JSON 文件
我们新的标准输入JSON文件为verify.json
接下来在区块资源管理器上访问该合约,并开始验证过程。
zkEVM Testnet 区块浏览器验证和发布合约
zkEVM Testnet Block Explorer 通过标准输入 JSON 验证
zkEVM Testnet Block Explorer验证Conifgure verify.json
一旦验证通过,我们就可以在zkEVM Testnet block explorer上看到我们合约的完整代码。
现在我们的合约已经被验证了,我们可以通过区块浏览器和我们的钱包与它交互。
为了本教程的目的,我们将铸造一个NFT tokenID为4,以纪念距离zkEVM主网测试版发布的天数--2023年3月27日。
在zkEVM Testnet上铸造NFT
确认在zkEVM Testnet上成功铸造了一个NFT
我们在zkEVM Testnet上铸造了NFT,现在我们希望能够直观地看到这个链上SVG NFT。
zkEVM Testnet区块浏览器读取NFT
复制tokenURI
查询的数据部分,在Chrome浏览器中,将其粘贴到地址栏。
zkEVM Testnet NFT tokenURI JSON数据
从JSON中复制image
值,在Chrome中,将其粘贴在地址栏中。
zkEVM Testnet NFT SVG
🎉 我们成功地部署了合约,验证了合约,并且我们能够看到铸造的NFT。
点击这里查看本教程的完整代码库.
请关注更多关于zkEVM的教程。
如果你想更深入地了解zkEVM,请查看Polygon University。
--
感谢 Chaintool 对本翻译的支持, Chaintool 是一个为区块链开发者准备的开源工具箱
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!