Skip to content

Ethereum Virtual Machine (EVM)

A fork-aware guide to EVM transactions, message-call frames, bytecode, stack, memory, storage, gas, REVERT, DELEGATECALL, precompiles, receipts and state reconciliation.

Updated

For educational purposes only; not investment or security advice. EVM behavior depends on the exact chain, fork, state, bytecode, call context, gas schedule, client and protocol upgrades.

Direct answer

The Ethereum Virtual Machine is the execution-layer state-transition machine that interprets bytecode under a specific fork’s rules. Given the same valid pre-state, top-level transaction or message, block environment and fork specification, conforming execution clients must compute the same post-state or failure result. The EVM does not choose transaction order, provide consensus finality, authenticate a frontend or make every EVM-compatible chain as secure as Ethereum.

An externally signed transaction is a top-level protocol object. Contract-to-contract activity consists of nested message calls and call frames, not independent transactions with their own transaction nonce, receipt or hash. Each frame has code, program counter, 256-bit stack, memory, calldata, returndata, gas and execution context; persistent storage belongs to an account, while transient storage has transaction-scoped lifetime under its fork rules.

How it works

  1. Pin the execution snapshot: chain and chainId, network, block number and hash, canonical or finalized status, fork, execution-client and spec revision, pre-state or state root, transaction type, raw signed bytes, transaction hash and receipt. Determinism is conditional on this exact context.
  2. Decode the transaction envelope and separate pre-execution validity from execution. Verify signature and sender, nonce, destination or creation, value, calldata, gas limit, fee fields, access list and any type-specific fields. A transaction rejected before inclusion is not an included transaction that executed and reverted.
  3. Build the top-level message and complete call tree. Record CALL, STATICCALL, DELEGATECALL, contract creation and precompile frames; caller, context address, code address, msg.sender, msg.value, value transfer, calldata, returndata, forwarded gas and success flag. An explorer’s internal transaction label is a trace view, not a signed transaction.
  4. Trace each frame’s program counter, stack, memory, calldata, returndata, logs, persistent storage and transient storage journal. CALL uses the callee’s address and storage context; DELEGATECALL executes target code in the caller’s address and storage context while preserving the upstream sender and value; STATICCALL forbids state modification.
  5. Apply the target fork’s gas rules: intrinsic gas, opcode dynamic costs, memory expansion, cold and warm accesses, call forwarding, stipends, precompile charges, refunds and refund caps. Then calculate transaction fees separately from value using gas used and the effective gas price. A gas estimate is conditional, not a guarantee.
  6. Resolve frame outcomes by scope. RETURN commits the frame if its ancestors later commit. REVERT rolls back that frame and descendants, returns data and need not consume all remaining frame gas; exceptional halts differ. A parent can catch a failed low-level call and continue, so top-level receipt status can be 1 even when a child failed. A top-level included failure still consumes the sender nonce and paid gas.
  7. Reconcile receipt status, gas used, logs, created address and return data against pre- and post-state balances, nonces, code, persistent and transient storage, token ledgers, traces and the block state root. Re-execute with an independent client where warranted, and audit proxy implementations, storage layouts, precompiles, compiler EVM targets and fork upgrades separately from consensus finality.

Worked examples

  • Included top-level revert and fee ledger. A type-2 transaction has gas limit 80,000, gas used 52,000, base fee 20 gwei, max priority fee 3 gwei and max fee 40 gwei. Effective gas price is min(40, 20 + 3) = 23 gwei; actual fee is 52,000 * 23 gwei = 0.001196 ETH, comprising 52,000 * 20 gwei = 0.001040 ETH burned and 52,000 * 3 gwei = 0.000156 ETH priority fee. The unused 28,000 gas is not charged. If top-level execution reverts, its storage, value-transfer and log effects roll back, but the sender nonce and actual fee remain.
  • Caught child failure. Contract A starts with A.x = 5. It calls B; B writes B.y = 9, emits a log and executes REVERT. B’s write and log roll back. A observes success = false, writes A.x = 7 and returns normally. Final receipt status is 1, A.x = 7 and B retains its prior value. Top-level success therefore does not prove every child call succeeded.
  • DELEGATECALL storage context. A proxy has slot0 = 5; an implementation account has slot0 = 99. Implementation code reads slot 0, adds 7 and stores the result. Executed through DELEGATECALL, it changes the proxy to slot0 = 12 while the implementation remains slot0 = 99; the proxy address and storage context apply, and the upstream sender and value are preserved. An incompatible storage layout can corrupt proxy state.
  • Fork-scoped SELFDESTRUCT. Under the EIP-6780 rule, an existing contract with 2 ETH executes SELFDESTRUCT to beneficiary B. B receives 2 ETH and the contract balance becomes zero, but the existing account, code and storage are not deleted. Deletion behavior remains only when the contract was created and self-destructed in the same transaction. This is fork-scoped behavior, not a rule to extrapolate backward or to every EVM chain.

Risks

  • Replaying against the wrong chain, block, fork, client or pre-state.
  • Treating a noncanonical or reorganized state as final execution context.
  • Confusing pre-validation rejection with included execution revert.
  • Assuming a pending simulation will match later included state.
  • Ignoring a top-level revert, exceptional halt or out-of-gas condition.
  • Missing a child failure caught by its parent.
  • Misreading call context, code address, caller, sender or value.
  • Corrupting proxy state through DELEGATECALL storage-layout mismatch.
  • Allowing reentrancy or unsafe external value and control transfer.
  • Trusting unvalidated returndata, success flags or custom errors.
  • Treating logs or traces as authoritative final state.
  • Miscalculating cold and warm accesses, memory or call-forwarded gas.
  • Misapplying refunds, refund caps, stipends or the 63/64 rule.
  • Using the wrong precompile address, input, gas rule or fork semantics.
  • Confusing persistent storage, memory and transient storage lifetimes.
  • Expecting state modification to succeed inside STATICCALL.
  • Applying pre-EIP-6780 SELFDESTRUCT deletion assumptions.
  • Missing proxy implementation, admin or compiler-target drift.
  • Running divergent or unupgraded execution-client rules.
  • Inferring consensus, bridge, token, governance or finality security from EVM compatibility.

Common misconceptions

  • Solidity source code is the object directly executed on-chain.
  • A reverted included transaction costs nothing and leaves the nonce unchanged.
  • Receipt status 1 proves that every internal call succeeded as intended.
  • An event or trace is the authoritative asset and storage state.
  • EVM compatibility guarantees identical opcodes, gas, precompiles, consensus and security.

Sources

Navigation

Search the wiki...