Tutoriel·8 min de lecture·Par Solingo

What is the EVM? Ethereum Virtual Machine Explained

Understand how the EVM executes smart contracts. Stack machine, opcodes, gas, storage — everything you need to know.

# What is the EVM? Ethereum Virtual Machine Explained

The Ethereum Virtual Machine (EVM) is the execution engine that runs smart contracts on Ethereum and all EVM-compatible blockchains (Polygon, Arbitrum, BSC, etc.). Understanding the EVM is crucial for writing efficient, secure Solidity code.

In this deep dive, we'll explore how the EVM works under the hood, why it matters for developers, and how to optimize your contracts for better performance.

What is a Virtual Machine?

A virtual machine is a software emulation of a computer. Just like you can run Windows on a Mac using VMware, the EVM runs bytecode in a sandboxed environment, isolated from the host system.

The EVM is:

  • Deterministic: Same input = same output, always
  • Sandboxed: Cannot access network, filesystem, or other contracts without explicit calls
  • Gas-metered: Every operation costs gas to prevent infinite loops
  • Stack-based: Uses a stack for computation (vs register-based like x86)

EVM Architecture

The Stack Machine Model

The EVM is a stack-based virtual machine. All operations manipulate a stack of 256-bit values.

Example: Adding Two Numbers

function add(uint a, uint b) public pure returns (uint) {

return a + b;

}

Compiled to bytecode, this becomes:

PUSH1 0x00   // Push 0 to stack

CALLDATALOAD // Load 'a' from calldata

PUSH1 0x20 // Push 32 (0x20) to stack

CALLDATALOAD // Load 'b' from calldata

ADD // Pop two values, push sum

PUSH1 0x00

MSTORE // Store result in memory

PUSH1 0x20

PUSH1 0x00

RETURN // Return 32 bytes from memory

Stack evolution:

[5]          // a = 5 pushed

[5, 3] // b = 3 pushed

[8] // ADD pops both, pushes 8

Memory Layout

