Skip to main content
Ethereum & smart contracts

How Smart Contracts Execute

Pomegra Learn

How Smart Contracts Execute

When you call a smart contract function, a complex sequence of events unfolds across thousands of computers. Your transaction travels through the network, is selected by a validator, executed deterministically by the EVM on every node, and its results become permanent on the blockchain. Understanding this execution model is crucial to grasping how smart contracts achieve consensus, why they cost gas, and what guarantees they provide.

Quick Definition

Smart contract execution is the process by which a transaction invoking a contract function is submitted to the network, selected for inclusion in a block, executed deterministically by the Ethereum Virtual Machine on all validating nodes, and finalized in the consensus layer. The entire process is atomic: the transaction either fully succeeds, updating the contract's state, or fully reverts, leaving the state unchanged.

Key Takeaways

  • Transactions are atomic: All state changes succeed together or fail together; partial execution does not occur.
  • Every node executes independently: The same contract code produces identical outputs on every validator, ensuring consensus.
  • Bytecode is deterministic: The same input to the EVM produces identical results regardless of which node executes it.
  • Gas metering prevents abuse: Computational work is metered; out-of-gas transactions revert, protecting the network.
  • State is consensus-validated: Contract state changes are only accepted if all honest validators agree.
  • Finality takes time: Transaction confirmation requires block proposals and consensus; finality is probabilistic initially, then absolute.

The Transaction Lifecycle

1. Transaction Creation and Submission

A user or dApp creates a transaction specifying:

  • To address: The contract address to call.
  • Function selector: A 4-byte identifier indicating which function to invoke.
  • Call data: Encoded function parameters.
  • Gas limit: Maximum gas the user will pay.
  • Gas price: Price per unit of gas in Gwei (billionths of Ether).
  • Value: Amount of Ether to send with the transaction (0 for most contract calls).
  • Nonce: A counter preventing replay attacks.

The user signs the transaction with their private key, proving they authorized it. The signed transaction is broadcast to the Ethereum peer-to-peer network.

Example: Calling transfer(0xAbc..., 100) on a token contract creates a transaction with encoded call data representing this call. The user sets gas limit to 100,000 and gas price to 50 Gwei, willing to spend up to 5,000,000 Gwei (0.005 ETH).

2. Mempool and Transaction Pool

The transaction enters the mempool—the collection of unconfirmed transactions awaiting inclusion in blocks. Validators maintain local mempools, prioritizing transactions by gas price (higher fees get included first). This incentivizes timely payment but also creates the front-running problem: if your transaction is in the public mempool, attackers can see it and insert higher-priority transactions before it.

Transactions remain in the mempool until:

  • Included in a block (and finalized).
  • Dropped due to expiration or low gas price.
  • Replaced by a higher-priority transaction from the same sender.

3. Block Proposal and Validator Selection

Every 12 seconds, Ethereum selects a validator to propose the next block. The validator chooses transactions from the mempool—typically prioritizing high-fee transactions—bundles them into a block, and broadcasts it to the network.

The block includes:

  • Block header: Metadata (timestamp, previous block hash, state root, etc.).
  • Transactions: The list of transactions the proposer selected.
  • Receipts: For each transaction, the status (success/failure) and logs emitted.

The proposer's goal is to maximize fee revenue; they include the highest-paying transactions first.

4. Block Validation and Consensus

Upon receiving a block, other validators verify it:

  1. Is the proposer authorized (did they hold the validator role this slot)?
  2. Do all transactions have valid signatures?
  3. Does block ordering respect the consensus rules?
  4. When each transaction executes, do the resulting state changes match the block's state root?

This last check is critical: validators independently execute every transaction and compute the resulting state root. If their computation matches the block's state root, the block is valid. If not, the block is rejected.

5. Execution and State Changes

Here is where smart contracts execute. For each transaction in the block:

  1. Load current state: Validators retrieve the current contract state (stored variables) from their local copy.
  2. Decode call data: The 4-byte function selector identifies which function to call; parameters are decoded from call data.
  3. Execute EVM bytecode: The function's compiled bytecode runs on the EVM. Each instruction consumes gas based on predefined costs.
  4. Update state: If the function writes to storage (e.g., updating a balance), these changes accumulate.
  5. Emit events: Any events emitted are logged.
  6. Return value: The function's return value is produced (though on-chain calls typically ignore returns).
  7. Gas accounting: The total gas consumed is calculated; the transaction's gas fee is deducted from the sender's account.

If at any point an error occurs (assertion failure, out of gas, invalid operation), the entire transaction reverts: all state changes are undone, but the sender still pays the gas fee.

Execution is deterministic: Given the same state and input, every validator executes the EVM bytecode identically. The same code path runs, the same memory operations occur, the same state changes result. This is fundamental to consensus: if execution were nondeterministic, validators would disagree on the resulting state, breaking the blockchain.

