본문 바로가기

코인/코인 개발

ERC-20 토큰(Fungible Token) 직접 만드는 방법

EIP(Ethereum Improvement Proposal)-20에 의해 다음과 같은 함수와 이벤트를 구현한 스마트 컨트랙트를 ERC-20 컨트랙트라고 합니다.

 

function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
function totalSupply() public view returns (uint256)
function balanceOf(address _owner) public view returns (uint256 balance)
function transfer(address _to, uint256 _value) public returns (bool success)
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
function approve(address _spender, uint256 _value) public returns (bool success)
function allowance(address _owner, address _spender) public view returns (uint256 remaining)

event Transfer(address indexed _from, address indexed _to, uint256 _value)
event Approval(address indexed _owner, address indexed _spender, uint256 _value)

 

유니스왑(UNI), 체인링크(LINK), 메이커(MKR), 컴파운드(COMP) 등등 수많은 dApps이 이러한 ERC-20 토큰 표준이 있었기에 이를 바탕으로 아이디어를 구현할 수 있었습니다. 현재 많은 ERC-20 생성기들이 유료로 제공되고 있는 것을 알 수 있는데, 이번 포스팅에서는 직접 ERC-20 컨트랙트를 등록하는 방법을 알아보려고 합니다. 사실 방법만 알고 있다면, 스마트 컨트랙트 등록 시에 발생하는 트랜잭션 비용을 제외하면 굳이 유료를 쓸 필요가 없으니까요.

 

먼저 이더리움 개발자 문서에서 제공하는 구현 제공자가 두 군데가 있습니다. 첫 번째는 OpenZeppelin이고, 두 번째는 ConsenSys인데 방법 자체는 대동소이하니 이번 포스팅에서는 OpenZeppelin을 이용해 RInkeby 테스트넷에 등록하는 예제를 작성해보도록 하겠습니다.

 

 

먼저 준비물은 MetaMask입니다. 설치 및 이용 방법을 모르신다면 이전 포스팅을 참고해주세요.

 

OpenZeppelin 구현은 다음과 같이 GitHub 저장소를 통해 제공되고 있습니다.

 

OpenZeppelin의 Github 저장소에 있는 ERC-20 구현. 출처: https://github.com/OpenZeppelin/openzeppelin-contracts/

 

구조는 다음과 같습니다.

 

  • IERC20.sol - ERC-20 인터페이스
  • ERC20.sol - ERC-20 베이스 구현
  • extensions/*.sol - 토큰 발행, 토큰 소각, 송수신 정지 등등 추가 기능 구현
  • presets/ERC20PresetFixedSupply.sol - 고정 발행량을 사용하는 ERC-20 Preset
  • presets/ERC20PresetMinterPauser.sol - 동적 발행 및 송수신 정지 등의 기능을 포함하는 ERC-20 Preset
  • utils/*.sol - 구현에서 사용할 유틸리티 구현

 

다양한 요구 기능에 대한 구현이 모듈 형태로 제공되어 있어 다양한 조합을 이용해 ERC-20 컨트랙트를 손쉽게 작성, 배포할 수 있습니다. 예제에서는 간단히 ERC20PresetMinterPauser.sol Preset을 사용해 작성해보도록 하겠습니다. 먼저 Remix를 열어 다음과 같은 컨트랙트를 작성합니다.

 

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

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";

contract CoinalimeToken is ERC20PresetMinterPauser("Coinalime Token", "CTK") {
}

 

예제에서 사용한 컨트랙트는 이름을 Coinalime Token으로, 심볼은 CTK를 사용했습니다. 이 부분은 원하시는 이름과 심볼로 적절히 변경하시면 되겠습니다. 또한 Solidity 0.8.0을 사용해 예제를 작성했는데, 이는 OpenZeppelin 최신 구현체가 사용하는 버전과 같은 버전을 사용하기 위해서 적용한 것이고, 이전 버전에서의 구현도 따로 제공하고 있으니 필요하시다면 import 부분을 변경해주시면 되겠습니다. Solidity 버전별 달라진 점은 Solidity 문서를 참고해주시기 바랍니다. 이제 Remix에서 왼쪽 메뉴 중 두 번째 Solidity Compiler 메뉴를 클릭하면 다음과 같은 화면이 나오는데 Complie 버튼을 눌러 컨트랙트를 컴파일합니다.

 

컨트랙트 컴파일. 출처: Remix

 

Optimization 옵션 사용 시 Opcodes 최적화를 통해 트랜잭션 비용을 절감할 수 있는 효과가 있으므로 필수적으로 사용할 것을 권장합니다. 컴파일을 마치고 나면 이제 배포가 가능한데 여기서는 Rinkeby 테스트넷을 사용해 배포해보도록 하겠습니다. MetaMask에서 Rinkeby 네트워크로 변경하고, 테스트를 위해 Faucet을 제공받아둡니다. Faucet 받는 방법은 이전 포스팅을 참고 부탁드립니다.

 

Remix에서 왼쪽 메뉴 중 세 번째 Deploy & Run Transactions 메뉴를 클릭하면 다음과 같은 화면이 나오는데 Injected Web3 환경을 선택하고 MetaMask와 연결한 뒤, Deploy 버튼을 눌러 컨트랙트를 배포합니다. MetaMask에서 트랜잭션 수행 여부를 묻는데, 승인(Confirm)하고 나면 트랜잭션은 mempool로 들어갑니다. 트랜잭션이 정상적으로 블록에 올라가면 아래 화면에서 맨 아랫부분 'Deployed Contracts' 목록에 배포된 컨트랙트가 나타납니다.

 

컨트랙트 배포. 출처: Remix

 

해당 컨트랙트를 열어보면 정말 많은 기능이 포함되어 있는 것을 확인하실 수가 있을 텐데, 이러한 토큰에 대한 다양한 기능을 바탕으로 dApp 개발에만 집중할 수 있을 것입니다. 또한 현재 정말 많은 ERC-20 토큰이 발행되어 있는데, 보시다시피 토큰의 발행 자체는 어려운 일이 아니고, 이를 바탕으로 어떤 애플리케이션을 구현하느냐가 핵심인 것을 아실 수 있을 것입니다.