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.
cd
out ofL1-governance
and initialize theL2-counter
project:npx zksync-cli create L2-counter --template hardhat_solidity
- 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
- In the
L2-counter/contracts/
directory, create a new fileCounter.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. - 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; } }
- Compile the contract from the
L2-counter
root:Compilations happens only after the installation of a compiler.npx hardhat compile
Deploy L2 Counter Contract
- 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.tsimport { 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}`); }
- 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.
- Create the
scripts
directory underL2-counter
. - Copy the
abi
array from the compilation artifact located at/L2-counter/artifacts-zk/contracts/Counter.sol/Counter.json
. - Create a new file
/L2-counter/scripts/counter.json
and paste in theabi
array. - 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.tsimport { 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; });
- Run the script:
npx ts-node ./scripts/display-value.ts
The output should be:The counter value is 0