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:cd L2-counter rm -rf ./contracts/* rm -rf ./scripts/*
Create L2 Counter Contract
- In the
L2-counter/contracts/
directory, create a new fileCounter.sol
.touch contracts/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. - 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.npm run compile
Deploy L2 Counter Contract
- Create a new file inside the
scripts
folder nameddeploy.ts
.touch scripts/deploy.ts
- Copy/paste the following code into
L2-counter/scripts/deploy.ts
, replacing<GOVERNANCE-ADDRESS>
with the address of the Governance contract we just deployed:L2-counter/scripts/deploy.tsimport { ethers, network } from 'hardhat'; import { utils } from 'zksync-ethers'; const GOVERNANCE_ADDRESS = process.env.GOVERNANCE_ADDRESS ?? '<GOVERNANCE-ADDRESS>'; async function main() { const CONTRACT_NAME = 'Counter'; const ARGS = [utils.applyL1ToL2Alias(GOVERNANCE_ADDRESS)]; console.log(`Deploying ${CONTRACT_NAME} contract to ${network.name}`); const contract = await ethers.deployContract(CONTRACT_NAME, ARGS, {}); await contract.waitForDeployment(); const contractAddress = await contract.getAddress(); console.log(`${CONTRACT_NAME} deployed to ${contractAddress}`); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
- Now deploy the contract from the
L2-counter/
folder root to ZKsync Era Sepolia:npm run deploy
You should see output like this:Deploying Counter contract to ZKsyncEraSepolia Counter deployed to 0x111C3E89Ce80e62EE88318C2804920D4c96f92bb
Read the Counter Value
Now both contracts are deployed, we can create a script to retrieve the value of the counter.
- Create a new file in the
scripts
folder nameddisplay-value.ts
.touch scripts/display-value.ts
- Copy and paste in the following code, adding the deployed counter contract address:L2-counter/scripts/display-value.ts
import { ethers } from 'hardhat'; async function main() { const COUNTER_ADDRESS = process.env.COUNTER_ADDRESS ?? '<COUNTER_ADDRESS>'; const [signer] = await ethers.getSigners(); const counterFactory = await ethers.getContractFactory('Counter'); const counterContract = counterFactory.connect(signer).attach(COUNTER_ADDRESS); const value = (await counterContract.value()).toString(); console.log(`The counter value is ${value}`); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });
- Run the script:
npx hardhat run ./scripts/display-value.ts
The output should be:The counter value is 0