どうもこじりょー(@kojiryoinvestor)です。
この記事はEthereum開発入門として、初心者の方に向けたシンプルなERC20トークンの作成を行うチュートリアルです。
シンプルなERC20トークンの作成を通じて、以下のこと学んでいきます。
・Ethereumの開発環境の構築(Truffle, Ganache)
・OpenZepplinを利用したERC20トークンの作成
それでは始めていきましょう!
ERC20トークンとは?
EIP20
トークンの送受信はすべてスマートコントラクト上で行われています。
スマートコントラクトは誰でも自由に作ることができるため、好きなようにトークンの仕組みを作ることができます。
しかし誰もが好きなように作ると、異なるトークン同士のやりとりが難しくなります。
そこで異なるトークン同士が簡単に相互運用できるようトークンを標準化する仕様の1つが「EIP20」です。
Ethereumベースのトークンのインターフェースの標準「ERC20」を提案したEIP20はFabian Vogelsteller氏とVitalik Buterin氏によって2015年11月に提出され、2017年9月に採用が決定しました。
Ethereumベースのトークンの標準ERC20 | Blockchain Biz
「EIP20」はイーサリアムコミュニティで公式に確立された仕様です。
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
その「EIP20」の仕様に沿って作成されたトークンを「ERC(Ethereum Request for Comments)20」と言います。
Ethereum開発環境の構築
node.jsとnpmのインストール
Ethereum開発で利用するツール等はJavaScript用のパッケージマネージャ「npm」を使って各プロジェクトにインストールしていきます。
まずは、公式サイトからnodejsとnpmをインストールしましょう。
Truffleのインストール
「Truffle」はEthereum上でスマートコントラクトの開発に必要なツールがまとまったフレームワークです。
npmのインストールが終わったらtruffleをインストールします。
$ npm install -g truffle
truffleのインストールが正常にできていれば、以下のコマンドを入力してバージョンを表示させることができます。
$ truffle version
Truffle v5.0.0 (core: 5.0.0)
Solidity v0.5.0 (solc-js)
Node v10.14.1
Ganacheのインストール
「Ganache」は「自分のPC内だけで動くイーサリアムネットワーク」またの名を「プライベート・ネットワーク」を簡単に構築してくれるツールです。

もちろん「Geth」などの「イーサリアムクライアント」でもプライベートネットを構築することはできます。
ただGanacheの方が、Gethで手間がかかる点(アカウントロックの解除、アカウントの作成..etc)を省力してくれるので開発に集中できます。
Ganacheは公式サイトからインストールすることができます。
VScodeのインストール
スマートコントラクトの開発はテキストエディタとターミナルの両方を利用する機会が多いです。
画面を切り替えて両者を交互に利用するのは地味に面倒です。
そこでテキストエディタ内でターミナルも表示することができる「VSCode」がおすすめです。

Visual Studio Code | Microsoft
ERC20トークンの作成
プロジェクトディレクトリ
まずはじめにトークンを作成するためのプロジェクトを作成しましょう。
お好きな名前でディレクトリを作成してください。
ここでは「simple」という名前でディレクトリを作成しました。
$ mkdir simple

ディレクトリを作成したらターミナルからディレクトリに移動して、Truffleで初期化を行います。
$ cd simple
$ truffle init
「truffle init」を実行すると開発に必要なディレクトリとファイルが自動で作成されます。


コントラクト
ERC20トークンの仕様
EIP20の仕様に沿ったトークンの作成が「ERC20トークン」と言いました。
つまりERC20トークンの作成とは、EIP20の仕様に沿ったコントラクトを作成するということです。
ERC20トークンの作成には以下のメソッドを実装します。
//供給されたトークンの合計を返す
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)
//_spenderの残高から_valueの量までトークンを引き落とすことを許可する
//簡単に言うとERC20トークンで小切手を実現する関数
function approve(address _spender, uint256 _value) public returns (bool success)
//_spenderから引き出せるトークンの量を返す
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
//トークンが送信された情報をログに表示する
event Transfer(address indexed _from, address indexed _to, uint256 _value)
//approve()が実行された内容をログに表示する
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
ERC20トークンの作成は以上のメソッドと、それらを利用したロジックを実装する必要があります。
これらを全て一から実装することもできますが、ここでは「OpenZeppelin」というライブラリを利用していきます。
OpenZepplinのインストール
「OpenZepplin」は、ERC20などの再利用可能なコントラクトを提供しているフレームワークです。
一からセキュアなプログラムを開発しなくても、「OpenZepplin」上のコードを再利用することで効率的にトークンの作成を行うことができます。
「OpenZepplin」はnpmを利用してプロジェクトにインストールします。
ターミナルからプロジェクトディレクトリに移動して、下記のコマンドを実行します。
//パッケージJSONを作成
$ npm init -f
//OpenZeppelinのインストール
$ npm install openzeppelin-solidity
上記のコマンドを実行すると、プロジェクトディレクトリ内に「node_modules」というディレクトリが自動で作成され、その中に「openzeppelin-solidity」がインストールされています。

コントラクトファイルの作成
まずは「contracts」ディレクトリ内にファイルを新規作成します。
お好きな名前で作りましょう。
ここではファイル名を「SimpleToken.sol」として作成しています。

続いてコントラクトの中身を以下のように作成します。
pragma solidity ^0.5.0;
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol";
contract SimpleToken is ERC20, ERC20Detailed {
}
まずはじめに、コントラクトが動作するSolidityのバージョンを指定します。
その次の「import」では、「OpenZepplin」の「ERC20.sol」と「ERC20Detailed.sol」というプログラムを利用できるように読み込みます。
クラスを継承するには「コントラクト名 is 継承の参照」という形で定義します。
ERC20.sol
「ERC20.sol」はEIP20で定義されたメソッドおよび、それらを利用したトークン処理のロジックが作成されています。
ERC20.sol | openzeppelin-solidity
ERC20Detailed.sol
「ERC20Detailed.sol」はトークンの詳細を定義することができるコントラクトです。
定義できる内容は以下の3つです。
・name (トークンの名前)
・symbol (トークンの単位)
・decimals (トークンの最小単位)
ERC20Detailed.sol | openzeppelin-solidity
コントラクトのロジック作成
まずはコントラクトの「constructor」を以下のように定義します。
「constructor」はコントラクトがデプロイされたときに一度だけ呼び出される関数です。
ここでの「constructor」は、トークンの名前(name)、シンボル(simbol)、最小単位(decimals)、初回に発行するトークンの量(initSupply)を引数で受け取ります。
受け取ったname・simbol・decimalsを「ERC20Detailed」のconstructorに渡して、トークンの詳細をブロックチェーンに記録します。
最後に「ERC20.sol」の「_mint()」を実行します。
「_mint()」はトークンを発行して、指定したイーサリアムアカウントにトークンを送ります。
マイグレーション
マイグレーションはコントラクトのデプロイや、デプロイ時に行いたい処理などを作成できます。
マイグレーションは「migrations」ディレクトリに内に作成していきます。

マイグレーションのファイル名は「番号_作業内容_コントラクト名称」という形でつけることが一般的と言われています。
スクリプトのファイル名は番号から開始し、アンダースコアで作業内容、さらにアンダースコアでコントラクト名称を入れることが一般的です。
加嵜 長門(著)・篠原 航(著)・丸山 弘詩(編)(2018)『ブロックチェーン・アプリケーション開発の教科書』(マイナビ出版)P.231
マイグレーションファイルは以下のようになります。
最小単位(decimals)の設定はトークンの用途によって決めるといいです。
・1トークンが現実世界の何か1つを表すとき、 decimals を 0 に
・小数以下の桁が決まってるとき、decimals をその桁数に
・どちらでもないとき、細かくトークンを使えるように decimalsを18に
ここでは特に用途は決まっていないのでETHと同じ「1ST = 1×10の18乗」としています。
またトークンの初回発行量は「100ST」としています。
Web3.jsでは巨大な数字を扱う際は「bn.js」ライブラリを利用していますので、以下のように「bn.js」のインスタンスに変換しています。
const initSupply = web3.utils.toBN(100*(10**decimals));
https://web3js.readthedocs.io/en/1.0/web3-utils.html#tobn
テスト
テストコードの作成
作成したコントラクトの動作を検証する「テストコード」を作成していきます。
テストコードは「test」ディレクトリ内に作成します。
テストコードのファイル名は分かりやすいようにコントラクトの名前にしておきましょう。

コントラクトのテストコードは以下のようになります。
上記のテストコードでは、コントラクトがデプロイされた際に、デプロイしたアカウントアドレスに100STトークン発行されているかどうかを検証しています。
プライベートネットへの接続設定
テストを実行する前に「Ganache」への接続設定をしていきます。
「truffle-config.js」を開き、49~53行あたりを以下のように修正します。
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*",
},
【2019/1/6 追記】openzeppelin@v2.1.1からSolidity@v0.5.0に対応したため、以下の修正は不要です。
続いて92行目付近にある、コンパイルのバージョンを以下のように修正します。
// Configure your compilers
compilers: {
solc: {
version: "0.4.24",
以下省略...
Truffleのv5.0.0からは、solidityのv0.5.0を利用してコンパイルされるようになりました。
しかしこの記事執筆時点(2018年12月時点)では、solidity-v0.5.0でopenzeppelinのコードをコンパイルするとエラーになります。
そのためコンパイルバージョンは「0.4.24」に指定しています。
テストの実行
それでは「Ganache」を起動して、コントラクトのテストを実行していきます。

続いてターミナルからプロジェクトディレクトリに移動し、下記のコマンドでテストを実行します。
$ truffle test
以下のようになればテスト成功です。

作成したERC20トークンの確認
デプロイ
それでは作成したコントラクトをプライベートネット内にデプロイしていきます。
完成したコードはGitHubで公開してありますので、よければご利用ください!
https://github.com/kojimaro/ERC20Tutorial
以下のコマンドを実行することでコントラクトをデプロイすることができます。
$ truffle migrate
成功すると以下のようにターミナル上に表示されます。

コントラクトを動かす
デプロイしたコントラクトを動かしていきましょう。
まずは以下のコマンドを実行してプライベートネットのノードにアクセスします。
$ truffle console
以下のような表示になればノードにアクセスしています。

続いてコントラクトのインスタンスを生成して、instance変数に格納します。
SimpleToken.deployed().then(i=>instance=i)
以下の内容を実行して、トークン名を表示できればインスタンスの生成に成功しています。
instance.name()

次にイーサリアムアカウントを取得して、accounts変数に格納します。
web3.eth.getAccounts().then(a=>accounts=a)
以下の内容を実行して出力されたアドレスが、Ganacheで一番上に表示されているアドレスと同じかどうか確認しておきましょう。
accounts[0]


次にaccounts[0]のトークンの残高を取得して、balance変数に格納します。
instance.balanceOf(accounts[0]).then(b=>balance=b)
続いて単位を変換します。initSupplyで指定したトークンの量が表示されていれば成功です。
web3.utils.fromWei(balance, "ether")

コントラクトのデプロイは、インデックス番号[0]のアカウント(コインベースアカウント)で行われます。
そのため、初回発行のトークンはaccounts[0]のアドレスに付与されています。
終了するには以下のコマンドを実行しましょう。
.eixt
おわりに
以上、シンプルなERC20トークンを作成するチュートリアルでした。
OpenZeppelinを利用すればさらに、トークンの追加発行や処分などが行えるように機能を拡張することができます。
ぜひ次のチュートリアルに進んで、トークンの追加発行の機能を実装してみてください!
[…] 【Truffle5.0対応】シンプルなERC20トークンを作成しよう! 目次 […]
[…] 参考記事:ERC20準拠トークンを作成してRopstenにデプロイする […]
[…] 1. 【Truffle5.0対応】シンプルなERC20トークンを作成しよう! […]
SimpleToken.deployed().then(i=>instance=i)
ここのコードはほかに参考にしたサイトと書き方が違うようですがどういう意味なのでしょうか?
このコードだとうまくいくので気になりました。
ほかのサイトだと
a = SimpleToken.at([“アドレス”])
みたいな感じで書かれていますがこちらは自分の環境では動きません。
ご質問、そして当記事を参考にしていただきありがとうございます!
ご質問のSimpleToken.deployed().then()のところはtruffle-contractを利用して、デプロイしたコントラクトのインスタンス(コントラクトを実際に動かすためのオブジェクト)を非同期で生成しています。
コードの中身はdeployed()を実行した結果をthen()(コールバック関数)で受け取り、まず引数「i」に渡します。
その後、コールバック関数内で引数「i」の中身を変数「instance」に格納しています。
コールバック関数内の関数の書き方を「アロー関数」といいfunctionよりコンパクトな記述ができます。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions
「.deployed()」と「.at()」どちらもデプロイしたコントラクトのインスタンスを取得するのが目的の関数です。
SimpleToken.at()の場合はデプロイしたコントラクトのアドレスが必要です。
デプロイしたコントラクトのアドレスはそれぞれの環境で違うため、もしかしたら引数に指定したコントラクトアドレスが間違っていたかもしれませんね…。
一方でSimpleToken.deployed()はデプロイしたコントラクトアドレスを指定する必要がないので、アドレスのコピペが面倒だった私はこちらを選択しました!
それぞれの詳細はこちらのtruffle-contractのgithubページに英語ですが書いてありますので、ぜひ参考にしてみてください!
https://github.com/trufflesuite/truffle-contract#readme