Introduction to Solidity
Lesson by Uvin Vindula
Solidity is the primary programming language for writing smart contracts on Ethereum and EVM-compatible blockchains (Polygon, Arbitrum, BNB Chain, Avalanche, and many others). If you want to build in Web3, Solidity is the most important language to learn. This lesson introduces the basic structure, syntax, and concepts — enough to read and understand simple smart contracts.
What is a Smart Contract?
A smart contract is a program that lives on the blockchain. Once deployed, it executes exactly as coded — no one can alter it (unless it's designed to be upgradeable). Smart contracts can hold funds, enforce rules, and interact with other contracts autonomously. They are the backbone of DeFi, NFTs, DAOs, and most Web3 applications.
Basic Solidity Structure
Every Solidity file follows a consistent structure. Here's the simplest possible smart contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract SimpleStorage {
// State variable — stored permanently on the blockchain
uint256 public storedNumber;
// Function to store a number
function store(uint256 _number) public {
storedNumber = _number;
}
// Function to retrieve the stored number
function retrieve() public view returns (uint256) {
return storedNumber;
}
}
Let's break down each part:
1. License and Pragma
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
The first line specifies the open-source license. The pragma statement tells the compiler which version of Solidity to use. The ^ means "this version or higher within the same major version."
2. Contract Declaration
contract SimpleStorage { ... }
A contract in Solidity is similar to a class in object-oriented programming. It encapsulates state variables and functions. Every smart contract starts with the contract keyword.
3. State Variables
uint256 public storedNumber;
State variables are stored permanently on the blockchain. Every change to a state variable costs gas (a transaction fee). Common types include:
uint256— unsigned integer (whole positive numbers, up to 2^256 - 1)int256— signed integer (positive and negative whole numbers)address— an Ethereum address (20 bytes)bool— true or falsestring— text datamapping— a key-value data structure (like a dictionary)
The public keyword automatically creates a getter function, allowing anyone to read the variable's value.
4. Functions
function store(uint256 _number) public {
storedNumber = _number;
}
Functions define what the contract can do. Key concepts:
public— callable by anyone (external accounts and other contracts)private— only callable from within the contractview— reads state but doesn't modify it (no gas cost when called externally)pure— doesn't read or modify statepayable— can receive ETH along with the function call
A More Practical Example
Here's a slightly more complex contract that demonstrates mappings, events, and access control:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract DonationTracker {
// State variables
address public owner;
uint256 public totalDonations;
mapping(address => uint256) public donations;
// Event — emitted when a donation is made
event DonationReceived(address indexed donor, uint256 amount);
// Constructor — runs once when the contract is deployed
constructor() {
owner = msg.sender; // The deployer becomes the owner
}
// Modifier — reusable access control
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_;
}
// Accept donations
function donate() public payable {
require(msg.value > 0, "Must send some ETH");
donations[msg.sender] += msg.value;
totalDonations += msg.value;
emit DonationReceived(msg.sender, msg.value);
}
// Withdraw funds (owner only)
function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
// Check contract balance
function getBalance() public view returns (uint256) {
return address(this).balance;
}
}
New concepts introduced:
- mapping:
mapping(address => uint256)maps each address to a donation amount — like a ledger tracking who donated how much. - events: Events log data to the blockchain that external applications can listen for. They're cheaper than state storage and essential for dApp frontends.
- constructor: Runs exactly once when the contract is deployed. Used for initialization.
- modifier: Reusable conditions that can be applied to functions.
onlyOwnerensures only the contract deployer can call certain functions. - msg.sender: The address calling the function. A built-in global variable.
- msg.value: The amount of ETH sent with the transaction.
- require: A condition check that reverts the transaction if not met.
Where to Practice
You don't need to install anything to start learning Solidity:
- Remix IDE (remix.ethereum.org): A browser-based Solidity IDE where you can write, compile, and deploy contracts to test networks instantly.
- CryptoZombies (cryptozombies.io): A gamified, interactive Solidity tutorial that teaches through building a zombie game.
- Solidity by Example (solidity-by-example.org): A collection of practical, minimal Solidity examples covering common patterns.
Key Takeaways
- •Solidity is the primary language for Ethereum and EVM-compatible smart contracts — the most important language for Web3 development
- •Smart contracts have state variables (stored on-chain, cost gas to modify), functions (define behavior), events (log data for dApps), and modifiers (reusable access control)
- •Key data types include uint256, address, bool, string, and mapping — understanding these is fundamental to reading smart contracts
- •Function visibility (public, private, view, pure, payable) controls who can call functions and what they can do
- •Practice with Remix IDE (browser-based), CryptoZombies (gamified tutorial), and Solidity by Example — never deploy contracts handling real value without professional audits
Quick Quiz
Question 1 of 3
0 correct so far
What does the "pragma solidity ^0.8.19" statement do?