EVM Fundamentals¶
A comprehensive guide to understanding the Ethereum Virtual Machine.
Introduction¶
The Ethereum Virtual Machine (EVM) is a stack-based virtual machine that executes smart contracts on the Ethereum blockchain. It provides a deterministic execution environment that runs consistently across all network nodes.
Key Characteristics¶
| Characteristic | Description |
|---|---|
| Deterministic | Same input always produces same output |
| Sandboxed | Isolated execution environment |
| Turing Complete | Can perform any computation (with gas limits) |
| State-based | Maintains global state across transactions |
The Virtual Machine Model¶
┌─────────────────┐
│ Transaction │ ← Input: Bytecode + Data
└─────────┬───────┘
│
┌─────────▼───────┐
│ EVM │ ← Execution Engine
│ ┌─────────┐ │
│ │ Stack │ │ ← 256-bit words, max 1024 items
│ ├─────────┤ │
│ │ Memory │ │ ← Temporary storage
│ ├─────────┤ │
│ │ Storage │ │ ← Persistent key-value store
│ └─────────┘ │
└─────────┬───────┘
│
┌─────────▼───────┐
│ Result │ ← Output: State Changes
└─────────────────┘
Components¶
- Stack: Primary data structure (LIFO - Last In, First Out)
- Memory: Temporary byte array for operations
- Storage: Persistent key-value mapping
- Program Counter: Tracks current instruction
- Gas: Execution cost tracking
The Stack Machine¶
The EVM stack can hold up to 1024 items, each 256 bits (32 bytes) wide.
Stack Operations¶
# Adding two numbers
# Initial stack: []
PUSH1 0x05 # Stack: [5]
PUSH1 0x03 # Stack: [3, 5]
ADD # Stack: [8] (pops 3 and 5, pushes 8)
Stack Manipulation Opcodes¶
| Opcode | Description | Gas | Stack Change |
|---|---|---|---|
| PUSH1-PUSH32 | Push 1-32 bytes | 3 | +1 |
| POP | Remove top item | 2 | -1 |
| DUP1-DUP16 | Duplicate item | 3 | +1 |
| SWAP1-SWAP16 | Swap items | 3 | 0 |
Example: Computing (a + b) * c¶
# Where a=10, b=20, c=3
PUSH1 0x0A # Stack: [10]
PUSH1 0x14 # Stack: [20, 10]
ADD # Stack: [30] (10 + 20)
PUSH1 0x03 # Stack: [3, 30]
MUL # Stack: [90] (30 * 3)
Memory Management¶
EVM memory is a linear byte array that expands during execution.
Memory Layout¶
Memory Offset: 0 32 64 96 128 ...
┌────┬────┬────┬────┬─────┐
Memory: │ A │ B │ C │ D │ ... │
└────┴────┴────┴────┴─────┘
32 32 32 32 32 bytes each
Memory Operations¶
# Store 32 bytes in memory at offset 0
PUSH1 0x42 # Stack: [0x42] (value to store)
PUSH1 0x00 # Stack: [0x00, 0x42] (offset)
MSTORE # Memory[0:32] = 0x42 (right-padded)
# Load 32 bytes from memory at offset 0
PUSH1 0x00 # Stack: [0x00] (offset)
MLOAD # Stack: [0x42...] (loaded value)
Memory Gas Costs¶
Memory expansion costs grow quadratically to prevent abuse:
Storage System¶
Storage is a persistent key-value mapping that survives between transactions.
Storage Operations¶
# Store value 0x123 at storage slot 0
PUSH2 0x0123 # Stack: [0x123] (value)
PUSH1 0x00 # Stack: [0x00, 0x123] (key)
SSTORE # Storage[0] = 0x123
# Load value from storage slot 0
PUSH1 0x00 # Stack: [0x00] (key)
SLOAD # Stack: [0x123] (loaded value)
Storage Gas Economics¶
| Operation | Cold Access | Warm Access |
|---|---|---|
| SLOAD | 2,100 gas | 100 gas |
| SSTORE (new) | 20,000 gas | - |
| SSTORE (modify) | 5,000 gas | 100 gas |
| SSTORE (delete) | Refund 4,800 | - |
Gas Economics¶
Why Gas Exists¶
- Prevents infinite loops: Every operation costs gas
- Resource allocation: Expensive operations cost more
- Network security: Spam protection
- Miner incentives: Gas fees reward miners
Gas Calculation¶
# Transaction: Transfer 1 ETH
# Base transaction cost: 21,000 gas
# If gas price = 20 Gwei:
# Total cost = 21,000 × 20 × 10^-9 ETH = 0.00042 ETH
Common Gas Costs¶
| Operation | Gas Cost | Notes |
|---|---|---|
| Basic arithmetic | 3-5 | ADD, SUB, MUL |
| Hash operations | 30+ | SHA3/KECCAK256 |
| Storage read | 100-2100 | SLOAD |
| Storage write | 100-20000 | SSTORE |
| Contract creation | 32000 | CREATE |
Transaction Lifecycle¶
1. Transaction Submitted
├─ Signature verification
├─ Nonce validation
├─ Gas limit check
└─ Balance verification
2. EVM Execution
├─ Initialize execution context
├─ Load contract bytecode
├─ Execute opcodes sequentially
├─ Track gas consumption
└─ Handle state changes
3. Transaction Completion
├─ Apply state changes
├─ Emit events/logs
├─ Calculate gas refund
└─ Update account balances
Execution Context¶
pub const ExecutionContext = struct {
caller: [20]u8, // msg.sender
origin: [20]u8, // tx.origin
gas_price: BigInt, // tx.gasprice
block_number: u64, // block.number
timestamp: u64, // block.timestamp
gas_limit: u64, // Available gas
value: BigInt, // msg.value
};
Smart Contracts¶
High-Level to Bytecode¶
// High-level Solidity
contract Token {
mapping(address => uint256) balances;
function transfer(address to, uint256 amount) {
balances[msg.sender] -= amount;
balances[to] += amount;
}
}
Compiles to EVM bytecode:
Contract Deployment¶
- Creation transaction: Contains constructor bytecode
- Constructor execution: Initializes state
- Code storage: Runtime code stored at address
- Address generation: Deterministic from creator + nonce
Call Types¶
CALL¶
Standard message call to another contract:
- Separate execution context
- Can transfer value
- Caller pays for gas
DELEGATECALL¶
Execute code in caller's context:
- Uses caller's storage
- Uses caller's msg.sender
- Uses caller's msg.value
STATICCALL¶
Read-only call:
- No state modifications allowed
- Reverts on state-changing opcodes
Parallel Execution¶
Traditional Sequential Execution¶
Parallel Execution¶
When Transactions Can Run in Parallel¶
Independent (can run in parallel):
Dependent (must run sequentially):
Conflict Types¶
- Balance conflicts: Same account sends in multiple TXs
- Storage conflicts: Same storage slot accessed
- Nonce conflicts: Same account's transactions
- Contract state: Shared contract storage
Advanced Topics¶
EIP-1559 Gas Model¶
pub fn calculateGasFee(
base_fee: u64,
max_fee: u64,
max_priority_fee: u64,
gas_used: u64
) u64 {
const effective_gas_price = std.math.min(
max_fee,
base_fee + max_priority_fee
);
return effective_gas_price * gas_used;
}
Precompiled Contracts¶
Special contracts at low addresses with optimized implementations:
| Address | Function |
|---|---|
| 0x01 | ECDSA recovery |
| 0x02 | SHA-256 |
| 0x03 | RIPEMD-160 |
| 0x04 | Identity |
| 0x05 | Modular exponentiation |
| 0x06-0x08 | BN256 curve operations |
| 0x09 | BLAKE2b |
State Trie¶
Ethereum uses a Merkle Patricia Trie for state storage:
Root Hash
│
┌───────────┼───────────┐
│ │ │
Account 1 Account 2 Account 3
│
┌───┴───┐
│ │
Storage Storage
Key 1 Key 2
Learning Path¶
Beginner¶
- Understand blockchain basics
- Learn stack machine concepts
- Practice simple opcode execution
- Build a basic calculator in EVM
Intermediate¶
- Study gas optimization
- Implement memory/storage operations
- Explore smart contract patterns
- Write a token contract from scratch
Advanced¶
- Master parallel execution
- Research latest EIPs
- Contribute to open source
- Develop novel optimizations