Smart Contracts Explained — Beginner's Guide
Smart contracts are self-executing programs stored on a blockchain that automatically enforce and execute the terms of an agreement when predetermined conditions are met.
What You’ll Learn
By the end of this tutorial, you’ll understand what smart contracts are, how they work on Ethereum and other platforms, basic Solidity syntax, and you’ll write and deploy your first smart contract.
Why Smart Contracts Matter
Smart contracts eliminate the need for intermediaries in digital agreements. Instead of trusting a lawyer, escrow agent, or bank to enforce a contract, you trust the code — which runs exactly as written on a decentralized blockchain. The global smart contract market is projected to exceed $3 billion by 2028.
Smart Contracts Learning Path
flowchart LR
A[Blockchain Basics] --> B[Bitcoin]
B --> C[Ethereum]
C --> D[Smart Contracts]
D --> E{You Are Here}
style E fill:#f90,color:#fff
What Is a Smart Contract? (The “Why” First)
Think of a smart contract like a vending machine. You put in money, press a button, and the machine gives you a snack — no human cashier needed. The machine “enforces” the contract: if you insert the correct amount, it dispenses the product. If you don’t, it doesn’t.
A smart contract on Ethereum works the same way:
- Someone deploys the contract (puts the vending machine in place)
- Users interact with it (insert money, press buttons)
- The code executes automatically (machine dispenses the product)
- The result is permanent and verifiable (everyone can see it on the blockchain)
Why Use Smart Contracts?
| Problem | Traditional Solution | Smart Contract Solution |
|---|---|---|
| Trust | Trust a lawyer, bank, or escrow | Trust the code (audited, transparent) |
| Cost | High fees for intermediaries | Lower fees (one-time deployment + gas) |
| Speed | Days for settlement | Minutes or seconds |
| Global access | Requires bank account | Anyone with internet can participate |
| Transparency | Private agreements | Publicly verifiable code and state |
Anatomy of a Smart Contract
Let’s build a smart contract step by step. We’ll create a voting contract — a real-world use case that demonstrates core concepts.
// Voting.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract Voting {
// ─── State Variables ───
// Struct to represent a candidate
struct Candidate {
string name;
uint256 voteCount;
}
// Array of candidates
Candidate[] public candidates;
// Track who has voted
mapping(address => bool) public hasVoted;
// Contract owner (who deploys it)
address public owner;
// Voting active flag
bool public votingOpen;
// ─── Events ───
event Voted(address indexed voter, uint256 candidateIndex);
event VotingEnded();
// ─── Constructor ───
constructor(string[] memory _candidateNames) {
owner = msg.sender;
votingOpen = true;
for (uint256 i = 0; i < _candidateNames.length; i++) {
candidates.push(Candidate({
name: _candidateNames[i],
voteCount: 0
}));
}
}
// ─── Functions ───
function vote(uint256 _candidateIndex) public {
// Requirements (checks)
require(votingOpen, "Voting is closed");
require(!hasVoted[msg.sender], "Already voted");
require(_candidateIndex < candidates.length, "Invalid candidate");
// Effect
hasVoted[msg.sender] = true;
candidates[_candidateIndex].voteCount++;
// Event
emit Voted(msg.sender, _candidateIndex);
}
function endVoting() public {
require(msg.sender == owner, "Only owner can end voting");
votingOpen = false;
emit VotingEnded();
}
function getWinner() public view returns (string memory winnerName, uint256 winnerVotes) {
require(!votingOpen, "Voting is still open");
uint256 maxVotes = 0;
uint256 winnerIndex = 0;
for (uint256 i = 0; i < candidates.length; i++) {
if (candidates[i].voteCount > maxVotes) {
maxVotes = candidates[i].voteCount;
winnerIndex = i;
}
}
return (candidates[winnerIndex].name, candidates[winnerIndex].voteCount);
}
function getCandidates() public view returns (Candidate[] memory) {
return candidates;
}
}Line-by-Line Explanation
Struct and State Variables:
struct Candidate— a custom data type grouping a name and vote countCandidate[] public candidates— an array of all candidates, stored permanentlymapping(address => bool) public hasVoted— a lookup table tracking who voted (prevents double voting)address public owner— the contract deployer’s addressbool public votingOpen— whether voting is active
Constructor:
- Runs once when the contract is deployed
- Sets the deployer as
owner - Initializes the candidates array
- Opens voting
Functions:
vote(uint256 _candidateIndex)— the core function. It checks that voting is open, the voter hasn’t voted, and the candidate is valid. Then records the vote.endVoting()— only the owner can call thisgetWinner()— returns the candidate with the most votes (only after voting ends)getCandidates()— returns the full candidate list
Why require Matters
The require statements are the contract’s security checks. Think of them as the vending machine checking that you inserted the correct coins before dispensing anything. Without these checks, anyone could vote multiple times or vote for non-existent candidates.
Testing the Contract (JavaScript)
Before deploying to the real network, you test locally:
// test_voting.js
// Using Hardhat testing framework
const { ethers } = require("hardhat");
const { expect } = require("chai");
describe("Voting Contract", function () {
let voting, owner, voter1, voter2;
beforeEach(async function () {
const Voting = await ethers.getContractFactory("Voting");
[owner, voter1, voter2] = await ethers.getSigners();
voting = await Voting.deploy(["Alice", "Bob", "Charlie"]);
});
it("Should initialize with 3 candidates", async function () {
const candidates = await voting.getCandidates();
expect(candidates.length).to.equal(3);
});
it("Should allow a vote", async function () {
await voting.connect(voter1).vote(0);
expect(await voting.hasVoted(voter1.address)).to.equal(true);
});
it("Should prevent double voting", async function () {
await voting.connect(voter1).vote(0);
await expect(voting.connect(voter1).vote(1)).to.be.revertedWith("Already voted");
});
it("Should return the correct winner", async function () {
await voting.connect(voter1).vote(0); // Vote for Alice
await voting.connect(voter2).vote(0); // Vote for Alice
await voting.endVoting();
const [winner, votes] = await voting.getWinner();
expect(winner).to.equal("Alice");
expect(votes).to.equal(2);
});
});Expected test output:
Voting Contract
✓ Should initialize with 3 candidates
✓ Should allow a vote
✓ Should prevent double voting
✓ Should return the correct winner
4 passing (2s)Deployment to Ethereum
Deploying a contract costs gas and requires a real transaction:
// deploy.js
async function main() {
const Voting = await ethers.getContractFactory("Voting");
const voting = await Voting.deploy(["Alice", "Bob", "Charlie"]);
await voting.deployed();
console.log("Voting deployed to:", voting.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});Expected output:
Voting deployed to: 0x1234567890abcdef1234567890abcdef12345678Once deployed, the contract lives on the blockchain forever. Anyone can interact with it through its public functions.
Real-World Smart Contract Use Cases
1. Decentralized Finance (DeFi)
Smart contracts power lending platforms (Aave, Compound), decentralized exchanges (Uniswap), and stablecoins (DAI). Users lend, borrow, and trade without banks.
2. Non-Fungible Tokens (NFTs)
ERC-721 contracts track ownership of digital assets — art, music, in-game items, and collectibles. Each token is unique with its own metadata.
3. Decentralized Autonomous Organizations (DAOs)
A DAO is an organization governed entirely by smart contracts. Members vote on proposals using tokens, and the contract automatically executes approved decisions.
4. Supply Chain Tracking
Smart contracts record every step of a product’s journey from manufacturer to consumer. Each transfer is timestamped and immutable.
5. Insurance
Parametric insurance contracts automatically pay out when predefined conditions are met (e.g., weather data shows a drought exceeded a threshold).
6. Escrow Services
A smart contract holds funds until both parties fulfill their obligations, then automatically releases payment. This is how decentralized marketplaces work.
Common Smart Contract Mistakes
1. Reentrancy Attacks
A contract calls an external contract, which calls back into the original contract before the first call completes, causing unexpected behavior. The 2016 DAO hack ($60M) was a reentrancy attack.
Fix: Update state before making external calls (checks-effects-interactions pattern).
2. Integer Overflow/Underflow
Solidity 0.8+ has built-in overflow checking, but earlier versions need SafeMath. Always use the latest compiler version.
3. Unrestricted Function Access
Functions that should be owner-only must use onlyOwner modifiers or require(msg.sender == owner) checks. Missing access control is one of the most common vulnerabilities.
4. Reliance on Block Timestamps
block.timestamp can be manipulated by validators within a small range. Don’t use it for critical logic like random number generation.
5. Storing Secrets On-Chain
Everything on the blockchain is public. Don’t store private keys, passwords, or sensitive data in plain text in your contract.
6. Not Testing Edge Cases
Test with zero values, maximum values, empty arrays, and unexpected inputs. Many exploits occur at edge cases the developer didn’t consider.
7. Skipping Professional Audits
If your contract handles real value, hire a professional auditing firm (OpenZeppelin, Trail of Bits, ConsenSys Diligence). A single vulnerability can cost millions.
Common Mistakes Beginners Make
1. Skipping the Fundamentals
Many beginners jump straight to advanced topics without mastering the basics. Take time to understand the core concepts before moving on.
2. Not Practicing Enough
Reading tutorials without writing code leads to shallow understanding. Code along with every example and experiment on your own.
3. Ignoring Error Messages
Error messages tell you exactly what went wrong. Read them carefully — they usually point to the line and type of issue.
4. Copy-Pasting Without Understanding
It’s tempting to copy code from tutorials, but typing it yourself and understanding each line builds real skill.
5. Giving Up Too Early
Every developer hits frustrating bugs. Take breaks, ask for help, and remember that struggling is part of learning.
Practice Questions
1. What makes a smart contract “smart”?
It automatically executes the terms of an agreement when conditions are met, without needing a middleman. The code is the contract’s enforcement mechanism.
2. What is a mapping in Solidity?
A mapping is a key-value store, similar to a hash table. mapping(address => bool) public hasVoted maps each address to whether they’ve voted. It’s how contracts efficiently track ownership, balances, and permissions.
3. Why do smart contracts use require?
require validates conditions before executing logic. If the condition fails, the transaction reverts (undoes all changes) and refunds unused gas. This is how contracts enforce rules.
4. What’s the checks-effects-interactions pattern?
A security pattern: first check conditions (require), then update state, then call external contracts. This prevents reentrancy attacks by ensuring state is updated before external calls.
5. Challenge: Write a simple counter contract with increment, decrement, and getCount functions.
contract Counter {
uint256 private count;
function increment() public {
count += 1;
}
function decrement() public {
require(count > 0, "Cannot go below zero");
count -= 1;
}
function getCount() public view returns (uint256) {
return count;
}
}Real-World Task: Deploy to Remix IDE
- Open https://remix.ethereum.org (free browser IDE)
- Create a new file
Voting.sol - Paste our Voting contract
- Compile with Solidity 0.8.19+
- Switch to “Deploy & Run Transactions” tab
- Select “JavaScript VM” (local sandbox — free, no real ETH)
- Deploy with candidates: [“Alice”, “Bob”, “Charlie”]
- Interact with the contract through the Remix interface
- Vote for candidates, check who voted, end voting, get the winner
This gives you hands-on experience deploying and interacting with smart contracts without spending real ETH.
FAQ
Try It Yourself
Write and deploy a simple storage contract in Remix:
// MyStorage.sol
pragma solidity ^0.8.19;
contract MyStorage {
string private message;
function setMessage(string calldata _newMessage) public {
message = _newMessage;
}
function getMessage() public view returns (string memory) {
return message;
}
}Try calling setMessage("Hello, Blockchain!") and then getMessage() to read it back. This demonstrates the fundamental pattern: you write data to the blockchain (costs gas) and read it back (free).
What’s Next
What’s Next
Congratulations on completing this Smart Contracts tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro