Skip to content

CaixiangFan/fullstack-zkevm

 
 

Repository files navigation

Create a Fullstack Counter Dapp on the Polygon zkEVM Testnet

General Setup

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.

Hardhat Smart Contract

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 {
  string greeting;

  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

Update the Frontend to turn it into a dapp

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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 72.4%
  • HTML 12.0%
  • Solidity 9.1%
  • CSS 6.5%