Solidity Programming Language and Ethereum Virtual Machine Architecture

Overview and Core Characteristics of Solidity

  • Execution Environment: Smart contracts written in Solidity run on the Ethereum Virtual Machine (EVM).

  • Language Inspirations: Solidity design is heavily inspired by C++, Python, and JavaScript.

  • Type System: Solidity is a statically typed programming language.

  • Supported Features:

    • Inheritance across contracts.

    • Complex User-Defined Types (UDTs).

  • Primary Smart Contract Applications:

    • Voting systems.

    • Multi-signature (multi-sig) wallets.

    • Blind auctions.

    • Crowdfunding platforms.

Solidity Contract Examples and Code Mechanics

  • SimpleStorage Contract Implementation:

    • License identifier: // SPDX-License-Identifier: GPL-3.0

    • Compiler version pragma: pragma solidity >=0.4.16 <0.9.0;

    • Contract Definition: solidity contract SimpleStorage { uint storedData; function set(uint x) public { storedData = x; } function get() public view returns (uint) { return storedData; } } &nbsp;&nbsp;&nbsp;&nbsp;

    • Deployment Characteristics: A deployed contract executes at a specific address on the EVM.

    • State Variables: storedData is an unsigned integer state variable that is 256 bits long.

    • Functions:

    • set(uint x): Public setter function modifying storedData.

    • get(): Public getter function marked view returning a uint value.

  • Coin / Cryptocurrency Contract Implementation:

    • Contract Definition and Logic: ```solidity error InsufficientBalance(uint requested, uint available);

    contract Coin { address public minter; mapping(address => uint) public balances;

    event Sent(address from, address to, uint amount);
    
    constructor() {
        minter = msg.sender;
    }
    
    function mint(address receiver, uint amount) public {
        require(msg.sender == minter);
        balances[receiver] += amount;
    }
    
    function send(address receiver, uint amount) public {
        require(amount <= balances[msg.sender], InsufficientBalance(amount, balances[msg.sender]));
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        emit Sent(msg.sender, receiver, amount);
    }
    

    }     ```

    • Error Declarations: Custom errors (such as InsufficientBalance) provide precise structural information to function callers regarding operational failure reasons.

    • State Variables and Visibility:

    • minter: Address variable marked public, making it accessible from other contracts.

    • balances: Public mapping from an address to a uint balance.

    • Events: event Sent allows off-chain clients to monitor and react to specific state changes.

    • Constructor: Executes exactly once when the contract is initially created on the EVM, setting minter = msg.sender.

    • Coin Minting: mint transfers newly created coins to a target address; restricted to execution by the contract creator (minter).

    • Coin Transfers: send transfers existing coins from the function caller (msg.sender) to a target address, enforcing balance limits via require and emitting a Sent event upon success.

Data Types, Control Flow, and Client Interaction

  • Address Data Type:

    • Represented as a 160-bit value.

    • Arithmetic operations are explicitly prohibited on address types.

    • Primary Uses:

    • Storing a contract address.

    • Storing a hash of an Externally Owned Account (EOA) public key.

  • Mapping Data Type:

    • Functions similarly to a virtual hash table without key collisions.

    • All possible key hashes are virtually created and mapped to an initial default value of zero.

    • Key-value iteration is impossible directly; contract logic must maintain separate tracking lists if key iteration is needed.

  • Events and Client-Side Listeners:

    • Events must be explicitly declared before being emitted.

    • Outside clients listen to emitted events via off-chain libraries such as web3.js or viem.

    • TypeScript Listener Pattern:typescript Coin.Sent().watch({}, '', function(error, result) { if (!error) { console.log("Coin transfer: " + result.args.amount + " coins were sent from " + result.args.from + " to " + result.args.to + "."); console.log("Balances now: " + "Sender: " + Coin.balances.call(result.args.from) + "Receiver: " + Coin.balances.call(result.args.to)); } }) &nbsp;&nbsp;&nbsp;&nbsp;

  • Condition Verification and Exceptions:

    • require statements impose pre-execution conditions on function logic (analogous to cassert in C++).

    • Failing a require condition causes transaction execution to revert, throwing an exception where the sender loses spent gas.

    • require with custom error payloads sends detailed context back to clients.

    • Unchecked integer arithmetic overflow also triggers automatic transaction reverts.

  • Global Environmental Variables:

    • Key global namespace variables include msg, tx, and block.

    • msg.sender: Specifies the address that directly invoked the currently executing contract function.

