# L1 -> L2 communication

This section describes the interface for interaction with zkSync from L1. It assumes that you are already familiar with the basic concepts of working with the priority queue. If you are new to this topic, you can read the conceptual introduction here. If you want to dive straight into the code, then you can read the cross-chain governance tutorial.

# Structure

For the most common usecase, there is "baseFee" for a transaction, which basically means the minimum amount the user has to pay to the operator for him to include this transaction. It is derived based on the ergsLimit for a transaction and the gas price on L1. In addition, whatever fee the user pays above is called layer2 tip and will be used to sort the transactions by the provided L2 fee.

At the moment, all the L1-> L2 transactions are served at the first-in-first-out basis, but in the future we will introduce "priority heap", which will allow for sorting the transactions. Basic costs are defined in gas and not in ETH, so the actual amount of ether that the submission of the transaction will cost depends on the transaction gas price. Generally the flow for calling any of these methods should be the following:

  1. Fetch the gas price that you will use to send the transaction.
  2. Get the base cost for the transaction.
  3. Send the transaction including the needed value.

# Using contract interface in your project

To interact with the zkSync mailbox contract using Solidity, you need to use the zkSync contract interface. There are two main ways to get it:

  • By importing it from the @matterlabs/zksync-contracts npm package (preferred).
  • By downloading the contracts from the repo (opens new window).

The @matterlabs/zksync-contracts package can be installed by running the following command:

yarn add -D @matterlabs/zksync-contracts

In the examples below we assume that the interface is accessed via the @matterlabs/zksync-contracts npm package.

# Getting the base cost

The following view function returns the amount of ETH that is needed to be supplied by the user to cover the base cost of the transaction.

function l2TransactionBaseCost(
    uint256 _gasPrice,
    uint256 _ergsLimit,
    uint32 _calldataLength
) external view returns (uint256);
  • _gasPrice is a parameter that contains the transaction gas price.
  • _ergsLimit is a parameter that contains the ergs limit of the transaction call. You can learn more about ergs and the zkSync fee system here.
  • _calldataLength is a parameter that contains the length of the calldata in bytes.

# Interface

The following function returns the canonical hash or the requested transaction, that can be used to track the execution of the transaction in L2.

function requestL2Transaction(
    address _contractAddressL2,
    uint256 _l2Value,
    bytes calldata _calldata,
    uint256 _ergsLimit,
    bytes[] calldata _factoryDeps
) external payable returns (bytes32 txHash);
  • _contractAddressL2 is a parameter that defines the address of the contract to be called.

  • _l2Value is a parameter that defines the amount of ETH you want to pass with the call to L2. This number will be used as msg.value for the transaction.

  • _calldata is a parameter that contains the calldata of the transaction call. It can be encoded the same way as on Ethereum.

  • _ergsLimit is a parameter that contains the ergs limit of the transaction call. You can learn more about ergs and the zkSync fee system here.

  • _factoryDeps is a list of bytecodes. It should contain the bytecode of the contract being deployed. If the contract being deployed is a factory contract, i.e. it can deploy other contracts, the array should also contain the bytecodes of the contracts that can be deployed by it.

With the method call, some amount of ETH should be supplied to cover the base cost of the transaction (including the _l2Value) + layer 2 operator tip.

TIP

A successful L1 -> L2 message produces an L2Log with key = l2TxHash, and value = bytes32(1) whereas a failed L1 -> L2 message produces an L2Log with key = l2TxHash, and value = bytes32(0).

# Examples

# Solidity

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
// Importing zkSync contract interface
import "@matterlabs/zksync-contracts/l1/contracts/zksync/interfaces/IZkSync.sol";
// Importing `Operations` contract which has the `QueueType` type
import "@matterlabs/zksync-contracts/l1/contracts/zksync/Operations.sol";
contract Example {
    function callZkSync(
        // The address of the zkSync smart contract.
        // It is not recommended to hardcode it during the alpha testnet as regenesis may happen.
        address _zkSyncAddress
    ) external payable returns(bytes32 txHash) {
        IZkSync zksync = IZkSync(_zkSyncAddress);
        address someL2Contract = 0xdba0833e8c4b37cecc177a665e9207962e337299;
        // calling L2 smart contract from L1 Example contract
        txHash = zksync.requestL2Transaction{value: msg.value}(
            // The address of the L2 contract to call
            someL2Contract,
            // We pass no ETH with the call
            0,
            // Encoding the calldata for the execute
            abi.encodeWithSignature("someMethod()"),
            // Ergs limit
            10000,
            // factory dependencies
            new bytes[](0)
        );
    }
}

# zksync-web3

import { Wallet, Provider } from "zksync-web3";
import { ethers, BigNumber } from "ethers";
const TEST_PRIVATE_KEY = "0xc8acb475bb76a4b8ee36ea4d0e516a755a17fad2e84427d5559b37b544d9ba5a";
const zkSyncProvider = new Provider("https://zksync2-testnet.zksync.dev");
const ethereumProvider = ethers.getDefaultProvider("goerli");
const wallet = new Wallet(TEST_PRIVATE_KEY, zkSyncProvider, ethereumProvider);
const gasPrice = await wallet.providerL1!.getGasPrice();
// The calldata can be encoded the same way as for Ethereum
const calldata = "0x...";
const ergsLimit = BigNumber.from(1000);
const txCostPrice = await wallet.getBaseCost({
  gasPrice,
  calldataLength: ethers.utils.hexDataLength(calldata),
  ergsLimit,
});
console.log(`Executing the transaction will cost ${ethers.utils.formatEther(txCostPrice)} ETH`);
// initiating L2 transfer via L1 execute from zksync wallet
const someL2Contract = "0x19a5bfcbe15f98aa073b9f81b58466521479df8d";
const executeTx = await wallet.requestExecute({
  calldata,
  ergsLimit,
  contractAddress: someL2Contract,
  overrides: {
    gasPrice,
  },
});
await executeTx.wait();
Last Updated: 11/23/2022, 2:43:27 PM