6. State Root Computation

After executing all transactions in a block, validators compute the state root—a cryptographic hash of the entire contract state tree. The block header includes this state root. If validators' computed state root matches the block's stated root, the block is valid.

Why this matters: The state root proves that all validators executed transactions identically. If an attacker tried to secretly change state (e.g., increasing their balance), the resulting state root would differ from other validators', causing the block to be rejected.

7. Consensus Finality

In Ethereum's Proof-of-Stake consensus (post-Merge):

  • Proposed: The proposer broadcasts the block.
  • Attested: Other validators vote on the block's validity.
  • Justified: If 2/3 of validators vote for the block, it is justified (casper consensus).
  • Finalized: If the next block is also justified, the previous block becomes finalized—reverted only if validators lose significant stake.

Finality provides certainty: a finalized block's state changes are effectively permanent. On Ethereum, finality typically occurs after 2 epochs (~25 minutes), though practical finality (95%+ confidence) occurs within 2 blocks (~25 seconds).

8. Confirmation and Irreversibility

Once a block is finalized, the contract state it established becomes part of the immutable blockchain. The transaction's effects—fund transfers, state updates, events—are permanent and visible to the entire world.

The Ethereum Virtual Machine (EVM)

Bytecode and Opcodes

When a Solidity contract is compiled, the result is EVM bytecode—a sequence of instructions (opcodes) understood by the EVM. Each opcode has a defined gas cost and effect.

Example opcodes:

  • PUSH1: Push a 1-byte value onto the stack (3 gas).
  • ADD: Pop two values, push their sum (3 gas).
  • SSTORE: Store a value in contract storage (20,000 gas for new keys, 2,900 for updates).
  • CALL: Invoke another contract (700 gas minimum, plus data costs).

A simple Solidity function:

function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}

Compiles to roughly:

PUSH1 0x00  // Load parameter a
PUSH1 0x20 // Load parameter b
ADD // Add them
RETURN // Return the result

The EVM executes these instructions sequentially, maintaining a stack for intermediate values. State changes (storage writes) are recorded but not applied until the transaction succeeds.

Stack, Memory, and Storage

The EVM uses three data areas:

Stack: A temporary store for intermediate values during computation. It is 1,024 items deep. Pushing and popping are free (included in opcode costs). Used for parameters, return values, and temporary calculations.

Memory: A linear byte array used for function parameters, return values, and temporary data. It costs gas to expand memory; each 256-byte word of new memory costs 0.5 Gwei. Memory is cleared after each transaction (not persistent).

Storage: The persistent state of a contract. Each storage slot (32 bytes) is addressed independently. Storage is expensive: 20,000 gas to write a new key, 2,900 gas to update an existing key, 2,100 gas to read. Storage costs drive much of contract gas consumption and design optimization.

Gas Metering and Out-of-Gas Reverts

Every operation consumes gas. The transaction specifies a gas limit (max gas allowed). As execution proceeds, the EVM accumulates gas consumed:

ADD:           3 gas
SSTORE (new): 20,000 gas
CALL: 700+ gas

If the accumulated cost exceeds the gas limit, execution halts, the transaction reverts, and all state changes are undone. The sender is charged for the gas used before running out.

Why this matters: Gas metering prevents denial-of-service attacks. Without metering, an attacker could write an infinite loop, consuming unlimited computation before any validator would detect and reject it. Gas metering makes computational cost explicit and expensive, discouraging abuse.

Contract Interaction and Call Depth

External Calls

A smart contract can call another contract using the CALL opcode:

function callOther() public {
SomeContract other = SomeContract(0x123...);
other.someFunction();
}

This invokes someFunction() on the contract at 0x123.... The called contract's code executes; if it modifies state, those changes are part of the same transaction's state update. The call can fail (if the callee reverts), causing the entire transaction to revert.

Call depth limit: Ethereum limits call depth to 1,024. A contract can call another, which calls a third, etc., up to 1,024 layers deep. The 1,024th call fails, causing the transaction to revert. This prevents denial-of-service via deep recursion.

Reentrancy Concerns

A critical scenario: contract A calls contract B, and B calls back into A before A's first function finishes. If A is not designed to handle this, B can exploit A's state inconsistencies.

Example:

contract Vulnerable {
mapping(address => uint256) balances;

function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount);
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] -= amount; // Updated after call!
}
}

An attacker contract receives the Ether, immediately calls withdraw() again, and repeats until the balance is drained. The state update (line 6) happens after the external call (line 4), allowing reentrancy.

Safeguard: Update state before calling external code:

function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // Update first
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}

State Transition and Finality

