Getting Started

Learn how to setup and use Foundry with your ZKsync project.

Prerequisites

The primary prerequisite for using foundry-zksync is the Rust Compiler.

Installation Guide

To integrate foundry-zksync into your projects, you have the flexibility to install its components individually or the entire suite at once. Follow the steps below to get started:

Step 1: Clone the repository:

git clone git@github.com:matter-labs/foundry-zksync.git

Step 2: Navigate to the project directory:

cd foundry-zksync

Step 3: Run the Installer: Execute the script to install the foundry-zksync binaries forge and cast

./install-foundry-zksync

Once the forge and cast binaries are installed, you can start using foundry-zksync. Source your preferred profile or refresh your terminal window to activate the changes. You are now ready to begin working with foundry-zksync.

For component-specific installations from source:

  • Forge: To install, execute:
cargo install --path ./crates/forge --profile local --force --locked
  • Cast: To install, run:
cargo install --path ./crates/cast --profile local --force --locked

For the entire suite:

  • Execute the following command for a comprehensive installation:
cargo build --release

Choose the installation that best fits your development needs.

Configuration

Initial Setup

After installation, initialize a new project with forge init <project_name>, which sets up the basic structure of a new Foundry project.

Project Configuration using foundry.toml

Foundry is designed to be very configurable. You can configure Foundry using a file called foundry.toml in the root of your project, or any other parent directory.

Configuration can be arbitrarily namespaced by profiles. The default profile is named default.

You can select another profile using the FOUNDRY_PROFILE environment variable. You can also override parts of your configuration using FOUNDRY_ or DAPP_ prefixed environment variables, like FOUNDRY_SRC.

forge init creates a basic, extendable foundry.toml file.

To see your current configuration, run forge config. To see only basic options (as set with forge init), run forge config --basic. This can be used to create a new foundry.toml file with forge config --basic > foundry.toml.

By default forge config shows the currently selected foundry profile and its values. It also accepts the same arguments as forge build. An example foundry.toml for ZKsync with zksolc configurations may look like:

[profile.default]
src = 'src'
out = 'out'
libs = ['lib']

[profile.zksync]
src = 'src'
libs = ['lib']
fallback_oz = true
is_system = false
mode = "3"

Private key setup with Foundry keystore

Follow these steps to securely store your wallet's private key to use it in Foundry projects:

  1. Extract Your Private Key: If you are using the local era node, use a private key from the available rich accounts. Otherwise, find your personal wallet's private key. For MetaMask users, here's how to export your wallet's private key.
  2. Create a Foundry keystore: Create a keystore and import your private key by running
cast wallet import myKeystore --interactive
# enter your PK when prompted, provide a password, and copy the returned address

It'll return an address (keystore address).

Note that the name myKeystore is arbitrary and can be updated. For our docs, we've chosen this name for consistency. If you decide to use another name, be sure to reference it when using cast.

Using the keystore

When running commands that require a private key, like forge create or cast send, use --account myKeystore --sender <KEYSTORE_ADDRESS>. This will require you to enter the keystore password you provided before.

Basic Usage

Running Tests

Use forge test --zksync to run tests written for your smart contracts.

For an overview of how to write tests using foundry-zksync please refer to Foundry testing here.

Deploying Smart Contracts with forge

Compilation with forge build --zksync

forge build --zksync is used for compiling smart contracts into ZKsync VM bytecode. The compiled files are stored in a structured directory at <PROJECT-ROOT>/zkout/.

Usage:

forge build [OPTIONS] --zksync

Key Compiler Options:

  • --use-zksolc <ZK_SOLC_VERSION>: Specify the zksolc version or a local zksolc path.
  • --is-system <SYSTEM_MODE>: Enables system contract compilation mode (true/false).
  • --force-evmla <FORCE_EVMLA>: Switch to the EVM legacy assembly pipeline.
  • --fallback-oz <FALLBACK_OZ>: Recompile with -Oz if bytecode is too large.
  • --detect-missing-libraries: Detect and report missing libraries.
  • -O, --optimization <LEVEL>: Set LLVM optimization levels.
  • --zk-optimizer: Optimize specifically for ZKsync.

Example Usage: Compile with default settings or specify zksolc version:

forge build --zksync

Deployment with forge create --zksync

The following commands make use of Foundry keystore instead of private keys. Learn how to create a keystore.

forge create --zksync deploys smart contracts to ZKsync.

Usage:

forge create <CONTRACT> [OPTIONS] --rpc-url <RPC-URL> --chain <CHAIN-ID> --account myKeystore --sender <KEYSTORE_ADDRESS> --zksync

Options:

  • --constructor-args <ARGS>: Specify constructor arguments.
  • --constructor-args-path <FILE>: File path for constructor arguments.
  • <CONTRACT>: Contract identifier in <path>:<contractname> format.
  • --factory-deps <FACTORY-DEPS>: Specify factory dependencies.