The EVM has three data storage areas:

  • Stack: 1024 items max, 256-bit values. Fast, free (within limits).
  • Memory: Byte-addressable RAM. Expandable, costs gas to expand.
  • Storage: Persistent key-value store. Expensive (20,000 gas for new slot).
  • Cost Comparison:

    Stack access:  3 gas
    

    Memory read: 3 gas

    Memory expand: quadratic cost

    Storage write: 20,000 gas (new), 5,000 gas (update)

    Storage read: 2,100 gas

    Key EVM Opcodes

    Opcodes are the assembly instructions the EVM executes. Solidity compiles to opcodes.

    Stack Operations

    PUSH1 0x42   // Push 1 byte (0x42) to stack
    

    PUSH2 0x1234 // Push 2 bytes to stack

    POP // Remove top item

    DUP1 // Duplicate top item

    SWAP1 // Swap top two items

    Arithmetic

    ADD          // a + b
    

    SUB // a - b

    MUL // a * b

    DIV // a / b (integer division)

    MOD // a % b

    EXP // a ** b (exponentiation)

    Comparison & Logic

    LT           // Less than
    

    GT // Greater than

    EQ // Equal

    ISZERO // Check if zero

    AND // Bitwise AND

    OR // Bitwise OR

    XOR // Bitwise XOR

    NOT // Bitwise NOT

    Memory Operations

    MLOAD        // Load 32 bytes from memory
    

    MSTORE // Store 32 bytes to memory

    MSTORE8 // Store 1 byte to memory

    Storage Operations

    SLOAD        // Load from persistent storage (2,100 gas)
    

    SSTORE // Store to persistent storage (20,000 gas for new slot)

    Call Operations

    CALL         // Call another contract
    

    DELEGATECALL // Call with caller's context (used in proxies)

    STATICCALL // Read-only call (cannot modify state)

    CREATE // Deploy a new contract

    CREATE2 // Deploy to deterministic address

    Environment Information

    ADDRESS      // This contract's address
    

    CALLER // msg.sender

    CALLVALUE // msg.value (ETH sent)

    CALLDATALOAD // Read from calldata

    CALLDATASIZE // Size of calldata

    TIMESTAMP // block.timestamp

    NUMBER // block.number

    GASPRICE // tx.gasprice

    How Transactions Execute

    When you send a transaction calling a smart contract:

  • Transaction submitted to a node
  • Transaction added to mempool (pending pool)
  • Miner/validator includes it in a block
  • EVM initializes:
  • - Set gas limit from transaction

    - Load contract bytecode

    - Prepare execution context (caller, value, calldata)

  • Bytecode execution:
  • - Execute opcodes sequentially

    - Deduct gas for each operation

    - Modify state (storage, balance)

  • Execution ends:
  • - Success: Changes committed, unused gas refunded

    - Revert: Changes discarded, gas consumed

    - Out of gas: Changes discarded, all gas consumed

    Gas Costs Explained

    Every opcode has a fixed gas cost. Gas prevents infinite loops and limits computation.

    Common Operations:

    ADD, SUB, MUL, DIV:     3-5 gas
    

    SLOAD (storage read): 2,100 gas

    SSTORE (new slot): 20,000 gas

    SSTORE (update): 5,000 gas

    LOG0: 375 gas + 8 gas per byte

    CALL: 700 gas base + transfer cost

    CREATE: 32,000 gas

    Why is storage so expensive?

    Storage is permanent and stored on every node. Writing to storage bloats the blockchain, so it's intentionally expensive to discourage overuse.

    Gas Optimization Example:

    // Bad: 3 SLOAD operations (6,300 gas)
    

    function badSum() public view returns (uint) {

    return myValue + myValue + myValue;

    }

    // Good: 1 SLOAD, use memory (2,100 gas)

    function goodSum() public view returns (uint) {

    uint val = myValue; // Cache storage read

    return val + val + val;

    }

    Understanding Calldata, Memory, Storage

    Calldata

    • Read-only function arguments
    • Cheapest to read (4 gas per non-zero byte, 16 gas per zero byte)
    • Use for function parameters in external functions
    function processArray(uint[] calldata data) external {
    

    // 'data' lives in calldata, not copied to memory

    }

    Memory

    • Temporary storage during function execution
    • Wiped after function returns
    • Use for temporary variables, dynamic arrays
    function buildArray() public pure returns (uint[] memory) {
    

    uint[] memory arr = new uint[](5);

    arr[0] = 1;

    return arr; // Exists only during execution

    }

    Storage

    • Permanent blockchain storage
    • Organized in 32-byte slots
    • Each contract has its own storage
    uint256 public myValue; // Slot 0
    

    mapping(address => uint) public balances; // Slot 1+

    EVM Bytecode Example

    Let's see what Solidity compiles to:

    contract SimpleStorage {
    

    uint256 public value;

    function setValue(uint256 _value) public {

    value = _value;

    }

    }

    Compile with solc --bin --asm SimpleStorage.sol:

    Bytecode (partial):

    PUSH1 0x80
    

    PUSH1 0x40

    MSTORE // Setup free memory pointer

    CALLVALUE

    DUP1

    ISZERO

    PUSH2 0x0F

    JUMPI // Revert if ETH sent to non-payable function

    ...

    CALLDATALOAD // Load function selector

    PUSH4 0x3fa4f245 // setValue(uint256) selector

    EQ

    PUSH2 0x1E

    JUMPI // Jump to setValue logic if selector matches

    ...

    Why does this matter?

    • Understanding bytecode helps you optimize gas usage
    • You can audit contracts by reading bytecode
    • Debugging reverts requires bytecode knowledge

    EVM vs Other VMs

    | Feature | EVM | WASM (Polkadot) | Move VM (Aptos) |

    |---------|-----|-----------------|-----------------|

    | Stack size | 1024 items | Dynamic | Register-based |

    | Gas model | Per-opcode | Per-instruction | Per-bytecode |

    | Determinism | Full | Full | Full |

    | Language | Solidity, Vyper | Rust, C++ | Move |

    | Upgradability | Proxy patterns | Native | Built-in |

    EVM-Compatible Chains

    The EVM's success led to many EVM-compatible chains:

    • Layer 2s: Arbitrum, Optimism, zkSync, Polygon zkEVM
    • Sidechains: Polygon PoS, Gnosis Chain
    • Alternative L1s: BNB Chain, Avalanche C-Chain, Fantom

    Why EVM compatibility?

    • Reuse Solidity contracts without changes
    • Same developer tools (Foundry, Hardhat, Remix)
    • Leverage Ethereum's massive ecosystem

    Optimizing for the EVM

    1. Pack Storage Variables

    // Bad: 3 storage slots
    

    uint256 a; // Slot 0

    uint128 b; // Slot 1

    uint128 c; // Slot 2

    // Good: 2 storage slots (b and c packed)

    uint256 a; // Slot 0

    uint128 b; // Slot 1 (first 128 bits)

    uint128 c; // Slot 1 (last 128 bits)

    2. Use immutable and constant

    // Bad: SLOAD every time (2,100 gas)
    

    address public owner;

    // Good: Hardcoded into bytecode (3 gas)

    address public immutable owner;

    3. Minimize Storage Writes

    // Bad: Update storage in loop
    

    for (uint i = 0; i < 10; i++) {

    total += i; // 10 SSTORE operations

    }

    // Good: Use memory, write once

    uint temp = total;

    for (uint i = 0; i < 10; i++) {

    temp += i;

    }

    total = temp; // 1 SSTORE

    4. Use Events Instead of Storage

    For data you don't need to query on-chain, use events:

    // Bad: Store every transaction (expensive)
    

    Transaction[] public transactions;

    // Good: Emit event (375 gas + data)

    event TransactionMade(address indexed user, uint amount);

    Why the EVM Matters for Developers

  • Write efficient code: Understanding gas costs = cheaper contracts
  • Debug smarter: Trace reverts by analyzing opcodes
  • Audit better: Read bytecode to verify contracts
  • Optimize strategically: Know what's expensive (storage) vs cheap (memory)
  • Build cross-chain: EVM knowledge transfers to all EVM chains
  • The Future: EVM Evolution

    Upcoming improvements:

    • EIP-4844 (Proto-Danksharding): Cheaper L2 data availability
    • Verkle Trees: Reduce state storage size
    • EVM Object Format (EOF): Better bytecode structure
    • Account Abstraction (ERC-4337): Programmable accounts

    Conclusion

    The EVM is the beating heart of Ethereum and the broader EVM ecosystem. It's a simple stack machine with deterministic execution, metered by gas to prevent abuse.

    As a Solidity developer, understanding the EVM helps you:

    • Write cheaper contracts
    • Debug faster
    • Architect better
    • Build confidently

    Next steps:

    • Explore bytecode with forge inspect YourContract bytecode
    • Use forge test -vvvv to see EVM traces
    • Learn assembly with assembly { ... } blocks

    The EVM is your runtime—master it, and you'll master Solidity. 💎

    Prêt à mettre en pratique ?

    Applique ces concepts avec des exercices interactifs sur Solingo.

    Commencer gratuitement