EVM Architecture, Accounts, and Transaction Flow

  • EVM Account Types:

    • Externally Owned Accounts (EOAs): Controlled by private key pairs.

    • Contract Accounts: Controlled by smart contract bytecode.

    • Account Structure: Both account types contain:

    • Storage (a key-value map from 256-bit words to 256-bit words).

    • Balance denominated in Wei, where 1018 Wei=1 Ether10^{18} \text{ Wei} = 1 \text{ Ether}.

  • Transaction Execution Flow:

    • Transactions originate from a sender and target a recipient address (target address can be empty).

    • Transactions include a binary data payload and Ether value.

    • Specified Target: If the target is a specified contract, the binary payload is executed as calldata.

    • Empty Target (Contract Creation):

    • Triggers creation of a new contract.

    • The EVM computes the new contract address using the sender's address and the sender's transaction count (nonce).

    • Assumes the transaction payload is initial EVM bytecode and executes it.

    • Stores the output resulting from bytecode execution as contract runtime code at the newly computed address.

    • Safety Constraint: External calls into a contract must not occur until its constructor completes execution.

EVM Gas Economics

  • Gas Payment Responsibility: tx.origin must pay sufficient gas upfront for transaction creation.

  • Gas Depletion Rules: Gas is consumed sequentially during execution according to pre-determined opcode cost rules.

  • Out-of-Gas Exceptions:

    • If remaining gas drops below zero (becomes negative), an out-of-gas exception is thrown.

    • Execution is terminated immediately.

    • All state changes caused by the transaction revert.

    • The transaction sender (tx.origin) forfeits all spent gas.

  • Gas Price and Upfront Capital:

    • tx.origin specifies gas_price and pays gas×gas_price\text{gas} \times \text{gas\_price} upfront to the EVM full node executor.

    • Unused excess gas is refunded to tx.origin upon successful transaction completion.

    • Setting a gas_price value below market thresholds risks rejection or non-inclusion by network miners.

EVM Data Storage Regions

  • Storage Region (storage):

    • Key-value store mapping 256-bit words to 256-bit words.

    • Non-enumerated data structure.

    • Operation costs: Extremely costly to read, and significantly more expensive to create or update.

    • Guideline: Minimize storage usage exclusively to essential runtime state variables.

    • Isolation: A contract cannot access the storage area of another account.

  • Transient Storage Region:

    • Automatically resets to default zero values at the end of every transaction.

    • Values persist only across inner function calls originating from the primary transaction call.

    • Subsequent transactions cannot access transient storage data from prior transactions.

    • Gas cost is substantially lower than standard storage operations.

  • Memory Region:

    • Linear and byte-addressable workspace.

    • Supports reads up to 256 bits and writes of 8 bits or 256 bits.

    • Instantiation: Every external message call receives a freshly cleared memory instance.

    • Expansion Mechanics: Memory expands by a 256-bit word whenever an unallocated memory word is accessed (for read or write).

    • Expansion Cost: Gas cost grows quadratically relative to total memory length utilized.

  • Stack Region:

    • Execution workspace where the EVM performs all computational processing.

    • Stack Capacity: Maximum stack limit of 1024 elements.

    • Word Size: Each stack element is 256 bits wide.

    • Permissible Operations:

    • Copying any element from the top 16 positions to the top of the stack.

    • Swapping any element in the top 16 positions with the top element.

    • Standard operations pop one, two, or more elements and push computation results back onto the stack.

    • Stack values can be moved directly to memory or storage.

