Skip to content
Smart Contracts Explained — Beginner's Guide

Smart Contracts Explained — Beginner's Guide

DodaTech Updated Jun 6, 2026 10 min read

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
  
Prerequisites: Blockchain basics and Ethereum fundamentals (EVM, gas). Basic programming knowledge in any language helps. No Solidity experience needed.

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?

ProblemTraditional SolutionSmart Contract Solution
TrustTrust a lawyer, bank, or escrowTrust the code (audited, transparent)
CostHigh fees for intermediariesLower fees (one-time deployment + gas)
SpeedDays for settlementMinutes or seconds
Global accessRequires bank accountAnyone with internet can participate
TransparencyPrivate agreementsPublicly 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 count
  • Candidate[] public candidates — an array of all candidates, stored permanently
  • mapping(address => bool) public hasVoted — a lookup table tracking who voted (prevents double voting)
  • address public owner — the contract deployer’s address
  • bool 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 this
  • getWinner() — 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: 0x1234567890abcdef1234567890abcdef12345678

Once 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

  1. Open https://remix.ethereum.org (free browser IDE)
  2. Create a new file Voting.sol
  3. Paste our Voting contract
  4. Compile with Solidity 0.8.19+
  5. Switch to “Deploy & Run Transactions” tab
  6. Select “JavaScript VM” (local sandbox — free, no real ETH)
  7. Deploy with candidates: [“Alice”, “Bob”, “Charlie”]
  8. Interact with the contract through the Remix interface
  9. 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

Which programming language is used for smart contracts?
Solidity is the most popular language for Ethereum smart contracts. Other options include Vyper (Python-like), Rust (for Solana, Near), and Michelson (for Tezos).
How much does it cost to deploy a smart contract?
Costs vary by contract complexity. A simple contract costs $10-50 in gas. Complex contracts (like a DeFi protocol) can cost thousands. Deployment costs scale with the amount of code and storage.
Can a smart contract be changed after deployment?
By default, contracts are immutable. Upgradeable patterns use proxy contracts — the user interacts with a proxy that delegates calls to an implementation contract. The proxy keeps the same address while the implementation can be swapped.
Are smart contracts legally enforceable?
The legal status of smart contracts varies by jurisdiction. Some US states (Arizona, Vermont) have passed laws recognizing smart contracts as legally binding. Most contracts use a “hybrid” approach: smart contract code + a traditional legal agreement.
What happens if a smart contract has a bug?
If a contract with significant value has a bug, the effects can be catastrophic (the DAO hack, Parity wallet freeze). Some bugs can be fixed via upgradeable patterns; others are permanent. This is why auditing and testing are critical.

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