Note:記事は執筆時点(2018年6月)の内容です。技術の進歩が早く、今後陳腐化する可能性もあるのでご留意ください。
はじめに
- ERC20のオリジナルトークンを作ってみよう
- Solidityでコントラクトを作成して、ローカル環境にデプロイしよう
事前準備
-
必要なもの
- Ubuntu
- Node.js
- Truffle
- 開発用エディタ(Visual Studio Code など)
環境構築がまだの方はこちらから↓
Ethereum開発環境構築:Windows編
参考資料
- 以下の先生の記事を参考しながら、進めます。先生に感謝。
Truffle で始める Ethereum 入門 - ERC20 トークンを作ってみよう
EthereumのERC20 トークンとして社内通貨(風)のものを作ってみます
作業の流れ
- プロジェクトフォルダの作成
- OpenZeppelinのインストール
- Solidityコードの作成
- Solidity コードのコンパイル
- マイグレーションファイルの作成
- コントラクトのデプロイ
- デプロイされたコントラクトの確認
【手順1】プロジェクトフォルダの作成
- シェルで以下コマンドを実行
プロジェクトフォルダの作成
# 作業フォルダ(mntした共有フォルダ)に移動後、以下のコマンドを実行
$ mkdir mycoin
$ cd mycoin
# truffleのinitコマンドでコントラクトの雛形を作成
$ truffle init
# 作成されたフォルダを確認
$ tree -L 2
## 実行結果(例)
.
├── contracts
│ └── Migrations.sol
├── migrations
│ └── 1_initial_migration.js
├── test
├── truffle-config.js
└── truffle.js
3 directories, 4 files
# デフォルト設定でpackage.jsonを作成
$ npm init -f
Note:
mycoin
の部分を各自変更してください。
例)tooricoin
【手順2】OpenZeppelinのインストール
- シェルで以下コマンドを実行
OpenZeppelinのインストール
# OpenZeppelinのインストール
$ npm install zeppelin-solidity --save
Note:OpenZeppelinは、ERC20のコントラクトが実装済みです。
コントラクトの中でimportすることで、簡単にERC20のコントラクトを作成できます。
【手順3】Solidityコードの作成
-
contracts
フォルダに移動 - VS Codeで
MyCoin.sol
ファイルを作成
Note:
MyCoin
の部分を各自変更してください。
例)TooriCoin.sol
- ファイル作成し、エディタで以下コードを貼り付け
Solidityコードの作成(サンプル)
pragma solidity ^0.4.18;
import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol";
contract MyCoin is StandardToken {
string public name = "MyCoin";
string public symbol = "MYCO";
uint public decimals = 18;
address public owner;
mapping (address => string) public thanksMessage;
uint public maxSupply;
uint public totalSupply;
constructor(uint _initialSupply, uint _maxSupply) public {
owner = msg.sender;
totalSupply = _initialSupply;
maxSupply = _maxSupply;
balances[msg.sender] = _initialSupply;
}
function addTotalSupply(uint256 _value) public {
require(owner == msg.sender);
require(maxSupply >= (totalSupply + _value));
totalSupply += _value;
balances[msg.sender] += _value;
}
function thanks(address _to, string _message) public {
transfer(_to, 100e18);
thanksMessage[_to] = _message;
}
function thanksMessage(address _address) public view returns (string) {
return thanksMessage[_address];
}
}
Note:コントラクトコードの説明
変数定義の他に
constructor
と3つのfunction
を定義しています
constructor
:コントラクト作成時に呼ばれるコンストラクタ
function addTotalSupply(uint256 _value)
:Coinの発行総数を増やす関数。増加数を引数に与えます。
function thanks(address _to, string _message)
:ありがとうメッセージと一緒にCoinを送信する関数。送信先アドレスとメッセージを引数に与えます。POSTメソットのイメージです。
function thanksMessage(address _address)
:送信されたthanksMessageを参照する関数。送信先アドレスを引数に与えます。GETメソットのイメージです。
- 今回は自分のTokenを作成したいので、以下のようにカスタマイズ。
Solidityコードの作成(実装例)
pragma solidity ^0.4.18;
import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol";
contract TooriCoin is StandardToken {
string public name = "TooriCoin";
string public symbol = "TORI";
uint public decimals = 18;
address public owner;
mapping (address => string) public thanksMessage;
uint public maxSupply;
uint public totalSupply;
constructor(uint _initialSupply, uint _maxSupply) public {
owner = msg.sender;
totalSupply = _initialSupply;
maxSupply = _maxSupply;
balances[msg.sender] = _initialSupply;
}
function addTotalSupply(uint256 _value) public {
require(owner == msg.sender);
require(maxSupply >= (totalSupply + _value));
totalSupply += _value;
balances[msg.sender] += _value;
}
function thanks(address _to, string _message) public {
transfer(_to, 100e18);
thanksMessage[_to] = _message;
}
function thanksMessage(address _address) public view returns (string) {
return thanksMessage[_address];
}
}
Note:変更箇所は以下の通り
contract名を
MyCoin
からTooriCoin
に変更nameを
MyCoin
からTooriCoin
に変更symbolを
MYCO
からTORI
に変更
【手順4】Solidity コードのコンパイル
- シェルで以下コマンドを実行
Solidity コードのコンパイル
# Solidity コードのコンパイル
$ truffle compile
- 正常にコンパイルされると
build
フォルダの中にjsonファイルが作成される
コンパイル結果の確認
# コンパイル結果の確認
$ cd build
$ tree -L 2
## 実行結果(例)
.
└── contracts
├── BasicToken.json
├── ERC20.json
├── ERC20Basic.json
├── Migrations.json
├── MyToken.json
├── SafeMath.json
├── StandardToken.json
└── TooriCoin.json
1 directory, 8 files
【手順5】マイグレーションファイルの作成
-
migrations
フォルダに移動 - VS Codeで
2_deploy_MyCoin.js
ファイルを作成
Note:
MyCoin
の部分を各自変更してください。
例)2_deploy_TooriCoin.jsNote:Ethereumでは、デプロイのことをマイグレーションと呼びます。
migrationsフォルダでは、デプロイ内容をjsファイルに定義します。migrationファイルは
[数字]_[名称].js
という形式にする必要があります。
[数字]の順にmigrationが実行されます
- ファイル作成し、エディタで以下コードを貼り付け
Migrationファイルの作成(サンプル)
const MyCoin = artifacts.require('./MyCoin.sol')
module.exports = (deployer) => {
let initialSupply = 1000000e18
let maxSupply = 100000000e18
deployer.deploy(MyCoin, initialSupply, maxSupply, {
gas: 2000000
})
}
- 今回は自分のTokenを作成したいので、以下のようにカスタマイズ。
Migrationファイルの作成(実装例)
const TooriCoin = artifacts.require('./TooriCoin.sol')
module.exports = (deployer) => {
let initialSupply = 1000000e18
let maxSupply = 100000000e18
deployer.deploy(TooriCoin, initialSupply, maxSupply, {
gas: 2000000
})
}
Note:変更箇所は以下の通り
MyCoin
からTooriCoin
に変更
【手順6】コントラクトのデプロイ
- シェルで以下コマンドを実行
エミュレータの起動
# ローカルでのエミュレータの起動
$ truffle develop
// 実行結果(例)
Truffle Develop started at http://127.0.0.1:9545/
Accounts:
(0) 0x627306090abab3a6e1400e9345bc60c78a8bef57
(1) 0xf17f52151ebef6c7334fad080c5704d77216b732
(2) 0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
(3) 0x821aea9a577a9b44299b9c15c88cf3087f3b5544
(4) 0x0d1d4e623d10f9fba5db95830f7d3839406c6af2
(5) 0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e
(6) 0x2191ef87e392377ec08e7c08eb105ef5448eced5
(7) 0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5
(8) 0x6330a553fc93768f612722bb8c2ec78ac90b3bbc
(9) 0x5aeda56215b167893e80b4fe645ba6d5bab767de
Private Keys:
(0) c87...
(1) ae6...
(2) 0db...
(3) c88...
(4) 388...
(5) 659...
(6) 82d...
(7) aa3...
(8) 0f6...
(9) 8d5...
Mnemonic: candy ...
- エミュレータ起動後に、truffleのコンソールモードになるので、以下コマンドを実行してデプロイ
コントラクトのデプロイ
// コントラクトのデプロイ
$ truffle(develop)> migrate
// 実行結果(例)
Using network 'develop'.
Running migration: 1_initial_migration.js
Replacing Migrations...
... 0x63fb52c38e0c0a6ffb49344b27f96222178399bae48b7bffe5cb1c4b9d808bd9
Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_tooricoin.js
Deploying TooriCoin...
... 0x7b30330b3c0f4f3c9ed0b90761be5ae7da706ea7ddb054bcf27eb854adf31d57
TooriCoin: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...
... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
Saving artifacts...
Note:migrateコマンドを実行したときに表示されるコントラクトのアドレス(0xで始まるコード)をこのあと使用するので控えておきましょう
例)TooriCoin: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
【手順7】デプロイされたコントラクトの確認
- truffleコンソールで以下コマンドを実行
コントラクトのインスタンス化
// コントラクトをインスタンス化します
truffle(develop)> contract = TooriCoin.at("<コントラクトのアドレス>")
// 実行結果(例)
[{...}] (contractが表示される。長いので省略)
- コントラクトの基礎情報を確認してみます
コントラクトの基礎情報確認
// コントラクトの名称を表示
truffle(develop)> contract.name()
// 実行結果(例)
'TooriCoin'
// Tokenの総額を表示
truffle(develop)> contract.totalSupply()
// 実行結果(例)
BigNumber { s: 1, e: 24, c: [ 10000000000 ] }
- ローカル環境上のアカウントの一覧を表示してみます
アカウント一覧の表示
// アカウント一覧の表示
truffle(develop)> web3.eth.accounts
// 実行結果(例)
[ '0x627306090abab3a6e1400e9345bc60c78a8bef57',
'0xf17f52151ebef6c7334fad080c5704d77216b732',
'0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
'0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
'0x0d1d4e623d10f9fba5db95830f7d3839406c6af2',
'0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e',
'0x2191ef87e392377ec08e7c08eb105ef5448eced5',
'0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5',
'0x6330a553fc93768f612722bb8c2ec78ac90b3bbc',
'0x5aeda56215b167893e80b4fe645ba6d5bab767de' ]
Note:ローカル環境上のアカウントはtruffle developコマンド実行時にtruffleが自動的に作成してくれます
- 各アカウント毎の残高を表示してみます
アカウント毎の残高確認(実行前)
// 0番目のアカウントの残高を表示
truffle(develop)> contract.balanceOf(web3.eth.accounts[0])
// 実行結果(例)
BigNumber { s: 1, e: 24, c: [ 10000000000 ] }
// 1番目のアカウントの残高を表示
truffle(develop)> contract.balanceOf(web3.eth.accounts[1])
// 実行結果(例)
BigNumber { s: 1, e: 0, c: [ 0 ] }
- 0番目のアカウントから1番目のアカウント(0xf17f52151ebef6c7334fad080c5704d77216b732)に対して、メッセージとTokenを送信してみます
メッセージとTokenの送信
// メッセージとTokenを送信
truffle(develop)> contract.thanks("0xf17f52151ebef6c7334fad080c5704d77216b732", "Thanks!")
// 実行結果(例)
{ tx:
'0x59aa94f5e629414d5fe64e0a49081783f09033b7dc09e2d8b7feba7fb15bd453',
receipt:
{ transactionHash:
'0x59aa94f5e629414d5fe64e0a49081783f09033b7dc09e2d8b7feba7fb15bd453',
transactionIndex: 0,
blockHash:
'0xd699f8e854ab2c79e935229093d9f932d8ebe8ee9b949a77297e781b292a8674',
blockNumber: 5,
gasUsed: 73071,
cumulativeGasUsed: 73071,
contractAddress: null,
logs: [ [Object] ],
status: '0x01',
logsBloom:
'0x00000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000008000000000000000000010000000080000000000000000000000000000000000000000000000000000000000000000010000000000000000000010000000000000000000000000000000000000000010000000002000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000010000000000000' },
logs:
[ { logIndex: 0,
transactionIndex: 0,
transactionHash:
'0x59aa94f5e629414d5fe64e0a49081783f09033b7dc09e2d8b7feba7fb15bd453',
blockHash:
'0xd699f8e854ab2c79e935229093d9f932d8ebe8ee9b949a77297e781b292a8674',
blockNumber: 5,
address: '0x345ca3e014aaf5dca488057592ee47305d9b3e10',
type: 'mined',
event: 'Transfer',
args: [Object] } ] }
Note:ローカル環境でテストする場合は、thanks関数の第一引数の
0xf17f52151ebef6c7334fad080c5704d77216b732
をweb3.eth.accounts[1]
と入力してもOKです。
- メッセージ送信後の各アカウント毎の残高を表示してみます
アカウント毎の残高確認(実行後)
// 0番目のアカウントの残高を表示
truffle(develop)> contract.balanceOf(web3.eth.accounts[0])
// 実行結果(例)
BigNumber { s: 1, e: 23, c: [ 9999000000 ] }
// 1番目のアカウントの残高を表示
truffle(develop)> contract.balanceOf(web3.eth.accounts[1])
// 実行結果(例)
BigNumber { s: 1, e: 20, c: [ 1000000 ] }
- メッセージ内容を表示してみます
メッセージ内容の表示
// メッセージ内容の表示
truffle(develop)> contract.thanksMessage(web3.eth.accounts[1])
// 実行結果(例)
'Thanks!'
Note:Tokenだけでなく、メッセージもEthereum上に記録されていることが確認できます
まとめ
- ローカル環境上でERC20のオリジナルトークンを作ることができました!
- 次のステップとして、作成したコントラクトをEthereumのテスト環境にデプロイしてみましょう
MyToken発行してみた(テスト環境編)