Skip to content

ETHAccra/Solidity-Mondays

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

54 Commits
Β 
Β 
Β 
Β 

Repository files navigation

ETHAccra Solidity Mondays: Weekly Solidity Tutorial

Welcome to Solidity Mondays! This is a structured weekly plan to help you learn Solidity, the programming language used to write smart contracts on Ethereum and EVM compatible chains. Each week, we’ll cover a new topic, gradually building your skills from beginner to advanced.


Weekly Schedule

Week 1: Introduction to Solidity and Blockchain Basics

Topics Covered:

  • Evolution of the Web: Web1, Web2, and Web3
  • Overview of blockchain and Ethereum.
  • Smart contracts: What they are and why they matter.
  • Setting up your development environment (Remix IDE, MetaMask, and Node.js).

πŸ“Œ Introduction to Blockchain: From Web1 to Solidity on Ethereum

πŸš€ Lesson Overview

This lesson covers the evolution of the web (Web1, Web2, Web3), blockchain fundamentals, wallets, Ethereum smart contracts, and Solidity programming.


1️⃣ Evolution of the Web: Web1, Web2, and Web3

🌐 Web1: The Static Web (1990s - early 2000s)

  • Read-only web where users could only consume content.
  • Static websites with minimal interaction.
  • Examples: Yahoo, early blogs, and company websites.

🌍 Web2: The Interactive Web (Mid-2000s - Present)

  • Read and write capabilities, allowing user-generated content.
  • Centralized platforms control data (Facebook, Google, Twitter).
  • Monetization through ads and data collection.
  • Problems: Privacy issues, censorship, platform dependence.

🌎 Web3: The Decentralized Web (Emerging Future)

  • Built on blockchain and smart contracts.
  • Users own their data, assets, and identities.
  • Peer-to-peer interactions without intermediaries.
  • Examples: Ethereum-based DApps, DAOs, DeFi, NFTs.

2️⃣ What is Blockchain?

πŸ”— Definition

Blockchain is a decentralized, distributed ledger that records transactions securely and transparently.

πŸ”‘ Key Features

  • Decentralization – No central authority.
  • Transparency – Publicly accessible transactions.
  • Security – Cryptographic encryption ensures integrity.
  • Immutability – Transactions cannot be altered once confirmed.

πŸ“Œ Types of Blockchains

  • Public Blockchains (Ethereum, Bitcoin) – Open networks, permissionless access.
  • Private Blockchains (Hyperledger) – Restricted access for enterprises.
  • Consortium Blockchains – Controlled by multiple entities.

3️⃣ Crypto Wallets

πŸ›  What is a Crypto Wallet?

A crypto wallet allows users to store, send, and receive digital assets.

πŸ“Œ Types of Wallets

  • Custodial Wallets – Centralized control (e.g., Binance, Coinbase).
  • Non-Custodial Wallets – User-controlled keys (e.g., MetaMask, Trust Wallet).
  • Hardware Wallets – Secure offline storage (e.g., Ledger, Trezor).

πŸ”‘ Wallet Addresses & Private Keys

  • Wallet Address – Public identifier for receiving funds.
  • Private Key – Secret code controlling wallet access.

4️⃣ Introduction to Ethereum

πŸ”Ή Ethereum Overview

  • A decentralized smart contract platform.
  • Uses Ether (ETH) as the native cryptocurrency.
  • Supports ERC20 (tokens) and ERC721 (NFTs).

πŸ”Ή Ethereum Use Cases

  • Decentralized Finance (DeFi) – Lending, borrowing, staking.
  • NFTs – Digital ownership of assets.
  • DAOs – Community-driven governance.

5️⃣ Solidity: Smart Contract Programming

πŸ“Œ What is Solidity?

A high-level language for writing smart contracts on Ethereum, similar to JavaScript and Python.

6️⃣ Deploying and Interacting with Smart Contracts

πŸš€ Using Remix IDE

  1. Open Remix.
  2. Create a new Solidity file (.sol).
  3. Compile and deploy using MetaMask.

Materials:


Week 2: Solidity Fundamentals

WEEK 2 PRESENTATION

https://docs.google.com/presentation/d/1etS78gVlWwDJBgpt2-aomsntDFG8mUpQ/edit?usp=sharing&ouid=109207709370381780005&rtpof=true&sd=true

REMIX PRESENTATION

https://docs.google.com/presentation/d/1UkdDAZYwNiS0rGIkdulOonFH6FDs4z9smaaj5iDim1s/edit#slide=id.g12e1023695a_0_0

