- Published on
Understanding Ethereum Smart Contracts
- Authors
- Name
- Frank
Understanding Ethereum Smart Contracts
This article was inspired by the excellent explanations put together by Jordan McKinney in his video explaining Ethereum Smart Contracts. The images are screenshots taken from his videos, because they're fantastic illustrations.
In this article, we'll explore the fundamentals of Ethereum smart contracts. We'll delve into the concept of the world state, examine the structure of accounts, discuss how addresses and balances work, and explain how the Ethereum Virtual Machine (EVM) executes smart contracts. We'll also touch on topics like the Application Binary Interface (ABI), visibility specifiers, modifiers, and transaction receipts.
The Ethereum World State
At the heart of the Ethereum network lies the world state, a vast repository of data that represents the current state of all accounts and contracts on the blockchain. All the complex mechanisms like blocks, hashing, and consensus algorithms exist to support and maintain this world state.
Types of Accounts
The world state consists of two types of objects:
- Externally Owned Accounts (EOAs): Controlled by private keys held by users.
- Contract Accounts: Controlled by the code (smart contracts) they contain.
Externally Owned Accounts (EOAs)
- Address: A 160-bit identifier derived from the user's public key.
- Nonce: A 64-bit number representing the number of transactions sent from the account.
- Balance: A 256-bit number indicating the amount of Ether held.
Contract Accounts
- Address: Similar to EOAs, but derived differently.
- Nonce: Represents the number of contracts created by the account.
- Balance: Amount of Ether the contract holds.
- Storage: Persistent data specific to the contract.
- Code: The compiled bytecode that defines the contract's functionality.
Account Addresses
Addresses in Ethereum are derived differently for EOAs and contract accounts.
Externally Owned Account Addresses
- Private Key: A randomly generated 64-character hexadecimal number.
- Public Key: Derived from the private key using Elliptic Curve Digital Signature Algorithm (ECDSA).
- Address: Obtained by taking the Keccak-256 hash of the public key and extracting the last 20 bytes.
Contract Account Addresses
Contract addresses are generated when the contract is deployed:
- Regular Creation:
- Combine the deployer's address and their nonce at deployment time.
- Hash the combination using Keccak-256.
- Extract the last 20 bytes to get the contract address.
- CREATE2 Opcode:
- Introduced in EIP-1014 as an alternative way to create contracts.
- Combine a predefined prefix (
0xff
), the deployer's address, a salt, and the hash of the contract's initialization code. - Hash the combination using Keccak-256 and extract the last 20 bytes.
Balances
- Unit of Measurement: Ether balances are measured in Wei, the smallest unit (1 Ether = 1e18 Wei).
- Representation: Balances are stored as 256-bit numbers.
- No Decimals: Since Wei is indivisible, all balances are whole numbers.
Ethereum Smart Contracts
Contract Bytecode
Smart contracts are compiled into bytecode before deployment:
- Bytecode Structure: A series of opcodes that the EVM can interpret.
- Opcodes: Each opcode is a byte (represented by two hexadecimal characters).
- Execution: The EVM reads and executes these opcodes sequentially.
For example, a snippet of bytecode might look like: 0x6080604052600436106100c1575f35
.
- EVM Opcodes Reference: You can find a list of opcodes at EVM Opcodes.
Interacting with Smart Contracts
To interact with a contract (e.g., to mint an NFT), you send a transaction containing calldata to the contract's address.
- Calldata: Encoded data specifying the function to call and the arguments.
- Function Selector: The first 4 bytes of the Keccak-256 hash of the function signature (function name and parameter types).
Example:
Function: mint(address minter, uint256 tokenId, uint256 quantity, bytes minterArguments)
Method ID: 0x731133e9
- The transaction input data starts with
0x731133e9
, followed by the encoded arguments. - The contract uses this data to execute the specified function with the provided arguments.
Executing Bytecode
When a transaction is sent to a contract:
- EVM Execution Start: The EVM begins executing the contract's bytecode from the beginning.
- Function Dispatching:
- The contract's bytecode includes logic to extract the function selector from the calldata.
- It compares this selector against known function selectors to determine which function to execute.
- Argument Decoding:
- Once the function is identified, the arguments are decoded from the calldata.
- Function Execution:
- The EVM executes the function's bytecode, potentially modifying the world state.
- Return Values:
- Any return values are encoded and returned to the caller.
This dispatching mechanism is generated by the Solidity compiler and is integral to how smart contracts handle function calls.
Constructors and Contract Initialization
When deploying a contract:
- Contract Creation Code: The bytecode uploaded during deployment includes:
- Constructor Logic: Initializes state variables and sets up the contract.
- Runtime Code: The actual code executed when interacting with the contract after deployment.
- Storage Allocation:
- State Variables: Storage is allocated for state variables and initialized to zero.
- Fixed Storage: The storage layout is fixed at deployment and cannot be altered to add new state variables.
Application Binary Interface (ABI)
- Definition: A standardized interface that allows interactions with contracts without needing the source code.
- Purpose: Specifies the functions and how to call them, including parameter types and return values.
- Format: Typically provided in JSON, making it both human-readable and machine-parsable.
- Language Agnostic: Allows different programming languages to interact with the contract.
Visibility Specifiers
Visibility specifiers define how and where functions and variables can be accessed.
external
:- Functions callable only from outside the contract.
- Cheaper in gas when called externally compared to
public
.
public
:- Functions callable from both inside and outside the contract.
- Cheaper in gas when called internally compared to
external
.
internal
:- Functions accessible only within the contract and its derived contracts.
private
:- Functions accessible only within the contract they are defined in.
Modifiers
Modifiers provide additional behavior or restrictions to functions.
pure
:- Functions that do not read or modify the state.
view
:- Functions that read but do not modify the state.
payable
:- Functions that can receive Ether during execution.
- If Ether is sent to a non-payable function, the transaction will revert.
virtual
:- Indicates that a function or modifier can be overridden in derived contracts.
override
:- Used in derived contracts to indicate that the function overrides a base contract's function.
State Variable Modifiers
constant
:- Variables that cannot change after initialization.
- Stored directly in the bytecode; does not occupy storage slots.
immutable
:- Variables assigned during contract construction and cannot change afterward.
- Does not occupy storage slots after deployment.
Event Modifiers
anonymous
:- Events that do not store the event signature as a topic.
indexed
:- Parameters stored as topics, allowing for efficient filtering.
Transaction Receipts and Events
- Transaction Receipts: Provide a summary of what happened during a transaction, including events emitted.
- Events:
- Used to log information to the blockchain.
- Useful for tracking state changes without parsing the entire world state.
- Efficiency: Checking transaction receipts and events is more efficient than scanning the world state for changes.
Visualizing Smart Contracts
- Inert World State: The Ethereum world state remains static until a transaction triggers a contract.
- EVM Activation: The EVM activates the contract, executing its functions and potentially altering the world state.
- Public and Private Functions:
- Public Functions: Accessible from outside the contract, marked with function selectors.
- Private/Internal Functions: Not accessible externally, used internally within the contract.
- Function Parameters and Returns:
- Parameters: Inputs provided to functions, represented as grids or slots.
- Return Values: Outputs from functions, which may or may not be present depending on the function.
Conclusion
Understanding the structure and execution of Ethereum smart contracts is crucial for developing decentralized applications. By grasping how the world state, accounts, and the EVM interact, developers can write more efficient and secure contracts. The ABI, visibility specifiers, and modifiers provide additional layers of functionality and security, ensuring that contracts behave as intended within the Ethereum ecosystem.