Intermediate

ERC-20 Token Implementation — Kudos build — reward token

Create a standard token.

Developer Intermediate
7/8 — ERC-20 Token Implementation — Kudos build — reward token

Objective Spin up the ERC-20 “Kudos” token that will power the rest of the app. Keep it boring: standard surface, events, and role-gated supply.

The root problem with conventional currency is all the trust that’s required to make it work.
— Satoshi Nakamoto

Flow Create a mintable/burnable token, gate minting behind a role, and emit all events. This token will be consumed by the badge gate in later lessons.

What to write

  1. SPDX & pragma
    Top-of-file identifiers keep tooling happy.
    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.24;

    Licensing + compiler lock reduce warnings and keep audits aligned.

  2. Scaffold
    ERC20 plus AccessControl with roles for minting.
    contract KudosToken is ERC20, AccessControl {
        bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    
        constructor() ERC20("Kudos", "KUDOS") {
            _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
            _grantRole(MINTER_ROLE, msg.sender);
            _mint(msg.sender, 1_000_000 ether);
        }

    Roles keep mint power narrow; initial mint seeds the treasury.

  3. Mint & burn
    Role-gated mint; user-controlled burn.
        function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
            _mint(to, amount);
        }
    
        function burn(uint256 amount) external {
            _burn(msg.sender, amount);
        }
    }

    Mint is controlled; burn lets holders opt out. Events keep explorers in sync.

Why Roles keep mint power narrow, burns allow users to opt out, and standard events make explorers and wallets happy. This token is the root of trust for later gates.

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

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

contract KudosToken is ERC20, AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor() ERC20("Kudos", "KUDOS") {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);
        _mint(msg.sender, 1_000_000 ether);
    }

    function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
        _mint(to, amount);
    }

    function burn(uint256 amount) external {
        _burn(msg.sender, amount);
    }
}

Next We’ll gate NFT badges on holding this token.

← Previous section
Next section →