🧊 ICE 7 — Club Stablecoin (Sepolia Testnet)

Deploy a minimal ERC-20, mint to classmates, import to MetaMask, and turn in proof.
← Back to Index

Overview

In this ICE, you’ll deploy a tiny, dollar-style ERC-20 (6 decimals) to the Sepolia Ethereum testnet using Remix + MetaMask, then mint tokens to teammates and import the token in wallets.

Checklist states are saved locally on this device (no server).

Prerequisites

Step 1 — Create the contract file

  1. Open Remix (https://remix.ethereum.org).
  2. Click “+” New File → name it ClubStablecoin.sol.
  3. Paste the code below, then compile with Solidity 0.8.20 (or any ^0.8.x).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * Club Stablecoin (simple ERC-20 demo)
 * - Owner (deployer) can mint and change owner.
 * - Anyone can transfer.
 * - Anyone can burn their own tokens.
 * - Decimals = 6 (dollar-like: 1.000000)
 * NOTE: Testnet demo only. Not audited. No fees/pausing/blacklists.
 */
contract ClubStablecoin {
    string public name;
    string public symbol;
    uint8  public immutable decimals;
    uint256 public totalSupply;

    mapping(address => uint256)                     private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;

    address public owner;
    modifier onlyOwner(){ require(msg.sender == owner, "not owner"); _; }

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    constructor(string memory _name, string memory _symbol, uint8 _decimals){
        owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
        name = _name;
        symbol = _symbol;
        decimals = _decimals; // e.g., 6 for $-style 1.000000
    }

    function balanceOf(address a) public view returns (uint256) { return _balances[a]; }
    function allowance(address a, address s) public view returns (uint256) { return _allowances[a][s]; }

    function transfer(address to, uint256 amount) public returns (bool) {
        _transfer(msg.sender, to, amount); return true;
    }
    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(msg.sender, spender, amount); return true;
    }
    function transferFrom(address from, address to, uint256 amount) public returns (bool) {
        uint256 allowed = _allowances[from][msg.sender];
        require(allowed >= amount, "insufficient allowance");
        if (allowed != type(uint256).max) {
            _allowances[from][msg.sender] = allowed - amount;
            emit Approval(from, msg.sender, _allowances[from][msg.sender]);
        }
        _transfer(from, to, amount); return true;
    }

    function mint(address to, uint256 amount) public onlyOwner {
        require(to != address(0), "mint to zero");
        totalSupply += amount; _balances[to] += amount;
        emit Transfer(address(0), to, amount);
    }
    function burn(uint256 amount) public {
        uint256 bal = _balances[msg.sender]; require(bal >= amount, "insufficient");
        _balances[msg.sender] = bal - amount; totalSupply -= amount;
        emit Transfer(msg.sender, address(0), amount);
    }
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), "zero owner");
        emit OwnershipTransferred(owner, newOwner); owner = newOwner;
    }

    function _transfer(address from, address to, uint256 amount) internal {
        require(to != address(0), "transfer to zero");
        uint256 bal = _balances[from]; require(bal >= amount, "insufficient balance");
        _balances[from] = bal - amount; _balances[to] += amount;
        emit Transfer(from, to, amount);
    }
    function _approve(address a, address s, uint256 amount) internal {
        require(s != address(0), "approve to zero");
        _allowances[a][s] = amount; emit Approval(a, s, amount);
    }
}

Step 2 — Connect MetaMask & Deploy to Sepolia

  1. In Remix → Deploy & Run TransactionsEnvironment: Injected Provider – MetaMask.
  2. Make sure MetaMask network is Sepolia (built-in testnet).
  3. Constructor arguments:
name:     Campus Dollar
symbol:   cUSD
decimals: 6
["Campus Dollar","cUSD",6]
  1. Click Deploy → approve in MetaMask.
  2. Copy the deployed contract address and paste it below to track links:

Step 3 — Mint to Members

As owner (the deployer), call mint(<memberAddress>, 1000000) to send 1.000000 cUSD (6 decimals). Repeat for 2–3 classmates.

Why 1,000,000? With 6 decimals, the on-chain unit is “micro-cUSD.” 1.000000 cUSD = 1,000,000 units.

Step 4 — Import Token in MetaMask

  1. In MetaMask (still on Sepolia), click Import tokens.
  2. Paste your contract address; symbol and decimals should auto-fill (cUSD, 6).
  3. Confirm; you should now see your balance.

Safety & Scope (put these in your README/slide)

Turn-in Checklist

  1. Contract address on Sepolia (paste in submission).
  2. 2–4 screenshots: Remix deployment, a mint transaction, and a wallet showing cUSD balance.
  3. 1–2 paragraphs:
    • What worked and what confused you?
    • One risk you’d mitigate before using anything like this on real money.

Troubleshooting

Remix can’t see MetaMask / wrong network
  • Set MetaMask network to Sepolia first, then in Remix choose Injected Provider – MetaMask.
  • Hard refresh Remix (Ctrl/Cmd + Shift + R) if the account list doesn’t populate.
Out of gas / no test ETH
  • Use a public Sepolia faucet (search “Sepolia faucet”).
  • Wait a minute after faucet; sometimes funds index slowly.
Deployed but MetaMask won’t auto-fill token import
  • Paste the contract address; if symbol/decimals don’t auto-fill, type: cUSD, 6.
Mint failed: “not owner”
  • You must be the deployer (the address that called the constructor). If ownership changed, the current owner must mint.
Can’t find the transaction
  • Click the TX in MetaMask → “View on block explorer” (Sepolia Etherscan). Or paste your address/contract into Etherscan’s search.