카테고리 없음

ERC20을 활용한 토큰 발행 - 코드 주석

https.. 2026. 1. 7. 18:48
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.7;

// ----------------------------------------------------------------------------
// Token Contract
// ----------------------------------------------------------------------------
// 이 컨트랙트는 ERC-20 토큰 표준을 기반으로
// 추가로 mint(발행)와 burn(소각) 기능을 포함하고 있습니다.
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
// ERC-20 표준 인터페이스 정의
// ----------------------------------------------------------------------------
interface ERC20Interface {
    function totalSupply() external view returns (uint); // 전체 발행량 조회
    function balanceOf(address tokenOwner) external view returns (uint balance); // 특정 주소의 잔액 조회
    function allowance(address tokenOwner, address spender) external view returns (uint remaining); // 토큰 사용 허용량 조회
    function transfer(address to, uint tokens) external returns (bool success); // 토큰 전송
    function approve(address spender, uint tokens) external returns (bool success); // 다른 주소에게 토큰 사용 허락
    function transferFrom(address from, address to, uint tokens) external returns (bool success); // 허락받은 토큰을 대신 전송

    // 이벤트 정의 (로그 기록용)
    event Transfer(address indexed from, address indexed to, uint tokens); // 전송 이벤트
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens); // 허락 이벤트
}

// ----------------------------------------------------------------------------
// ERC-20 Token 구현 + Mint & Burn 기능
// ----------------------------------------------------------------------------
contract INVENToken is ERC20Interface {

    // -------------------------- 상태 변수 --------------------------
    string public symbol;      // 토큰 심볼 (예: ATC)
    string public name;        // 토큰 이름
    uint8 public decimals;     // 소수점 자리수
    uint public _totalSupply;  // 총 발행량
    address public owner;      // 토큰 소유자 / 민트 권한자

    // 각 주소의 토큰 잔액을 저장
    mapping(address => uint) balances;
    // 다른 주소에게 허락된 토큰 사용량
    mapping(address => mapping(address => uint)) allowed;

    // -------------------------- Modifier --------------------------
    // onlyOwner: 오너만 실행 가능한 함수 제한
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner"); // 호출자가 오너인지 확인
        _;
    }

    // -------------------------- Constructor --------------------------
    // 배포 시 초기 설정 및 초기 토큰 발행
    constructor() {
        symbol = "THN";         // 토큰 심볼
        name = "Thsisno Token"; // 토큰 이름
        decimals = 2;           // 소수점 2자리
        owner = msg.sender;     // 배포자를 오너로 지정

        _mint(owner, 100000);   // 초기 발행량 100,000 토큰을 오너에게 지급
    }

    // -------------------------- Total Supply --------------------------
    // 전체 발행량 반환
    function totalSupply() public view override returns (uint) {
        return _totalSupply;
    }

    // -------------------------- Balance Of --------------------------
    // 특정 주소의 토큰 잔액 조회
    function balanceOf(address tokenOwner) public view override returns (uint balance) {
        return balances[tokenOwner];
    }

    // -------------------------- Transfer --------------------------
    // 토큰 전송 함수
    function transfer(address to, uint tokens) public override returns (bool success) {
        require(balances[msg.sender] >= tokens, "Insufficient balance"); // 잔액 확인

        balances[msg.sender] -= tokens; // 보내는 사람 토큰 차감
        balances[to] += tokens;         // 받는 사람 토큰 증가

        emit Transfer(msg.sender, to, tokens); // 이벤트 발생
        return true;
    }

    // -------------------------- Approve --------------------------
    // 다른 주소에게 토큰 사용 허락
    function approve(address spender, uint tokens) public override returns (bool success) {
        allowed[msg.sender][spender] = tokens; // 허용량 설정
        emit Approval(msg.sender, spender, tokens); // 이벤트 발생
        return true;
    }

    // -------------------------- Transfer From --------------------------
    // 허락받은 토큰을 대신 전송
    function transferFrom(address from, address to, uint tokens) public override returns (bool success) {
        require(balances[from] >= tokens, "Insufficient balance"); // 보내는 주소 잔액 확인
        require(allowed[from][msg.sender] >= tokens, "Allowance exceeded"); // 허락량 확인

        balances[from] -= tokens;                 // 보내는 주소 차감
        allowed[from][msg.sender] -= tokens;     // 허락량 차감
        balances[to] += tokens;                   // 받는 주소 증가

        emit Transfer(from, to, tokens);         // 이벤트 발생
        return true;
    }

    // -------------------------- Allowance --------------------------
    // 특정 주소가 다른 주소로부터 사용할 수 있는 토큰 조회
    function allowance(address tokenOwner, address spender) public view override returns (uint remaining) {
        return allowed[tokenOwner][spender];
    }

    // -------------------------- Mint --------------------------
    // 오너만 호출 가능, 새로운 토큰 발행
    function mint(address to, uint amount) public onlyOwner {
        _mint(to, amount);
    }

    // 내부에서 실제 발행 처리
    function _mint(address to, uint amount) internal {
        _totalSupply += amount;    // 총 발행량 증가
        balances[to] += amount;    // 토큰 지급
        emit Transfer(address(0), to, amount); // 이벤트 기록 (0 주소 = 발행)
    }

    // -------------------------- Burn --------------------------
    // 호출자 자신의 토큰 소각
    function burn(uint amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance"); // 잔액 확인

        balances[msg.sender] -= amount; // 토큰 차감
        _totalSupply -= amount;         // 총 발행량 감소

        emit Transfer(msg.sender, address(0), amount); // 이벤트 기록 (0 주소 = 소각)
    }
}

 
 
코드 설명
ERC-20 인터페이스

