# ZKsync Era contracts: the L1 and L2 contract set behind a zero-knowledge rollup

> The era-contracts repository holds the Solidity contracts of the ZKsync Era rollup, split into L1, L2, data availability, system and gas bound caller packages, with a hash check that proves the deployed bytecode matches the source. It suits a developer integrating with ZKsync or auditing the rollup itself, and the project states plainly that the system is still in alpha.

**matter-labs/era-contracts** — Smart Contract Submodule For zkSync Era

- Repository: https://github.com/matter-labs/era-contracts
- Stars: 539 · Forks: 425
- Language: Solidity
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/matter-labs-era-contracts

## What the rollup is, and what this repository contains

ZKsync Era is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum without compromising on security or decentralization. Since it is EVM compatible, in Solidity and Vyper, the claim is that 99% of Ethereum projects can redeploy without refactoring or re-auditing a single line of code. The project also uses an LLVM-based compiler that will eventually let developers write smart contracts in C++, Rust and other popular languages.

This repository contains both the L1 and the L2 ZKsync smart contracts, which is the distinction that matters when you read the source. The L1 contracts live on Ethereum and carry the parts of the system that must be settled there, while the L2 contracts are the contracts that run inside the rollup and handle the execution of transactions. Reading a bug report about ZKsync usually means working out which side of the bridge the problem is on.

The tree shows the split in full, with l1-contracts/ and l2-contracts/ as the two main packages, da-contracts/ for the data availability side, system-contracts/ for the contracts that implement the rollup's own system behaviour, gas-bound-caller/ as a separate small package, and lib/ and tools/ for shared code and command line utilities. There is also a docker/ directory, an audits/ directory, a docs/ directory, and a scripts/ directory.

Two files at the root are worth noticing because they are unusual for a contract repository. AllContractsHashes.json records the hashes of the contracts, and SystemConfig.json holds the system configuration, and there is a recompute_hashes.sh script and a CODEOWNERS file. The combination says the repository is managed as a release artefact with a verification step, not just as a pile of Solidity files.

The primary language is Solidity, the licence is MIT with the details in the LICENSE-MIT file, and the recent releases are v31-mainnet from 2026-10-08, v0.33.2 from 2026-10-06 and v0.33.0 from 2026-08-27, with the last push to the repository on 2026-10-20.

## The hash check, and why it is the most important script here

The package manifest defines three scripts around the hashes, and they are the interesting part of the build.

```json
  "scripts": {
    "calculate-hashes:check": "ts-node scripts/calculate-hashes.ts --check-only",
    "calculate-hashes:fix": "ts-node scripts/calculate-hashes.ts",
    "calculate-hashes:rebuild-and-fix": "./recompute_hashes.sh",
    "lint:check": "yarn lint:md && yarn lint:sol && yarn lint:ts && yarn prettier:check",
    "l1": "yarn workspace l1-contracts",
    "l2": "yarn workspace l2-contracts",
    "sc": "yarn workspace system-contracts",
    "da": "yarn workspace da-contracts",
    "gas-bound-caller": "yarn workspace gas-bound-caller"
  }
```

The check mode compares the computed contract hashes against the recorded ones and fails if they differ, the fix mode rewrites them, and the rebuild-and-fix script goes further and runs a rebuild. That is the mechanism that answers the question every contract audit asks, which is whether the bytecode deployed on chain is the bytecode that was reviewed.

For a rollup this matters more than for a normal contract, and the reason is in the disclaimers rather than in the scripts. A contract that is immutable, as many rollup system contracts are, cannot be fixed after deployment, so a mistake in the hash or in the source is a permanent problem rather than an upgradeable one. A repository that keeps the hashes in a file and checks them in CI is treating the deployed artefact as something to be pinned deliberately.

The per-workspace shortcuts are a convenience for the common case, and each one runs the underlying script inside a single package, so a change to the L1 contracts can be compiled and tested without touching the rest of the tree. That is a useful shape for a repository with this many packages, and it is why the yarn workspace configuration lists all five.

## The workspace layout, and the one dependency rule that comes with it

The yarn workspace configuration lists five packages, and the second half of it is the part that is easy to miss.

```json
  "workspaces": {
    "packages": [
      "l1-contracts",
      "l2-contracts",
      "da-contracts",
      "system-contracts",
      "gas-bound-caller"
    ],
    "nohoist": [
      "**/@openzeppelin/**"
    ]
  }
```

The nohoist entry for the OpenZeppelin packages is a deliberate exception. In a yarn workspace, the dependencies of the workspace packages are hoisted into a single top-level node_modules directory, which means two packages that need different versions of the same library silently share one. For a contract repository that pulls in OpenZeppelin, that is not acceptable, because the version of the library a contract compiles against is part of what gets reviewed and audited. Hoisting it would mean the actual code compiled into a contract could differ from the version declared in one of the packages.

So the workspace is configured to keep the OpenZeppelin dependencies local to the package that declares them, at the cost of a duplicated install and a slightly different dependency tree from a normal monorepo.

The repository root also carries a package.json marked private with its own version of 0.1.0, which is the convention for a workspace root that is not itself published. Everything here is consumed as source by a build, and the private flag stops any attempt to publish the root.

The rest of the root is the usual accompaniment of a project with a security process: a yarn.lock, a .gitmodules file, a SECURITY.md with a disclosure policy, a CONTRIBUTING.md, a CODEOWNERS file that names the people who must review changes to a given area, and an audits/ directory holding the results of previous audits. That last one, together with the disclaimer about further audits to come, is the clearest statement of how the project treats its own maturity.

## Linting across four languages, and the tooling choices behind it

The lint setup is unusual for a Solidity repository, because it covers Markdown, Solidity and TypeScript in one command, and it does it with a separate tool for each.

