ブロックチェーンにはトークンという概念があります。英語では「Token」と言い、証拠、記念品、代用貨幣、引換券などの意味があります。ブロックチェーンにおける「トークン」は、代用貨幣や引換券という意味が特に強く、仮想通貨そのものとはまた違う立ち位置のものとして扱われています。
今回は、Solidity言語のフレームワークである「Truffle」と、ライブラリの「OpenZeppelin」を使って、Ethereum上で独自のトークンをつくってみたいと思います。
目次
トークンとは
トークンとは、冒頭に出てきた「引換券」という言葉を使うと、「ブロックチェーン上で発行された、何かしらの価値と交換できる引換券」と表現できます。トークンの定義自体、想像がしにくく非常に曖昧なものなので、世間一般的には「ブロックチェーン上で発行された独自コイン」のことを皆「トークン」と呼んでいます。
ビットコインのBTC、EthereumのEtherなど、よく知られている仮想通貨は「トークン」とは呼びません。例えば、ビットコイン上で発行されるBTC以外のもの、Ethereum上で発行されるEther以外のものをトークンと呼びます。仮想通貨とトークンの違いを簡単に表現すると、
- 仮想通貨: 新規のブロックチェーンで発行される基本となる通貨
- トークン: 既存のブロックチェーンで発行されるもの
と言えます。
トークンの最大のメリットは、やはりそのお手軽さです。仮想通貨のように、1から新しいブロックチェーンを構築する必要はなく、既存のブロックチェーンの上で発行すればよいので、発行手数料はかかりますが基本的にそれ以外のコストはほとんどかかりません。
ERC20トークンとは
※2018/6現在、ERC20トークンに重大な脆弱性が発見されています。ただし、これはEthereumやERC20のバグではなく、ERC20に準拠して実装された一部のトークンに発見されているバグとのことです。
Ethereum上で発行するトークンには標準規格があります。ERC20はEthereum Requests for Comments:Token Standard #20の略で、Ethereum上で発行するトークンの仕様を定義したものです。ERC20に準拠したトークンを実装すると何が嬉しいのかというと、
- 準拠したトークン同士はインターフェースが共通となるので、
新しいトークンがどんどん増えていったとしても管理が楽 - ERC20に準拠したサードパーティ製のウォレットなどが使える
という点が挙げられます。
今回は、「Truffle」というSolidityのフレームワークと、ERC20トークンの実装が含まれている「OpenZeppelin」というスマートコントラクト開発の便利なライブラリを使って、独自のERC20トークンを実装してみたいと思います!
ERC20トークンを実装してみる
Node.jsをインストールする
Truffleをインストールする際にnpmを使いたいので、まずNode.jsをインストールします。Node.js公式サイトから実行ファイルをダウンロードし、ローカル環境にNode.jsをインストールします。インストールが完了したら、–versionオプションでバージョン情報を確認しましょう。2018/6現在の最新バージョン(LTS)はv8.11.3です。
1 2 |
> node --version v8.11.3 |
Truffleをインストールする
Node.jsがインストールできたら、npmコマンドでTruffleをインストールします。インストールが完了したら、「truffle version」コマンドでバージョン情報を確認してみましょう。2018/6現在の最新バージョンはv4.1.11で、同時にSolidity v0.4.24もインストールされました。
1 2 3 4 |
> npm install -g truffle > truffle version Truffle v4.1.11 (core: 4.1.11) Solidity v0.4.24 (solc-js) |
Truffleプロジェクトを作成する
Truffleのインストールも出来たので、早速Truffleプロジェクトを作成したいと思います。任意の場所に、今回作成するトークン用のディレクトリを作成します。作成したら、そのディレクトリに移動しましょう。
1 2 |
> mkdir myToken > cd myToken |
ディレクトリを移動したら、「truffle init」コマンドでTruffleプロジェクトを作成します。
1 |
> truffle init |
treeコマンドでディレクトリの中身を見てみると、自動的に以下のようなディレクトリ、ファイル構成が作成されていることが確認できると思います。
1 2 3 4 5 6 7 8 9 10 11 12 |
> tree /F C:. │ truffle-config.js │ truffle.js │ ├─contracts │ Migrations.sol │ ├─migrations │ 1_initial_migration.js │ └─test |
このように、Truffleを使うと、コントラクトを開発するためのデフォルトセットを自動的に作成してくれます。
OpenZeppelinを導入する
次に、ERC20トークンの土台を提供してくれるOpenZeppelinというライブラリを導入したいと思います。先程作成したTruffleプロジェクトのディレクトリにてnpmコマンドを実行し、OpenZeppelinをインストールします。
1 2 |
> npm init -y > npm install -E openzeppelin-solidity |
独自トークンコントラクトを実装する
TruffleとOpenZeppelinの導入が完了したところで、ついにERC20に準拠した独自トークンを実装していくわけですが、実装する前に「ERC20に準拠」が具体的にどのようなことを意味しているのか、少しだけ触れておきます。
EthereumコミュニティによるThe Ethereum Wikiに「ERC20トークンが実装すべきもの」として、以下のインターフェースが定義されています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// ---------------------------------------------------------------------------- // ERC Token Standard #20 Interface // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md // ---------------------------------------------------------------------------- contract ERC20Interface { function totalSupply() public constant returns (uint); function balanceOf(address tokenOwner) public constant returns (uint balance); function allowance(address tokenOwner, address spender) public constant returns (uint remaining); function transfer(address to, uint tokens) public returns (bool success); function approve(address spender, uint tokens) public returns (bool success); function transferFrom(address from, address to, uint tokens) public returns (bool success); event Transfer(address indexed from, address indexed to, uint tokens); event Approval(address indexed tokenOwner, address indexed spender, uint tokens); } |
要するに、上記の機能やイベントを全部実装してね、というのがERC20トークンのルールです。ただ、これらを全部実装するのは結構大変…ということで、負荷を軽減してくれるのがOpenZeppelinなのです。OpenZeppelinが提供してくれているトークンの実装を継承して独自トークンをつくることにより、実装の手間を省くことができます。それだけでなく、独自のコードに起因する事故を防ぐという点でもOpenZeppelinは有用かと思います。
それでは、独自トークンのコントラクトを作成しましょう。Truffleプロジェクトディレクトリ配下のcontractsディレクトリの下に、以下のMyErc20Token.solを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
pragma solidity ^0.4.24; import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; contract MyErc20Token is StandardToken { string public name = "MyErc20Token"; string public symbol = "TKN"; uint public decimals = 18; function MyErc20Token(uint initialSupply) public { totalSupply_ = initialSupply; balances[msg.sender] = initialSupply; } } |
コード冒頭の「pragma solidity ^0.4.24」では、Solidityコンパイラのバージョンを指定しています。2行目のimport文で、OpenZeppelinライブラリをインポートします。今回はERC20トークンなので、ERC20の実装であるStandardToken.solをインポートしています。
先に書いたとおり、ERC20に準拠するためには、本来もっとたくさんの関数やイベントを実装しなければならないのですが、OpenZeppelinのStandardToken.solを継承していることによって、変更が必要な部分のみ実装すればOKです。今回は、3つの状態変数(name、symbol、decimals)とコンストラクタのみ実装しています。
構成ファイルを編集する
ここまでで独自トークンの実装自体はほぼ終わりましたが、TruffleプロジェクトにはいくつかのJavaScriptファイルが含まれており、これらのJavaScriptファイルに環境情報やデプロイ動作を定義することで、より楽~にコントラクトをデプロイすることができます。
truffle.jsの編集
TruffleのConfigファイルであるtruffle.jsを編集します。Truffleプロジェクトディレクトリ直下にあるtruffle.jsを以下のように編集します。
1 2 3 4 5 6 7 8 9 10 11 12 |
module.exports = { // See <http://truffleframework.com/docs/advanced/configuration> // to customize your Truffle configuration! networks: { development: { host:"localhost", port:8545, network_id: "*", gas:4600000 } } }; |
networksブロックの配下に、今回は「development」という名前で環境情報を定義します。これはコントラクトをデプロイするブロックチェーンの接続情報です。「network_id」は一意の値を指定してもよいですし、上記の例のように「*」(すべてのネットワークID)とすることも可能です。また、「gas」として定義しているのは、トランザクション実行時に必要な手数料で、Gas Limit(Gasの上限)と言われるものです。
今回は「development」のみとしていますが、複数の環境情報を定義することも可能です。
デプロイ用JavaScriptの作成
次に、migrationsディレクトリ配下に、以下の2_deploy_contracts.jsを作成します。
1 2 3 4 5 6 |
var MyErc20Token = artifacts.require("./MyErc20Token.sol"); module.exports = function(deployer) { let initialSupply = 50000e18; deployer.deploy(MyErc20Token, initialSupply); }; |
「initialSupply」として定義している値は、MyErc20Tokenのコンストラクタ引数となる値で、トークンの初期化に使用されます。ちなみにファイル名に付番されている番号にはちゃんと意味があり、この番号の順番でmigrationsディレクトリ下のJavaScriptファイルが実行されていきます。migrationsディレクトリにはもう既に「1_initial_migration.js」が存在するので、これが実行された後、今回作成したファイルが実行されます。
コントラクトをデプロイする
それでは、ついにコントラクトをデプロイしてみたいと思います。まずはデプロイ先のGethを起動します。eth.accounts[0]のロックを解除し、更にマイニングも開始しておきます。
1 2 3 |
> geth --rpc --networkid 10 --nodiscover --datadir ./eth_private_net console 2>>./eth_private_net/geth.log > personal.unlockAccount(eth.accounts[0]) > miner.start() |
次に、Truffleプロジェクトディレクトリで「truffle migrate」コマンドを実行します。
1 |
> truffle migrate |
これでデプロイは完了です!コマンド1つでデプロイできるなんて、Truffleすごいです。
ちなみに、truffle.jsに定義しているネットワーク情報の中からデプロイ先を指定したい場合は、「truffle migrate –network [ネットワークID]」を実行してください。(例えば「truffle migrate –network development」)
また、同じ名前のコントラクトが既にデプロイされている場合には、migrateの後ろに「–reset」を付加すれば再デプロイ可能です。
Truffleコンソールでコントラクトを実行する
Truffleが提供するコンソールで、先程デプロイしたコントラクトを操作することができます。「truffle console」コマンドでTruffleコンソールを起動します。
1 |
> truffle console |
以下のように、「myToken」オブジェクトを取得し、このオブジェクト経由でコントラクトを操作します。
1 |
> myToken = MyErc20Token.at(MyErc20Token.address) |
まずは状態変数を取得してみます。コントラクトで定義した値や、コンストラクタ引数として指定した初期値が返却されることを確認できます。
1 2 3 4 5 6 7 8 |
> myToken.name() 'MyErc20Token' > myToken.symbol() 'TKN' > myToken.decimals() BigNumber { s: 1, e: 1, c: [ 18 ] } > myToken.totalSupply() BigNumber { s: 1, e: 22, c: [ 500000000 ] } |
transfer関数を使って送金をしてみます。まずはeth.accounts[0]の残高を照会します。まだ何もしていないので、初期値の50000TKNが返却されます。
1 2 3 4 5 |
)> web3.eth.accounts [ '0x49b7e521797d797b0225d52dd87f165c0f0cba2c', '0x7106e57b2aba38e55db29bd4a6f13fa3f53d0a14' ] > myToken.balanceOf("0x49b7e521797d797b0225d52dd87f165c0f0cba2c") BigNumber { s: 1, e: 22, c: [ 500000000 ] } |
それではeth.accounts[0]からeth.accounts[1]へ1000TKN送金してみます。送金前に、eth.accounts[0]のロックが解除されていることを確認してください。
1 |
> myToken.transfer("0x7106e57b2aba38e55db29bd4a6f13fa3f53d0a14", 1000) |
eth.accounts[0]とeth.accounts[1]の残高をそれぞれ確認してみます。eth.accounts[0]の残高が1000TKN減っていることが確認できます。
1 2 3 4 |
> myToken.balanceOf("0x49b7e521797d797b0225d52dd87f165c0f0cba2c") BigNumber { s: 1, e: 22, c: [ 499999999, 99999999999000 ] } > myToken.balanceOf("0x7106e57b2aba38e55db29bd4a6f13fa3f53d0a14") BigNumber { s: 1, e: 3, c: [ 1000 ] } |
まとめ
長くなってしまいましたが、今回はTruffle+OpenZeppelinでERC20トークンを実装してみました。
今回作成した独自トークンはかなり簡単な内容ではありましたが、OpenZeppelinを使うことによって実装自体はかなり楽することができました。また、Truffleを使うことによって、多少構成ファイルを作成・編集する必要はありますが、デプロイが1コマンドで出来るようになり、煩わしさが大きく軽減されます。動作確認時に使用したTruffleコンソールも使いやすく、個人的にはかなりお気に入りです。
ブロックチェーン界では「トークン」はとても基本的な概念になりますので、コントラクト開発に興味がある方は、ぜひぜひTruffle+OpenZeppelinの良さも体験してみてください。
執筆者プロフィール
- 社内の開発プロジェクトの技術支援や、新技術の検証に従事しています。主にアプリケーション開発系支援担当で、Java&サーバサイドが得意です。最近は、サーバーレスonAWSを推進しています。