4: Introduction to Smart Contracts
1. What Are Smart Contracts?
The Paradox: Neither Smart Nor Contracts
Despite their name, smart contracts are neither smart nor contracts in the traditional sense. This paradoxical statement helps us understand what they truly are:
- Not "smart": They are simple programs that execute predetermined logic without artificial intelligence or learning capabilities
- Not "contracts": They are not legal documents but rather self-executing code stored on a blockchain
Definition and Core Concept
A smart contract is a user-defined program that runs on top of a blockchain. More formally, smart contracts are:
Autonomous agents that live inside the blockchain execution environment, always executing specific code when triggered by a message or transaction, with direct control over their own cryptocurrency balance and persistent storage.
Think of smart contracts as vending machines: you insert coins (cryptocurrency), select an option (call a function), and receive a predetermined output (tokens, services, or state changes) without requiring a trusted intermediary.
Historical Context: Nick Szabo's Vision (1994)
Nick Szabo conceptualized smart contracts long before blockchain technology existed:
"A smart contract is a computerized transaction protocol that executes the terms of a contract. The general objectives are to satisfy common contractual conditions (such as payment terms, liens, confidentiality, and even enforcement), minimize exceptions both malicious and accidental, and minimize the need for trusted intermediaries."
The key innovation that Satoshi Nakamoto's Bitcoin introduced in 2009 was the underlying blockchain technology as a tool for distributed consensus. While Bitcoin primarily focused on currency, it demonstrated that blockchain could support more complex applications.
3. Understanding Smart Contracts from a Developer's Perspective
The Programming Model
Smart contracts operate on a simple yet powerful model:
Contract Classes and Objects
- Contract Class: Defines the program code and storage variables
- Contract Object: An instance of the class living on the blockchain at a specific address
Core Components
Storage Fields Persistent variables stored in the contract's state. These survive across function calls and transactions.
balances: mapping(address => uint256)
owner: address
totalSupply: uint256
Functions/Methods Code that can be invoked to read or update the contract state.
transfer(address to, uint256 amount)
getBalance(address account) returns (uint256)
Access Control
Use require() statements to enforce authorization rules. Transactions that fail these checks are reverted.
require(msg.sender == owner, "Only owner can call this");
Example: Domain Name Registry
Let's examine a simple domain name registry to understand the contract model:
Storage Structure
domains: mapping(string => address)
Registration Function
function registerDomain(string memory name) public {
require(domains[name] == address(0), "Domain already registered");
domains[name] = msg.sender;
}
Lookup Function
function lookupDomain(string memory name) public view returns (address) {
return domains[name];
}
Account Model in Ethereum
Ethereum uses an account model rather than Bitcoin's UTXO model. There are two types of accounts:
Externally Owned Accounts (EOAs)
- Controlled by private keys
- Have an ether balance
- Can send transactions
- Have no code
Contract Accounts
- Controlled by their contract code
- Have an ether balance
- Have contract code and storage
- Execute code when receiving messages/transactions
Each Ethereum account contains:
- Nonce: Transaction counter (prevents replay attacks)
- Balance: Current ether balance
- Contract Code: For contract accounts only
- Storage: Key-value store for persistent data
4. Ethereum Programming Basics
Introduction to Solidity
Solidity is a high-level, statically-typed programming language designed for writing smart contracts on Ethereum. It compiles to Ethereum Virtual Machine (EVM) bytecode.
Solidity Source Code → Solidity Compiler → EVM Bytecode → Deployed to Blockchain
Data Types
Integers
uint256 totalSupply; // Unsigned 256-bit integer (default)
uint8 decimals; // Unsigned 8-bit integer
int256 balance; // Signed 256-bit integer
Note: Solidity is statically typed like Java, C, or Rust, unlike Python or JavaScript.
Addresses
address owner; // 20-byte Ethereum address
address payable recipient; // Can receive Ether
Mappings (Hash Tables)
mapping(address => uint256) public balances;
mapping(string => address) private domains;
Important:
- Every key initially maps to zero
- No built-in way to query length or iterate over non-zero elements
- Must maintain separate data structures for enumeration
Arrays
uint256[10] fixedArray; // Fixed size
uint256[] dynamicArray; // Dynamic size (more expensive)
address[] public voters; // Storage array (persists)
Strings and Bytes
bytes32 hash; // Fixed size (returned by hash functions)
bytes memory data; // Dynamic byte array
string memory name; // UTF-8 string
Function Structure
function transfer(address to, uint256 amount)
public // Visibility modifier
returns (bool success) // Return type
{
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
balances[to] += amount;
return true;
}
Function Components
- Name:
transfer - Arguments:
(address to, uint256 amount) - Visibility:
public,private,internal,external - Mutability:
pure,view,payable - Returns:
returns (bool success)