Solidity programming basics โ data types, functions, modifiers, and events
- Solidity is a statically-typed language for EVM smart contracts โ JavaScript-like syntax with blockchain-specific primitives.
- Mappings are the core data structure; events are cheap logging for off-chain listeners; modifiers enforce reusable access control.
- Custom errors (revert) are cheaper than require strings โ prefer them in production code.
Understanding solidity programming basics is the foundation of this guide. Below we break it down step by step so you can act with confidence.
Solidity is a statically-typed, contract-oriented programming language designed for the Ethereum Virtual Machine. If you have written JavaScript, Java, or C#, the syntax will feel familiar. If programming is new to you, start with our Programming Academy first.
This lesson teaches Solidity fundamentals with code you can paste directly into Remix IDE and execute in your browser โ no installation needed.
Solidity Programming Basics: Data types and state variables
Solidity has several core data types. Every state variable is stored on the blockchain and costs gas to modify:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract DataTypes {
// Value types
bool public isActive = true;
uint256 public amount = 1000; // unsigned integer (0 to 2^256-1)
int256 public balance = -500; // signed integer
address public owner; // 20-byte Ethereum address
bytes32 public dataHash; // fixed-size byte array
// Reference types
string public name = "MyToken";
uint256[] public numbers; // dynamic array
mapping(address => uint256) public balances; // key-value store
// Special variables (available in every function)
// msg.sender โ address that called this function
// msg.value โ ETH sent with the call (in wei)
// block.timestamp โ current block time
constructor() {
owner = msg.sender;
numbers.push(42);
balances[msg.sender] = 1000;
}
function addNumber(uint256 n) public {
numbers.push(n);
}
function getNumbersLength() public view returns (uint256) {
return numbers.length;
}
}Key rule: mapping is the most important data structure in Solidity. Token balances, ownership records, approvals โ almost everything in DeFi uses mappings. They are hash tables with O(1) read/write, but you cannot iterate over them or get their size.
Functions, visibility, and modifiers
Functions have visibility specifiers and can use modifiers for reusable checks:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract AccessControl {
address public owner;
uint256 public value;
bool public paused;
// Custom errors (cheaper than string messages)
error NotOwner();
error ContractPaused();
// Modifier โ reusable access check
modifier onlyOwner() {
if (msg.sender != owner) revert NotOwner();
_;
}
modifier whenNotPaused() {
if (paused) revert ContractPaused();
_;
}
constructor() {
owner = msg.sender;
}
// public โ anyone can call
function setValue(uint256 _val) public onlyOwner whenNotPaused {
value = _val;
}
// view โ reads state, no gas when called externally
function getValue() public view returns (uint256) {
return value;
}
// pure โ uses no state at all
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
// payable โ can receive ETH
function deposit() public payable {
// msg.value contains the ETH sent
}
function getBalance() public view returns (uint256) {
return address(this).balance;
}
function setPaused(bool _paused) public onlyOwner {
paused = _paused;
}
}Deploy in Remix. Call setValue from the deployer โ it works. Switch to another account and try โ it reverts. Toggle setPaused(true) and even the owner cannot call setValue.
Events and logging
Events are how smart contracts communicate with the outside world. They are stored in transaction logs (much cheaper than storage writes) and are indexed for efficient querying:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract EventDemo {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Deposit(address indexed user, uint256 amount, uint256 timestamp);
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value, block.timestamp);
}
function transfer(address to, uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
}
}indexed parameters can be filtered in queries โ wallets and dApps use this to track your transactions without scanning every block.
// Listen for Transfer events with ethers.js
const contract = new ethers.Contract(address, abi, provider);
// Listen for real-time events
contract.on('Transfer', (from, to, amount) => {
console.log(`Transfer: ${from} -> ${to}: ${ethers.formatEther(amount)} ETH`);
});
// Query historical events
const filter = contract.filters.Transfer(myAddress, null);
const events = await contract.queryFilter(filter, -10000); // last 10k blocks
events.forEach(e => console.log(e.args));Explore Crypto Trading
Cryptocurrency trading is one of the fastest-growing financial markets โ open 24/7, globally accessible, and full of opportunity for those who learn the fundamentals first.
- Trade spot, futures & copy-trading on one platform
- Start with as little as $10 โ practice risk-free on the testnet
- Built-in TradingView charts, grid bots & DCA tools
Error handling patterns
Solidity provides several ways to handle errors:
- require(condition, message) โ check inputs and preconditions. Refunds remaining gas on failure.
- revert CustomError() โ custom errors are cheaper than string messages (saves gas). Preferred in modern Solidity.
- assert(condition) โ check invariants that should never be false. Consumes all gas on failure. Use for internal bugs only.
- try/catch โ handle failures in external calls gracefully.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract ErrorHandling {
error InsufficientBalance(uint256 available, uint256 required);
error ZeroAddress();
mapping(address => uint256) public balances;
function withdraw(uint256 amount) public {
// Custom error with parameters (gas efficient)
if (balances[msg.sender] < amount)
revert InsufficientBalance(balances[msg.sender], amount);
balances[msg.sender] -= amount;
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok, "Transfer failed");
}
function safeTransfer(address to, uint256 amount) public {
if (to == address(0)) revert ZeroAddress();
require(balances[msg.sender] >= amount, "Not enough");
balances[msg.sender] -= amount;
balances[to] += amount;
}
}When evaluating solidity programming basics, start with the fundamentals: what problem it solves, who it serves, and how it compares to the alternatives buyers consider first.
Start with the fundamentals. Define who you are serving, the single outcome you want, and the budget you are comfortable committing before you compare any options.
A practical rule is to favour clarity over cleverness. The choice that is easiest to understand, spell, remember and act on tends to win over the long run.
Context changes the answer. What is optimal for a fast-moving startup is rarely optimal for an established brand, so weigh your own stage, market and ambitions.
Avoid the common mistakes: rushing the decision, copying competitors blindly, and ignoring the total cost of ownership beyond the very first year of use.
Think in terms of trade-offs rather than a single perfect answer. Every option trades memorability, cost, reach and flexibility in a slightly different mix.
Document your reasoning. Writing down why you chose one path makes it far easier to revisit the decision later and to explain it to partners and investors.
WSOT 2026: Squad Leader Slots Are Open โ Up to $128,000 per Leader
Join NowFrequently asked questions
Do I need to install anything to write Solidity?
No. Remix IDE (remix.ethereum.org) runs entirely in your browser. For serious projects, install Hardhat or Foundry locally, but Remix is perfect for learning.
Is Solidity hard to learn?
If you know JavaScript or any C-family language, the syntax is straightforward. The hard part is understanding blockchain-specific concepts: gas optimization, reentrancy risks, and the immutability of deployed code.
What Solidity version should I use?
Always use the latest 0.8.x release. Version 0.8.0 introduced automatic overflow checks, preventing a large class of vulnerabilities present in older versions.
Free Courses โ Learn New Skills
No sign-up · No fees · Learn at your own pace
Start Trading
Trade crypto and premium domains โ digital assets with real opportunities.
๐ Need a custom article?
Our editorial team writes it for you โ carefully crafted, SEO-optimized, about your topic, your domain name or your service. Multiple languages available. From $15 per language.
Find your name on Atom
DominantBrand curates the best premium, brandable names from Atom.com — the marketplace with a free AI appraisal, a USPTO trademark check, and secure escrow. Every listing even ships with a designed logo.