EVM Calldata, Returndata, and Code Memory Regions

  • Calldata Region:

    • Read-only payload data supplied within a smart contract transaction.

    • Formats: Constructor bytecode during deployment, or function parameter values during external method calls.

    • Encoding: First stored in ABI-encoded format, subsequently decoded into target function variables.

    • Decoding Behavior:

    • Parameters defined with the memory keyword are eagerly decoded upon entering the function.

    • Parameters defined with the calldata keyword are lazily decoded at the specific location of their first use.

    • Value types and storage pointers are loaded directly onto the stack.

  • Returndata Region:

    • Transient location holding ABI-encoded return values produced by contract function calls.

  • Code Region:

    • Dedicated region where EVM instruction opcodes of a smart contract are permanently stored.

    • Opcodes are sequentially read, interpreted, and executed by the EVM.

    • Stores immutable and constant state values.

    • References to immutable variables are replaced directly with literal values at runtime.

    • Constant expressions are inlined directly at all reference points during compilation.

  • EVM Instruction Set:

    • Designed to ensure strict determinism to prevent execution inconsistencies that lead to consensus failures across nodes.

    • Operates on 256-bit words, byte arrays, or memory slices.

    • Supports arithmetic, bitwise, logical, relational, conditional jump, and unconditional jump operators.

    • Enables reading execution block properties, including block number and block timestamp.

Message Calls, Interoperability, and Execution Contexts

  • Message Call Mechanics:

    • Mechanism allowing contracts to call methods in other contracts or transfer Ether to EOAs.

    • Characteristics: Possesses source, target, data payload, Ether value, gas allocation, and return data.

    • Hierarchy: Top-level transaction creates the initial message call, which can spawn nested inner message calls.

    • Gas Forwarding: Contracts forward a portion of remaining gas to inner calls, capped at a maximum of 6364\frac{63}{64} of current available gas while retaining the remainder.

    • Inner Execution Context:

    • Receives a freshly cleared memory instance.

    • Accesses payload via the calldata area.

    • Writes output into caller-preallocated memory as returndata.

    • Message calls execute fully synchronously.

    • Depth Limits: Capped at a maximum theoretical depth of 1024. Due to the 6364\frac{63}{64} gas forwarding limit, practical execution depth limits out at slightly less than 1000. Iterative loops are strongly preferred over recursive calls for iterations surpassing 1000.

  • Inner Call Exception Handling:

    • Failures (such as running out of gas) place an error code onto the stack.

    • Calling contracts must check error results and raise explicit exceptions to cause errors to bubble up the call stack.

  • Delegate Calls and Libraries:

    • Target address code is executed within the context (address, storage, and balance) of the calling contract.

    • Context Persistence: msg.sender and msg.value remain unchanged during execution.

    • Functionality: Enables contracts to dynamically load code from external addresses at runtime while operating on the host contract's state.

    • Primary Use Case: Formulates the foundational mechanism for Solidity libraries.

Event Logging, Contract Lifecycle, and Pre-compiles

  • Log Mechanics:

    • Log structures implement Solidity events, indexed partially via bloom filters mapped up to the block level.

    • Contract Access: Smart contracts generate log records but are strictly prohibited from reading log data post-creation.

    • External Access: Searchable cryptographically by light nodes and external client software off-chain.

  • Contract Instantiation (CREATE):

    • Smart contracts can create child contracts during runtime.

    • EVM processes creation payload and writes the resulting output as code, returning the newly generated contract address to the parent contract's stack.

  • Contract Deactivation and Self-Destruct:

    • selfdestruct Operation: Historical method for removing contract code and storage state from the active EVM state.

    • Ether Handling: Remaining Ether balance at the self-destructed address is forcefully transferred to a specified target address.

    • Historical Records: Bytecode remains permanently recorded in historical blockchain blocks.

    • Risks: Sending Ether to a self-destructed contract results in permanent loss of funds.

    • Deprecation Notice: selfdestruct is officially deprecated as of Solidity 0.8.18 and must not be used in newly developed smart contracts.

    • Modern Deactivation Pattern: Smart contracts should be disabled by altering internal state flags to cause all functions to revert. Reverting returns incoming Ether immediately and renders the contract unusable.

  • Pre-compiled Contracts:

    • Native specialized EVM contracts residing at fixed address ranges 0x01 through 0x0a (with address range 0x0001 through 0xffff reserved).

    • Implemented directly within the EVM execution environment with predefined gas cost algorithms.