# 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

To prevent users from requesting too many heavy operations from the operator, users must provide the fee to be paid to the zkSync operator in ETH. Each transaction has base cost in gas and the layer 2 tip, which is equal to the basic cost of the requested transaction subtracted from the ETH value passed with the call.

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 during the transaction call.
  2. Get the base cost for the transaction.
  3. Call the transaction, while passing the needed value with the call.

# Using contract interface in your project

To interact with the zkSync the 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.

# 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: 8/30/2022, 2:33:15 PM