Q1: What is the complete execution pipeline for a single transaction?
The pipeline lives in three files:
state_processor.go — block-level: iterate transactions, produce receiptsstate_transition.go — single-tx engine: preCheck → EVM → refund → feesevm.go — build BlockContext and TxContext for the EVMBlock-level setup
Before any transaction executes, Process() sets up a shared environment:
gp = new(GasPool).AddGas(block.GasLimit()) // block gas budgetevm = vm.NewEVM(blockContext, statedb, ...) // one EVM reused for all txsGasPool is just a uint64 representing how much gas the block has left. Each transaction draws from this pool at the start and returns unused gas at the end. If the pool is exhausted, no more transactions can fit in the block.
The EVM instance is shared across the entire block — not created per transaction. Block-level context (coinbase, block number, timestamp, baseFee) stays constant; only the transaction-level context (sender, gasPrice) is swapped per transaction.
Then each transaction goes through the following stages:
Stage 1: preCheck — is this transaction eligible to execute?
preCheck() ├─ Nonce must exactly equal the state nonce (no gaps, no replays) ├─ Sender must be an EOA (no contract code, unless EIP-7702 delegated) ├─ gasFeeCap >= baseFee (bid must meet the floor price) └─ buyGas()All checks happen before any gas is spent. If any check fails, the transaction is not included in the block — this is a consensus-level error.
Stage 2: buyGas — pre-pay maximum fees
// Balance check: gasLimit × gasFeeCap + value + blobFee (worst case)// Actual deduction: gasLimit × effectiveGasPrice (real cost)st.state.SubBalance(sender, gasLimit × gasPrice)st.gp.SubGas(msg.GasLimit)st.gasRemaining = msg.GasLimitKey distinction: validation uses gasFeeCap (the max you’re willing to pay), but the deduction uses effectiveGasPrice (the actual clearing price). Like an auction — you bid 100, clearing price is 60, the system checks you have 100 but only charges 60.
Stage 3: IntrinsicGas — baseline cost
Before the EVM even starts, a minimum fee is deducted:
| Component | Cost |
|---|---|
| Simple transfer | 21,000 gas |
| Contract creation | 53,000 gas |
| Calldata zero bytes | 4 gas each |
| Calldata non-zero bytes | 16 gas each |
| Access list addresses | 2,400 gas each |
| Access list storage keys | 1,900 gas each |
If gasRemaining < intrinsicGas, the transaction fails immediately. Otherwise the intrinsic gas is subtracted, and the remainder enters the EVM.
Stage 4: EVM dispatch
if msg.To == nil { // Contract creation: Data is init code, executed to produce deployed bytecode ret, _, st.gasRemaining, vmerr = evm.Create(sender, data, gasRemaining, value)} else { // Regular call: increment nonce first, then execute target contract st.state.SetNonce(sender, nonce+1) ret, st.gasRemaining, vmerr = evm.Call(sender, to, data, gasRemaining, value)}A critical point: EVM errors (vmerr) are NOT consensus errors. Even if the EVM returns OutOfGas or Revert, the transaction is still included in the block — gas is consumed, nonce is incremented, but the EVM’s internal state changes are rolled back. Only preCheck-stage errors prevent a transaction from being included.
Stage 5: calcRefund — refund calculation
Certain operations (e.g., SSTORE clearing a slot to zero) accumulate a refund counter. But refunds are capped:
- Pre-London: max
gasUsed / 2 - Post-London (EIP-3529): max
gasUsed / 5
The tighter cap prevents “gas token” exploits — deliberately creating and destroying storage slots to farm refunds.
Stage 6: returnGas — refund ETH to sender
state.AddBalance(sender, gasRemaining × gasPrice) // return unspent ETHgp.AddGas(gasRemaining) // return gas to block poolThe reverse of buyGas. The sender gets back the unspent portion.
Stage 7: fee distribution
effectiveTip = gasPrice - baseFeestate.AddBalance(coinbase, gasUsed × effectiveTip) // tip goes to block producer// The baseFee portion? Nobody receives it. It is burned.Stage 8: receipt creation
receipt.Status = success / failedreceipt.GasUsed = gas consumed by this transactionreceipt.CumulativeGasUsed = cumulative gas in block up to this transactionreceipt.Logs = events emitted during EVM executionreceipt.ContractAddress = new contract address (if contract creation)Then statedb.Finalise() is called, promoting dirty state to pending, preparing for the next transaction.
Q2: What is the complete lifecycle of gas — where does the money come from and where does it go?
Concrete example
Alice sends a transaction: gasLimit=100,000, gasFeeCap=30 gwei, gasTipCap=2 gwei, current baseFee=10 gwei.
Effective gas price calculation:
effectiveGasPrice = min(gasTipCap + baseFee, gasFeeCap) = min(2 + 10, 30) = 12 gwei
effectiveTip = effectiveGasPrice - baseFee = 12 - 10 = 2 gweibuyGas — pre-payment
Balance check (worst case): 100,000 × 30 gwei = 3,000,000 gweiActual deduction: 100,000 × 12 gwei = 1,200,000 gwei
Alice balance -= 1,200,000 gweiBlock GasPool -= 100,000gasRemaining = 100,000IntrinsicGas — baseline deduction
Assuming a simple transfer with no calldata:
intrinsicGas = 21,000gasRemaining = 100,000 - 21,000 = 79,000EVM execution — gas consumption
Assuming the EVM consumes 29,000 gas:
gasRemaining = 79,000 - 29,000 = 50,000calcRefund — refund
Assuming SSTORE clears accumulated 5,000 refund. Cap check:
gasUsed = 100,000 - 50,000 = 50,000cap = 50,000 / 5 = 10,000actual refund = min(5,000, 10,000) = 5,000
gasRemaining = 50,000 + 5,000 = 55,000returnGas — refund ETH
Return to Alice: 55,000 × 12 gwei = 660,000 gweiAlice balance += 660,000 gweiBlock GasPool += 55,000Fee distribution
Final gasUsed = 100,000 - 55,000 = 45,000
Coinbase receives tip: 45,000 × 2 gwei = 90,000 gweiBaseFee burned: 45,000 × 10 gwei = 450,000 gwei (nobody receives this)Verification
Alice total spend = 1,200,000 - 660,000 = 540,000 gweiDistribution = 90,000 (tip) + 450,000 (burned) = 540,000 gwei ✓Flow diagram
Alice prepays 1,200,000 gwei (gasLimit × effectiveGasPrice) │ ├─ Returned to Alice: 660,000 gwei (unused gas × effectiveGasPrice) │ └─ Actually consumed: 540,000 gwei (gasUsed × effectiveGasPrice) │ ├─ Coinbase: 90,000 gwei (gasUsed × tip) │ └─ Burned: 450,000 gwei (gasUsed × baseFee) ← EIP-1559The baseFee burn is the core EIP-1559 design — it makes ETH deflationary. Validators only receive the tip portion and cannot profit by manipulating the baseFee.
Some information may be outdated






