What Are Smart Contracts?
Smart contracts are programs that run on a blockchain. They execute automatically when called, with results that anyone can verify. No middlemen, no trust required — the code is the rule.
The basic idea
A smart contract is code that:
- Lives on the blockchain (immutable once deployed)
- Has its own address (you call it like sending a transaction)
- Executes deterministically (same inputs always produce same outputs)
- Can hold and transfer assets (tokens, NFTs, ETH/SOL)
Think of it as a vending machine: you put in money, press a button, and the machine gives you what the code says. No human decides whether to give you the soda — the machine (contract) follows its programming.
EVM: Smart contracts (Solidity)
On EVM chains, smart contracts are written in Solidity, compiled to bytecode, and deployed to the chain.
// A simple token contract
contract MyToken {
mapping(address => uint256) public balances;
function transfer(address to, uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
balances[to] += amount;
}
}
Key characteristics
- Stateful: Contracts store data (balances, settings, ownership) directly on-chain
- Immutable: Once deployed, the code can't be changed (unless you use a proxy pattern)
- Composable: Contracts can call other contracts, enabling DeFi "money legos"
- Gas-metered: Every operation costs gas, preventing infinite loops
The contract lifecycle
- Write Solidity code
- Compile to EVM bytecode
- Deploy via a transaction (costs gas)
- Contract gets its own address
- Anyone can call its public functions by sending transactions to that address
Solana: Programs
Solana calls them "programs" instead of "smart contracts," and the architecture is fundamentally different.
// A Solana program (using Anchor framework)
#[program]
pub mod my_token {
pub fn transfer(ctx: Context<Transfer>, amount: u64) -> Result<()> {
let from = &mut ctx.accounts.from;
let to = &mut ctx.accounts.to;
require!(from.balance >= amount, ErrorCode::InsufficientBalance);
from.balance -= amount;
to.balance += amount;
Ok(())
}
}
Key characteristics
- Stateless: Programs don't store data — data lives in separate "accounts"
- Account model: Everything is an account: programs, data, tokens, metadata
- Explicit accounts: Every instruction declares which accounts it reads/writes
- Parallel execution: Because accounts are explicit, Solana can run non-overlapping transactions in parallel
The program lifecycle
- Write Rust code (usually with Anchor framework)
- Compile to BPF bytecode
- Deploy via a transaction
- Program gets its own address
- Users send transactions with instructions that call the program
EVM vs Solana: The architecture difference
The biggest difference is where data lives:
EVM:
┌─────────────────────┐
│ Token Contract │
│ ┌─────────────────┐ │
│ │ Code (transfer) │ │
│ │ Data (balances) │ │ ← Code and data live together
│ └─────────────────┘ │
└─────────────────────┘
Solana:
┌──────────────┐ ┌──────────────┐
│ Token Program │ │ Data Account │
│ ┌──────────┐ │ │ balance: 100 │ ← Code and data are separate
│ │ Code │ │ └──────────────┘
│ │ (logic) │ │ ┌──────────────┐
│ └──────────┘ │ │ Data Account │
└──────────────┘ │ balance: 50 │
└──────────────┘
EVM: Each token has its own contract containing both the logic AND the data. The USDC contract holds all USDC balances.
Solana: The SPL Token program holds the logic. Each user's balance is a separate "data account" that the program operates on. One program, many data accounts.
How dApps interact with contracts
EVM
// Call a contract function
const result = await contract.balanceOf(address); // Read (free)
const tx = await contract.transfer(to, amount); // Write (costs gas)
Solana
// Send an instruction to a program
const instruction = createTransferInstruction(from, to, owner, amount);
const tx = new Transaction().add(instruction);
await sendTransaction(tx, connection);
Key concepts
Immutability
Once deployed, a smart contract's code cannot be changed. This is a feature — users can trust that the rules won't change. But it means bugs are permanent.
Upgradeable contracts (EVM: proxy pattern, Solana: upgradeable BPF loader) allow updates, but this introduces trust in the upgrade authority.
Composability
Contracts can call other contracts. This enables:
- A DEX contract calling a token contract to move funds
- A lending contract calling an oracle contract for price data
- A yield aggregator calling multiple DeFi contracts
This composability is why DeFi is called "money legos."
Auditing
Because contract code is on-chain and (usually) open-source, anyone can audit it. This transparency is a core property of web3 — you don't have to trust the team, you can verify the code.
But: Most users can't read Solidity/Rust. Professional audits (Trail of Bits, OpenZeppelin, Neodyme) are the industry standard for high-value contracts.
PetarStoev02