SEPOLIA FAUCET

https://cloud.google.com/application/web3/faucet/ethereum/sepolia

Topics Covered:

  • Basic syntax and structure of a Solidity contract.
  • Data types: uint, address, bool, string, etc.
  • Variables: State variables, local variables, and constants.
  • Functions: Visibility (public, private, internal, external), and modifiers.

Solidity Mondays: Solidity Fundamentals

1. Basic Structure of a Solidity Contract

A Solidity smart contract starts with the pragma directive, followed by the contract definition. Solidity contracts contain functions, variables, and logic that define how they interact on the blockchain.


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19; // Specifies the Solidity version

contract MyFirstContract { // Contract content goes here }

2. Data Types in Solidity

Value Types

  • Boolean (bool): Stores true or false.
  • Unsigned Integer (uint): Represents non-negative integers.
  • Signed Integer (int): Stores positive and negative integers.
  • Address (address): Stores Ethereum addresses.
  • Bytes (bytes1 to bytes32): Used for cryptographic operations.
  • String (string): Used for storing text.

3. Functions in Solidity

Basic Structure Of A function

Functions define the behavior of a smart contract. They can be public, private, view (read-only), or payable (can receive Ether).


function getName() public pure returns (string memory) {
    return "Solidity Mondays"; // Returns a fixed string
}

4. Variables in Solidity

State Variables

State variables are permanently stored on the blockchain. They retain their values even after the contract execution ends.


contract Example {
    uint256 public storedNumber; // A state variable stored on the blockchain
    function setNumber(uint256 _num) public {
        storedNumber = _num; // Updates the state variable
    }
}

Local Variables

Local variables exist only within a function's execution scope. They do not persist on the blockchain.


function getNumber() public pure returns (uint256) {
    uint256 localNumber = 10; // Local variable, exists only in this function
    return localNumber;
}

Global Variables

Global variables provide blockchain-related information such as the sender's address, block number, or timestamp.


uint256 public blockNumber = block.number; // Gets the current block number
address public sender = msg.sender; // Gets the address of the sender

5. Control Structures (If-Else, Loops)

If-Else Statement

The if-else statement allows conditional execution of code based on specific conditions.


function checkEven(uint256 num) public pure returns (string memory) {
    if (num % 2 == 0) {
        return "Even"; // Returns "Even" if the number is divisible by 2
    } else {
        return "Odd"; // Returns "Odd" if the number is not divisible by 2
    }
}

6. Mappings and Structs

Mappings

Mappings store key-value pairs, where keys are unique, and values can be of any type.


mapping(address => uint256) public balances; // Maps addresses to balances

function updateBalance(address _user, uint256 _amount) public { balances[_user] = _amount; // Updates the balance for the user }

Structs

Structs are used to define custom data structures, grouping multiple data fields.


struct Student {
    string name;
    uint256 age;
}

Student public student; // Declares a student struct variable

function setStudent(string memory _name, uint256 _age) public { student = Student(_name, _age); // Assigns values to the student struct }

7. Events and Logging

Events in Solidity allow logging data on the blockchain. They are mainly used to track actions like transactions or contract updates.


event UserRegistered(address indexed user, uint256 timestamp); // Declares an event

function registerUser() public { emit UserRegistered(msg.sender, block.timestamp); // Emits an event when a user registers }

8. Modifiers

Modifiers define rules that must be met before executing a function. They help enforce access control and conditions.


modifier onlyOwner() {
    require(msg.sender == owner, "Not the owner"); // Checks if the caller is the contract owner
    _;
}

function restrictedFunction() public onlyOwner { // Function logic that only the owner can execute }

9. Payable Functions (Handling Ether)

Payable functions allow contracts to receive and send Ether. The msg.value property holds the amount of Ether sent.


function deposit() public payable {
    require(msg.value > 0, "Must send some Ether"); // Ensures Ether is sent
}

function getBalance() public view returns (uint256) { return address(this).balance; // Returns the contract's balance }

Materials:

  • Book: Mastering Ethereum (Chapter 7: Smart Contracts and Solidity).
  • Practice: Write a simple "Hello World" contract in Remix IDE.

Week 3: Advanced Data Structures

WEEK 3 PRESENTATION

https://docs.google.com/presentation/d/1p6cXHXr3mGk1zcAaTovLzI_t8PEfgo6K/edit?usp=sharing&ouid=109207709370381780005&rtpof=true&sd=true

Topics Covered:

  • Arrays: Fixed-size and dynamic arrays.
  • Structs: Custom data types.
  • Mappings: Key-value pairs.
  • Enums: User-defined types for constants.

In this session, we explore Arrays, Structs, Mappings, and Enums in depth, which are essential for smart contract development.

  • βœ… Arrays: Storing multiple values.
  • βœ… Structs: Grouping multiple pieces of data.
  • βœ… Mappings: Storing key-value pairs.
  • βœ… Enums: Defining fixed choices.

1️⃣ Arrays: Storing Multiple Values

An array is a list that holds multiple values of the same type.

πŸ“Œ Two Types of Arrays
  • πŸ”Ή Fixed-size array – has a set number of items.
  • πŸ”Ή Dynamic array – can grow or shrink.
Example 1: Fixed-size Array

// A fixed-size array that holds 3 numbers
uint[3] numbers = [10, 20, 30];
Example 2: Dynamic Array

uint[] numbers; // Can grow or shrink

function addNumber(uint _num) public { numbers.push(_num); // Adds a number to the array }

function removeLast() public { numbers.pop(); // Removes the last number }

Looping through an Array

function getAllNumbers() public view returns (uint[] memory) {
    return numbers;
}

2️⃣ Structs: Grouping Multiple Pieces of Data

A struct allows you to combine multiple data types into a single entity.

Example: Defining a Struct


struct Student {
    string name;
    uint age;
    bool enrolled;
}
Example: Using a Struct

Student public student;

function setStudent(string memory _name, uint _age, bool _enrolled) public { student = Student(_name, _age, _enrolled); }

Structs Inside Arrays

Student[] public students;

function addStudent(string memory _name, uint _age, bool _enrolled) public { students.push(Student(_name, _age, _enrolled)); }

3️⃣ Mappings: Storing Key-Value Pairs

A mapping is a key-value store, like a dictionary.

Example: Storing Account Balances

mapping(address => uint) public balances;

function deposit(uint _amount) public { balances[msg.sender] += _amount; }

function checkBalance() public view returns (uint) { return balances[msg.sender]; }

βœ… Key: msg.sender (user’s wallet address)
βœ… Value: The amount of tokens the user has

Nested Mappings

You can have mappings inside mappings! For example, each user can have multiple token balances.


mapping(address => mapping(string => uint)) public tokenBalances;

function setTokenBalance(string memory _token, uint _amount) public {
    tokenBalances[msg.sender][_token] = _amount;
}

function getTokenBalance(string memory _token) public view returns (uint) {
    return tokenBalances[msg.sender][_token];
}

4️⃣ Enums: Creating Custom Options

An enum (short for β€œenumeration”) is used when you have a fixed set of choices.

Example: Order Status

enum OrderStatus { Pending, Shipped, Delivered }
OrderStatus public status;
Updating the Enum

function setStatus(uint _status) public {
    status = OrderStatus(_status); // 0 = Pending, 1 = Shipped, 2 = Delivered
}
Checking the Status

function isDelivered() public view returns (bool) {
    return status == OrderStatus.Delivered;
}

Online Shop Smart Contract

This smart contract simulates an online store using all the data structures.


    
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract OnlineShop {
    enum OrderStatus { Pending, Shipped, Delivered }

    struct Product {
        string name;
        uint price;
    }

    struct Order {
        address buyer;
        uint productId;
        OrderStatus status;
    }

    Product[] public products;
    mapping(uint => Order) public orders;
    uint public orderCount;

    function addProduct(string memory _name, uint _price) public {
        products.push(Product(_name, _price));
    }

    function placeOrder(uint _productId) public {
        require(_productId < products.length, "Invalid product ID");
        orders[orderCount] = Order(msg.sender, _productId, OrderStatus.Pending);
        orderCount++;
    }

    function updateOrderStatus(uint _orderId, OrderStatus _status) public {
        require(_orderId < orderCount, "Invalid order ID");
        orders[_orderId].status = _status;
    }

    function getOrder(uint _orderId) public view returns (address, string memory, uint, OrderStatus) {
        require(_orderId < orderCount, "Invalid order ID");
        Order storage order = orders[_orderId];
        Product storage product = products[order.productId];
        return (order.buyer, product.name, product.price, order.status);
    }
}

πŸ”Ή Summary

  • βœ… Arrays – Store multiple values in a list.
  • βœ… Structs – Group different types of data together.
  • βœ… Mappings – Store key-value pairs for quick lookups.
  • βœ… Enums – Define fixed choices for specific conditions.

Compile Solidity Code Using Hardhat

1. Install Hardhat

Run the following command in your project directory:

npm install --save-dev hardhat

2. Create a Hardhat Project

If you haven't initialized Hardhat, run:

npx hardhat

Select "Create a basic sample project" and follow the prompts.

3. Check Hardhat Configuration

Ensure your hardhat.config.js or hardhat.config.ts file has the correct Solidity compiler version:

module.exports = {
  solidity: "0.8.20",
};

4. Compile Your Solidity Code

Run:

npx hardhat compile

This will compile all Solidity files in the contracts/ directory and store the artifacts in artifacts/ and cache/.

5. (Optional) Fix Errors and Warnings

If you encounter errors, review them in the terminal and adjust your Solidity code or compiler version accordingly.

6. Verify Compilation Output

Check the artifacts/contracts/ directory to ensure the .json files (ABI & Bytecode) are generated.

Now your Solidity code is compiled successfully using Hardhat! πŸš€

🎯 Practice Task

Try adding a feature where users can leave reviews for products.

Materials:

  • Book: Mastering Ethereum (Chapter 7: Smart Contracts and Solidity).

Week 4: Control Structures and Error Handling

Topics Covered:

  • Conditional statements: if, else, else if.
  • Loops: for, while.
  • Error handling: require, assert, revert.

🧠 Week 4: Control Structures & Error Handling in Solidity

Welcome to Week 4 of Solidity Mondays! This session focuses on control flow and defensive programming in Solidity. We'll explore conditional logic, loops, and error handling with examples from our OnlineShop smart contract.


βœ… 1. Conditional Statements

Solidity allows if, else if, and else to control decision-making in your contracts.

πŸ”Ή Marketplace Example: Buyer Tiers Based on Spending

function getBuyerTier(uint totalSpent) public pure returns (string memory) {
    if (totalSpent >= 1000 ether) {
        return "Platinum";
    } else if (totalSpent >= 500 ether) {
        return "Gold";
    } else {
        return "Regular";
    }
}

This helps the contract give loyalty rewards based on how much a user has spent.


πŸ” 2. Loops

Loops are useful for iterating through arrays or performing repeated actions. Use them carefully to avoid high gas consumption!

πŸ”Ή for Loop: Total Product Price

function totalProductPrice() public view returns (uint total) {
    for (uint i = 0; i < products.length; i++) {
        total += products[i].price;
    }
}

πŸ”Ή while Loop: Count Pending Orders

function countPendingOrders() public view returns (uint count) {
    uint i = 0;
    while (i < orderCount) {
        if (orders[i].status == OrderStatus.Pending) {
            count++;
        }
        i++;
    }
}

⚠️ Warning: Avoid using unbounded loops in functions that will be called in transactions β€” they can run out of gas!


β›” 3. Error Handling

Solidity offers three main tools for handling errors: require, assert, and revert.

πŸ”Ή require(): Check External Conditions

function placeOrder(uint _productId) public {
    require(_productId < products.length, "Invalid product ID");
    orders[orderCount] = Order(msg.sender, _productId, OrderStatus.Pending);
    orderCount++;
}

πŸ”Ή assert(): Check Invariant/Internal Logic

function checkOrderExists(uint _orderId) public view {
    assert(_orderId <= orderCount); // Should always be true if order creation works
}

πŸ”Ή revert(): Custom Error Trigger

function cancelOrder(uint _orderId) public {
    if (_orderId >= orderCount) {
        revert("Order does not exist");
    }
    delete orders[_orderId];
}

Use these tools to stop transactions when conditions aren't met β€” saving gas and preventing bugs!


πŸ“˜ Summary Table

Keyword Use Case Reverts? Custom Message?
require() Input validation, permissions βœ… βœ…
assert() Invariants, internal logic checks βœ… ❌
revert() Manual error handling βœ… βœ…

πŸ’» Practice Contract: Online Marketplace with Control Structures

pragma solidity ^0.8.0;

contract OnlineShop { enum OrderStatus { Pending, Shipped, Delivered }

struct Product {
    string name;
    uint price;
}

struct Order {
    address buyer;
    uint productId;
    OrderStatus status;
}

Product[] public products;
mapping(uint => Order) public orders;
uint public orderCount;

function addProduct(string memory _name, uint _price) public {
    products.push(Product(_name, _price));
}

function placeOrder(uint _productId) public {
    require(_productId &lt; products.length, "Invalid product ID");
    orders[orderCount] = Order(msg.sender, _productId, OrderStatus.Pending);
    orderCount++;
}

function updateOrderStatus(uint _orderId, OrderStatus _status) public {
    require(_orderId &lt; orderCount, "Invalid order ID");
    orders[_orderId].status = _status;
}

function getOrder(uint _orderId) public view returns (address, string memory, uint, OrderStatus) {
    require(_orderId &lt; orderCount, "Invalid order ID");
    Order storage order = orders[_orderId];
    Product storage product = products[order.productId];
    return (order.buyer, product.name, product.price, order.status);
}

function totalProductPrice() public view returns (uint total) {
    for (uint i = 0; i &lt; products.length; i++) {
        total += products[i].price;
    }
}

function countPendingOrders() public view returns (uint count) {
    uint i = 0;
    while (i &lt; orderCount) {
        if (orders[i].status == OrderStatus.Pending) {
            count++;
        }
        i++;
    }
}

function cancelOrder(uint _orderId) public {
    if (_orderId &gt;= orderCount) {
        revert

Week 5: Function Modifiers and Events

Topics Covered:

  • Function modifiers: view, pure, payable.
  • Custom modifiers.
  • Events: Logging and listening to events.

Solidity Mondays: Function Modifiers, Custom Modifiers, and Events

Welcome to today's Solidity Mondays!
Today, we'll dive deep into function modifiers, custom modifiers, and events β€” key concepts for writing clean, secure smart contracts.


πŸ“š 1. Function Modifiers: view, pure, payable

In Solidity, modifiers tell us how a function interacts with the blockchain.

πŸ”΅ view

  • A view function promises NOT to modify the blockchain state.
  • It can read state variables but cannot change them.
  • Useful for fetching data without paying gas (if called externally).
uint public balance;

function getBalance() public view returns (uint) {
    return balance;
}

πŸ”΅ pure

  • A pure function neither reads nor modifies the blockchain state.
  • It only works with its own parameters or internal variables.
  • Perfect for mathematical calculations.
function add(uint a, uint b) public pure returns (uint) {
    return a + b;
}

πŸ”΅ payable

  • A payable function can receive Ether.
  • Without it, sending ETH to a function will fail.
function deposit() public payable {
    // Contract can now receive ETH
}

πŸ“‹ Quick Summary Table

Modifier Reads State? Modifies State? Can Receive Ether? Purpose
view βœ… ❌ ❌ Read blockchain state
pure ❌ ❌ ❌ Pure computation only
payable βœ… (optional) βœ… (optional) βœ… Accept ETH payments

πŸ“š 2. Custom Modifiers

Custom modifiers are reusable rules you can apply to functions to enforce conditions.

✏️ How Modifiers Work:

  • Define a modifier that runs some checks.
  • Use _; to represent where the original function should continue if checks pass.

πŸ”΅ Example: Only Owner Can Withdraw

address public owner;

modifier onlyOwner() {
    require(msg.sender == owner, "Not the owner");
    _;
}

function withdraw() public onlyOwner {
    payable(msg.sender).transfer(address(this).balance);
}
  • require checks that only the contract owner can call withdraw.
  • _; tells Solidity where the withdraw function should continue after the check.

πŸ”΅ Modifiers Can Accept Arguments

modifier costs(uint price) {
    require(msg.value >= price, "Not enough Ether");
    _;
}

function buy() public payable costs(1 ether) {
    // User must send at least 1 ETH
}

πŸ“š 3. Events: Logging and Listening

Events are like announcements that something important happened inside your contract.

They are recorded on the blockchain logs and are used to communicate with frontends.

✏️ How Events Work

  1. Define an Event:
event Deposited(address indexed user, uint amount);
  • indexed lets users filter by address when searching.
  1. Emit the Event inside a function:
function deposit() public payable {
    emit Deposited(msg.sender, msg.value);
}
  1. Listen to Events from the frontend (example with ethers.js):
contract.on("Deposited", (user, amount) => {
    console.log(`${user} deposited ${amount} wei!`);
});

πŸ”΅ Why Use Events?

  • Track activities (e.g., deposits, withdrawals).
  • Update frontend UIs in real-time.
  • Reduce expensive state writes (cheaper than storage).

πŸ“¦ Full Example: Putting It All Together

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract SimpleBank {
    address public owner;
    uint public totalBalance;

    event Deposited(address indexed sender, uint amount);
    event Withdrawn(address indexed receiver, uint amount);

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Caller is not the owner");
        _;
    }

    function deposit() public payable {
        totalBalance += msg.value;
        emit Deposited(msg.sender, msg.value);
    }

    function withdraw(uint _amount) public onlyOwner {
        require(totalBalance >= _amount, "Insufficient balance");
        totalBalance -= _amount;
        payable(msg.sender).transfer(_amount);
        emit Withdrawn(msg.sender, _amount);
    }

    function getBalance() public view returns (uint) {
        return totalBalance;
    }

    function calculateBonus(uint amount) public pure returns (uint) {
        return amount * 10 / 100;
    }
}

✨ Key Takeaways

  • view, pure, and payable describe function behaviors.
  • Custom modifiers help you enforce rules and conditions efficiently.
  • Events are critical for logging actions and communicating with dApps and UIs.

Mastering these areas will level up your ability to write secure, scalable, and professional smart contracts!


πŸš€ Practice Challenge (Optional)

Write a smart contract:

  • Users can deposit ETH (payable function).
  • Only users who deposited can withdraw (custom modifier).
  • Emit events when deposits and withdrawals happen.

Week 6: Inheritance and Interfaces

Topics Covered:

  • Inheritance: is keyword, parent and child contracts.
  • Abstract contracts.
  • Interfaces: Defining and implementing interfaces.
Week 6: Inheritance, Abstract Contracts, and Interfaces

Solidity Mondays: Inheritance, Abstract Contracts, and Interfaces

  • ERC-20: Fungible tokens.
  • ERC-721: Non-fungible tokens (NFTs).
  • ERC-1155: Multi-token standard.

Welcome back to Solidity Mondays!
In this session, we’re diving into three advanced but powerful topics: Inheritance, Abstract Contracts, and Interfaces β€” using real-world ERC-20 token contract examples.


πŸ“š 1. Inheritance in Solidity

Solidity supports inheritance, which allows you to create a base contract and extend it into child contracts. This helps with code reuse, structure, and modularity. ERC-20 tokens use inheritance to add features like minting and burning.

πŸ”΅ The is Keyword

  • Use is to make a contract inherit from another.
  • The child contract gains access to all public and internal variables/functions of the parent.

πŸ“˜ Example: ERC-20 Token with Minting Inherited

// Base ERC20 contract
contract ERC20 {
    mapping(address => uint256) public balanceOf;
    uint256 public totalSupply;
    string public name;
    string public symbol;

    constructor(string memory _name, string memory _symbol) {
        name = _name;
        symbol = _symbol;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        require(balanceOf[msg.sender] >= amount, "Not enough balance");
        balanceOf[msg.sender] -= amount;
        balanceOf[to] += amount;
        return true;
    }
}

// Child token with minting
contract MyToken is ERC20 {
    address public owner;

    constructor() ERC20("MyToken", "MTK") {
        owner = msg.sender;
    }

    function mint(address to, uint256 amount) public {
        require(msg.sender == owner, "Only owner can mint");
        balanceOf[to] += amount;
        totalSupply += amount;
    }
}
  • MyToken inherits from ERC20 and adds custom minting logic.

πŸ“š 2. Abstract Contracts

An abstract contract is a contract that cannot be deployed directly.

It contains at least one unimplemented function. ERC-20 token libraries often use abstract contracts to enforce structure.

πŸ“˜ Example: Abstract ERC20 Token

abstract contract AbstractERC20 {
    mapping(address => uint256) public balanceOf;
    uint256 public totalSupply;

    function transfer(address to, uint256 amount) public virtual returns (bool);
    function decimals() public view virtual returns (uint8);
}

contract RealERC20 is AbstractERC20 {
    string public name = "RealToken";
    string public symbol = "RTL";

    constructor() {
        balanceOf[msg.sender] = 1000;
        totalSupply = 1000;
    }

    function transfer(address to, uint256 amount) public override returns (bool) {
        require(balanceOf[msg.sender] >= amount, "Insufficient balance");
        balanceOf[msg.sender] -= amount;
        balanceOf[to] += amount;
        return true;
    }

    function decimals() public pure override returns (uint8) {
        return 18;
    }
}
  • AbstractERC20 defines structure, while RealERC20 provides implementation.

πŸ“š 3. Interfaces in Solidity

Interfaces are like contracts but can only declare functions β€” they cannot contain any implementation or state.

They are widely used to define standards like ERC-20 so that wallets, DEXs, and other smart contracts can interact with tokens seamlessly.

🧩 Rules for Interfaces:

  • All functions must be external and unimplemented.
  • No constructor, state variables, or function bodies.

πŸ“˜ Example: ERC-20 Interface Implementation

// Interface
interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
}

// Implementing Contract
contract MiniToken is IERC20 {
    mapping(address => uint256) private _balances;
    uint256 private _supply;

    constructor() {
        _supply = 1000;
        _balances[msg.sender] = _supply;
    }

    function totalSupply() external view override returns (uint256) {
        return _supply;
    }

    function balanceOf(address account) external view override returns (uint256) {
        return _balances[account];
    }

    function transfer(address to, uint256 amount) external override returns (bool) {
        require(_balances[msg.sender] >= amount, "Not enough tokens");
        _balances[msg.sender] -= amount;
        _balances[to] += amount;
        return true;
    }
}
  • MiniToken conforms to the IERC20 interface for ERC-20 compatibility.

βœ… Summary Table

Concept Description ERC-20 Example
Inheritance Child contracts inherit variables and functions from base contracts. MyToken is ERC20
Abstract Contracts Define structure but not full logic; must be extended. AbstractERC20 with transfer()
Interfaces Only declare external functions β€” no implementation. IERC20

πŸŽ‰ That wraps up Week 6 of Solidity Mondays. You're now equipped with tools to structure smart contracts like ERC-20 tokens β€” using inheritance, abstract contracts, and interfaces!

Materials:

  • Online Resources: Solidity Documentation.
  • Practice: Create a parent contract with shared functionality and a child contract that inherits from it.

Week 7: Security Best Practices

πŸ” Solidity Mondays: Smart Contract Security – Vulnerabilities, Tools & Secure Coding

Welcome to this extended session of Solidity Mondays. Today, we dive into one of the most important topics in smart contract development: SECURITY.

Unlike traditional software, smart contracts are immutable once deployed. This means a bug or vulnerability can't be patched without deploying a new contract. If someone exploits a weakness in your code, it could result in permanent loss of funds, broken logic, or even total contract destruction.

This guide explains key vulnerabilities, how to avoid them, and the tools you can use to protect your smart contracts.

1. Reentrancy Attack

What is it? A reentrancy attack happens when a contract sends ETH to an external contract or address before updating its state. If the external contract has fallback logic (e.g., a receive() or fallback() function), it can re-enter the original contract and repeat actions before the state updates β€” allowing attackers to withdraw more funds than they should.

Simple example:

function withdraw(uint _amount) public {
    require(balances[msg.sender] >= _amount);
    (bool sent, ) = msg.sender.call{value: _amount}("");
    require(sent, "Send failed");
    balances[msg.sender] -= _amount;
}

Here, the contract sends ETH before updating the balance, making it vulnerable.

Fix: Use the Checks-Effects-Interactions pattern:

function withdraw(uint _amount) public {
    require(balances[msg.sender] >= _amount);
    balances[msg.sender] -= _amount;
    (bool sent, ) = msg.sender.call{value: _amount}("");
    require(sent, "Send failed");
}

Also consider using OpenZeppelin's ReentrancyGuard to prevent nested calls.

2. Integer Overflow and Underflow

What is it? Integer overflow occurs when a number goes beyond its maximum value (e.g., 255 + 1 = 0 for uint8), and underflow is the opposite (e.g., 0 - 1 = 255).

Why it’s dangerous: Attackers can use it to manipulate balances, counters, or timers.

Example: (before Solidity 0.8)

uint8 x = 255;
x += 1; // x becomes 0, wraps around

Fix: Use Solidity 0.8 or above, which has built-in checks for overflows. For earlier versions, use SafeMath from OpenZeppelin.

3. Unprotected selfdestruct()

What is it? If you expose the selfdestruct function without restricting access, anyone can destroy the contract and send its ETH to any address.

Bad:

function destroy() public {
  selfdestruct(payable(msg.sender));
}

Fix: Always limit access to critical functions:

modifier onlyOwner {
  require(msg.sender == owner, "Not authorized");
  _;
}

function destroy() public onlyOwner { selfdestruct(payable(owner)); }

4. Timestamp Manipulation

What is it? Miners can slightly control block.timestamp within a range of 15 seconds. If your logic depends on it (e.g., randomness or time-based conditions), it can be manipulated.

Example:

if (block.timestamp % 2 == 0) {
  winner = msg.sender;
}

Fix: Use trusted randomness sources like Chainlink VRF. Avoid using block.timestamp or blockhash for random decisions.

5. tx.origin Authentication Flaw

What is it? tx.origin returns the original external account that started the transaction. If a user interacts with a malicious contract that then calls your contract, tx.origin still shows the user β€” allowing attackers to bypass checks.

Bad:

require(tx.origin == owner);

Fix: Use msg.sender, which represents the immediate caller of the function.

6. Front-running

What is it? If your contract allows users to submit transactions that reveal valuable information (like bids, prices, or timing), others can copy and send similar transactions with a higher gas fee to β€œcut the line.”

Example: DEX trade or NFT minting price set in the open.

Fix:

  • Use commit-reveal schemes
  • Design logic to be less predictable (randomize when possible)
  • Don’t store sensitive information publicly before execution
7. Unchecked External Calls

What is it? Calling unknown or external contracts can introduce security risks β€” especially if you don’t check the return value or response.

Fix: Always check if the call was successful. Prefer using interfaces and trusted contracts.

8. Missing Access Controls

What is it? Many developers forget to restrict who can call sensitive functions. This means anyone can call mint(), pause(), or even withdraw().

Fix: Always use access control modifiers like onlyOwner, onlyAdmin, or role-based access via AccessControl from OpenZeppelin.

9. Writing Secure Solidity – Best Practices
  • βœ… Use require() to validate all inputs
  • βœ… Use Checks-Effects-Interactions pattern
  • βœ… Prefer msg.sender over tx.origin
  • βœ… Avoid complex logic inside fallback or receive functions
  • βœ… Avoid loops that can grow indefinitely β€” they’re expensive
  • βœ… Keep functions and contracts as small and modular as possible
  • βœ… Use OpenZeppelin libraries and templates for common patterns
10. Security Tools

πŸ” Slither (Static Analyzer)

pip install slither-analyzer
slither contracts/MyContract.sol

Find issues like reentrancy, unused variables, shadowed functions, and more.

🧠 MythX / Mythril (Symbolic Execution)

pip install mythril
myth analyze contracts/MyContract.sol

Simulates execution paths to find logic flaws and vulnerabilities.

πŸ§ͺ Foundry

forge test --fuzz

Fuzz testing: Generate random inputs to find bugs.

πŸ”§ Remix Analyzer Plugin

Built-in static analysis in the Remix IDE. Very beginner-friendly.

11. Smart Contract Security Checklist
  • βœ… Use the latest Solidity compiler
  • βœ… Validate all inputs using require()
  • βœ… Use ReentrancyGuard when sending ETH
  • βœ… Prefer transfer and call with care β€” always check success
  • βœ… Write unit tests and run fuzz testing
  • βœ… Set appropriate visibility (public/private/internal)
  • βœ… Avoid relying on block data for randomness
  • βœ… Use OpenZeppelin libraries and audit your code
  • βœ… Remove test functions before deploying to mainnet
12. Recommended Resources

πŸ”’ Always remember: Every line of code is a potential threat vector. Test thoroughly. Audit continuously. Write defensively.

Keep building safely! πŸš€

Topics Covered:

  • Common vulnerabilities: Reentrancy, integer overflow, and more.
  • Security tools: Slither, MythX.
  • Writing secure code.

Materials:


Week 8: Deploying and Interacting with Contracts

Topics Covered:

  • Deploying contracts to testnets (Ropsten, Rinkeby, etc.).
  • Interacting with contracts using Web3.js or Ethers.js.
  • Gas optimization techniques.

Materials:

  • Book: Mastering Ethereum (Chapter 10: Tokens).
  • Tools: Infura, Alchemy, or Hardhat for deployment.
  • Practice: Deploy your voting contract to a testnet and interact with it using a simple frontend.

Week 9: Advanced Topics and Final Project

Topics Covered:

  • Upgradeable contracts using proxies.
  • Layer 2 solutions: Optimism, Arbitrum.
  • Decentralized Autonomous Organizations (DAOs).

Materials:

  • Book: Mastering Ethereum (Chapter 11: Oracles and Chapter 12: Decentralized Applications).
  • Final Project: Build and deploy a decentralized application (dApp) that incorporates everything you’ve learned.

Additional Resources


Final Thoughts

By following this Solidity Fridays schedule, you’ll gain a solid understanding of Solidity and Ethereum development. Happy coding! πŸš€

Don't forget to follow these ETHAccra channels to get regular Updates.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors