The EVM Deep Dive
Module 2 of Ethereum & Smart Contracts
What Is the EVM?
The Ethereum Virtual Machine (EVM) is the runtime environment for smart contracts. Every Ethereum node runs an identical copy.
Think of it as:
- The "CPU" of Ethereum's world computer
- A sandboxed execution environment
- A deterministic state machine
Key Properties
1. Deterministic
Same input ALWAYS produces same output.
Given:
- Current state
- Transaction
Output is completely predictable.
No randomness, no external data (without oracles).
2. Isolated
Contracts can only interact through defined interfaces.
- Cannot access filesystem
- Cannot access network
- Cannot access other contracts' storage directly
3. Stack-Based
Operations work on a Last-In-First-Out stack.
Stack size: 1024 elements max
Element size: 256 bits (32 bytes)
4. Resource-Metered
Every operation costs gas. This:
- Prevents infinite loops
- Compensates validators
- Creates a fee market
EVM Architecture
Memory Model
┌─────────────────────────────────────┐
│ STACK │
│ - 1024 max depth │
│ - 256-bit words │
│ - Cheapest computation │
├─────────────────────────────────────┤
│ MEMORY │
│ - Byte-addressable │
│ - Volatile (cleared after call) │
│ - Expansion costs gas │
├─────────────────────────────────────┤
│ STORAGE │
│ - Key-value store (256-bit → 256-bit)│
│ - Persistent across calls │
│ - Most expensive operations │
├─────────────────────────────────────┤
│ CALLDATA │
│ - Read-only input data │
│ - Passed in transaction │
├─────────────────────────────────────┤
│ CODE │
│ - Immutable contract bytecode │
│ - Can be read with CODECOPY │
└─────────────────────────────────────┘
Gas Costs by Area
| Operation | Gas Cost | Why |
|---|---|---|
| Stack (ADD, MUL) | 3-5 | Cheap, in-memory |
| Memory read/write | 3 | Volatile, ephemeral |
| Storage read (warm) | 100 | Persistent, cached |
| Storage read (cold) | 2,100 | Persistent, uncached |
| Storage write (new) | 20,000 | Permanent state change |
| Storage write (existing) | 5,000 | Modifying existing |
Bytecode and Opcodes
What Is Bytecode?
Compiled smart contract code:
Solidity:
function add(uint a, uint b) returns (uint) {
return a + b;
}
Bytecode:
6080604052... (hex string)
Opcodes:
PUSH1 0x80
PUSH1 0x40
MSTORE
...
ADD
Common Opcodes
Stack Operations
| Opcode | Hex | Description |
|---|---|---|
| PUSH1 | 0x60 | Push 1 byte |
| POP | 0x50 | Remove top |
| DUP1 | 0x80 | Duplicate top |
| SWAP1 | 0x90 | Swap top two |
Arithmetic
| Opcode | Hex | Description |
|---|---|---|
| ADD | 0x01 | Addition |
| MUL | 0x02 | Multiplication |
| SUB | 0x03 | Subtraction |
| DIV | 0x04 | Division |
Comparison
| Opcode | Hex | Description |
|---|---|---|
| LT | 0x10 | Less than |
| GT | 0x11 | Greater than |
| EQ | 0x14 | Equality |
Memory/Storage
| Opcode | Hex | Description |
|---|---|---|
| MLOAD | 0x51 | Load from memory |
| MSTORE | 0x52 | Store to memory |
| SLOAD | 0x54 | Load from storage |
| SSTORE | 0x55 | Store to storage |
Control Flow
| Opcode | Hex | Description |
|---|---|---|
| JUMP | 0x56 | Unconditional jump |
| JUMPI | 0x57 | Conditional jump |
| STOP | 0x00 | Halt execution |
| RETURN | 0xF3 | Return data |
| REVERT | 0xFD | Revert with data |
Execution Context
Transaction Context
Available during execution:
msg.sender // Immediate caller address
msg.value // ETH sent with call
msg.data // Calldata (function + args)
tx.origin // Original EOA (dangerous!)
tx.gasprice // Gas price of transaction
Block Context
block.number // Current block number
block.timestamp // Current block timestamp
block.coinbase // Block validator address
block.gaslimit // Block gas limit
block.basefee // Current base fee (EIP-1559)
Contract Interactions
Types of Calls
| Call Type | State Change | Gas | Returns |
|---|---|---|---|
| CALL | Yes (other contract) | Forwarded | Yes |
| DELEGATECALL | Yes (this contract) | Forwarded | Yes |
| STATICCALL | No | Forwarded | Yes |
| CALLCODE | Deprecated | - | - |
CALL vs DELEGATECALL
CALL:
Caller → Contract B
- msg.sender = Caller
- Modifies B's storage
DELEGATECALL:
Caller → Contract B (as library)
- msg.sender = Original sender
- Modifies Caller's storage
- B's code runs in Caller's context
DELEGATECALL is powerful but dangerous — used in proxy patterns.
Contract Creation
CREATE
Deploy at deterministic address:
address = keccak256(deployer, nonce)[-20:]
Address depends on deployer's nonce (changes each transaction).
CREATE2
Deploy at predictable address:
address = keccak256(0xff, deployer, salt, keccak256(bytecode))[-20:]
Same salt + bytecode = same address (before deployment).
Use case: Counterfactual contracts, deterministic deployments.
Error Handling
REVERT
Returns unused gas, reverts all state changes.
require(condition, "Error message");
// If false, reverts with message
Out of Gas
When gas runs out:
- All state changes reverted
- Gas is NOT refunded
- Transaction marked as failed
Assert Failures
assert(condition);
// If false, consumes ALL remaining gas
// Use for invariants that should never fail
Precompiled Contracts
Special contracts at low addresses with native implementations:
| Address | Function | Gas | Use Case |
|---|---|---|---|
| 0x01 | ECRECOVER | 3000 | Signature recovery |
| 0x02 | SHA256 | 60+ | Hashing |
| 0x03 | RIPEMD160 | 600+ | Hashing |
| 0x04 | IDENTITY | 15+ | Data copy |
| 0x05 | MODEXP | Variable | RSA verification |
| 0x06-0x08 | BN128 ops | Variable | ZK proofs |
| 0x09 | BLAKE2B | Variable | Zcash compatibility |
These are much cheaper than implementing in Solidity.
EVM Limitations
1. No Floating Point
Only integers. Use fixed-point math:
// Instead of 0.5
uint256 HALF = 5 * 10**17; // 0.5 * 10**18
2. No Randomness
Blockchain is deterministic. "Random" solutions:
- Commit-reveal schemes
- Chainlink VRF (external oracle)
- Block hash (weak, manipulable)
3. No External Data
Can't fetch APIs or URLs. Solutions:
- Oracles (Chainlink, API3)
- User-provided data (with verification)
4. 256-bit Words
Smaller types still use full word:
uint8 a = 1; // Still 256 bits in storage
Pack multiple values to save gas.
The Future: EVM Evolution
EVM Object Format (EOF)
New bytecode format:
- Separation of code and data
- Better static analysis
- Removal of dynamic jumps
Verkle Trees
Replace Merkle Patricia Tries:
- Smaller proofs
- Enable stateless clients
Alternative VMs
Some chains use different VMs:
- SVM (Solana)
- MoveVM (Aptos, Sui)
- WASM (Polkadot, Near)
Key Takeaways
- EVM is a stack-based, deterministic VM
- Three memory areas: Stack (cheap), Memory (volatile), Storage (expensive)
- Gas meters all computation — prevents abuse
- Opcodes are low-level — Solidity abstracts them
- Different call types have different security implications
- Precompiles provide efficient cryptographic operations
Questions to Consider
- Why is storage so expensive compared to memory?
- When would you use CREATE2 vs CREATE?
- What makes DELEGATECALL dangerous?
- Could the EVM be replaced with WASM?