interface ERC20Interface {
    function totalSupply() external view returns (uint);
    function balanceOf(address tokenOwner) external view returns (uint balance);
    function allowance(address tokenOwner, address spender) external view returns (uint remaining);
    function transfer(address to, uint tokens) external returns (bool success);
    function approve(address spender, uint tokens) external returns (bool success);
    function transferFrom(address from, address to, uint tokens) external returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
  • interface : 함수의 형식만 정의, 구현은 없음
  • external : 외부에서만 호출 가능, 컨트랙트 내부 호출은 안됨
  • view : 읽기 전용 함수, 상태 변수 변경 불가
  • event : 블록체인 로그 이벤트
  • indexed : 필드를 검색 가능하게 만듦 

상태 변수

string public symbol;
string public name;
uint8 public decimals;
uint public _totalSupply;
address public owner;

mapping(address => uint) balances;
mapping(address => mapping(address => uint)) allowed;
  • symbol, name, decimals : 토큰 정보
  • _totalSupply : 전체 발행량
  • owner : 민트(Mint) 권한자
  • mapping(address => uint) balances : 각 주소의 잔액
  • mapping(address => mapping(address => uint)) allowed : allowed[A][B] = B가 A의 토큰을 쓸 수 있는 허락량
  • public : 자동으로 getter 함수 생성

Modifier

modifier onlyOwner() {
    require(msg.sender == owner, "Only owner");
    _;
}
  • modifier : 함수를 조건부로 실행하도록 제한
  • msg.sender : 함수를 호출한 지갑 주소
  • require : 조건이 참인지 확인, 거짓이면 트랜잭션 revert
  • _ : 실제 함수 코드가 들어갈 자리
  • 적용 Ex.
function mint(address to, uint amount) public onlyOwner { ... }

→ owner만 mint 가능


Constructor

constructor() {
    symbol = "THN";
    name = "Thsisno Token";
    decimals = 2;
    owner = msg.sender;
    _mint(owner, 100000);
}
  • constructor() : 배포 시 한 번만 실행
  • 초기값 설정 + 초기 토큰 발행
  • _mint(owner, 100000) : 배포자에게 초기 토큰 1000 지급

Total Supply

function totalSupply() public view override returns (uint) {
    return _totalSupply;
}
  • override : 인터페이스(ERC20Interface)의 함수를 구현한다는 의미
  • view : 상태 변경 없이 읽기 전용
  • returns(uint) : 반환값 타입

BalanceOf

function balanceOf(address tokenOwner) public view override returns (uint balance) {
    return balances[tokenOwner];
}
  • 특정 주소의 토큰 잔액 조회
  • view : 상태 변경 없음

Transfer

function transfer(address to, uint tokens) public override returns (bool success) {
    require(balances[msg.sender] >= tokens, "Insufficient balance");
    balances[msg.sender] -= tokens;
    balances[to] += tokens;
    emit Transfer(msg.sender, to, tokens);
    return true;
}
  • 토큰 전송 함수
  • require : 잔액 확인
  • emit Transfer(...) : 전송 이벤트 발생
  • 반환값 bool success : 성공 여부

Approve

function approve(address spender, uint tokens) public override returns (bool success) {
    allowed[msg.sender][spender] = tokens;
    emit Approval(msg.sender, spender, tokens);
    return true;
}
  • 다른 지갑에게 토큰 사용 허락
  • allowed에 저장 → 나중에 transferFrom에서 사용

TransferFrom

function transferFrom(address from, address to, uint tokens) public override returns (bool success) {
    require(balances[from] >= tokens, "Insufficient balance");
    require(allowed[from][msg.sender] >= tokens, "Allowance exceeded");
    balances[from] -= tokens;
    allowed[from][msg.sender] -= tokens;
    balances[to] += tokens;
    emit Transfer(from, to, tokens);
    return true;
}
  • transferFrom : 허락받은 토큰 전송
  • allowed[from][msg.sender] 체크
  • ERC-20 표준에서 DeFi 계약에서 토큰 사용할 때 필수

Allowance

function allowance(address tokenOwner, address spender) public view override returns (uint remaining) {
    return allowed[tokenOwner][spender];
}
  • 특정 주소가 다른 주소로부터 사용할 수 있는 토큰 조회
  • view → 읽기 전용

Mint

function mint(address to, uint amount) public onlyOwner {
    _mint(to, amount);
}

function _mint(address to, uint amount) internal {
    _totalSupply += amount;
    balances[to] += amount;
    emit Transfer(address(0), to, amount);
}
  • mint : owner만 새로운 토큰 발행
  • _mint : 내부 함수 (internal)
    • internal → 컨트랙트 내부에서만 호출 가능
    • 외부 호출 불가
  • _totalSupply 증가 + 토큰 지급 + 이벤트 발생
  • address(0) : 발행/소각 표기용

Burn

function burn(uint amount) public {
    require(balances[msg.sender] >= amount, "Insufficient balance");
    balances[msg.sender] -= amount;
    _totalSupply -= amount;
    emit Transfer(msg.sender, address(0), amount);
}
  • 호출자 자신의 토큰 소각
  • _totalSupply 감소
  • 이벤트 발생 → 블록체인에서 소각 기록 확인 가능

Solidity 키워드/문법 

public외부에서 접근 가능 + 자동 getter 생성
private외부 접근 불가, 내부에서만 사용 가능
internal내부/상속 컨트랙트에서만 사용 가능
external컨트랙트 외부에서만 호출 가능
view상태 변수 변경 없이 조회만 가능
override상속/인터페이스 구현 시 필수
modifier함수 호출 조건/권한 제한
require조건 만족 안하면 트랜잭션 revert
emit이벤트 기록
constructor()배포 시 한 번만 실행되는 함수
msg.sender함수를 호출한 지갑 주소
address(0)특별한 0주소, 발행/소각 표시용