- Walk through the instructions in this README (main branch) to build a Counter dapp and deploy to the Polgon zkEVM testnet.
- Check out the completed code branch here: https://github.com/oceans404/fullstack-zkevm/tree/complete
- add the Polygon zkEVM Testnet Network to your Metamask Networks: https://www.youtube.com/watch?v=Y1gOkTsXgSY
- Get some zkEVM testnet ETH: https://www.youtube.com/watch?v=eYZAPkTCgwg
Star this repo and clone it locally
git clone https://github.com/oceans404/fullstack-zkevm
Install dependencies and start react app
cd fullstack-zkevm
npm i
npm start
Install dependencies
npm install ethers hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers dotenv
cp .env.sample .env;
Update .env to set your ACCOUNT_PRIVATE_KEY environment variable. Here's an article on how to get your private key from MetaMask.
Before running npx hardhat, rename your README.md file temporarily. (README.md -> README-tutorial.md) Hardhat can't initialize a sample project if there's an existing README file.
npx hardhat
What do you want to do? … ❯ Create a JavaScript project
Hardhat project root: default
Do you want to add a .gitignore? y
Open the hardhat.config.js and paste in this code:
require("dotenv").config();
require("@nomicfoundation/hardhat-toolbox");
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.9",
paths: {
artifacts: "./src",
},
networks: {
zkEVM: {
url: `https://rpc.public.zkevm-test.net`,
accounts: [process.env.ACCOUNT_PRIVATE_KEY],
},
},
};
Notice that we've added a different path to artifacts so that the React app will be able to read the contract ABI within the src folder
Create a new file in the contracts folder Counter.sol
touch contracts/Counter.sol
Copy paste in the Counter contract code
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract Counter {
uint256 currentCount = 0;
function increment() public {
currentCount = currentCount + 1;
}
function retrieve() public view returns (uint256){
return currentCount;
}
}
Create a new file in the scripts folder deploy-counter.js
touch scripts/deploy-counter.js
and add the following code to the deploy-counter.js
file
const hre = require("hardhat");
async function main() {
const CounterContractFactory = await hre.ethers.getContractFactory("Counter");
const counterContract = await CounterContractFactory.deploy();
await counterContract.deployed();
console.log(
`Counter contract deployed to https://explorer.public.zkevm-test.net/address/${counterContract.address}`
);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Compile your contract code
npx hardhat compile
npx hardhat run scripts/deploy-counter.js --network zkEVM
Verify the contract by following my verification instructions
In App.js, import the ethers, the Counter file and log the contract's abi. Update the counterAddress to your deployed address.
import { ethers } from "ethers";
import Counter from "./contracts/Counter.sol/Counter.json";
const counterAddress = "your-contract-address"
console.log(counterAddress, "Counter ABI: ", Counter.abi);
Update frontend counter to read from blockchain
useEffect(() => {
// declare the data fetching function
const fetchCount = async () => {
const data = await readCounterValue();
return data;
};
fetchCount().catch(console.error);
}, []);
async function readCounterValue() {
if (typeof window.ethereum !== "undefined") {
const provider = new ethers.providers.Web3Provider(window.ethereum);
console.log("provider", provider);
const contract = new ethers.Contract(
counterAddress,
Counter.abi,
provider
);
console.log("contract", contract);
try {
const data = await contract.retrieve();
console.log(data);
console.log("data: ", parseInt(data.toString()));
setCount(parseInt(data.toString()));
} catch (err) {
console.log("Error: ", err);
alert(
"Switch your MetaMask network to Polygon zkEVM testnet and refresh this page!"
);
}
}
}
Let's track a loader. Add this to your state
const [isLoading, setIsLoading] = useState(false);
Let frontend counter write to the blockchain by adding the following 2 functions.
async function requestAccount() {
await window.ethereum.request({ method: "eth_requestAccounts" });
}
async function updateCounter() {
if (typeof window.ethereum !== "undefined") {
await requestAccount();
const provider = new ethers.providers.Web3Provider(window.ethereum);
console.log({ provider });
const signer = provider.getSigner();
const contract = new ethers.Contract(counterAddress, Counter.abi, signer);
const transaction = await contract.increment();
setIsLoading(true);
await transaction.wait();
setIsLoading(false);
readCounterValue();
}
}
Update the incrementCounter function to
const incrementCounter = async () => {
await updateCounter();
};
Update the increment button code to
<Button
onClick={incrementCounter}
variant="outlined"
disabled={isLoading}
>
{isLoading ? "loading..." : "+1"}
</Button>
You did it! 🚀 Want to deploy your dapp to Fleek for decentralized hosting? Follow my instructions here