Ethereum Deep Dive
Ethereum = a global, single-threaded, deterministic VM (the EVM) whose memory is the world state and whose billing unit is gas. Once you see it that way, everything else clicks.
1. Two kinds of accounts
| EOA (Externally Owned) | Contract | |
|---|---|---|
| Controlled by | Private key | Code |
| Can initiate tx? | Yes | No (only in response) |
| Has code? | No | Yes (immutable bytecode) |
| Analogy | User's email | Lambda function with a balance |
2. The EVM, in SWE terms
- Stack machine (256-bit words, max 1024 deep). No registers. No floats.
- Three memory regions:
storage(persistent, expensive),memory(per-call, cheap),calldata(read-only input). - Deterministic — no wall clock, no randomness, no filesystem. Only inputs: tx data + current block header + state.
- Single-threaded globally — every node runs every tx in the same order.
3. Gas — the rate limiter
Every opcode has a gas cost. ADD is 3 gas; reading a storage slot (SLOAD) is 2,100 cold / 100 warm; writing a new slot (SSTORE) is 20,000. Your tx pays gas_used × gas_price.
| Op | Gas | Notes |
|---|---|---|
| ADD, SUB | 3 | Arithmetic is free-ish |
| KECCAK256 | 30 + 6/word | Hashing isn't free |
| SLOAD (cold) | 2,100 | Reading chain state |
| SSTORE (new) | 20,000 | Writing new state |
| SSTORE (update) | 2,900 | Mutating existing |
| CREATE | 32,000 + code | Deploying |
| CALL | ~100+ | Calling another contract |
4. EIP-1559: base fee + tip
effective_gas_price = min(max_fee, base_fee + max_priority_fee)
// base_fee is burned, tip goes to validator
Base fee adjusts ±12.5% per block to target 50% block usage. It's an auto-scaling load balancer for blockspace — very much like AWS spot pricing.
5. A transaction's lifecycle
6. Storage layout & the state trie
Each contract has a map from 32-byte slot → 32-byte value. Solidity variables are packed into slots in declaration order. A mapping(k => v) stores v at keccak256(k || slot_index).
contract Foo {
uint256 a; // slot 0
uint128 b; uint128 c; // slot 1 (packed)
mapping(address => uint256) d; // slot 2 — entries at keccak256(addr || 2)
}
7. Hands-on: local chain in 5 minutes
npm init -y
npm i --save-dev hardhat
npx hardhat init # pick "JavaScript project"
npx hardhat node # local chain on :8545, 20 funded accounts
# in another terminal:
npx hardhat console --network localhost
> const [a,b] = await ethers.getSigners()
> (await ethers.provider.getBalance(a.address)) / 10n**18n
10000n // 10,000 test ETH
This is your dev loop for the rest of the course. Hardhat gives you console.log inside Solidity — yes, really.
8. Reading a receipt
const tx = await contract.doThing(42);
const r = await tx.wait();
console.log({
status: r.status, // 1 = success, 0 = reverted
gasUsed: r.gasUsed,
logs: r.logs, // events
blockNumber: r.blockNumber,
});
9. Project
Lock.sol from Hardhat's init, call it from the console, read storage slot 0 with provider.getStorage(addr, 0), and match the raw bytes against the Solidity variable.Quiz
- Ethereum caches the value on-chain forever
- EIP-2929 "access lists" — first touch is cold (DB read), subsequent touches are warm (in-memory)
- Validators charge less for popular slots
- Gas price halves between ops