L2 counter contract

Deploy a contract in ZKsync Era that has a counter.

L2 Counter

Now that we have an address for the L1 governance contract, we can build, deploy, and test the counter contract on L2.

  1. cd out of L1-governance and initialize the L2-counter project:
    npx zksync-cli create L2-counter --template hardhat_solidity
    
  2. For the purposes of this tutorial, we don't need the example smart contracts and deployment script files generated by zksync-cli. Run the following commands to remove them:
    rm -rf ./contracts/*
    rm -rf ./deploy/*
    

Create L2 Counter Contract

  1. In the L2-counter/contracts/ directory, create a new file Counter.sol.
    This contract contains the address of the governance contract deployed previously on layer 1, and an incrementable counter which can only be invoked by the governance contract.
  2. Copy/paste the following code into the file:
    Counter.sol
    // SPDX-License-Identifier: Unlicense
    
    pragma solidity ^0.8.17;
    
    contract Counter {
        uint256 public value = 0;
        address public governance;
    
        constructor(address newGovernance) {
            governance = newGovernance;
        }
    
        function increment() public {
            require(msg.sender == governance, "Only governance is allowed");
    
            value += 1;
        }
    }
    
  3. Compile the contract from the L2-counter root:
    Compilations happens only after the installation of a compiler.
    npx hardhat compile
    

Deploy L2 Counter Contract

  1. Copy/paste the following code into L2-counter/deploy/deploy.ts, replacing <GOVERNANCE-ADDRESS> with the address of the Governance contract we just deployed:
    L2-counter/deploy/deploy.ts
    import { utils, Wallet } from "zksync-ethers";
    import { HardhatRuntimeEnvironment } from "hardhat/types";
    import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
    // load env file
    import dotenv from "dotenv";
    dotenv.config();
    
    // Insert the address of the governance contract
    const GOVERNANCE_ADDRESS = "<GOVERNANCE-ADDRESS>";
    
    // An example of a deploy script that will deploy and call a simple contract.
    export default async function (hre: HardhatRuntimeEnvironment) {
      console.log(`Running deploy script for the Counter contract`);
    
      const PRIVATE_KEY = process.env.WALLET_PRIVATE_KEY || "";
      if (!PRIVATE_KEY) throw "⛔️ Private key not detected! Add it to the .env file!";
      // Initialize the wallet.
      const wallet = new Wallet(PRIVATE_KEY);
    
      // Create deployer object and load the artifact of the contract you want to deploy.
      const deployer = new Deployer(hre, wallet);
      const artifact = await deployer.loadArtifact("Counter");
    
      // Deploy this contract. The returned object will be of a `Contract` type, similar to the ones in `ethers`.
      // The address of the governance is an argument for contract constructor.
      const counterContract = await deployer.deploy(artifact, [utils.applyL1ToL2Alias(GOVERNANCE_ADDRESS)]);
    
      const receipt = await counterContract.deploymentTransaction()?.wait();
    
      // Show the contract info.
      const contractAddress = receipt?.contractAddress;
      console.log(`${artifact.contractName} was deployed to ${contractAddress}`);
    }
    
  2. Now deploy the contract from the L2-counter/ folder root to ZKsync:
    npx hardhat deploy-zksync
    

    You should see output like this:
    Running deploy script for the Counter contract
    Counter was deployed to 0x3c5A6AB2390F6217C78d2F6F403A9dFb7e7784FC
    

Read the Counter Value

Now both contracts are deployed, we can create a script to retrieve the value of the counter.

  1. Create the scripts directory under L2-counter.
  2. Copy the abi array from the compilation artifact located at /L2-counter/artifacts-zk/contracts/Counter.sol/Counter.json.
  3. Create a new file /L2-counter/scripts/counter.json and paste in the abi array.
  4. Create a /L2-counter/scripts/display-value.ts file and paste in the following code, adding the counter contract address:
    L2-counter/scripts/display-value.ts
    import { Contract, Provider } from "zksync-ethers";
    
    const COUNTER_ADDRESS = "<COUNTER-ADDRESS>";
    const COUNTER_ABI = require("./counter.json");
    
    async function main() {
      // Initialize the provider
      const l2Provider = new Provider("https://sepolia.era.zksync.dev");
    
      const counterContract = new Contract(COUNTER_ADDRESS, COUNTER_ABI, l2Provider);
    
      const value = (await counterContract.value()).toString();
    
      console.log(`The counter value is ${value}`);
    }
    
    main().catch((error) => {
      console.error(error);
      process.exitCode = 1;
    });
    
  5. Run the script:
    npx ts-node ./scripts/display-value.ts
    

    The output should be:
    The counter value is 0
    

Made with ❤️ by the ZKsync Community