System Contracts

Many EVM instructions require special handling by the System Contracts. Among them are: ORIGIN, CALLVALUE, BALANCE, CREATE, SHA3, and others. To see the full detailed list of instructions that require special handling, see the EVM instructions reference.

There are several types of System Contracts from the perspective of how they are handled by the ZKsync Era compilers:

  1. Environmental data storage.
  2. KECCAK256 hash function.
  3. Contract deployer.
  4. Ether value simulator.
  5. Simulator of immutables.
  6. Event handler.

Environmental Data Storage

Such storage contracts are accessed with static calls in order to retrieve values for the block, transaction, and other environmental entities: CHAINID, DIFFICULTY, BLOCKHASH, etc.

One good example of such contract is SystemContext that provides the majority of the environmental data.

Since EVM is not using external calls for these instructions, we must use the auxiliary heap for their calldata.

Steps to handle such instructions:

  1. Store the calldata for the System Contract call on the auxiliary heap.
  2. Call the System Contract with a static call.
  3. Check the return status code of the call.
  4. Revert or throw if the status code is zero.
  5. Read the ABI data and extract the result. All such System Contracts return a single 256-bit value.
  6. Return the value as the result of the original instruction.

For reference, see the LLVM IR codegen source code.

KECCAK256 Hash Function

Handling of this function is similar to Environmental Data Storage with one difference:

Since EVM also uses heap to store the calldata for KECCAK256, the required memory chunk is allocated by the IR generator, and ZKsync Era compiler does not need to use the auxiliary heap.

For reference, see the LLVM IR codegen source code.

Contract Deployer

See handling CREATE and dependency code substitution instructions on ZKsync Era documentation.

For reference, see LLVM IR codegen for the deployer call and CREATE-related instructions.

Ether Value Simulator

EraVM does not support passing Ether natively, so this is handled by a special System Contract called MsgValueSimulator.

An external call is redirected through the simulator if the following conditions are met:

  1. The call has the Ether value parameter.
  2. The Ether value is non-zero.

Calls to the simulator require extra data passed via ABI using registers:

  1. Ether value.
  2. The address of the contract to call.
  3. The system call bit, which is only set if a call to the ContractDeployer is being redirected, that is CREATE or CREATE2 is called with non-zero Ether.

Passing Ether value in EraVM is implemented by using a combination of:

The process of setting up a value and capturing it is described in details in the section Context Register of the EraVM specification.

For reference, see the LLVM IR codegen source code.

Simulator of Immutables

See handling immutables on ZKsync Era documentation.

For reference, see LLVM IR codegen for instructions for immutables and RETURN from the deploy code.

Event Handler

Event payloads are sent to a special System Contract called EventWriter. Like on EVM, the payload consists of topics and data:

  1. The topics with a length-prefix are passed via ABI using registers.
  2. The data is passed via the default heap, like on EVM.

For reference, see the LLVM IR codegen source code.

Auxiliary Heap

Both zksolc and zkvyper compilers for EraVM operate on the IR level, so they cannot control the heap memory allocator which remains a responsibility of the high-level source code compilers emitting the IRs.

However, there are several cases where EraVM needs to allocate memory on the heap and EVM does not. The auxiliary heap is used for these cases:

Returning immutables

  1. Returning immutables from the constructor.
  2. Allocating calldata and return data for calling the System Contracts.

While the ordinary heap contains calldata and return data for calls to user contracts, auxiliary heap contains calldata and return data for calls to System Contracts. This ensures better compatibility with EVM as users should be able to call EraVM-specific System Contracts in a transparent way, without System Contracts affecting calldata or return data. This prevents situations where calling System Contracts interferes with the heap layout expected by the contract developer.

For more details on the heaps, refer to the EraVM specification, which describes types of heaps, their connections to the stack frames and memory growth, and their role in communication between contracts.

Made with ❤️ by the ZKsync Community