How To Deploy A Contract To Polygon zkEVM Testnet
What Is zkEVM?
You might be asking “What is zkEVM?”
Polygon zkEVM is the first zk-Rollup with source code available, providing complete EVM opcode equivalence for a frictionless user experience and the security of Ethereum.
- https://polygon.technology/solutions/polygon-zkevm
Is it a different network than Polygon Matic POS?
Yes it is a completely different network with its own token and wallet configurations.
Does that mean it uses its own tokens?
Yes it uses it’s own native tokens and not Matic or Mumbai Testnet Tokens.
Ok, but what does EVM-equivalence mean?
Equivalence refers to a Type 2 ZK-EVM that is defined a bit better by Vitalik’s blog on ZK-EVMs.
Type 2 ZK-EVMs strive to be exactly EVM-equivalent, but not quite Ethereum-equivalent. That is, they look exactly like Ethereum “from within”, but they have some differences on the outside, particularly in data structures like the block structure and state tree.
- https://vitalik.eth.limo/general/2022/08/04/zkevm.html
For Developers
What that means for developers is you can deploy you existing solidity code without going through any extra steps to compile your code to get it to work on this network. Compared to other ZK-EVM solutions out there, Type 2 offers an easier way to work with that ZK-EVM solution compared to Type 3 and Type 4 solutions.
The goal is to be fully compatible with existing applications, but make some minor modifications to Ethereum to make development easier and to make proof generation faster.
TL;DR
Jump to the Full Code section for the GitHub repository.
Wallet Configuration
It should be noted that a lot of this information already exists on the official Polygon Wiki For zkEVM and that these settings may change when the zkEVM Mainnet is available.
Is the zkEVM wallet configuration on Chainlist.org? Unfortunately not yet because the network configurations and port number might change.
Our configurations setting for zkEVM Testnet are as followed:
⚠️ NOTE: March 17 — Details Have Changed
- Network Name: Polygon zkEVM Testnet
- RPC URL: https://rpc.public.zkevm-test.net
- Chain ID: ̶1̶4̶0̶2̶ → 1442
- Currency Symbol: ETH
- Block Explorer URL: https://explorer.public.zkevm-test.net
ℹ️ You can also quickly add the Polygon zkEVM Testnet by searching for “zkevm” on https://chainid.network.
You can add this to your current MetaMask wallet by going to your networks and adding a network manually.
Let’s take a look at our wallet on the zkEVM Block Explorer.
Getting Testnet Tokens From A Faucet
It’s a bit different to get Testnet tokens for zkEVM compared to other Testnet networks. In order to get tokens, you need to get Goerli Testnet tokens and then bridge them over to zkEVM.
We’ll be using the QuickNode Goerli Faucet, but you can also use anything of the following (please let me know if I missed one):
NOTE: If a faucet isn’t currently working please refer to the respective RPC providers for network issues.
Once we have Goerli Testnet Tokens, we need to bridge them to the zkEVM Testnet by using https://public.zkevm-test.net/.
Connect your preferred wallet, make sure the network is set to Ethereum Goerli, enter the desired amount to bridge, and click Continue.
Confirm the bridge transaction.
Finalise the transaction by switching to zkEVM and then confirming the transaction.
If the transaction was successful, you should a confirmation screen and see the result on the Block Explorer.
Deploying An ERC20 Contract To zkEVM Testnet
This next part we’re going to setup and deploy and ERC20 contract to zkEVM with Hardhat.
Requirements
Make sure to have the following installed on your computer before hand.
- nvm or node
v18.12.1
- pnpm
v7.15.0
Setting Up Hardhat
The first thing we’re going to do is create a new project folder, initiate pnpm, install Hardhat, and configure it.
mkdir zkevm-erc20;
cd zkevm-erc20;
git init;
pnpx hardhat;
# Expected Prompts
# 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.12.3 👷
#
# ? What do you want to do? …
# Create a JavaScript project
# ❯ Create a TypeScript project
# Create an empty hardhat.config.js
# Quit
# ? Hardhat project root: › /path/to/zkevm-erc20
# ? Do you want to add a .gitignore? (Y/n) › y
# ? Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) › y
pnpm install;
Let’s double check that our Hardhat setup is working as expected by running a node, deploying the default contract, and then testing that contract.
Terminal 1
# FROM: ./zkevm-erc20
./node_modules/.bin/hardhat node;
# Expected Output:
# Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/
#
# Accounts
# ========
#
# WARNING: These accounts, and their private keys, are publicly known.
# Any funds sent to them on Mainnet or any other live network WILL BE LOST.
# ...
Terminal 2
Deploy the Lock.sol
contract to the local node we’re running.
# FROM: ./zkevm-erc20
./node_modules/.bin/hardhat run scripts/deploy.ts
# Expected Output:
# Compiled 1 Solidity file successfully
# Lock with 1 ETH and unlock timestamp 1701595951 deployed to 0x5FbDB2315678afecb367f032d93F642f64180aa3
Run the tests that were generated with the original scaffolded project.
# FROM: ./zkevm-erc20
./node_modules/.bin/hardhat test;
# Expected Output:
# Lock
# Deployment
# ✔ Should set the right unlockTime (894ms)
# ✔ Should set the right owner
# ✔ Should receive and store the funds to lock
# ✔ Should fail if the unlockTime is not in the future
# Withdrawals
# Validations
# ✔ Should revert with the right error if called too soon
# ✔ Should revert with the right error if called from another account
# ✔ Shouldn't fail if the unlockTime has arrived and the owner calls it
# Events
# ✔ Should emit an event on withdrawals
# Transfers
# ✔ Should transfer the funds to the owner
#
# 9 passing (1s)
Creating An ERC20 Contract
We’re going to base our contract from OpenZeppelin’s ERC20 Solidity contract, which will create token with an initial 10,000 tokens and allows the owner to mint more tokens.
Add Dependencies
# FROM: ./zkevm-erc20
pnpm add -D @openzeppelin/contracts;
Configure ERC20
Use The OpenZepplin Wizard to configure an ERC20 token.
Make New Contract
We’re going to rename our existing Lock.sol
for zkerc20.sol
and replace it with the code we generated from the OpenZeppeling wizard.
# FROM: ./zkevm-erc20
mv ./contracts/Lock.sol ./contracts/zkERC20.sol;
File: ./contracts/zkERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract ZkERC20 is ERC20, Ownable {
constructor() ERC20("zkERC20", "ZK20") {
_mint(msg.sender, 10000 * 10 ** decimals());
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
Testing Our ERC20 Contract
First we’re going to make a single test for our ERC20 contract and then verify that it’s working correctly.
Deploy Script
First we need to modify our deploy script to account for the new contract name.
File: ./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 ZkERC20
const zkERC20Contract = await ethers.getContractFactory("ZkERC20");
const contract = await zkERC20Contract.deploy();
await contract.deployed();
console.log(`ZkERC20 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;
});
Creating Our Test
Next we’re going to rename our Lock.ts
test file to zkERC20.test.ts
and add a test that confirms the total balance of the minted erc20 token.
# FROM: ./zkevm-erc20
mv ./test/Lock.ts ./test/zkERC20.test.ts;
File: ./test/zkERC20.test.ts
// Imports
// ========================================================
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { expect } from "chai";
import { ethers } from "hardhat";
// Tests
// ========================================================
describe("zkERC20", function () {
// We define a fixture to reuse the same setup in every test.
// We use loadFixture to run this setup once, snapshot that state,
// and reset Hardhat Network to that snapshot in every test.
async function deployZkERC20() {
// Contracts are deployed using the first signer/account by default
const [owner, otherAccount] = await ethers.getSigners();
// Make sure in the contract factory that it mateches the contract name in the solidity file
// Ex: contract ZkERC20
const zkERC20Contract = await ethers.getContractFactory("ZkERC20");
const zkERC20 = await zkERC20Contract.deploy();
return { zkERC20, owner, otherAccount };
};
/**
*
*/
describe("Deployment", function () {
/**
*
*/
it("Should deploy with initial 10,000 supply", async function () {
// Setup
const { zkERC20 } = await loadFixture(deployZkERC20);
// Init + Test
expect(await zkERC20.totalSupply()).to.equal(ethers.utils.parseEther(`10000`).toString());
});
});
/**
*
*/
describe("Minting", function () {
/**
*
*/
it("Should mint and increase the supply by 137", async function () {
// Setup
const { zkERC20, owner } = await loadFixture(deployZkERC20);
// Init
await zkERC20.connect(owner).mint(owner.address, ethers.utils.parseUnits('137', 18));
// Init + Test
expect(await zkERC20.totalSupply()).to.equal(ethers.utils.parseEther(`10137`).toString());
});
});
});
Let’s run the Hardhat node and test this contract.
Terminal 1
Running a local node.
# FROM: ./zkevm-erc20
./node_modules/.bin/hardhat node;
# Expected Output:
# Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/
#
# Accounts
# ========
#
# WARNING: These accounts, and their private keys, are publicly known.
# Any funds sent to them on Mainnet or any other live network WILL BE LOST.
# ...
Terminal 2
Running our tests.
# FROM: ./zkevm-erc20
./node_modules/.bin/hardhat test;
# Expected Output:
# zkERC20
# Deployment
# ✔ Should deploy with initial 10,000 supply (803ms)
# Minting
# ✔ Should mint and increase the supply by 137
#
# 2 passing (819ms)
Deploying An ERC20 Contract
̶I̶t̶ ̶s̶h̶o̶u̶l̶d̶ ̶b̶e̶ ̶n̶o̶t̶e̶d̶ ̶t̶h̶a̶t̶ ̶c̶u̶r̶r̶e̶n̶t̶l̶y̶ ̶z̶k̶E̶V̶M̶ ̶d̶e̶p̶l̶o̶y̶m̶e̶n̶t̶s̶ ̶a̶r̶e̶n̶’̶t̶ ̶s̶u̶p̶p̶o̶r̶t̶e̶d̶ ̶i̶n̶ ̶H̶a̶r̶d̶h̶a̶t̶ ̶c̶u̶r̶r̶e̶n̶t̶l̶y̶. We can deploy our contracts onHardhat but in this case we’ll use Remix to show how you could do it an alternative way. To deploy your contracts, you’ll need to use the Injected Provider configuration with Ethereum Remix.
Make sure to create an zkERC20.sol
file in remix with the contract code we made above.
Switch to the compile section and click the large compile button to get the green checkmark that everything compiled correctly.
Switch over to the deploy section in left navigation bar, set the Environment to Injected Provider — Metamask
. and make sure your wallet is set to the zkEVM network.
When ready, click the deploy button and confirm the transaction in your wallet.
Viewing Our Tokens In MetaMask
Open your MetaMask wallet, click on the transaction, and view the transaction on the block explorer. In the block explorer click the contract address to open up the contract. With the contract loaded in the block explorer, copy the contract address.
Open your MetaMask, click on Assets, and click on Import tokens.
Paste the contract address, and fill the other fields as needed. They might automatically populate, and then click Add custom token.
You should now be able to follow the prompts and then see the new ZK20 token in your wallet with the correct initial amount.
🎉 There you go, you’ve fully deployed an ERC20 contract to Polygon Hermes zkEVM Testnet.
Full Code
Here is the full code for the zkEVM ERC20 token. Just remember that Hardhat deployments aren’t currently supported, yet.
What’s Next
Please look out for the ERC721 tutorial coming soon.
If you got value from this, please give it some love, and please also follow me on twitter (where I’m quite active) @codingwithmanny and instagram at @codingwithmanny.