Cross Chain Tutorial on Local Nodes Part 1
Prerequisites
Complete Project
Download the complete project on GitHub.
Project Setup
Open a terminal window, create a new folder for the project tutorial, e.g. mkdir cross-chain-tutorial
, and cd
into
the folder.
Now create separate folders to store contracts and scripts on L1 and L2. For now we will start with L1-governance folder.
mkdir L1-governance
L1-governance
code is a default Hardhat project used to deploy a contract on L1.
The L2-counter
code includes all ZKsync dependencies and configurations for L2.L1 Governance
cd
into theL1-governance
folder.
cd L1-governance
- Initialise and set up the L1 project.
npx hardhat init
Select the option Create a Typescript project and accept the defaults for everything else.
- Import it from the
@matterlabs/zksync-contracts
npm package (preferred). - Download it from the contracts repo.
- Install the following dependencies:
npm i -D typescript ts-node @openzeppelin/contracts @matterlabs/zksync-contracts@beta @nomicfoundation/hardhat-ethers @typechain/ethers-v6 @typechain/hardhat typechain ethers dotenv
Create L1 Governance Contract
L1-governance
folder.The following Solidity code defines the Governance smart contract.
The constructor sets the contract creator as the single governor.
The callZkSync
function calls a transaction on L2 which can only be called by the governor.
- Remove the existing
/test
directory and any contracts that exist in/contracts
. - Create a new file called
Governance.sol
inside the emptycontracts
folder.
touch contracts/Governance.sol
- Copy/paste the code below into it.
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.13;
import { IBridgehub, L2TransactionRequestDirect } from '@matterlabs/zksync-contracts/contracts/l1-contracts/bridgehub/IBridgehub.sol';
contract Governance {
address public governor;
constructor() {
governor = msg.sender;
}
function callZkSync(
uint256 chainId,
address bridgeHubAddress,
address contractAddr,
bytes memory data,
uint256 gasLimit,
uint256 gasPerPubdataByteLimit,
uint256 cost
) external payable {
require(msg.sender == governor, 'Only governor is allowed');
IBridgehub zksyncBridgeHub = IBridgehub(bridgeHubAddress);
L2TransactionRequestDirect memory requestInput = L2TransactionRequestDirect(
chainId,
cost,
contractAddr,
0,
data,
gasLimit,
gasPerPubdataByteLimit,
new bytes[](0),
msg.sender
);
zksyncBridgeHub.requestL2TransactionDirect{ value: msg.value }(requestInput);
}
}
Starting a Local Node
Open Docker Desktop so the Docker daemon is running in the background,
and use zksync-cli
to start a local node.
First, configure the CLI to use a dockerized node:
npx zksync-cli dev config
Use the arrow key to select Dockerized node
in the prompt.
To run a local block explorer, use the arrow keys and space bar to select the Block Explorer
option in the prompt.
Finally, press the Enter
key to confirm the configuration.
Once configured, start the local dockerized node.
npx zksync-cli dev start
Deploy L1 Governance Contract
Create the file L1-Governance/.env
and copy/paste the code below.
NODE_RPC_URL=http://127.0.0.1:8545
PRIVATE_KEY=0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110
Replace the code in hardhat.config.ts
with the following:
import type { HardhatUserConfig } from 'hardhat/config';
import '@nomicfoundation/hardhat-toolbox';
import dotenv from 'dotenv';
dotenv.config();
const config: HardhatUserConfig = {
solidity: '0.8.24',
networks: {
// Sepolia network
sepolia: {
url: process.env.NODE_RPC_URL,
accounts: [process.env.PRIVATE_KEY!],
},
},
};
export default config;
Next, create the file Governance.ts
inside the /ignition/modules
folder and copy/paste the following code into it:
touch ignition/modules/Governance.ts
import { buildModule } from '@nomicfoundation/hardhat-ignition/modules';
const GovernanceModule = buildModule('GovernanceModule', (m) => {
const governance = m.contract('Governance', [], {});
return { governance };
});
export default GovernanceModule;
Then, from the L1-governance
folder root, compile and deploy the contract:
npx hardhat compile
npx hardhat ignition deploy ./ignition/modules/Governance.ts --network sepolia
You should see the following output:
Deploying [ GovernanceModule ]
Batch #1
Executed GovernanceModule#Governance
[ GovernanceModule ] successfully deployed 🚀
Deployed Addresses
GovernanceModule#Governance - 0xA7d27A1202bE1237919Cf2cb60970141100725b4
Save the address to use in a later step.