Skip to main content

Ethereum & the EVM

Ethereum is a transaction-based state machine: state' = STF(state, tx). The Ethereum Virtual Machine is the deterministic runtime that every node uses to apply a transaction.

Architecture After The Merge

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” Engine API (JWT-auth RPC) β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Consensus client β”‚ <---------------------------> β”‚ Execution client β”‚
β”‚ Prysm / Lighthouse β”‚ newPayload / forkchoiceUpd β”‚ Geth / Nethermind β”‚
β”‚ Teku / Nimbus β”‚ β”‚ Besu / Reth β”‚
β”‚ - fork choice, FFG β”‚ β”‚ - EVM, state, mempoolβ”‚
β”‚ - validators, p2p β”‚ β”‚ - JSON-RPC for dApps β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Client diversity matters: a bug in a client with > 2/3 share could finalise an invalid chain; > 1/3 share could stall finality.

Accounts & World State

account = { nonce, balance, storageRoot, codeHash }
worldState = MerklePatriciaTrie( keccak256(address) -> rlp(account) )

Each contract has its own storage trie (slot -> 32-byte word). Block header commits to stateRoot. Archive nodes keep all historical states (~15+ TB); full nodes keep recent ones (~1 TB); light/stateless approaches are in progress (Verkle, EIP-4444 history expiry).

Transaction Lifecycle

StageWhat happens
1. SignType-2 tx: chainId, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to, value, data, accessList -> ECDSA signature
2. GossipSent via eth_sendRawTransaction; propagates; sits in mempool (txpool) keyed by (sender, nonce)
3. BuildBlock builder (often MEV-Boost relay/builder) orders txs to maximise value
4. ProposeValidator proposes the builder's block for the slot (PBS)
5. ExecuteEach node runs the EVM, computes receiptsRoot and stateRoot, verifies against header
6. Attest/FinalizeCommittees attest; checkpoint finalises (~13 min)

The EVM: Stack Machine

  • 256-bit words, stack (1024 items max), memory (byte-addressable, volatile, quadratic expansion cost), storage (persistent), calldata (read-only input), transient storage (TSTORE/TLOAD, EIP-1153, cleared per tx), and return data.
  • Deterministic: no floats, no wall-clock randomness, no network I/O.

Gas Cost Landmarks (post-Berlin/Cancun)

OperationApprox gasWhy it matters
ADD, MUL, stack ops3-5Cheap compute
KECCAK25630 + 6/wordMapping key hashing
SLOAD cold / warm2100 / 100Access lists (EIP-2929): first touch is expensive
SSTORE 0 -> nonzero20,000 (+ cold 2100)Storage is the dominant cost
SSTORE nonzero -> nonzero2,900 (+ cold)Pack variables to reduce slots
Refund (clear slot)up to 20% of tx gas (EIP-3529)Gas-token tricks died
CALL w/ value9,000 (+ 25,000 new account)External calls are costly + risky
LOG n topics375 + 375n + 8/byteEvents are cheaper than storage for off-chain consumers
Calldata16 per non-zero byte, 4 per zero byteDrives L2 data cost, blob rationale
Tx base21,000Floor for any tx

Storage Layout (Solidity)

contract Packed {
uint128 a; // slot 0 (low 16 bytes)
uint128 b; // slot 0 (high 16 bytes) -> packed with a, 1 SSTORE for both
uint256 c; // slot 1
mapping(address => uint256) bal; // slot 2; element at keccak256(abi.encode(key, 2))
uint256[] arr; // slot 3 holds length; data at keccak256(3) + index
}

Understanding layout is essential for proxy upgrades (storage collisions) and for reading private-looking state: private only hides from other contracts, not from eth_getStorageAt.

Fee Market: EIP-1559

baseFee : protocol-set, adjusts +-12.5% per block toward 50% full (target 15M gas, max 30M)
priorityFee : tip to block producer
paid per gas = min(maxFeePerGas, baseFee + maxPriorityFeePerGas)
baseFee portion is BURNED -> ETH can become deflationary under load

Benefits: predictable fees, no first-price auction guessing. Set maxFeePerGas ~ 2 x baseFee + tip to survive several consecutive full blocks.

Data Availability: EIP-4844 (Proto-Danksharding)

Type-3 transactions carry blobs (128 KiB each, target 3, max 6 per block at launch; increasing with Pectra/PeerDAS). Blobs are committed via KZG commitments, stored by consensus nodes for ~18 days, not accessible by the EVM (only the BLOBHASH), and priced by a separate blob base-fee market. They cut rollup data costs by an order of magnitude. See Sharding & Rollups.

Account Abstraction (ERC-4337 & EIP-7702)

EOAs only sign ECDSA. ERC-4337 adds smart accounts: UserOperation objects go to an alt-mempool, bundlers submit them to the singleton EntryPoint, and the account's validateUserOp defines authentication (passkeys, multisig, session keys), with paymasters sponsoring gas. EIP-7702 lets an EOA temporarily delegate to contract code.

Token Standards

StandardPurposeKey functions
ERC-20Fungible tokenstransfer, approve, transferFrom, allowance
ERC-721NFTownerOf, safeTransferFrom (calls onERC721Received)
ERC-1155Multi-token (fungible + NFT)batch transfers
ERC-2612Gasless approval (permit)EIP-712 signature
ERC-4626Tokenised vaultdeposit, redeem, share/asset conversion

Gotchas: non-standard ERC-20s (USDT does not return bool) require SafeERC20; fee-on-transfer and rebasing tokens break AMM/vault accounting; the infamous approve race is mitigated with increaseAllowance / permit; infinite approvals are a phishing risk.

Execution Client RPC Cheat Sheet

MethodPurpose
eth_blockNumber, eth_getBlockByNumber("finalized")Chain head / finalized block
eth_callRead-only EVM execution at a block
eth_estimateGasBinary-search gas (can under-estimate with refund/63/64 rule)
eth_getLogsEvent query (range-limited by providers)
eth_getTransactionReceiptstatus, gasUsed, logs - pending returns null
eth_feeHistoryPercentile priority fees for fee estimation
debug_traceTransaction / trace_*Opcode / call traces for forensics

Senior Gotchas

  1. 63/64 rule: a CALL forwards at most 63/64 of remaining gas - affects griefing and gas-estimation in relayers.
  2. block.timestamp / prevrandao: proposers can bias slightly; never use as sole randomness (use VRF like Chainlink VRF).
  3. tx.origin auth is phishable; use msg.sender.
  4. Reorgs on L1 are rare post-Merge but 1-2 slot reorgs occur; wait for safe/finalized for value.
  5. Contract size limit 24,576 B (EIP-170) and initcode limit 49,152 B (EIP-3860) drive diamond/proxy architectures.
  6. Selfdestruct semantics largely removed (EIP-6780): do not rely on it for upgrade/cleanup.

Next: Smart Contract Security.

πŸ“–
Track Page Progress0 / 635 Read
Knowledge Base Completion0%