Homework of Week 6 – Build an ERC‑20 Token
Instructional Video • Quiz • Part I (Ganache – Windows) • Part II (Truffle + Ganache) • Part III (MetaMask)

Build & Deploy an ERC‑20 Token (Week 6)

Use Ganache + MetaMask + Remix to launch your own token, then transfer it between accounts.

Instructional Video

(Instructor: paste your video link here)

▶️ Watch
Quick Quiz

10 questions, answers at the end of the page.

Take Quiz
Three Parts
  • Part I — Ganache (Windows example) workspace with 10,000 ETH & 8,000,000 gas limit
  • Part II — Optional: Truffle + Ganache workflow
  • Part III — MetaMask setup and connection to Ganache

What is ERC‑20?

ERC‑20 is the most widely used standard for fungible tokens on Ethereum. It defines a consistent interface so wallets, dApps, and exchanges can interoperate.

Standard Functions
  • totalSupply() – total tokens
  • balanceOf(address) – account balance
  • transfer(address,uint256) – send tokens
  • approve(address,uint256) – allow spender
  • transferFrom(address,address,uint256) – spender moves tokens
  • allowance(owner,spender) – remaining approved amount
Use‑Cases
  • Currency (e.g., stablecoins)
  • Voting power in DAOs
  • Ownership claims to assets
  • Access control or staking incentives

Bitcoin tokens live on the Bitcoin blockchain. ERC‑20 tokens live on Ethereum. Different chains, different standards.

Part I — Ganache (Windows example)

  1. Install & open GanacheNew Workspace (name it “Voting Token Station”).
  2. Accounts & Keys → set Default Balance to 10000 ETH.
  3. Server → set Gas Limit to 8,000,000.
  4. Save & Start. Note the RPC (usually http://127.0.0.1:7545).
Outcome: local chain running with 10k ETH per account and higher gas limit.

Part II — (Optional) Truffle + Ganache

If you prefer a framework workflow for later labs:

npm install -g truffle
mkdir erc20-truffle && cd erc20-truffle
truffle init
# Configure development network (host 127.0.0.1, port 7545, network_id "*") in truffle-config.js

For HW5 you can stay in Remix; this Truffle path is optional.

Part III — MetaMask

  1. Install MetaMask extension → create a wallet and store the seed securely.
  2. Network → Add network
    • Name: Ganache
    • RPC URL: http://127.0.0.1:7545 (or 8545 for CLI)
    • Chain ID: 1337 (or Ganache’s shown ID)
    • Symbol: ETH
  3. (Optional) Import a Ganache private key to use a prefunded account.
Outcome: MetaMask connected to Ganache with lots of test ETH.

ERC‑20 Token Code (OpenZeppelin)

Use OpenZeppelin’s audited implementation. In Remix, create VotingToken.sol and paste:

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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract VotingToken is ERC20 {
    address public owner;
    constructor(uint256 initialSupply) ERC20("VotingToken", "VOTE") {
        owner = msg.sender;
        // Mint using token units (not wei): pass 1_000_000 to get 1,000,000 * 10^18
        _mint(owner, initialSupply * 10 ** decimals());
    }
}
If Remix can’t resolve @openzeppelin/…

Enable the "OpenZeppelin Contracts" library in Remix (File Explorers → remixd & libraries) or replace the import with a GitHub URL (version‑pin recommended):

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.2/contracts/token/ERC20/ERC20.sol";
Plan B: Minimal ERC‑20 (no external imports)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract MiniERC20 {
    string public name = "VotingToken";
    string public symbol = "VOTE";
    uint8 public decimals = 18;
    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    constructor(uint256 initialSupply){
        totalSupply = initialSupply * (10 ** uint256(decimals));
        balanceOf[msg.sender] = totalSupply;
        emit Transfer(address(0), msg.sender, totalSupply);
    }

    function transfer(address to, uint256 value) external returns (bool){
        require(balanceOf[msg.sender] >= value, "balance");
        balanceOf[msg.sender] -= value; balanceOf[to] += value;
        emit Transfer(msg.sender, to, value); return true;
    }

    function approve(address spender, uint256 value) external returns (bool){
        allowance[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value); return true;
    }

    function transferFrom(address from, address to, uint256 value) external returns (bool){
        require(balanceOf[from] >= value, "balance");
        require(allowance[from][msg.sender] >= value, "allowance");
        allowance[from][msg.sender] -= value;
        balanceOf[from] -= value; balanceOf[to] += value;
        emit Transfer(from, to, value); return true;
    }
}

Deploy with Remix + MetaMask + Ganache

  1. Open Remix.
  2. Compile VotingToken.sol (Solidity 0.8.x).
  3. Deploy & Run tab → Environment: Injected Web3 (connects to MetaMask on Ganache).
  4. Select your Ganache account in MetaMask.
  5. Constructor input: initialSupply (e.g., 1_000_000).
  6. Click Deploy → approve in MetaMask.
Outcome: Contract appears under Deployed Contracts in Remix; MetaMask shows the tx.

Interact

  1. Use balanceOf(account) to check balances.
  2. Use transfer(recipient, amount) to send tokens between Ganache accounts (remember 18 decimals if using raw units).
  3. Optional voting idea: treat balances as voting power, or add your own vote(proposalId, amount) function in a separate contract that reads balanceOf.

If the Code or Ports Don’t Work

Free Port 8545 on Windows

netstat -ano | findstr :8545
# Note the PID from the LISTENING row
 taskkill /PID <PID> /F

Start Ganache CLI on 8545 (alt to GUI 7545)

ganache-cli --port 8545 --networkId 1337 --chain.chainId 1337

Connect MetaMask

Point Remix to MetaMask

In Remix → Deploy & Run → Environment: Injected Web3. Confirm MetaMask is on the Ganache network.

Optional: Use a different port (8546)
ganache-cli --port 8546 --networkId 1337 --chain.chainId 1337

Update MetaMask RPC URL accordingly.

Quiz (answers below)

  1. What does ERC‑20 standardize?
  2. Which chain do ERC‑20 tokens live on?
  3. Name three core ERC‑20 functions.
  4. What does allowance(owner, spender) return?
  5. Why multiply by 10 ** decimals() when minting?
  6. What MetaMask Environment should you pick in Remix?
  7. Default Ganache GUI RPC URL/port?
  8. How do you free port 8545 on Windows?
  9. Two token use‑cases in dApps?
  10. How would you map token balance to voting power?
Show/Hide Answers
  1. A standard interface for fungible tokens (transfer, approve, allowance, etc.).
  2. Ethereum (or EVM‑compatible chains following ERC‑20).
  3. Examples: totalSupply, balanceOf, transfer, approve, transferFrom, allowance.
  4. The remaining amount a spender is allowed to transfer from an owner.
  5. To convert human‑readable tokens to smallest units (18‑decimal base by default).
  6. Injected Web3 (uses MetaMask’s current network, i.e., Ganache).
  7. http://127.0.0.1:7545 with network ID shown in Ganache.
  8. netstat -ano | findstr :8545 → note PID → taskkill /PID <PID> /F.
  9. Currency/payments, governance voting, staking/access, ownership claims.
  10. Use balanceOf(addr) as voting weight or snapshot balances before voting.

Submission Checklist

Name your PDF: HW5_FirstnameLastname.pdf with screenshots in order.