L05: Ethereum & Smart Contracts
The Vision: From Digital Cash to World Computer
01Bitcoin proved one thing beyond reasonable doubt: you can transfer value between strangers without any bank or clearinghouse in the middle. That is a significant achievement. But Vitalik Buterin, reading the Bitcoin whitepaper in 2011 at age 17, saw a narrower tool than its reputation suggested. Bitcoin’s scripting language — called Script — is deliberately limited. It can express simple conditions like “release this coin if signature X is provided,” but it cannot loop, cannot store state between transactions, and cannot implement anything resembling a general agreement.
Buterin’s 2013 whitepaper proposed a different question: what if the blockchain were not just a payment ledger, but a general-purpose programmable platform? Any computation expressible as a program should be expressible as a contract on a shared, censorship-resistant, globally replicated virtual machine. The result, launched on 30 July 2015 as the “Frontier” release, was Ethereum.
The practical leap from Bitcoin to Ethereum rests on three design decisions. First, Turing completeness: the Ethereum Virtual Machine (EVM) can execute any algorithm, given enough gas. Loops, conditionals, dynamic data structures — all supported. Second, account-based state: the blockchain tracks the current state of every account rather than just a chain of transactions. Third, gas metering: every EVM instruction carries a fixed gas cost, preventing infinite loops and compensating validators for their computational resources.
The result, a decade later, is a platform that hosts over \$100 billion in locked value across DeFi protocols, the vast majority of the world’s NFT activity, hundreds of DAOs governing billions in treasury assets, and thousands of ERC-20 tokens — all running on the same virtual machine without requiring any operator to be trusted.
The Account Model: State, Nonces, and Two Account Types
02Bitcoin’s UTXO model treats the blockchain as a set of unspent coins: your “balance” is the sum of all output coins locked to your address, and spending means destroying those outputs and creating new ones. It is elegant, stateless, and privacy-friendly (you can use a fresh address per transaction). But it makes complex programming extraordinarily awkward — a contract that needs to remember state between calls has no natural home in a UTXO model.
Ethereum chose the account model instead. The world state is a mapping from 160-bit addresses to account records. Every account — whether controlled by a human or by code — has four fields stored in Ethereum’s global state trie.
| Field | Type | EOA (human wallet) | Contract Account |
|---|---|---|---|
| nonce | uint256 | Transactions sent from this address (prevents replay) | Number of contracts this contract has created |
| balance | uint256 (wei) | ETH held; 1 ETH = 1018 wei | ETH held by the contract (e.g., an escrow) |
| codeHash | bytes32 | Hash of empty string — EOAs have no code | Keccak-256 of the deployed EVM bytecode (immutable) |
| storageRoot | bytes32 | Hash of empty trie — no storage | Root of the per-contract storage trie (all variables) |
There are exactly two account types in Ethereum. An Externally Owned Account (EOA) is controlled by a private key — your MetaMask wallet is an EOA. It has balance and nonce, but no code and no storage. Any transaction on Ethereum must be initiated by an EOA. A Contract Account is controlled by its deployed bytecode. It cannot initiate transactions; it only responds to calls. It has all four fields populated, including a storage trie that persists contract variables (token balances, proposal states, lending positions) indefinitely.
The nonce field deserves special attention. For EOAs, the nonce is a transaction counter that increments with each outgoing transaction. Ethereum nodes reject any transaction whose nonce is not exactly one higher than the account’s current nonce. This prevents replay attacks — an adversary who captures a signed transaction cannot resubmit it because the nonce would already be consumed. It also means transactions from the same account are strictly ordered, unlike Bitcoin UTXOs which can be spent in any order.
The EVM: Stack Machine, Opcodes, and Gas Metering
03The Ethereum Virtual Machine is the runtime environment in which all smart contract code executes. Every full node on the Ethereum network runs an identical copy of the EVM and executes every transaction against it. Because execution is deterministic — the same inputs always produce the same outputs — all honest nodes agree on the resulting state without trusting each other. This is the core technical guarantee that makes smart contracts “trustless.”
The EVM is a stack-based virtual machine with a word size of 256 bits (32 bytes). This unusually large word size was chosen to accommodate 256-bit cryptographic hashes and Ethereum addresses natively without awkward casting. The stack can hold up to 1,024 items; a stack overflow aborts execution and reverts all state changes. Alongside the stack, the EVM provides three memory regions:
- Stack: Last-in, first-out; 256-bit words; up to 1,024 items; wiped after each call.
- Memory: Byte-addressable, linear; grows on demand; wiped after each call. Expansion cost increases quadratically to prevent unbounded allocation.
- Storage: 256-bit key to 256-bit value mapping; persists between calls; most expensive by far. This is where contract state lives.
EVM execution is gas-metered. Every opcode has a fixed gas cost reflecting its computational and storage burden. The two most important cost categories are arithmetic (cheap: ADD costs 3 gas) and storage (expensive: SSTORE to a new slot costs 20,000 gas — 6,600x more expensive than an addition). This steep cost gradient was deliberately designed to discourage state bloat: unnecessary on-chain storage accumulates forever and burdens every future node that must hold the state.
Why does the EVM exist at all, rather than running native machine code? Three reasons. First, portability: nodes run on Linux, macOS, Windows, ARM, and x86. The EVM abstracts over all hardware. Second, metering: native execution cannot be metered without OS-level instrumentation; the EVM meters every instruction by definition. Third, isolation: contract code runs in a sandbox with no file system access, no network calls, and no access to external randomness. Any contract that needs external data must call an oracle that posts that data as an on-chain transaction first.
EIP-1559: The Fee Market Revolution and ETH Burn
04Before August 2021, Ethereum used a first-price auction for gas. Users submitted bids (“I will pay up to X gwei per gas unit”) and block producers included the highest bidders. This mechanism had three pathological properties. Users overbid systematically because underbidding meant waiting indefinitely during congestion. Fee volatility was extreme — the same transaction might cost \$0.50 at 2am and \$200 during a popular NFT mint twelve hours later. And the auction mechanism made Maximal Extractable Value (MEV) strategies by miners straightforward, since they could reorder transactions to maximize extracted value.
EIP-1559, activated in the London hard fork on 5 August 2021, replaced this with a two-component model that has become one of Ethereum’s most consequential design decisions.
The economic implications of the base-fee burn are significant. On days of high network activity, more ETH is burned than is issued as staking rewards — making ETH net deflationary. Since the London fork, over 4 million ETH has been burned, representing billions of dollars removed from circulation. This mechanism aligns validator incentives (earn tips) with network health (high usage means more burning, which benefits all ETH holders by increasing scarcity). Critics note that burning does not directly fund protocol development; that resource allocation happens through governance.
• Total fee: 150,000 × (25 + 2) = 4,050,000 gwei = 0.00405 ETH
• Burned (base fee): 150,000 × 25 = 0.00375 ETH
• Validator tip: 150,000 × 2 = 0.0003 ETH
Of your \$8.10 fee at ETH = \$2,000, \$7.50 is permanently destroyed and \$0.60 goes to the validator.
World State: Merkle Patricia Tries and Storage Layout
05Ethereum must track the state of hundreds of millions of accounts and thousands of contract storage mappings efficiently. It must also allow light clients — nodes that do not download the entire chain — to verify that a specific account state or storage value is authentic without trusting any server. The data structure that serves both requirements is the Modified Merkle Patricia Trie (MPT).
A Merkle Patricia Trie is a key-value store where the keys are paths through the trie and each internal node is identified by the hash of its children. The root hash commits to the entire trie: changing any leaf changes all hashes on the path to the root, producing a different state root. This means the single 32-byte stateRoot in a block header is a cryptographic fingerprint of all 400 million+ account states at that block height.
Ethereum maintains four separate tries per block:
- State trie: Maps address → (nonce, balance, codeHash, storageRoot) for all accounts.
- Storage trie (one per contract): Maps 256-bit slot key → 256-bit value for each contract’s variables.
- Transaction trie: Ordered list of all transactions in the block.
- Receipt trie: Execution receipts (gas used, logs, status) for each transaction.
Within a contract’s storage trie, variables are assigned storage slots numbered from 0. The Solidity compiler assigns fixed-size variables in declaration order. Two uint128 variables occupy one 32-byte slot (slot packing). Dynamic types use derived slot addresses: a mapping balances at slot 3 stores the value for key k at keccak256(k, 3). This deterministic layout is why upgrading a contract via a proxy pattern requires extreme care — if the new implementation has a different variable declaration order, it will read the wrong slot and produce corrupted state.
Smart Contracts: Lifecycle, Deployment, and Anatomy
06A smart contract is a program whose bytecode is stored on-chain and whose execution is triggered by transactions. The word “smart” is a slight misnomer: contracts are not intelligent — they are extremely literal. They execute exactly what their bytecode specifies, nothing more and nothing less. This literalism is both the source of their power (no counterparty can deviate from the agreed terms) and their vulnerability (bugs are exploitable as intended behavior).
The deployment process has five steps. First, a developer writes Solidity (or Vyper) and compiles it to EVM bytecode. The compiler produces two artifacts: init code (constructor logic) and runtime bytecode (the deployed contract). Second, the developer sends a transaction with the to field empty (null) and the init code in the data field. Third, the EVM executes the init code, which runs the constructor and returns the runtime bytecode. Fourth, the EVM stores the runtime bytecode at a newly computed address. Fifth, the address is determined by keccak256(deployer_address, nonce) for CREATE, or keccak256(0xff, deployer, salt, codeHash) for CREATE2 (which enables deterministic, pre-computable addresses).
After deployment, the code is immutable. The codeHash field is set at deployment and cannot change. This is what “code is law” means in practice: once a contract is deployed, its logic is frozen. Upgradability requires the proxy pattern — a minimal proxy contract stores the address of a separate implementation contract; calls are forwarded to the implementation via delegatecall. The proxy can be updated to point to a new implementation, but this reintroduces a trusted administrator who controls the upgrade.
A transaction that calls a deployed contract specifies the function selector in its data field — the first 4 bytes of the Keccak-256 hash of the function signature (e.g., transfer(address,uint256) becomes 0xa9059cbb). The contract’s dispatcher checks this selector and jumps to the matching function’s code. Arguments are ABI-encoded in the remaining bytes. Events emitted by contracts are stored in transaction receipts as logs, accessible to off-chain applications via eth_getLogs but not from within other contracts during execution.
The pattern above — Checks, Effects, Interactions (CEI) — is the canonical defense against reentrancy attacks. By updating state (Effects) before making external calls (Interactions), the contract prevents recursive re-entry from draining more than the caller’s balance. The DAO hack of 2016, which drained \$60 million, was caused precisely by violating this ordering.
Vulnerabilities: What the EVM Cannot Protect You From
07Ethereum’s EVM guarantees that contracts execute exactly as written. This guarantee cuts both ways: correct code runs correctly, and buggy code runs buggily, with no possibility of a patch after deployment. The immutability that makes smart contracts trustworthy is the same property that makes smart contract bugs catastrophic. Understanding the most common vulnerability classes is not optional for anyone writing or auditing contracts.
withdraw function sent ETH to the caller before updating the balance. The attacker deployed a malicious contract whose fallback function recursively called withdraw again before the balance was decremented — draining \$60 million in a reentrancy loop. The Ethereum community hard-forked the chain to reverse the theft, creating Ethereum Classic (ETC) as the dissenting minority. Every withdraw function written since uses Checks-Effects-Interactions or a reentrancy guard to prevent this class of attack.
Reentrancy
External call re-enters the contract before state is updated. Drain: send ETH, re-enter, repeat. Fix: update state before external calls (CEI pattern) or use a mutex lock.
Oracle Manipulation
Price oracles based on spot DEX prices can be manipulated within one block using flash loans. A \$1B flash loan can move a thin market, manipulate the oracle, and profit, all atomically. Fix: use time-weighted average price (TWAP) oracles.
Integer Overflow
Solidity prior to 0.8.0 did not revert on arithmetic overflow. Adding 1 to the maximum uint256 wrapped to zero. Fix: Solidity 0.8+ has built-in overflow protection; older code used SafeMath libraries.
Access Control
Functions that should only be callable by the owner or governance are left public by mistake. Accounted for a large fraction of 2022 DeFi hacks. Fix: onlyOwner modifiers, role-based access control.
Flash Loan Attacks
Uncollateralized loans of any size within one atomic transaction. Used to amplify oracle manipulation, governance attacks, and liquidity exploits. Flash loans are not inherently malicious but weaponize previously impractical attack scales.
Proxy Storage Collision
When an upgradeable proxy and its implementation use the same storage slots for different purposes, writes collide and corrupt state. Fix: EIP-1967 standardized proxy storage slots at specific hash-derived positions.
Mitigation strategies form a defense-in-depth stack: automated static analysis (Slither, MythX) catches common patterns; manual audits by specialist firms (Trail of Bits, OpenZeppelin) catch logical flaws; formal verification (Certora Prover) mathematically proves invariants; economic audits assess incentive structures; bug bounties crowdsource ongoing monitoring. Even with all of these, DeFi lost over \$3.8 billion to exploits in 2022 alone — demonstrating that smart contract security remains an unsolved engineering problem rather than a checklist exercise.
The Merge and Roadmap: PoS, Surge, Verge, Purge
08On 15 September 2022 at 06:42:42 UTC, Ethereum’s consensus mechanism switched from Proof of Work to Proof of Stake in an event called The Merge. The execution layer (the EVM, transactions, smart contracts) continued running unchanged. The consensus layer (block proposal, finality, slashing) switched from mining to a validator set of over 900,000 accounts each staking 32 ETH. From the outside, the only observable differences were: block time became a fixed 12 seconds (vs Bitcoin’s variable ~10 minutes), energy consumption dropped by 99.95%, and ETH issuance fell by roughly 88%.
• Consensus: Proof of Work → Proof of Stake
• Block time: variable → fixed 12 seconds
• Energy: ~100 TWh/yr → ~0.01 TWh/yr (comparable to a large office building)
• Issuance: ~13,000 ETH/day → ~1,700 ETH/day
• Block proposer: mining pool → randomly selected validator
• EVM execution model (all contracts run unchanged)
• Transaction format (EIP-1559 still applies)
• Smart contract compatibility (no migration required)
• Account model and storage layout
• Base fee burn mechanism
Ethereum’s roadmap beyond The Merge comprises four phases, each named with characteristic alliteration. The Surge targets 100,000+ TPS by combining data availability sampling (EIP-4844 “proto-danksharding,” already live) with full danksharding, which distributes large blobs of rollup data across the validator set. EIP-4844, activated in March 2024, immediately reduced Layer 2 transaction costs by 10–100x by introducing a separate, cheaper data lane for rollups. The Verge replaces Merkle Patricia Tries with Verkle trees — a cryptographic data structure with much smaller proofs (a few kilobytes vs tens of kilobytes) that enables stateless clients to validate blocks without storing the entire state. The Purge removes historical data obligations from nodes, allowing nodes to prune data older than one year, dramatically reducing node storage requirements. The Splurge is the catch-all for improvements that do not fit the other categories, including EVM Object Format (EOF), which reorganises the bytecode structure for better performance and security.
| Roadmap Phase | Key Upgrade | Primary Benefit | Status (2024) |
|---|---|---|---|
| The Merge | PoS consensus via Beacon Chain | 99.95% energy reduction; fixed 12s blocks | Complete (Sep 2022) |
| The Surge | EIP-4844 proto-danksharding | L2 data costs reduced 10–100x | EIP-4844 live (Mar 2024) |
| The Surge | Full danksharding | 100,000+ TPS with L2s; data availability sampling | In research |
| The Verge | Verkle trees replacing MPT | Stateless clients; smaller proofs | In development |
| The Purge | History expiry (EIP-4444) | Nodes can prune old data; lower hardware requirements | In research |
| The Splurge | EVM Object Format (EOF) | Safer, more analysable bytecode structure | Scheduled |
The central lesson of Ethereum’s evolution is that the design choices made at launch — account model, Turing completeness, gas metering, the state trie — were not arbitrary. They reflect a coherent philosophy about what a programmable blockchain requires. The ongoing roadmap is not abandoning that philosophy; it is engineering around its inherent scaling constraints by separating the functions of execution, consensus, and data availability into distinct layers. Understanding that layered architecture — EVM on top, Beacon Chain below, rollups alongside — is the prerequisite for understanding every subsequent lecture in this course, from DeFi protocols to token standards to governance systems.