Validium Docs
  • Overview
  • Connect to Validium
  • Start Coding 🚀
    • Quickstart
      • Overview
      • Deploy using Validium CLI
      • Deploy using Quickstart Repository
      • Deploy your first contract
      • Create an ERC20 token
  • Tooling
    • Block Explorers
    • Hardhat-Validium
      • Overview
      • Installation
      • Guides
        • Getting started
        • Migrating Hardhat project to Validium
        • Compiling non-inlinable libraries
      • Plugins
        • hardhat-zksync
        • hardhat-zksync-solc
        • hardhat-zksync-vyper
        • hardhat-zksync-deploy
        • hardhat-zksync-upgradable
        • hardhat-zksync-verify
        • hardhat-zksync-verify-vyper
        • hardhat-zksync-ethers
        • hardhat-zksync-node
        • Hardhat Community Plugins
    • Foundary
      • Overview
      • Installation
      • Getting Started
      • Migration Guide
        • Overview
        • Compilation
        • Deployment
        • Testing
  • Test and Debug
    • Getting Started
    • Docker L1 - L2 Nodes
    • In-Memory Node
    • Continuous Integration
    • Hardhat
    • Foundry
  • API Reference
    • Overview
    • Conventions
    • ZKs JSON-RPC API
    • Debug JSON-RPC API
    • Ethereum JSON-RPC API
    • PubSub JSON-RPC API
  • Concepts
    • Transaction Lifecycle
    • Blocks and Batches
    • Validium Network Fee Mechanism
    • Finality
    • System Upgrades
    • ZK Chains
    • Data Availability
      • Overview
      • Recreating L2 state from L1 pubdata
      • Validiums
    • Account Abstraction
    • L1 <-> L2 Communication
  • Components
    • Overview
    • Smart & System Contracts
      • Smart Contracts
      • System Contracts
    • Shared Bridges
    • Sequencer / Server
    • Validium Network EVM
      • Overview
      • Bootloader
      • Precompiles
      • Virtual Machine Specification
        • ZKsync Virtual Machine primer
        • VM Formal Specification
    • Prover
      • Overview
      • ZK Terminology
      • Running the Prover
      • Circuits
        • Overview
        • Circuit Testing
        • CodeDecommitter
        • DemuxLogQueue
        • ECRecover
        • KeccakRoundFunction
        • L1MessagesHasher
        • LogSorter
        • Main VM
        • RAMPermutation
        • Sha256RoundFunction
        • StorageApplication
        • Sorting and Deduplicating
          • Overview
          • SortDecommitments
          • StorageSorter
          • LogSorter
      • Boojum Gadgets
      • Boojum Function - `check_if_satisfied`
    • Compiler
      • Compiler Toolchain Overview
        • Compiler Toolchain Overview
        • Solidity Compiler
        • Vyper Compiler
        • LLVM Framework
      • Specification
        • Overview
        • Code Separation
        • System Contracts
        • Exception Handling
        • EVM Legacy Assembly Translator
        • Instructions
          • Instruction Reference
          • EVM
            • Native EVM Instructions
            • Arithmetic
            • Bitwise
            • Block
            • Call
            • Create
            • Environment
            • Logging
            • Logical
            • Memory
            • Return
            • Sha3
            • Stack
          • Extensions
            • Overview
            • Validium Network Extension Simulation (call)
            • Validium Network Extension Simulation (verbatim)
          • EVM Legacy Assembly
          • Yul
        • EraVM Binary Layout
    • Fee Withdrawer
    • Portal - Wallet + Bridge
    • Block Explorer
    • Transaction filtering
Powered by GitBook
On this page
  • Ethereum / ZKsync differences in contract deployment
  • Smart contract security
  • Deploying contracts from L1
  1. Developer Reference
  2. Ethereum Differences

Contract Deployment

Overview of the differences in contract deployment.

Last updated 7 months ago


In order to maintain the same level of security as the L1, the ZKsync operator is required to publish the code for each contract it deploys on the Ethereum chain. However, if multiple contracts are deployed using the same code, the operator only needs to publish it on Ethereum once. While the initial deployment of contracts can be relatively expensive, utilizing contract factories that deploy contracts with the same code multiple times can lead to huge savings compared to the L1.

These specific requirements ensure that the process of deploying smart contracts on zkEVM complies to a crucial rule: the operator must be aware of the contract's code before deployment. Consequently, deploying contracts can only be accomplished through EIP712 transactions, with the factory_deps field containing the bytecode provided.

.

Ethereum / ZKsync differences in contract deployment

How deploying contracts works on Ethereum.

To deploy a contract on Ethereum, a user sends a transaction with the empty to field and with the data field of the transaction equal to the contract creation bytecode concatenated with the constructor parameters.

How deploying contracts works on ZKsync.

To deploy a contract on ZKsync Era, a user calls the create function of the providing the hash of the contract to be published, as well as the constructor arguments. The contract bytecode itself is supplied in the factory_deps field of the transaction (as it's an ). If the contract is a factory (i.e. it can deploy other contracts), these contracts' bytecodes should be included in the factory_deps as well.

We recommend using the plugin, to simplify the deployment process. It provides classes and methods to take care of all the deployment requirements, like generating the .

Note on factory_deps

You might wonder how validators obtain the preimage of the bytecode hashes necessary to execute the code. This is where the concept of factory dependencies, or factory_deps for short, comes into play. Factory dependencies refer to a list of bytecode hashes whose corresponding preimages were previously revealed on the L1 (where data is always available).

Under the hood, ZKsync does not store bytecodes of contracts in its state tree, but . You can see that the system contract accepts the bytecode hash of the deployed contract and not its bytecode. However, for contract deployment to succeed, the operator needs to know the bytecode. The factory_deps field of the transaction is used for this reason: it contains the bytecodes that should be known to the operator for this transaction to succeed. Once the transaction succeeds, these bytecodes are published on L1 and are considered "known" to the operator forever.

Some examples of usage are:

  • The obvious one is when you deploy a contract, you need to provide its code in the factory_deps field.

  • On ZKsync, factories (i.e. contracts that can deploy other contracts) do not store bytecodes of their dependencies, i.e. contracts that they can deploy. They only store their hashes. That's why you need to include all the bytecodes of the dependencies in the factory_deps field.

Both of these examples are already seamlessly done under the hood by our .

Note that the factory deps do not necessarily have to be used by the transaction in any way. These are just markers that these bytecodes should be published on L1 with this transaction. If your contract contains a lot of various factory dependencies and they do not fit inside a single L1 block, you can split the list of factory dependencies between multiple transactions.

For example, let's say that you want to deploy contract A which can also deploy contracts B and C. This means that you will have three factory dependencies for your deployment transaction: A,B and C. If the pubdata required to publish all of them is too large to fit into one block, you can send a dummy transaction with only factory dependencies A and B (assuming their combined length is small enough) and do the actual deploy with a second transaction while providing the bytecode of contract C as a factory dependency for it. Note that if some contract on its own is larger than the allowed limit per block, this contract has to be split into smaller ones.

Contract size limit and format of bytecode hash

Each zkEVM bytecode must adhere to the following format:

  • Its length must be divisible by 32.

  • Its length in words (32-byte chunks) should be odd. In other words, bytecodeLength % 64 == 32.

  • There is a VM limit, the bytecode can not be more than 2^16 32-byte words, i.e. 2^21 bytes.

  • The bootloader has a memory limit for supplying pubdata of 450999 bytes, therefore limiting the contract size to it as well. This limit is valid for Validium ZK chains, that don’t have to publish the bytecode to the base layer.

  • For rollups that must publish the deployed bytecode to the base layer (e.g. Ethereum), there is an additional pubdata limit, which is normally smaller. By default, for each batch, this limit is set to 100000 bytes for ZK chains using calldata DA, or 120000*number_of_blobs, for ZK chains using EIP-4844 blobs.

The 32-byte hash of the bytecode of a ZKsync contract is calculated in the following way:

  • The first 2 bytes denote the version of bytecode hash format and are currently equal to [1,0].

  • The second 2 bytes denote the length of the bytecode in 32-byte words.

  • The rest of the 28-byte (i.e. 28 low big-endian bytes) are equal to the last 28 bytes of the sha256 hash of the contract's bytecode.

Smart contract security

Smart contract security is critical. A single vulnerability in a smart contract can lead to loss of funds. Make sure your contracts are secure against common threats.

A common Solidity smart contract attack is reentrancy. This threat exploits vulnerabilities in contract code that allow an attacker to repeatedly call a function that withdraws funds.

Auditing smart contracts for security holes prevents theft and other malicious activities. An audit involves a thorough review of the contract's code and its underlying logic to identify any vulnerabilities or weaknesses that could be exploited by attackers. Auditors look for things like buffer overflows, integer overflows, and other types of security issues that can lead to the loss of assets or other unwanted outcomes. This review process should include both manual and automated testing to ensure that all vulnerabilities are identified.

The process of auditing a smart contract should be carried out by experts who have the necessary knowledge and experience to identify potential security risks. Investing in a thorough audit can help prevent security breaches and protect investors and users from losses, reputation damage, and legal issues. Therefore, it's essential to prioritize smart contract security and take proactive measures to ensure that they are thoroughly audited for security holes before deploying your smart contract on ZKsync Era network.

For detailed information on smart contract vulnerabilities and security best practices, refer to the following resources:

Differences in create() behaviour

This distinction implies that, while the nonce on ZKsync behaves similarly to Ethereum for smart contracts, calculating the address of a deployed contract for externally owned accounts (EOAs) is not as straightforward.

Deploying contracts from L1

Deploying contracts on ZKsync Era is also possible via L1-L2 communication.

.

.

.

To facilitate , ZKsync splits the nonce of each account into two parts: the deployment nonce and the transaction nonce. The deployment nonce represents the number of contracts the account has deployed using the create() opcode, while the transaction nonce is used for protecting against replay attacks for transactions.

On Ethereum, it can be safely determined using the formula hash(RLP[address, nonce]). However, on ZKsync, it is advisable to wait until the contract is deployed and catch the ContractDeployed event emitted by the , which provides the address of the newly deployed contract. The SDK handles all of these processes in the background to simplify the workflow.

To have a deterministic address, you should use the create2 method from . It is available for EOAs as well.

The for submitting L1->L2 transactions accepts the list of all the factory dependencies required for this particular transaction. The logic for working with them is the same as for the default L2 deployments. The only difference is that since the user has already published the full preimage for the bytecodes on L1, there is no need to publish these bytecodes again on L1.

To learn more about L1-L2 communication on ZKsync Era, visit .

Cyfrin Updraft Security & Auditing Curriculum
Consensys smart contract best practices
Solidity docs security considerations
Security considerations and best practices on ZKsync
support for account abstraction
ContractDeployer
ContractDeployer
interface
this section of the docs
Learn more about EIP712 transactions here
ContractDeployer system contract
EIP712 transaction
hardhat-zksync-deploy
ContractDeployer
hardhat-zksync-deploy
bytecode hash of the contract
specially formatted hashes of the bytecodes