Installing the ZKsync Vyper Compiler Toolchain

To compile contracts for ZKsync, you need the ZKsync Vyper compiler toolchain. It consists of two components:

  1. The main component: zkvyper.
  2. The additional component: vyper, which produces Vyper artifacts used by zkvyper.

System Requirements

It is recommended to have at least 4 GB of RAM to compile large projects. The compilation process is parallelized by default, so the number of threads used is equal to the number of CPU cores.

Large projects can consume a lot of RAM during compilation on machines with a high number of cores. If you encounter memory issues, consider reducing the number of threads using the --threads option.

The table below outlines the supported platforms and architectures:

CPU/OSMacOSLinuxWindows
x86_64
arm64

Please avoid using outdated distributions of operating systems, as they may lack the necessary dependencies or include outdated versions of them. zkvyper is only tested on recent versions of popular distributions, such as MacOS 11.0 and Windows 10.

musl-based builds are deprecated, but they are still supported to preserve tooling compatibility.
Starting from zkvyper v1.5.4, we are shipping builds statically linked with the GNU C library.

Versioning

The zkvyper versioning scheme does not yet follow the Semantic Versioning specification. Instead, its major and minor versions match those of the EraVM protocol for which zkvyper produces bytecode. The patch version is incremented with each release, regardless of whether breaking changes are introduced. Therefore, please consult the changelog before updating the compiler.

We recommend always using the latest version of zkvyper and vyper to benefit from the latest features and bug fixes.

Installing zkvyper

You can install the ZKsync Vyper compiler toolchain using the following methods:

  1. Use Foundry, Hardhat, or other popular toolkits, so they will manage the compiler installation and their dependencies for you. See Ethereum Development Toolkits.
  2. Download pre-built binaries of vyper and zkvyper. See Static Executables.
  3. Build zkvyper from sources. See Building from Source.

For small projects, learning and research purposes, zkvyper and vyper executables without a toolkit are sufficient.

Installing vyper

Running zkvyper requires the Vyper compiler vyper, which is called by zkvyper as a child process. To point zkvyper to the location of vyper, use one of the following methods:

  1. Add the location of vyper to the environment variable PATH.

    For example, if you have downloaded vyper to the directory /home/username/opt, you can execute the following command, or append it to the configuration file of your shell:

    export PATH="/home/username/opt:${PATH}"
    
  2. Alternatively, when you run zkvyper, provide the full path to vyper using the --vyper option.

    For example, if vyper is located in your current working directory, you can point to it with this command:

    zkvyper --vyper './vyper' --bin 'Greeter.vy'
    

The second option is more convenient if you are using different versions of vyper for different projects. zkvyper only supports vyper of versions 0.3.3 and 0.3.9 and newer, but does not support versions from 0.3.4 to 0.3.8.

Ethereum Development Toolkits

For large codebases, it is more convenient to use the ZKsync compiler toolchain via toolkits like Foundry and Hardhat. These tools manage the compiler executables and their dependencies, and provide additional features like incremental compilation and caching.

The ZKsync toolchain is supported by the following toolkits:

TODO: Add links to the tutorials

Static Executables

We ship zkvyper binaries on the releases page of matter-labs/era-compiler-vyper repository. This repository maintains intuitive and stable naming for the executables and provides a changelog for each release. Tools using zkvyper will download the binaries from this repository and cache them locally.

The matter-labs/era-compiler-vyper repository only contains builds for versions 1.4.0 and newer.
You can download older versions from the main branch or the releases page of the deprecated repository for zkvyper executables.
If any of your projects are still using the old locations, please change their download URLs to the new one.

All binaries are statically linked and must work on all recent platforms without issues. zkvyper is fully written in Rust, aiming to minimize incompatibilities with the environment.

Building from Source

Please consider using the pre-built executables before building from source. Building from source is only necessary for development, research, and debugging purposes. Deployment and production use cases should rely only on the officially released executables.

  1. Install the necessary system-wide dependencies.

    • For Linux (Debian):
    apt install cmake ninja-build curl git libssl-dev pkg-config clang lld
    
    • For Linux (Arch):
    pacman -Syu which cmake ninja curl git pkg-config clang lld
    
    • For MacOS:

      1. Install the Homebrew package manager by following the instructions at brew.sh.

      2. Install the necessary system-wide dependencies:

        brew install cmake ninja coreutils
        
      3. Install a recent build of the LLVM/Clang compiler using one of the following tools:

  2. Install Rust.

    The easiest way to do it is following the latest official instructions.

The Rust version used for building is pinned in the rust-toolchain.toml file at the repository root. cargo will automatically download the pinned version of rustc when you start building the project.

  1. Clone and checkout this repository.

    git clone https://github.com/matter-labs/era-compiler-vyper
    
  2. Install the ZKsync LLVM framework builder. This tool clones the repository of ZKsync LLVM Framework and runs a sequence of build commands tuned for the needs of ZKsync compiler toolchain.

    cargo install compiler-llvm-builder
    

    To fine-tune your build of ZKsync LLVM framework, refer to the section Fine tuning ZKsync LLVM build

Always use the latest version of the builder to benefit from the latest features and bug fixes. To check for new versions and update the builder, simply run cargo install compiler-llvm-builder again, even if you have already installed the builder. The builder is not the ZKsync LLVM framework itself, but a tool to build it. By default, it is installed in ~/.cargo/bin/, which is usually added to your PATH during the Rust installation process.

  1. Clone and build the ZKsync LLVM framework using the zksync-llvm tool.

    # Navigate to the root of your local copy of this repository.
    cd era-compiler-vyper
    # Clone the ZKsync LLVM framework. The branch is specified in the file `LLVM.lock`.
    zksync-llvm clone
    # Build the ZKsync LLVM framework.
    zksync-llvm build
    

    For more information and available build options, run zksync-llvm build --help.

    You can also clone and build LLVM framework outside of the repository root. In this case, do the following:

    1. Provide an LLVM.lock file in the directory where you run zksync-llvm. See the default LLVM.lock for an example.

    2. Ensure that LLVM.lock selects the correct branch of the ZKsync LLVM Framework repository.

    3. Before proceeding to the next step, set the environment variable LLVM_SYS_170_PREFIX to the path of the directory with the LLVM build artifacts. Typically, it ends with target-llvm/build-final, which is the default LLVM target directory of the LLVM builder. For example:

      export LLVM_SYS_170_PREFIX=~/repositories/era-compiler-vyper/target-llvm/build-final 
      
  2. Build the zkvyper executable.

    cargo build --release
    

    The zkvyper executable will appear at ./target/release/zkvyper, where you can run it directly or move it to another location.

    If cargo cannot find the LLVM build artifacts, return to the previous step and ensure that the LLVM_SYS_170_PREFIX environment variable is set to the absolute path of the directory target-llvm/build-final.

Tuning the ZKsync LLVM build

  • For more information and available build options, run zksync-llvm build --help.

  • Use the --use-ccache option to speed up the build process if you have ccache installed.

  • To build ZKsync LLVM framework using specific C and C++ compilers, pass additional arguments to CMake using the --extra-args option:

    # Pay special attention to character escaping.
    
    zksync-llvm build \
      --use-ccache \
      --extra-args \
        '\-DCMAKE_C_COMPILER=/opt/homebrew/Cellar/llvm@18/18.1.8/bin/clang' \
        '\-DCMAKE_BUILD_TYPE=Release' \
        '\-DCMAKE_CXX_COMPILER=/opt/homebrew/Cellar/llvm@18/18.1.8/bin/clang++' 
    

Building LLVM manually

  • If you prefer building your ZKsync LLVM manually, include the following flags in your CMake command:

    # We recommended using the latest version of CMake.
    
    -DLLVM_TARGETS_TO_BUILD='EraVM;EVM'
    -DLLVM_ENABLE_PROJECTS='lld'
    -DBUILD_SHARED_LIBS='Off'
    

