Open Zeppelin을 활용한 이더리움 ERC20 기반 ICO 진행

Eun Woo Nam
RayonProtocol
Published in
13 min readJun 19, 2018

2013년 비탈릭 부테린이 처음으로 백서를 공개한 이후 지속적이고 활발한 개발 활동을 통해, 이더리움은 블록체인 생태계의 주축으로 자리 매김하고 있다. 따라서 많은 ICO 프로젝트들이 이더리움을 ERC20 규격에 따라 신규 코인을 발행하고 있다.

truffle은 이더리움 스마트 컨트랙트 통합개발환경이고 Open Zeppelin은 주로 ICO 기능을 포함하는 이더리움 라이브러리 프로젝트이다.

그럼 지금부터 truffle과 open zeppelin를 이용하여, 이더리움 테스트넷에서 ICO를 진행해보겠다.

이 과정을 통해, 새로운 ERC20 규격의 코인 ‘MyCoin’을 이더리움 블록체인을 통해 등록 및 발행하고, 클라우드세일 스마트 컨트랙트에 이더가 전송되면 즉시 MyCoin을 신규로 발행하여 클라우드세일 참여자에게 전송해주게 된다.

환경 설정

메타마스크 설치

이더나 트랜잭션의 전송을 위해서 우리는 메타마스크를 사용한다. 메타마스크는 크롬 웹스토어에서 설치가 가능하다.

# 트러플 설치/초기화
npm i -g truffle
mkdir my-ico && cd my-ico
truffle init
# yarn 초기화
yarn init -y
yarn add -E zeppelin-solidity
yarn add truffle-hdwallet-provider

우선 전역적으로 트러플을 설치하고 ico를 위한 폴더를 만들어 해당 폴더 내에서 트러플을 초기화한다. init 키워드를 사용하면 contracts, migrations, test 폴더와 truffle.js, truffle-config.js가 만들어진다.

다음으로 yarn init으로 초기화 이후 zeppelin-solidity와 truffle-hdwallet-provider를 설치해준다.

  1. zeppelin-solidity 설치 시 -E 의 옵션을 설정하면,컨트랙트에서 “zeppelin”의 경로로 직접 import가 가능해진다.
  2. npm의 파이썬 버전이 3 이상이면 open zeppline의 설치가 제대로 되지 않으므로, 버전 에러 발생 시 아래의 코드입력하자
$ npm config set python python2.7

각 모듈의 사용법과 기능에 대해서는 다음 챕터에서 설명하도록 한다.

스마트 컨트랙트 구현

토큰 — MyCoin

우리가 작성한 MyCoin의 구조는 다음과 같다. ERC20Basic에서 부터 MintableToken까지, 모두 open zeppelin에서 제공해주는 코드들이며, 이를 상속받아 custom coin을 구현하게된다. 따라서 우리는 복잡한 validation이나 구현등을 배제하고, 코드 몇 줄 만으로 custom token을 만들 수 있다.

  1. ERC20Basic(interface) : 총량, 주소별 잔고, 전송 규격
  2. ERC20(interface) : 전송 위임 승인, 전송 위임 용량 확인
  3. BasicToken : ERC20Basic 구현
  4. StandardToken : ERC20 구현
  5. MintableToken : 발행 종료 전까지 토큰 발행, 총량에 추가
  6. Ownerble : 컨트랙 소유자 기록, 기본적인 인증 수행

소스 코드는 다음과 같다. 필수적인 구현은 name, symbol, decimals로써 전송, 위임등의 사항들은 모두 부모 컨트랙트가 가지고 있으므로 복잡한 구현이 필요없다. 단 코드 3줄로 사용자 정의 토큰을 만들어냈다.

이렇게 작성한 코드는 my-ico/contract 폴더 아래에 생성하도록 한다.

토큰 세일 — MyCrowdsale

MyCoinCrowdSale 역시 open zeppelin의 CrowdSale을상속받아 생성된다. 부모 컨트랙트의 생성자를 호출함으로써 내부 변수를 설정하여 컨트랙 생성하게 되는 것이다.

  1. Crowdsale : 토큰, 비율, 지갑을 입력으로 생성, buyTokens 호출시 기존 토큰 지급
  2. TimedCrowdsale : 기간 제한 기능 추가
  3. MintedCrowdsale : buyTokens 호출 시 토큰 발행해서 지급

앞서 설명했듯이 MyCoinCrowdsale의 생성자는 다음의 인자들을 값으로 받아 Crowdsale을 진행한다.

  1. openingTime : Crowdsale 시작 시간, 현재 시간보다 빨라서는 안된다.
  2. closingTime : Crowdsale 마감 시간
  3. rate : 이더, 토큰의 교환비. 예를 들어 rate가 5인 경우 이더와 토큰은 5:1의 교환비를 가진다.
  4. wallet : 투자자로부터 받은 이더가 전송될 지갑의 주소이다.
  5. token : 사용자가 생성한 커스텀 토큰 컨트랙트의 주소이다.

이 코드 역시 my-ico/contract 폴더 아래에 생성한다.

스마트 컨트랙트 배포

트러플에서의 배포를 위해 migrations 폴더내에 세팅이 필요하다. migrations 폴더의 파일들은 1번 부터 순차적으로 배포된다. 1번은 기본 설정 파일이므로 이후에 동작할 2_depoy_contract.js 파일을 생성하도록하자.

소스 코드는 위와 같다. 우선 MyCoinCrowdsale에 인자로 사용될 MyCoin을 먼저 배포한다. 이후에 시작시간, 마감시간, 교환비, 지갑 등을 변수에 할당하고 MyCoinCrowdsale을 배포해야한다.

파라미터들은 MyCoinCrowdsale의 인자와 동일하다. 추가로 start time의 동작 및 시간을 확인하기 위해 로그를 출력시켰다.

  1. startTime : Crowdsale 시작 시간, 현재 시간보다 빨라서는 안된다.
  2. closeTime : Crowdsale 마감 시간
  3. rate : 이더, 토큰의 교환비. 예를 들어 rate가 5인 경우 이더와 토큰은 5:1의 교환비를 가진다.
  4. wallet : 투자자로부터 받은 이더가 전송될 지갑의 주소이다.

시작 시간은 현재 시간보다 빠르게 시작하면 require를 만족하지 않기 때문에, 임의의 숫자를 더해, 배포 시간 기준 약 3분 후에 시작하도록 설정했다.

배포 네트워크 설정

테스트넷에 배포를 하기 위해서는 Ropsten의 풀 노드가 필요하다. 그러나 풀 노드를 가져오기 위해서는 상당한 시간과 용량이 필요하므로, 이를 api로 제공해주는 Infura라는 서비스를 이용할 것이다. Infura을 사용해야하므로, www.infura.io에 접속해서 가입을 진행한다.

Infura 메인, Get strated for free를 클릭하면 가입된다.

홈페이지에서 ‘Get started from free’ 버튼을 클릭하면 가입 양식이 나오는데, 이메일과 기본 인적사항 등이다. 필수 사항을 입력하고 완료 버튼을 클릭하자.

메일로 전송된 api 키

조금 기다리면 가입 시 입력한 메일로 위와 같이 API키가 전송된다. 테스트넷 네트워크 설정에 사용되므로 기록해두고, 이후 나오는 소스 코드에 입력해주자.

infura의 API키와 배포할 때 사용될 계정의 니모닉을 변수에 저장한다. 그리고 네트워크 프로퍼티를 development(개발 노드)와 Ropsten(테스트넷)설정해준다.

  1. provider: HD wallet이 가능한 web3를 제공한다. 이를 사용해 12개 니모닉에서 파생된 주소에 대한 트랜잭션을 서명한다.
  2. gas: gas limit
  3. gasPrice: gas의 가격
  4. network_id : 네트워크 고유의 id 이다. 메인넷은 1, kovan은 2, ropsten은 3, rinkeby는 4의 id 값을 가진다.

이렇게 정의된 값은 truffle의 — network 옵션으로 설정이 가능하다

실제 메인넷에 배포하기 위한 코드를 작성할 때,니모닉과 API키는 외부에 노출되면 안되므로, 코드내부에 적는 것이 아니라 반드시 보안이 철저하게 관리되어야한다.

# 컴파일 및 배포
truffle compile && truffle migrate --network ropsten

모든 설정이 완료되었으면 컨트랙트를 컴파일 후에 배포해보도록 하자. 위의 명령어를 입력하면 truffle에서 컴파일 후에 테스트넷으로 배포가 진행된다.

이때 주의할점은 배포 수수료 지불을 위해 계정에 일정량의 이더가 존재해야한다. 이더가 부족한 경우 Ropsten faucet(http://faucet.ropsten.be:3001/)에서 무료로 지급받을수 있다.

Writing artifacts to ./build/contractsUsing network 'ropsten'.Running migration: 1_initial_migration.js
Deploying Migrations...
... 0x065a670d4d13f1aab7e8404b63c959cee5424529418059e0eab4a27ca6b0102d
Migrations: 0x22d3ae685bdf627bdd4bcb2ede6f5eafb586a8b2
Saving successful migration to network...
... 0xd53b7a95a4451f43c2df59ce6ea1fec3b18d1b220db0893576420e03470d9a3d
Saving artifacts...
Running migration: 2_deploy_contracts.js
Running step...
Deploying MyCoin...
... 0x012f05d5b933bf6536339fe8c5ec7b7eb7fb95906de8be148d678125b8a0431a
MyCoin: 0x58155c5f41f71511e64179d73559fa323a2ccbe9
startTime 1529372295
Deploying MyCoinCrowdsale...
... 0x5821140355321f2d1ea6286c7017540b2b32f84be0d5a471bbfae4183e9e6cb3
MyCoinCrowdsale: 0x5d2c1f918816c2938c13dafe52cc5c4c7dbd3983
Saving successful migration to network...
... 0xc627381d4628962adb02d976674f09d472173aaff285fc510a795873ea7f552f
Saving artifacts...

결과는 다음과 같다. Bold 처리된 텍스트가 배포된 컨트랙트의 주소이다(위 — Coin, 아래 — CoinCrowdsale). 이 주소들은 내부 함수를 호출하기 위해 사용되므로, 꼭 기록해두기 바란다.

클라우드 세일 운영

MyCoin의 owner를 MyCoinCrowdsale로 지정

MyCoinCrowdsale은 MintedClowdsale을 상속받았고, 이 클래스의 _deliverTokens 메소드에서 토큰을 on-demand로 신규 발급하여 전송하게 되어 있다. 이 때 MyCoin.mint를 호출해야 하는데, 그렇게 하기 위해서는 MyCoin의 owner가 MyCoinClowdsale이어야 한다. 따라서 MyCoin의 owner를 변경해야 한다.

마이이더월렛(https://www.myetherwallet.com/)에 접속하여 Contract 탭에 들어간다.

메타마스크 연결

다음으로 메타마스크의에서 스마트 컨트랙트를 배포한 계정을 선택하고, 마이이더월렛에서 메타마스크를 연결한다. 이로써 마이 이더 월렛과 메타마스크가 연결되고, 내부 함수를 호출할 수 있는 상태가 되었다.

컨트랙트 정보 입력

앞서 기록해 두었던 MyCoin의 컨트랙트 주소와 myCoin/build/MyCoin.json의 abi값을 입력한 후 Access를 클릭한다.

transfer ownership

다음으로 Crowdsale에게 토큰의 발행권을 위임하기 위해 transferOwnership을 실행한다. 제일 하단에 있는 박스를 보면 Read/Write Contract가 있는데, 여기서 transferOwnership을 선택하고, _newOwner에 MyCoinCrowdSale의 주소를 적는다.

다음으로 함수를 transferOwnership을 선택한 후 새로운 Owner의 주소를 입력한다. 트랜잭션이 성공적으로 적용된다면 Owner는 입력해준 컨트랙트의 주소로 변경될 것이다.

ICO 참여

메타마스크를 가상의 투자자 계정으로 변경

메타마스크를 돈을 보내는 가상의 투자자 계정으로 변경한다. 여기서 SEND 버튼을 클릭하면 이더 전송이 가능하다.

MyCoinCrowdsale의 주소와 보낼 이더의 양을 입력한 후 Next를 클릭한다. 테스트를 위해 0.2ETH를 전송했다. 교환비가 5대 1이므로 토큰은 1개가 전송될 것이다.

전송 트랜잭션은 메타마스크에서 확인할 수 있으며 (…)과 그림으로 완료 여부를 확인할 수 있다.

마이이더월렛을 이용하지 않는 이유는, 내부적으로 fallback 함수가 구현되어 있어서 메타마스크로 보내게 되는 경우라도 buyTokens 함수가 동작하기 때문이다.

결과

토큰 추가

토큰 전송 트랜잭션이 완료된 후, 메타마스크의 Token 탭을 누르면 나오는Add Token 버튼을 클릭해 커스텀 토큰을 추가할 수 있다. MyCoin 컨트랙트의 주소를 입력하면 자동으로 Symbol과 decimal이 입력된다.

결과

결과적으로 0.2ETH를 보냈을 때, 5:1의 교환비로 1MCO가 가상투자자의 계정으로 도착했다.

여기까지 테스트넷에 배포하는 ICO의 과정을 살펴보았다.

--

--