Example: Deploy Greeter.sol to ZKsync Sepolia Testnet:

Click to view the `Greeter.sol` contract
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

contract Greeter {
    string private greeting;

    constructor(string memory _greeting) {
        greeting = _greeting;
    }

    function greet() public view returns (string memory) {
        return greeting;
    }

    function setGreeting(string memory _greeting) public {
        greeting = _greeting;
    }
}
forge create src/Greeter.sol:Greeter --constructor-args "Hello ZKsync" --account myKeystore --sender <KEYSTORE_ADDRESS> --rpc-url https://sepolia.era.zksync.dev --chain 300 --zksync

Deploying Factory Contracts

To deploy contracts like GreeterFactory.sol, use the is-system flag.

Click to view the `GreeterFactory.sol` contract
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "./Greeter.sol";

contract Factory {
    Greeter[] public GreeterArray;

    function CreateNewGreeter(string memory _greeting) public {
        Greeter greeter = new Greeter(_greeting);
        GreeterArray.push(greeter);
    }

    function gfSetter(uint256 _greeterIndex, string memory _greeting) public {
        Greeter(address(GreeterArray[_greeterIndex])).setGreeting(_greeting);
    }

    function gfGetter(uint256 _greeterIndex) public view returns (string memory) {
        return Greeter(address(GreeterArray[_greeterIndex])).greet();
    }
}

Compile GreeterFactory.sol:

forge build --is-system=true --zksync

Deploy GreeterFactory.sol:

forge create src/GreeterFactory.sol:Factory --factory-deps src/Greeter.sol:Greeter --account myKeystore --sender <KEYSTORE_ADDRESS> --rpc-url https://sepolia.era.zksync.dev --chain 300 --zksync

Deploy Greeter.sol via GreeterFactory.sol:

cast send <FACTORY_ADDRESS> "CreateNewGreeter(string)" "ZKsync Rules"  --account myKeystore --sender <KEYSTORE_ADDRESS> --rpc-url https://sepolia.era.zksync.dev --chain 300

Interact with Greeter.sol

cast call <CONTRACT_ADDRESS> "greet()(string)" --rpc-url https://sepolia.era.zksync.dev --chain 300

Output:

0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c7a6b53796e632052756c65730000000000000000000000000000000000000000

To decode the output to a readable string:

cast to-ascii  0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000c7a6b53796e632052756c65730000000000000000000000000000000000000000

Output:

ZKsync Rules

Basic ZKsync Chain Interactions with cast

Introduction

This guide introduces you to fundamental interactions within the ZKsync chain using cast, a component of the foundry-zksync toolkit. Learn how to query chain IDs, retrieve client versions, check L2 ETH balances, obtain gas prices, and more.

Chain ID Retrieval

  • Local Node:
    Retrieve the Chain ID for a local ZKsync node with:
    cast chain-id --rpc-url http://localhost:3050
    

    Expected Output: 270, indicating the Chain ID of your local ZKsync node.
  • ZKsync Sepolia Testnet:
    For the ZKsync Sepolia Testnet, use:
    cast chain-id --rpc-url https://sepolia.era.zksync.dev
    

    Expected Output: 300, the Chain ID for the ZKsync Sepolia Testnet.

Client Version Information

Knowing the client version is vital for compatibility checks and debugging:

cast client --rpc-url https://sepolia.era.zksync.dev

Expected Output: ZKsync/v2.0, denoting the client version.

L2 Balance Check

Verify the Layer 2 (L2) balance of an account:

cast balance 0x8b1d48a69ACEbC6eb201e2F4d162A002203Bfe8E --rpc-url https://sepolia.era.zksync.dev

Expected Output: A numerical value, e.g., 774909739323110932, representing the account's L2 balance.

Current Gas Price

Fetch the current gas price on the network for transaction cost estimations:

cast gas-price --rpc-url https://sepolia.era.zksync.dev

Expected Output: A value such as 100000000, indicating the current gas price.

Latest Block Details

Gain insights into the latest block on the ZKsync chain:

cast block latest --rpc-url https://sepolia.era.zksync.dev

Expected Output: Detailed information about the latest block, including base fee per gas, gas limit, block hash, and more.

Sending Transactions

Initiate transactions, such as contract function calls, using cast:

cast send <CONTRACT_ADDRESS> <FUNCTION_SIGNATURE> <ARGUMENTS> --rpc-url <RPC-URL> --account myKeystore --sender <KEYSTORE_ADDRESS> --chain <CHAIN-ID>

Example:

cast send 0xe34E488C1B0Fb372Cc4a5d39219261A5a6fc7996 "setGreeting(string)" "Hello, ZKsync!" --rpc-url https://sepolia.era.zksync.dev --account myKeystore --sender <KEYSTORE_ADDRESS> --chain 300

This command calls the setGreeting function of a contract, updating the greeting to "Hello, ZKsync!".


Made with ❤️ by the ZKsync Community