The check script chains four steps: Markdown linting over the markdown files, Solidity linting with solhint over the solidity files, TypeScript linting with ESLint, and a Prettier format check. The fix script is the same chain with the fix flags. So the rules that matter for a contract, the solhint rules, sit in the same command as the rules that matter for the documentation, and a contributor gets all of them or none of them.

The configuration files are all in the repository, which is the part that makes the setup reproducible. There is a .solhint.json with a .solhintignore beside it, a .prettierrc.js and a .prettierignore, a .markdownlintrc and a .markdownlintignore, an .eslintrc, a .editorconfig, a _typos.toml for spelling, and a .nvmrc pinning the Node version. The .prettierrc.js exists because Prettier is configured through a JavaScript file, and the Prettier plugin for Solidity is what allows the formatter to handle .sol files alongside the other languages, which is why the format command covers js, json, md, sol, ts and yaml in one pass.

The repository also has the scanning and hygiene tooling that a security project is expected to have: a .codespell/ directory, an .oss-scanner/ directory, a .lycheeignore for the link checker, a .githooks/ directory, and a SECURITY.md, a CONTRIBUTING.md and a CODEOWNERS file. The .oss-scanner directory in particular is a signal about what the project worries about, since it points at dependency scanning of the Solidity dependency tree rather than of the JavaScript one.

The dev dependency list is a good summary of the whole approach: the Matter Labs shared ESLint and Prettier configurations, typescript-eslint with the import resolver, eslint-plugin-import and eslint-plugin-prettier, prettier-plugin-solidity, solhint, markdownlint-cli, prettier and eslint. Nothing in there is a testing framework, which matches the fact that the verification story in this repository is the hash check rather than a unit test suite.

## Two disclaimers, and the one that should stop a fork

The README carries two disclaimers and they are not the same kind of statement.

The first one is about tooling. It is used as a submodule of a private repo, and the compilation and test scripts should work without additional tooling, but others may not. That is a modest warning about the repository not being a self-contained project, which is what you would expect of a directory that is designed to be vendored into a larger build.

The second one is much more serious, and it is the one to read carefully. ZKsync Era has been through lots of testing and audits, and although it is live, it is still in alpha state and will go through more audits and bug bounty programs. The project says it would love to hear the community's thoughts and suggestions about it, and then states the consequence directly: it is important to state that forking it now can potentially lead to missing important security updates, critical features and performance improvements.

That last sentence is a statement about the economics of forking a rollup, and it is more specific than a general caution. A rollup is not a library you can vendor and forget, because its correctness depends on the implementation matching the proof system, the operator, the upgrade path and the rest of the protocol. A fork that misses a security fix inherits the vulnerability without the fix, and misses performance work because the fork is a different codebase with a different maintainer.

The alpha status also qualifies the first claim in the readme, that 99% of Ethereum projects can redeploy without refactoring or re-auditing a single line of code. The EVM compatibility is what makes the redeployment cheap, and it is the compatibility of the execution environment, not a statement that the rollup is finished. A team moving a contract to ZKsync Era is moving it to a system that is live but still changing, and the documentation for the contracts is maintained in the separate zksync-era repository under the contracts specifications, which is where a reader should go for the behaviour rather than for the source.

## Conclusion

The era-contracts repository is worth reading if you are integrating with ZKsync Era or evaluating its security model, because it is the actual source of the contracts the rollup runs, and because the hash verification in it is the mechanism that ties a deployed contract to a reviewed source file. It is the wrong tool if you want to deploy an application, since the framework is for building on the rollup rather than on the contracts themselves. Before you build on it, check three things: that you read the alpha disclaimer, because the documentation states the system has been through lots of testing and audits but is still in alpha and will go through more audits and bug bounty programs; that you do not fork it, because the same notice warns that forking now can lead to missing important security updates, critical features and performance improvements, and this repository is explicitly consumed as a submodule of a private repository rather than a release you copy; and that you follow the documented tooling, because the README says the compilation and test scripts should work without additional tooling but others may not, and the hash scripts are part of the workflow rather than an optional extra.

## FAQ

### What is in the ZKsync Era era-contracts repository?

It contains both the L1 and the L2 ZKsync smart contracts, organised as yarn workspaces for l1-contracts, l2-contracts, da-contracts, system-contracts and gas-bound-caller, alongside shared lib/ and tools/ code, an audits directory, a docker directory and the AllContractsHashes.json and SystemConfig.json files at the root. The contracts are Solidity and the repository is MIT licensed.

### How do the contract hash scripts work?

There are three of them. calculate-hashes:check runs the hash script in check-only mode and fails when the computed hashes differ from the recorded ones, calculate-hashes:fix rewrites them, and calculate-hashes:rebuild-and-fix runs the recompute_hashes.sh script for a full rebuild. The point of the check is to tie the bytecode that is deployed to the source that was reviewed.

### Is ZKsync Era ready to build on, and should I fork the contracts?

The project states that although it is live, it is still in alpha and will go through more audits and bug bounty programs, and it warns that forking it now can potentially lead to missing important security updates, critical features and performance improvements. The documentation for the contracts is maintained in the separate zksync-era repository, and the contracts repository itself is used as a submodule of a private repository.

## Sources

- [Issues](https://github.com/matter-labs/era-contracts/issues)
- [License: MIT](https://github.com/matter-labs/era-contracts/blob/main/LICENSE)
- [matter-labs/era-contracts on GitHub](https://github.com/matter-labs/era-contracts)
- [README](https://github.com/matter-labs/era-contracts/blob/main/README.md)
- [Releases](https://github.com/matter-labs/era-contracts/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/matter-labs-era-contracts
