---
description: Understand the differences in testing smart contracts using Foundry ZKsync.
---

# Testing

***

#### Bytecode Constraints <a href="#bytecode-constraints" id="bytecode-constraints"></a>

**Issue**

On Validium, bytecode must conform to specific size and length constraints to be considered valid. These constraints ensure that the bytecode operates correctly within Validium's modified zkEVM environment. Bytecode that does not meet these criteria will result in compilation or deployment errors.

The bytecode constraints are as follows:

* The bytecode length (in bytes) must be divisible by 32 (32-byte words).
* The bytecode must have fewer than 2^16 words.
* The bytecode length (in words) must be an odd number.

**Problematic Code**

Consider a test scenario where we attempt to manually set the bytecode at a specific address using Foundry’s `vm.etch` cheatcode. In this example, some attempts to etch the bytecode violate the size and word alignment rules.

```solidity
contract FooTest is Test {
    function testFoo() public {
        // Invalid, word-size of 1 byte
        vm.etch(address(65536), hex"00");

        // Invalid, even number of words
        vm.etch(
            address(65536),
            hex"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
        );

        // Valid, 32-byte word, odd number of words
        vm.etch(
            address(65536),
            hex"0000000000000000000000000000000000000000000000000000000000000000"
        );
    }
}
```

In this code:

* The first `vm.etch` call uses a 1-byte word, which is invalid because Validium requires the bytecode length to be divisible by 32 bytes.
* The second `vm.etch` call uses a bytecode of 64 bytes (even number of words), which is also invalid because Validium requires an odd number of words.
* The third `vm.etch` call uses a valid bytecode length of 32 bytes (1 word), which is both divisible by 32 bytes and contains an odd number of words.

**Error**

The first two cases will produce errors due to violating the bytecode size and word constraints:

```bash
Error: Invalid bytecode length: not divisible by 32
Error: Invalid bytecode: length of even number of words
```

These errors indicate that the bytecode fails to meet Validium’s requirements for bytecode validity.

**Solution**

To resolve these errors, ensure that the bytecode conforms to the following rules:

* The length of the bytecode must always be divisible by 32 bytes.
* The total word count (in 32-byte words) must be an odd number.
* The bytecode should be less than 2^16 words in length.

**Fixed Code Example**

Here’s how to correctly etch the bytecode in Validium, following the bytecode constraints:

```solidity
contract FooTest is Test {
    function testFoo() public {
        // Valid, 32-byte word, odd number of words
        vm.etch(
            address(65536),
            hex"0000000000000000000000000000000000000000000000000000000000000000"
        );
    }
}
```

In this fixed example:

* The bytecode length is exactly 32 bytes (one 32-byte word), which satisfies Validium's requirement for bytecode size divisibility by 32.
* The total word count is odd (1 word), meeting the requirement for an odd number of words.

#### Cheatcode Limitations <a href="#cheatcode-limitations" id="cheatcode-limitations"></a>

**Issue**

In Validium's zkEVM, cheatcodes are only supported at the root level of an executing test, meaning they must be called outside of any `CREATE` or `CALL` operations that are dispatched to the zkEVM. Cheatcodes used within contract constructors or function calls that dispatch transactions will not work and can lead to undefined behavior.

**Valid Cheatcode Usage**

Cheatcodes can be used normally in tests as long as they are outside of any contract creation (`CREATE`) or external function calls (`CALL`). Here's an example demonstrating valid cheatcode usage:

```solidity
contract MyContract {
    function getNumber() public returns (uint256) {
        return 42;
    }
}

contract FooTest is Test {
    function testFoo_1() public {
        vm.roll(10);                    // valid
        vm.assertEq(10, block.number);   // valid cheatcode usage
    }

    function testFoo_2() public {
        vm.roll(10);                    // valid
        new MyContract();                // valid because vm.roll is called outside of contract creation
    }

    function testFoo_3() public {
        vm.roll(10);                    // valid
        MyContract testContract = new MyContract();
        testContract.getNumber();        // valid as cheatcodes are used before the contract interaction
    }
}
```

In these cases, the `vm.roll` cheatcode is valid because it is called outside of any contract creation or function calls that interact with zkEVM.

**Cheatcode Usage in Libraries**

Since libraries do not result in a `CREATE` or `CALL`, you can use cheatcodes within library functions without issues. Here's an example of how libraries can use cheatcodes:

```solidity
library MyLibrary {
    function setBlockNumber(uint256 value) public {
        vm.roll(value);                 // valid cheatcode usage in a library
    }
}

contract FooTest is Test {
    function testFoo_1() public {
        vm.roll(10);                    // valid
        vm.assertEq(10, block.number);
        MyLibrary.setBlockNumber(20);    // valid cheatcode usage within a library
        vm.assertEq(10, block.number);   // valid
    }
}
```

Libraries can call cheatcodes since they do not trigger a contract creation or external call, making them valid for cheatcode usage in Validium.

**Problematic Code**

Cheatcodes used within contract constructors or functions that dispatch transactions to the zkEVM will result in undefined behavior or fail to execute. Here's an example demonstrating invalid cheatcode usage:

```solidity
contract MyContract {
    constructor() {
        vm.roll(20);                    // invalid cheatcode usage inside a constructor
    }

    function getNumber() public returns (uint256) {
        vm.roll(20);                    // invalid cheatcode usage inside a function
        return 42;
    }
}

contract FooTest is Test {
    function testFoo_1() public {
        vm.roll(10);                    // valid
        MyContract testContract = new MyContract();  // invalid due to vm.roll in constructor
        testContract.getNumber();        // invalid due to vm.roll in function
    }
}
```

In this example, `vm.roll` is used inside the constructor and a function of `MyContract`, which will not work on Validium because the cheatcode is executed within a `CREATE` or `CALL` operation.

**Error**

Although the error messages may not be explicit, calling cheatcodes inside a contract's constructor or function will either lead to undefined behavior or failure during the test execution.

**Solution**

To ensure cheatcodes work as expected on Validium, always call them outside of contract constructors or external function calls. Use libraries or test-level cheatcodes for reliable test execution.

**Fixed Code Example**

Here’s an example of valid cheatcode usage in a test:

```solidity
contract MyContract {
    function getNumber() public returns (uint256) {
        return 42;
    }
}

contract FooTest is Test {
    function testFoo() public {
        vm.roll(10);                    // valid cheatcode usage
        MyContract testContract = new MyContract();  // no cheatcodes inside contract
        testContract.getNumber();        // no cheatcodes inside function
    }
}
```

This example ensures that the cheatcodes are called only at the test level, outside any contract creation or external function calls.

#### Forking <a href="#forking" id="forking"></a>

When using forking cheatcodes such as `vm.selectFork` or `vm.createSelectFork` in tests, the execution context automatically switches based on the network being forked.

* **Forking to a Validium Network**: If the RPC endpoint supports Validium (verified by checking the presence of the `zks_L1ChainId` method), the test execution switches to the Validium context. This means the test will follow Validium-specific behaviors and limitations.
* **Forking to a non-Validium network**: If the selected fork is not a Validium endpoint, the test execution remains in the standard EVM context.

#### Cheatcode Override <a href="#cheatcode-override" id="cheatcode-override"></a>

To manually control the execution context, a custom cheatcode `vm.zkVm` is provided:

* **Enabling Validium mode**: Use `vm.zkVm(true)` to switch the test execution to Validium mode.
* **Switching back to EVM mode**: Use `vm.zkVm(false)` to revert back to the standard EVM mode during test execution.

**Note**

Using the `--zksync` flag when running tests is equivalent to placing `vm.zkVm(true)` as the first statement in the test, automatically setting the execution to Validium mode.

#### Origin Address <a href="#origin-address" id="origin-address"></a>

**Issue**

In Validium's zkEVM, calls to `tx.origin` are not supported, unlike in Ethereum where `tx.origin` can be used to retrieve the original external account that initiated the transaction. As a result, any attempts to mock or interact with `tx.origin` in Validium will fail.