A transaction's state changes progress from uncertain to certain:

  1. Pending: Transaction is in the mempool; state changes have not occurred. This is probabilistic; the transaction might be dropped.
  2. Included: The transaction is in a proposed block. State changes are computed but not yet finalized. Validators are voting.
  3. Attested: Validators are voting for the block. Probability of inclusion is high.
  4. Justified: 2/3+ validators voted for the block. Reversion now requires 1/3+ validators behaving dishonestly.
  5. Finalized: The block after this one is also justified. Reversion requires a supermajority of validators losing collateral. This is practically irreversible.

The progression from pending to finalized typically takes 25 minutes but provides increasing certainty. For most applications, 1 block confirmation (~12 seconds) suffices; for high-value transactions, waiting for finality is prudent.

Gas Cost Structure

Gas costs are designed to reflect computational expense:

Storage operations:

  • SSTORE (write new): 20,000 gas (most expensive, reflects permanent state addition).
  • SSTORE (update existing): 2,900 gas.
  • SLOAD (read): 100 gas, or 2,100 gas if first accessed in the transaction.

Arithmetic operations:

  • ADD, SUB, MUL: 3 gas each.
  • DIV: 5 gas.
  • HASH: 30 gas.

Data operations:

  • MSTORE (write to memory): 3 gas.
  • MLOAD (read from memory): 3 gas.
  • Memory expansion: 0.5 gwei per 256-byte word.

Contract interaction:

  • CALL: 700 gas minimum, plus called contract's execution cost.
  • CREATE (deploy contract): 32,000 gas.

This cost structure incentivizes efficient code: developers optimize storage access, batch writes, and minimize external calls.

Example: A Token Transfer

Let us trace a token transfer from Alice to Bob:

  1. Create transaction: Alice calls transfer(Bob, 100) on a token contract. Gas limit: 100,000, gas price: 50 Gwei.
  2. Submit: Alice signs and broadcasts the transaction.
  3. Mempool: The transaction waits in validators' mempools.
  4. Block proposal: A validator selects Alice's transaction (high gas price) and includes it in the next block.
  5. Execution: The EVM executes transfer():
    • Load Alice's balance from storage: 2,100 gas (first access).
    • Check if Alice has enough funds: 3 gas.
    • Update Alice's balance (decrement by 100): 2,900 gas (existing key update).
    • Load Bob's balance from storage: 100 gas (already cached).
    • Update Bob's balance (increment by 100): 2,900 gas.
    • Emit Transfer event: 375 gas.
    • Total: ~10,378 gas.
  6. Finalization: The block is attested by validators. After 2 epochs (~25 minutes), it is finalized.
  7. Permanence: The token balances are permanently updated. Alice has 100 fewer tokens; Bob has 100 more.

Alice pays 10,378 * 50 Gwei = 518,900 Gwei = 0.0005189 ETH.

Common Execution Pitfalls

Out of Gas

If the transaction runs out of gas before completing, all state changes revert, but the sender pays for the gas used. Preventing this requires either:

  • Setting gas limit high enough.
  • Designing contracts to avoid expensive operations in single transactions.
  • Using batch operations or off-chain computation.

Integer Overflow/Underflow

In Solidity < 0.8, arithmetic did not check for overflow/underflow. A calculation like 0 - 1 silently wrapped to 2^256 - 1. Solidity 0.8+ checks by default; use unchecked blocks when convinced it is safe.

Front-Running

A user submits a transaction visible in the mempool. Attackers insert higher-priority transactions, extracting value (e.g., in Uniswap swaps, front-runners insert buy orders before the user's order, raising prices, then sell after, pocketing the spread).

Timestamp Dependence

Contracts that depend on block.timestamp are vulnerable: validators can slightly vary the timestamp within bounds, allowing manipulation. Use block.number for precise ordering when possible.

FAQ

How long does contract execution take?

Execution is nearly instantaneous (microseconds per transaction) on modern hardware. The blockchain consensus adds latency: ~12 seconds to inclusion, ~25 minutes to finality.

Can contracts call themselves recursively?

Yes, up to the call depth limit (1,024). However, recursion is inefficient and dangerous; most designs avoid it.

What happens if a contract throws an exception?

The entire transaction reverts, undoing all state changes. The sender pays gas but receives no refund.

Are contract interactions atomic?

Yes. If contract A calls contract B, which calls contract C, and C fails, all three revert together. The atomicity spans the entire call chain.

Summary

Smart contract execution is a precisely orchestrated process: transactions are submitted, selected by validators, executed deterministically by the EVM, and finalized through consensus. Every operation is metered in gas, preventing abuse while making computation cost explicit. State changes are atomic—they succeed together or fail together—providing strong guarantees for applications. Understanding execution, gas costs, and potential pitfalls is essential for developing secure, efficient smart contracts that operate reliably on Ethereum.

Next

What is a Decentralized App (dApp)?