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
  1. Tooling
  2. Foundary
  3. Migration Guide

Deployment

Understand the differences in deploying smart contracts on ZKSync using foundry-zksync.

PreviousCompilationNextTesting

Last updated 7 months ago


Address Derivation

Issue

ZKsync uses a different strategy for deriving addresses in CREATE and CREATE2 operations compared to Ethereum. This can cause discrepancies if your project relies on hardcoded Ethereum-style addresses for contract deployment.

Problematic Code

In Ethereum, you might have code that calculates the address of a contract deployed using CREATE2 based on a salt and bytecode hash:

  function computeCreate2Address(
      address deployer,
      bytes32 salt,
      bytes memory bytecode
  ) public pure returns (address) {
      return address(uint160(uint(keccak256(abi.encodePacked(
          bytes1(0xff),
          deployer,
          salt,
          keccak256(bytecode)
      )))));
  }

This method won’t work in ZKsync because the address derivation logic is different. To learn more about these differences refer to the documentation on .

Error

When deploying contracts using CREATE2, the actual address of the deployed contract may differ from the expected address derived using Ethereum’s method. This can cause failures when interacting with contracts by their derived address.

Solution

To derive ZKsync-compatible CREATE2 addresses, you need to use ZKsync's specific hashing mechanism that includes a zksyncCreate2 prefix.

Fixed Code Example

The following is an example of how you can compute a valid CREATE2 address for ZKSync. This function uses ZKsync’s specific address derivation logic and ensures the contract addresses are correctly computed.

    function computeCreate2Address(
        address _sender,
        bytes32 _salt,
        bytes32 _bytecodeHash,
        bytes32 _constructorInputHash
    ) external pure returns (address) {
        bytes32 senderBytes = bytes32(uint256(uint160(_sender)));
        bytes32 data = keccak256(
            bytes.concat(keccak256("zksyncCreate2"), senderBytes, _salt, _bytecodeHash, _constructorInputHash)
        );

        return address(uint160(uint256(data)));
    }

Batch Transactions

Issue

In Foundry ZKsync, batched transactions may be executed out of order, which can cause failures if subsequent transactions depend on the execution of prior transactions.

Problematic Code

In Ethereum, you might use batched transactions within your scripts like this:

contract Calculator {
    function add(uint8 a, uint8 b) public pure returns (uint8) {
        return a + b;
    }
}

contract FooScript is Script {
    function run() public {
        vm.startBroadcast();
        Calculator calc = new Calculator();     // tx1
        uint8 sum = calc.add(1, 2);             // tx2
        vm.stopBroadcast();
    }
}

In this example, the deployment of the Calculator contract (tx1) and the invocation of add (tx2) are batched together. However, on ZKsync, tx2 might execute before tx1, causing the script to fail.

Error

The error occurs when the second transaction (tx2) is executed before the first transaction (tx1), leading to a failure since the contract isn't deployed yet:

Error: Transaction reverted: contract not deployed

Solution

To ensure transactions are executed correctly on ZKsync using Foundry ZKsync, use the --slow flag, which sends transactions one at a time and keeps them in order.

Fixed Code Example

Here’s how you can run the script with proper transaction execution:

forge script script/FooTest.s.sol:FooScript --zksync --rpc-url https://sepolia.era.zksync.dev --broadcast --slow

The --slow flag forces transactions to be sent individually to the RPC endpoint, ensuring they are executed in the correct order.

Bytecode Hash

Any code making assumptions about bytecode hashes around EVM-scope will need to be migrated to accommodate for ZKsync's bytecode hashes.

from the hash obtained via solc. The most glaring difference is the first (most-significant) byte denotes the version of the format, which at present is 1. This leads to all zksolc bytecode hashes to begin with 1, whereas solc bytecodes are merely the keccak hash of the bytecode.

Bytecode hashes output by zksolc are fundamentally different
Differences with address derivation