**Problematic Code**

In the following example, the `tx.origin` address is used in a mocked call. While this works in Ethereum, it will not work in Validium because `tx.origin` is unsupported.

```solidity
library IFooBar {
    function number() external view returns (uint8);
}

contract FooTest is Test {
    function testFoo() public {
        address target = tx.origin; // Invalid on Validium

        vm.mockCall(
            address(target),
            abi.encodeWithSelector(bytes4(keccak256("number()"))),
            abi.encode(5)
        );

        IFooBar(target).number(); // This call will fail on Validium
    }
}
```

In this code, `tx.origin` is used to obtain the address of the original transaction sender, but on Validium, any interaction involving `tx.origin` will fail.

**Error**

Attempting to use `tx.origin` on Validium will result in failed transactions or mocked calls. However, Validium may not provide specific error messages related to the failure, making it difficult to trace the root cause.

**Solution**

You should use `msg.sender` instead of using `tx.origin` to track the address of the current external caller, which is supported on Validium.

**Fixed Code Example**

```solidity
library IFooBar {
    function number() external view returns (uint8);
}

contract FooTest is Test {
    function testFoo() public {
        address target = msg.sender; // Valid on Validium

        vm.mockCall(
            address(target),
            abi.encodeWithSelector(bytes4(keccak256("number()"))),
            abi.encode(5)
        );

        IFooBar(target).number(); // This will now work on Validium
    }
}
```

In this fixed example, `msg.sender` is used instead of `tx.origin`, which ensures compatibility with Validium.

#### Reserved Address Range <a href="#reserved-address-range" id="reserved-address-range"></a>

**Issue**

On Validium's zkEVM, addresses in the range `[0..2^16-1]` are reserved for kernel space, meaning they are not available for contract deployment or other user-level interactions. Attempting to use these addresses, either directly or through mocking, can lead to undefined behavior in your tests.

**Problematic Code**

In this test example, the address `0` is used in a mocked call. Since this address is part of the reserved range, using it will result in undefined behavior and test failures.

```solidity
contract FooTest is Test {
    function testFoo() public {
        // Invalid: Using an address within the reserved range
        vm.mockCall(
            address(0),
            abi.encodeWithSelector(bytes4(keccak256("number()"))),
            abi.encode(5)
        );
    }
}
```

This example attempts to mock a call to `address(0)`, which is part of the reserved kernel space and therefore invalid on Validium.

**Error**

Using addresses within the reserved range may lead to undefined behavior and possible test failures without specific error messages. It's crucial to avoid these addresses entirely to ensure test reliability.

**Solution**

Ensure that addresses used for testing and mocking are outside of the reserved kernel space range. Start from address `65536` onwards for any user-level interaction.

In fuzz testing, you can exclude the reserved address range either by setting assumptions or using specific fuzz configuration.

Here’s a corrected test example:

**Fixed Code Example**

```solidity
contract FooTest is Test {
    function testFoo() public {
        // Valid: Using an address outside of the reserved range
        vm.mockCall(
            address(65536),
            abi.encodeWithSelector(bytes4(keccak256("number()"))),
            abi.encode(5)
        );
    }
}
```

In this corrected example, we use `address(65536)`, which is a valid user-space address in Validium.

**Additional Configuration**

During fuzz testing, it's important to avoid generating addresses within the reserved range. This can be done in two ways:

1.  **Using `vm.assume`**: You can assert that the address generated in fuzz testing is greater than or equal to `65536`:

    ```bash
    vm.assume(address(value) >= 65536);
    ```
2.  **Fuzz Configuration**: Alternatively, you can set the `no_zksync_reserved_addresses` option in your fuzz configuration to automatically exclude addresses in the reserved range.

    ```bash
    [profile.default]
    fuzz = { no_zksync_reserved_addresses = true }
    ```

These approaches ensure that fuzz testing avoids the reserved kernel space, preventing test failures and ensuring compliance with Validium's address rules.
