L10: Solidity Basics Study Guide

Master the fundamentals of Solidity smart contract development: data types, functions, state variables, ERC-20 implementation, and basic voting contracts.

Estimated Time: 6-9 hours across 3 weeks

Learning Objectives

By the end of this study block, you will be able to:

  • Distinguish Solidity data types and choose the correct type for a use case
  • Write functions with correct visibility, state mutability, and return types
  • Explain the difference between storage, memory, and calldata
  • Implement a basic ERC-20 token from scratch
  • Build a simple on-chain voting contract with delegation
  • Use Remix IDE to compile, deploy, and test contracts on a local VM and testnet

Week-by-Week Study Plan

Week 1 — Language Fundamentals (2-3 hours)

Read the Solidity Introduction and the Types section. Focus on: uint256, address, bool, bytes32, string, mapping, and array. Work through CryptoZombies Lessons 1-3.

Goal: Write a contract from scratch that stores a number, a name, and a mapping of addresses to balances.

Week 2 — Functions, Events, and ERC-20 (2-3 hours)

Study function visibility (public, private, internal, external) and state mutability (view, pure, payable). Read the ERC-20 standard (EIP-20). Implement a minimal ERC-20 token by hand — do not copy-paste from OpenZeppelin yet.

Goal: Deploy your ERC-20 on the Remix VM, transfer tokens between test accounts, and confirm balance updates.

Week 3 — Voting Contract and Testnet Deployment (2-3 hours)

Study the Solidity Voting example from the official docs. Understand how struct, mapping, and delegation work together. Deploy your ERC-20 and a voting contract to Sepolia. Use the hands-on tools guide for faucet and deployment steps.

Goal: Deploy both contracts to Sepolia, verify them on Etherscan, and demonstrate a complete vote via Remix.

Key Concepts to Review

Value Types vs Reference Types

Value types (uint, bool, address, bytes32): copied on assignment, stored directly in storage slot.

Reference types (array, struct, mapping): you must specify data location — storage (on-chain, persistent), memory (in-function, temporary), or calldata (read-only, from external call). Forgetting location causes a compile error.

Key gotcha: mapping values default to zero — there is no "key not found" error. Check logic carefully.

Function Visibility

public: callable by anyone, inside or outside the contract. Generates a getter for state variables.

external: only callable from outside (more gas-efficient for large inputs — uses calldata).

internal: callable within the contract and derived contracts. Used for shared logic.

private: callable only within the same contract. Does not prevent reading from the blockchain directly — it only hides from other contracts.

State Mutability

view: reads state but does not modify it. No gas when called externally (off-chain).

pure: neither reads nor modifies state. Useful for math helpers.

payable: can receive ETH. Without this, sending ETH to the function reverts.

Omitting a mutability modifier means the function can read and write state — the most expensive default.

ERC-20 Standard Functions

totalSupply(): returns total tokens minted.

balanceOf(address): returns token balance of an address.

transfer(to, amount): move tokens from msg.sender to to.

approve(spender, amount): authorize spender to pull up to amount from your balance.

transferFrom(from, to, amount): pull tokens from from to to, using pre-approved allowance.

allowance(owner, spender): returns remaining approved amount.

Events: Transfer(from, to, value) and Approval(owner, spender, value) must be emitted.

Voting Contract Patterns

Struct for voters: struct Voter { bool voted; uint vote; uint weight; address delegate; } — packs all voter state in one mapping.

Delegation: Voter A delegates to B, who may have already delegated to C. Follow the chain to the final delegate (avoid loops).

Chairperson role: One address has permission to call giveRightToVote. Access control using require(msg.sender == chairperson).

Winner determination: Iterate over proposals array and find the maximum voteCount. Cannot use sorting on-chain efficiently — linear scan is standard.

Practice Exercises

Exercise 1: Modify SimpleStorage to store a list of numbers (using a dynamic array). Add a function to push a new number, a function to get a number by index, and a function to return the total count of stored numbers.
Use uint256[] public numbers; for the array. The push function: function addNumber(uint256 n) public { numbers.push(n); }. The getter: function getNumber(uint256 i) public view returns (uint256) { return numbers[i]; }. Count: function count() public view returns (uint256) { return numbers.length; }. Note: accessing an out-of-bounds index reverts — no bounds check needed explicitly.
Exercise 2: Add a burn function to your ERC-20 token. The function should reduce the caller's balance and the total supply by the specified amount. Emit a Transfer event to the zero address.
function burn(uint256 amount) public {
  require(balanceOf[msg.sender] >= amount, "Insufficient balance");
  balanceOf[msg.sender] -= amount;
  totalSupply -= amount;
  emit Transfer(msg.sender, address(0), amount);
}
Using address(0) as the destination in the Transfer event is the ERC-20 convention for burn events. Wallets and explorers recognize this pattern.
Exercise 3: In the Voting contract from the Solidity docs, add a deadline variable set in the constructor. Modify vote() so that voting is only allowed before the deadline. Use block.timestamp for the current time.
In the constructor, add: uint256 public deadline; and set deadline = block.timestamp + _durationSeconds;
In the vote() function, add at the top: require(block.timestamp < deadline, "Voting has ended");
Important caveat: block.timestamp can be manipulated by validators by a few seconds. For high-stakes contracts, use block numbers instead of timestamps, or add a buffer of at least 15 seconds.
Exercise 4: Write a MintableToken contract where only the owner (set in the constructor) can call a mint(address to, uint256 amount) function. Use a custom error instead of a require string to save gas.
Declare: error NotOwner(); at the top of the contract (outside any function).
In mint(): if (msg.sender != owner) revert NotOwner();
Custom errors (introduced in Solidity 0.8.4) are cheaper than require strings because the error selector (4 bytes) is cheaper to store and transmit than an ABI-encoded string. This pattern is now preferred in production contracts.

Recommended Resources

Official Documentation

  • Solidity Language Documentation v0.8.20 DOCS

    The authoritative reference. Read "Introduction to Smart Contracts", "Types", and "Units and Globally Available Variables".

  • EIP-20: Token Standard STANDARD

    The original ERC-20 specification. Short and readable — understanding this directly is more valuable than copying OpenZeppelin.

Interactive Learning

  • CryptoZombies INTERACTIVE

    Game-based Solidity tutorial. Complete Lessons 1-6 for full coverage of L10 concepts. Free, browser-based, no setup.

  • Ethernaut by OpenZeppelin INTERACTIVE

    Security-focused puzzle game. Levels 0 (Hello), 1 (Fallback), and 2 (Fal1out) are accessible after completing L10 fundamentals.

Video

Self-Check Questions

Test your understanding before moving to L11

  • Can you explain the difference between storage and memory in Solidity? When would you use each?
  • Why does a mapping not revert when you access a key that was never set?
  • What are the 6 required functions in the ERC-20 standard? What are the 2 required events?
  • What is the purpose of the approve + transferFrom pattern? Why not allow direct transfers?
  • Can you trace the delegation chain in the Voting contract? What happens if voter A delegates to B, and B has already voted?
  • What does pragma solidity ^0.8.20 mean? What does the caret (^) symbol do?
  • What is an event in Solidity? Why do contracts emit events rather than returning values from state-changing functions?

If you can answer all of these, you are ready for L11: Building DeFi, where you will combine these building blocks into a functioning automated market maker.

Home Lessons Quizzes Glossary © Joerg Osterrieder 2025-2026. All rights reserved.