The Ethereum Virtual Machine, or EVM, is the core component responsible for executing transactions on the Ethereum network. It processes transactions by converting them into a Message object and executing them within the EVM environment.
If the transaction is a simple transfer of value, the EVM directly updates the account balances in the StateDB (State Database). However, if the transaction involves the creation or invocation of a smart contract, the EVM interpreter loads and executes the corresponding bytecode. During execution, the contract may read from or modify the StateDB.
Intrinsic Gas: The Base Fee
Every transaction processed by the EVM incurs a base fee known as Intrinsic Gas. This fee is calculated as follows:
- For transactions without additional data payloads (e.g., simple transfers), the fee is 21,000 gas units.
- For transactions with data payloads, the fee includes an additional cost per byte: 4 gas for a zero byte and 68 gas for a non-zero byte. This is why many contract optimizations focus on reducing the number of non-zero bytes in transaction data to minimize gas costs.
Creating the Contract Object
When a transaction is converted into a Message object and passed to the EVM, the EVM generates a Contract object for execution. This object loads the contract code from the StateDB based on the contract address. The execution is subject to a gas limit, which is defined by the network’s block gas limit configuration.
Execution by the Interpreter
With the code and input data ready, the EVM interpreter begins execution. The EVM is a stack-based virtual machine, and its interpreter manages four key components during execution:
- PC (Program Counter): Points to the current instruction being executed.
- Stack: A 256-bit wide stack with a maximum depth of 1,024 items.
- Memory: A temporary memory space for data storage.
- Gas: The gas pool, which must not be depleted during execution to avoid transaction failure.
The execution process involves reading OpCodes (operation codes) from the contract code. Each OpCode is one byte long, allowing for up to 256 possible instructions. The interpreter retrieves the corresponding operation from a jump table, calculates the gas cost, and executes the instruction if sufficient gas is available. Instructions may read from or write to the stack, memory, or StateDB.
Calling Contract Functions
To call a specific function within a contract, the transaction includes an Input data field. This data consists of:
- A 4-byte function signature, which is the first four bytes of the Keccak hash of the function’s name and parameter types.
- The arguments required by the function, of variable length.
During compilation, the Solidity compiler adds a function selection logic to the bytecode. This logic compares the provided function signature against the contract’s available functions and jumps to the corresponding code block if a match is found.
The EVM provides several instructions for loading data:
CALLDATALOAD: Loads input data onto the stack.CALLDATACOPY: Copies input data to memory.CODECOPY: Copies the current contract’s code to memory.EXTCODECOPY: Copies an external contract’s code to memory (less commonly used).
Inter-Contract Calls
Contracts can call other contracts using one of four methods:
CALLCALLCODEDELEGATECALLSTATICCALL
CALL vs. CALLCODE
The key difference between CALL and CALLCODE lies in the execution context:
CALLmodifies the storage of the callee (the contract being called).CALLCODEmodifies the storage of the caller (the contract making the call).
CALLCODE vs. DELEGATECALL
DELEGATECALL is essentially a bug-fixed version of CALLCODE. The main difference is how they handle msg.sender:
CALLCODEsetsmsg.senderto the address of the caller contract.DELEGATECALLpreserves the originalmsg.sender(the transaction initiator).
STATICCALL
STATICCALL is designed for calls that should not modify state. It is intended for future use in compiling view and pure functions, ensuring runtime enforcement of state immutability. Currently, Solidity checks this at compile time, but STATICCALL would enable runtime checks.
Creating Contracts
A transaction with a to address set to nil indicates contract creation. The contract address is generated using the formula: Keccak(RLP(caller_address, nonce))[12:]. The contract code is stored at this address, and its state is maintained in a storage trie within the StateDB. While the code is immutable, the storage can be modified using instructions like SSTORE.
Gas Calculation
Gas costs for EVM instructions are defined in the Ethereum Yellow Paper and implemented in the Ethereum client code (e.g., core/vm/gas.go and core/vm/gas_table.go in Go Ethereum).
Frequently Asked Questions
What is the Ethereum Virtual Machine (EVM)?
The EVM is a Turing-complete virtual machine that executes smart contracts on the Ethereum blockchain. It processes transactions and updates the network state based on predefined rules.
How is gas calculated for transactions?
Gas is calculated based on the complexity of the transaction. Simple transfers have a fixed cost, while contract interactions incur additional costs based on data size and computational steps.
What are the differences between CALL, CALLCODE, and DELEGATECALL? CALL changes the callee’s storage, CALLCODE changes the caller’s storage, and DELEGATECALL preserves the original msg.sender while using the caller’s storage.
Why is DELEGATECALL preferred over CALLCODE? DELEGATECALL ensures the original transaction initiator is preserved as msg.sender, which is critical for security and access control in smart contracts.
What is STATICCALL used for? STATICCALL is intended for calls that should not modify state, such as view or pure functions. It enforces immutability at runtime.
How are contract addresses generated?
Contract addresses are derived from the sender’s address and nonce using a Keccak hash function. This ensures each contract has a unique and deterministic address.
👉 Explore advanced EVM techniques
👉 Learn more about gas optimization
Understanding the EVM is essential for developers working with Ethereum. Its design ensures security, determinism, and efficiency in executing smart contracts. By mastering concepts like gas calculation and inter-contract calls, developers can build more optimized and secure decentralized applications.