For most users, the ZKsync LLVM builder is the recommended way to build the ZKsync LLVM framework. This section exists for the ZKsync toolchain developers and researchers with specific requirements and experience with the LLVM framework. We are going to present a more detailed guide for LLVM contributors in the future.

Command Line Interface (CLI)

The CLI of zkvyper is designed with resemblance to the CLI of vyper. There are two input/output (I/O) modes in the zkvyper interface:

All toolkits using zkvyper must be operating in combined JSON mode and follow its specification. It will make the toolkits more robust and future-proof, as the combined JSON mode is the most versatile and used for the majority of popular projects.

This page focuses on the basic CLI mode. For more information on combined JSON, see this page.

Basic CLI

Basic CLI mode is the simplest way to compile a file with the source code.

To compile a basic Vyper contract, make sure that the vyper compiler is present in your environment and run the example.

The rest of this section describes the available CLI options and their usage. You may also check out zkvyper --help for a quick reference.

--vyper

Specifies the path to the vyper compiler. Useful when the vyper compiler is not available in the system path.

Usage:

zkvyper './Simple.vy' --vyper '/path/to/vyper'

Examples in the subsequent sections assume that vyper is installed and available in the system path. If you prefer specifying the full path to vyper, use the --vyper option with the examples below.

Input Files

zkvyper supports multiple input files. The following command compiles two Vyper files and prints the bytecode:

zkvyper './Simple.vy' './Complex.vy'

--format / -f

This option can be used for two purposes:

  1. Switch to combined JSON mode.
  2. Select the desired output in basic CLI mode.

In basic CLI mode, the following selectors are available:

SelectorSourceDescription
combined_jsonbothSwitches to combined JSON mode. Cannot be used with other selectors.
ir_jsonvyperVyper LLL IR that is used by zkvyper to produce LLVM IR.
astvyperAbstract Syntax Tree (AST) of the Vyper source code.
abivyperApplication Binary Interface (ABI) of the Vyper contract.
method_identifiersvyperHashes of function signature of the Vyper contract.
layoutvyperStorage and code layouts of the Vyper contract.
userdocvyperUser documentation of the Vyper contract.
devdocvyperDeveloper documentation of the Vyper contract.
eravm_assemblyzkvyperEraVM assembly of the Vyper contract.
project_metadatazkvyperProject metadata of the Vyper contract.

Some data above is produced by vyper, whereas the rest is produced by zkvyper, as designated in the Source column.

Usage:

zkvyper './Simple.vy' --format 'ir_json,ast,abi,method_identifiers,layout,userdoc,devdoc,eravm_assembly,project_metadata'

Output:

Contract `/Users/hedgarmac/src/era-compiler-tester//tests/vyper/simple/default.vy`:
0x0000000100200190000000110000c13d0000000a001001980000001d0000613d...(truncated)
{"seq":[(truncated)]}
{"contract_name":"/Users/hedgarmac/src/era-compiler-tester/tests/vyper/simple/default.vy","ast":{(truncated)}}
[{"inputs":[],"name":"first","outputs":[{"name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"second","outputs":[{"name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}]
{"first()":"0x3df4ddf4","second()":"0x5a8ac02d"}
{}
{}
{}
Contract `/Users/hedgarmac/src/era-compiler-tester//tests/vyper/simple/default.vy` assembly:
        .text
        .file   "default.vy"
        .globl  __entry
__entry:
.func_begin0:
        and!    1, r2, r0
        jump.ne @.BB0_9
        and!    code[@CPI0_1], r1, r0
        jump.eq @.BB0_10
... (truncated)

Project metadata:
{"evm_version":"cancun","llvm_options":[],"optimizer_settings":"M3B3","source_code_hash":[147,242,126,144,(truncated),22,153,132,218],"source_version":"0.4.0","zk_version":"1.5.8"}

The output order above is fixed and cannot be changed by the order of the selectors in the --format argument:

  1. Bytecode
  2. LLL IR JSON
  3. AST
  4. ABI
  5. Method identifiers
  6. Layout
  7. User documentation
  8. Developer documentation
  9. EraVM assembly
  10. Project metadata

--output-dir

Specifies the output directory for build artifacts. Can only be used in basic CLI and combined JSON modes.

Usage in basic CLI mode:

zkvyper './Simple.vy' --output-dir './build/'
ls './build/'

Output:

default.vy.zbin

Usage in combined JSON mode:

zkvyper './Simple.vy' --format 'combined_json' --output-dir './build/'
ls './build/'

Output:

combined.json

--overwrite

Overwrites the output files if they already exist in the output directory. By default, zkvyper does not overwrite existing files.

Can only be used in combination with the --output-dir option.

Usage:

zkvyper './Simple.vy' --format 'combined_json' --output-dir './build/' --overwrite

If the --overwrite option is not specified and the output files already exist, zkvyper will print an error message and exit:

Refusing to overwrite an existing file "./build/combined.json" (use --overwrite to force).

--version

Prints the version of zkvyper and the hash of the LLVM commit it was built with.

Usage:

zkvyper --version

--help

Prints the help message.

Usage:

zkvyper --help

Other I/O Modes

To switch to combined JSON mode, use the --format option with the combined_json argument:

zkvyper './Simple.vy' --format 'combined_json'

The mode-altering CLI options are mutually exclusive. This means that only one of the options below can be enabled at a time:

  • --format / -f
  • --llvm-ir
  • --eravm-assembly
  • --disassemble

zkvyper Compilation Settings

The options in this section are only configuring the zkvyper compiler and do not affect the underlying vyper compiler.

--optimization / -O

Sets the optimization level of the LLVM optimizer. Available values are:

LevelMeaningHints
0No optimizationBest compilation speed: for active development
1Performance: basicFor optimization research
2Performance: defaultFor optimization research
3Performance: aggressiveDefault value. Best performance: for production
sSize: defaultFor optimization research
zSize: aggressiveBest size: for contracts with size constraints

For most cases, it is fine to use the default value of 3. You should only use the level z if you are ready to deliberately sacrifice performance and optimize for size.

Large contracts may hit the EraVM or EVM bytecode size limit. In this case, it is recommended to use the --fallback-Oz option rather than set the z level.

--fallback-Oz

Sets the optimization level to z for contracts that failed to compile due to overrunning the bytecode size constraints.

Under the hood, this option automatically triggers recompilation of contracts with level z. Contracts that were successfully compiled with the original --optimization setting are not recompiled.

It is recommended to have this option always enabled to prevent compilation failures due to bytecode size constraints. There are no known downsides to using this option.

--metadata-hash

Specifies the hash function used for project metadata.

For security reasons, the source code of all input Vyper contracts are hashed together, so a change in one contract will affect the metadata hash appended to the bytecode of all contracts, even if there are no dependency relations between them. It may be changed in the future, so each contract will be hashed separately just like *vyper* does.

The following values are allowed:

ValueSizePaddingReference
none0 B0-32 B
keccak25632 B0-32 BSHA-3 Wikipedia Page
ipfs44 B20-52 BIPFS Documentation

The default value is keccak256.

EraVM requires its bytecode size to be an odd number of 32-byte words. If the size after appending the hash does not satisfy this requirement, the hash is prepended with zeros according to the Padding column in the table above.

Usage:

zkvyper './Simple.vy' --metadata-hash 'ipfs'

--suppress-warnings

Tells the compiler to suppress specified warnings. The option accepts multiple string arguments, so make sure they are properly separated by whitespace.

Only one warning can be suppressed with this option: txorigin.

Usage:

zkvyper './Simple.vy' --suppress-warnings 'txorigin'

--llvm-options

Specifies additional options for the LLVM framework. The argument must be a single quoted string following a = separator.

Usage:

zkvyper './Simple.vy' --llvm-options='-eravm-jump-table-density-threshold=10'

The --llvm-options option is experimental and must only be used by experienced users. All supported options will be documented in the future.

vyper Compilation Settings

The options in this section are only configuring vyper, so they are passed directly to its child process, and do not affect the zkvyper compiler.

--evm-version

Specifies the EVM version vyper will produce artifacts for. Only LLL IR is known to be affected by this option. For instance, if the EVM version is set to cancun, the LLL IR may contain mcopy instructions.

EVM version only affects IR artifacts produced by vyper and does not affect EraVM bytecode produced by zkvyper.

The following values are allowed by zkvyper:

  • homestead
  • tangerineWhistle
  • spuriousDragon
  • byzantium
  • constantinople
  • petersburg
  • istanbul
  • berlin
  • london
  • paris
  • shanghai
  • cancun
  • prague

However, have in mind that many of them are not supported by vyper, or only supported by its newer versions. For instance, the --help message of vyper v0.4.0 only declares the following EVM versions as supported: london, paris, shanghai, cancun. For the full list of supported EVM versions, refer to the official vyper documentation.

Usage:

zkvyper './Simple.vy' --evm-version 'cancun'

--disable-vyper-optimizer

Disables the optimizer of the vyper compiler.

The optimizer is enabled by default for vyper v0.3.x. For vyper v0.4.x it is disabled and cannot be enabled, as the optimized LLL IR is not compatible with zkvyper.

zkvyper relies on the LLVM optimizer, so the vyper optimizer is not affecting the size or performance of the final bytecode significantly.

Usage:

zkvyper './Simple.vy' --disable-vyper-optimizer

--enable-decimals

Enables decimals in vyper v0.4.0.

Usage:

zkvyper './Simple.vy' --enable-decimals

--search-paths

Passes additional search paths to vyper.

Usage:

zkvyper './Simple.vy' --search-paths '/path/to/libraries-1/' '/path/to/libraries-2/'

Multi-Language Support

zkvyper supports input in multiple programming languages:

The following sections outline how to use zkvyper with these languages.

--llvm-ir

Enables the LLVM IR mode. In this mode, input is expected to be in the LLVM IR language. The output works the same way as with Vyper input.

Unlike vyper, zkvyper is an LLVM-based compiler toolchain, so it uses LLVM IR as an intermediate representation. It is not recommended to write LLVM IR manually, but it can be useful for debugging and optimization purposes. LLVM IR is more low-level than Vyper LLL in the ZKsync compiler toolchain IR hierarchy, so vyper is not used for compilation.

Usage:

zkvyper --llvm-ir './Simple.ll'

Output:

Contract `<absolute-path>/Simple.ll`:
0x000000000002004b000000070000613d000000200100003900000000001004...

--eravm-assembly

Enables the EraVM Assembly mode. In this mode, input is expected to be in the EraVM assembly language. The output works the same way as with Vyper input.

EraVM assembly is a representation the closest to EraVM bytecode. It is not recommended to write EraVM assembly manually, but it can be even more useful for debugging and optimization purposes than LLVM IR.

For the EraVM assembly specification, visit the EraVM documentation.

Usage:

zkvyper --eravm-assembly './Simple.zasm'

Output:

Contract `<absolute-path>/Simple.zasm`:
0x000000000120008c000000070000613d00000020010000390000000000100435000000000001043500000005010000410000000c0001042e0000002a01000039000000000010043500000004010000410000000c0001042e0000000b000004320000000c0001042e0000000d00010430000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000043bbf1d8e1b7b1d452f006fe83028ba3b7853f9ea8a4635f4c584fe1dc6429b5

Integrated Tooling

zkvyper includes several tools provided by the LLVM framework out of the box, such as disassembler and linker. The following sections describe the usage of these tools.

--disassemble

Enables the disassembler mode.

zkvyper includes an LLVM-based disassembler that can be used to disassemble compiled bytecode.

The disassembler input must be files with a hexadecimal string. The disassembler output is a human-readable representation of the bytecode, also known as EraVM assembly.

Usage:

cat './input.zbin'

Output:

0x0000008003000039000000400030043f0000000100200190000000140000c13d00000000020...
zkvyper --disassemble './input.zbin'

Output:

File `input.zbin` disassembly:

       0: 00 00 00 80 03 00 00 39       add     128, r0, r3
       8: 00 00 00 40 00 30 04 3f       stm.h   64, r3
      10: 00 00 00 01 00 20 01 90       and!    1, r2, r0
      18: 00 00 00 14 00 00 c1 3d       jump.ne 20
      20: 00 00 00 00 02 01 00 19       add     r1, r0, r2
      28: 00 00 00 0b 00 20 01 98       and!    code[11], r2, r0
      30: 00 00 00 23 00 00 61 3d       jump.eq 35
      38: 00 00 00 00 01 01 04 3b       ldp     r1, r1

Debugging

--debug-output-dir

Specifies the directory to store intermediate build artifacts. The artifacts can be useful for debugging and research.

The directory is created if it does not exist. If artifacts are already present in the directory, they are overwritten.

The intermediate build artifacts can be:

NameFile extension
LLLlll
LLVM IRll
EraVM assemblyzasm

Usage:

zkvyper './Simple.vy' --debug-output-dir './debug/'
ls './debug/'

Output:

<absolute-path-with-underscores>_Simple.vy.lll
<absolute-path-with-underscores>_Simple.vy.runtime.optimized.ll
<absolute-path-with-underscores>_Simple.vy.runtime.unoptimized.ll
<absolute-path-with-underscores>_Simple.vy.zasm

The output file name is constructed as follows: <AbsoluteContractPathWithUnderscores>_<ContractName>.<Modifiers>.<Extension>.

--llvm-verify-each

Enables the verification of the LLVM IR after each optimization pass. This option is useful for debugging and research purposes.

Usage:

zkvyper './Simple.vy' --llvm-verify-each

--llvm-debug-logging

Enables the debug logging of the LLVM IR optimization passes. This option is useful for debugging and research purposes.

Usage:

zkvyper './Simple.vy' --llvm-debug-logging

Combined JSON

Combined JSON is an I/O mode designed as a convenient way of using zkvyper from tooling that call it as a child process. In this mode, input data is provided via the CLI, and JSON output can be easily read by both humans and programs.

Usage

To enable combined JSON, pass the -f combined_json option to zkvyper:

zkvyper './MyContract.vy' --format 'combined_json'
It is only possible to use Combined JSON with Vyper input, so the path to vyper must be always provided to *zkvyper*. Support for other languages is planned for future releases.

Output Format

The format below is a modification of the original combined JSON output format implemented by vyper. It means that there are:

  • zkvyper-specific options that are not present in the original format: they are marked as zkvyper in the specification below.
  • vyper-specific options that are not supported by zkvyper: they are not mentioned in the specification below.

zkvyper always produces absolute contract paths in combined JSON output. It was done for unification purposes, as various versions of vyper are known to produce either absolute or relative paths.

{
  "<absolute-path>/MyContract.vy": {
    // The bytecode as a hexadecimal string.
    "bytecode": "0000008003000039000000400030043f0000000100200190000000130000c13d...",
    // For EraVM, same as "bytecode".
    "bytecode_runtime": "0000008003000039000000400030043f0000000100200190000000130000c13d...",
    // Contract Vyper LLL IR, used by zkvyper to produce LLVM IR.
    "ir_json": {/* ... */},
    // Contract AST.
    "ast": {/* ... */},
    // Hashes of function signatures.
    "method_identifiers": {/* ... */},
    // Contract ABI specification.
    "abi": [/* ... */],
    // Storage layout.
    "layout": {/* ... */},
    // User documentation.
    "userdoc": {/* ... */},
    // Developer documentation.
    "devdoc": {/* ... */},
    // zkvyper: EraVM assembly.
    "assembly": "\t.text\n\tincsp\t3\n\t.file\t\"main.vy\"\n...",
    // zkvyper: Warnings produced during compilation.
    "warnings": [/* ... */],
    // zkvyper: Optional bytecode hash of the minimal proxy, if the contract uses "create_minimal_proxy_to".
    "factory_deps": {
      "01000035999a1d871cf4d876ed735fa6a8f3bbeb3f94d210bf4520ed94f35654": "__VYPER_MINIMAL_PROXY_CONTRACT"
    }
  },
  // zkvyper: Metadata preimage whose hash can be appended to the bytecode.
  "extra_data": {
    // EVM version passed to the vyper compiler.
    "evm_version": "cancun",
    // LLVM extra options.
    "llvm_options": [/* ... */],
    // LLVM optimizer settings.
    // The format is "M{level}B{level}", where M = LLVM middle-end, B = LLVM back-end, and levels: 0-3 | s | z.
    "optimizer_settings": "M3B3",
    // Byte-array hash of the whole project's source code.
    "source_code_hash": [147,242,126,144,/* ... */22,153,132,218],
    // Version of vyper.
    "source_version": "0.4.0",
    // Version of zkvyper.
    "zk_version": "1.5.8"
  },
  // Version of vyper.
  "version": "0.4.0",
  // zkvyper: Version of zkvyper.
  "zk_version": "1.5.8"
}

Building ZKsync compiler with sanitizers

This is a guide on how to build the ZKsync Vyper compiler with sanitizers enabled.

Introduction

Sanitizers are tools that help find bugs in code. They are used to detect memory corruption, leaks, and undefined behavior. The most common sanitizers are AddressSanitizer, MemorySanitizer, and ThreadSanitizer.

If you are not familiar with sanitizers, see the official documentation.

Who is this guide for?

This guide is for developers who want to debug issues with ZKsync compilers.

Prerequisites

For sanitizers build to work, the host LLVM compiler version that is used to build LLVM MUST have the same version as the LLVM compiler that is used internally by `rustc` to build the ZKsync compiler.

You can check the LLVM version used by rustc by running the following command rustc --version --verbose.

Build steps

The general steps to have a sanitizer enabled build include:

  1. Build the LLVM framework with the required sanitizer enabled.
  2. Build zkvyper with the LLVM build from the previous step.

Please, follow the common installation instructions until the zksync-llvm build step.

This guide assumes the build with AddressSanitizer enabled.

Build LLVM with sanitizer enabled

When building LLVM, use --sanitizer <sanitizer> option and set build type to RelWithDebInfo:

zksync-llvm build --sanitizer=Address --build-type=RelWithDebInfo
Please note that the default Apple Clang compiler is not compatible with Rust. You need to install LLVM using Homebrew and specify the path to the LLVM compiler in the `--extra-args` option. For example:
zksync-llvm build --sanitizer=Address \
  --extra-args '\-DCMAKE_C_COMPILER=/opt/homebrew/opt/llvm/bin/clang' \
               '\-DCMAKE_CXX_COMPILER=/opt/homebrew/opt/llvm/bin/clang++'

Build zkvyper with sanitizer enabled

To build the ZKsync compiler with sanitizer enabled, you need to set the RUSTFLAGS environment variable to -Z sanitizer=address and run the cargo build command. Sanitizers build is the feature that is available only for the nightly Rust compiler, it is recommended to set RUSTC_BOOTSTRAP=1 environment variable before the build.

It is also mandatory to use --target option to specify the target architecture. Otherwise, the build will fail. Please, check the table below to find the correct target for your platform.

PlatformLLVM Target Triple
Linux arm64aarch64-unknown-linux-gnu
Linux x86x86_64-unknown-linux-gnu
macOS arm64aarch64-apple-darwin
macOS x86x86_64-apple-darwin

Additionally, for proper reports symbolization it is recommended to set the ASAN_SYMBOLIZER_PATH environment variable. For more info, see symbolizing reports section of LLVM documentation.

For example, to build the ZKsync compiler for macOS arm64 with AddressSanitizer enabled, run the following command:

export RUSTC_BOOTSTRAP=1
export ASAN_SYMBOLIZER_PATH=$(which llvm-symbolizer) # check the path to llvm-symbolizer
TARGET=aarch64-apple-darwin # Change to your target
RUSTFLAGS="-Z sanitizer=address" cargo test --target=${TARGET}

Congratulations! You have successfully built the ZKsync compiler with sanitizers enabled.

Please, refer to the official documentation for more information on how to use sanitizers and their types.