OpenZeppelinとTruffleを使ったトークン開発方法

OpenZeppelinとTruffleを使ったトークン開発方法

はじめに

今回の内容


ERC20などのトークンを簡単に開発可能にするライブラリであるOpenZeppelinとスマートコントラクト開発を行うフレームワークであるTruffleを使ってトークン開発を行います。

本記事は、過去にコンセンサス・ベイスが主宰していたオンラインサロンの記事です。記事は2017年~2018年にかけて執筆されたため、一部は、既に古くなっている可能性があります。あらかじめご了承ください。

前提知識


Javascriptの基礎知識、ブロックチェーンの仕組みの理解、Solidityの基礎知識

第1章 OpenZeppelinとは


OppenZeppelinはセキュリティソリューションを提供するスマートコントラクトライブラリです。

分散型のアプリケーション(Dapp)、プロトコル、分散自律組織(DAO)などの実現にはブロックチェーン上でスマートコントラクトの実行が不可欠です。。

イーサリアムでのアプリケーション開発が高い人気を誇っています。イーサリアムでのスマートコントラクトの実装はSolidityというチューリング完全の言語で簡単に開発できるようになっています。

しかしスマートコントラクト開発には非常に注意しなくてはいけない点があります。それは書き換えることが難しい点です。つまり一度デプロイしてしまうと、バグが起きても対処が非常に難しいです。

OpenZeppelinはスマートコントラクトの開発においてセキュアなコードパターンの適用、コミュニティによるコード監査を行なっています。そのため、OpenZeppelinは様々なDapp開発時に利用されています。

第2章 どんなコントラクトがあるのか


ここでは比較的よく使われるライブラリーを紹介します。

クラウドセール


ICOは投資家等に暗号通貨を発行・販売し、資金調達を行なう方法です。ICOは一般的にEthereumのスマートコントラクトとERC20を利用し行われます。

本記事ではOpenZepplinを使ってICOコントラクトをどのように開発するのかを簡単に紹介します。

ERC Token


ERCはEthereum request for commentの略です。ERC20は初めてトークンスタンダードを決めた技術仕様です。ERC 20以外には数種類のトークンスタンダードがあります。ERC721はNon-Fungible-Token(NFT)という代替不可能なトークン仕様を持っています。

人気のDappゲームCryptoKittiesはERC721で実装されています。ERC20とERC721はOpenZeppelinにライブラリ化された仕様です。他の様々な仕様はmocksという分類に入っていますが、まだユースケースが少なく、実験的な要素が大きいです。

Ownership


コントラクトを生成したアカウントのみトークン発行などの操作ができるように設定することが出来る。

Math


計算をライブラリ化したものです。演算対象がunsigned整数であるため、負の数になる可能性がありますがセキュリティ的にアンダフローのチェックを行ないそれを防いでいます。

Payment


Dappでの決済を可能にする仲介者を介した支払い・引き出し機能

第3章 OpenZepplinとTruffleを使ってトークンを作成する

Truffleとは


Consensysによって開発されたスマートコントラクト開発のフレームワークの一つです。アプリケーション開発に特化したものであり、オープンなネットワークを使用する際に主にgethを使用します。

Ganacheとは


Truffleと同じくConsensysによって開発されたプライベートチェーン構築サービスです。簡単にローカルでスマートコントラクト等のテストを行なうことを可能にします。

環境構築


Truffleをまだインストールされていない場合は
$ npm install -g truffle

無い場合は、ganacheもインストールしてください
$ npm install -g ganache-cli

project作成用のディレクトリを作成し、移動します。
$ mkdir myTokenTest
$ cd $_
myTokenTest ~$ truffle init

Downloading…

Unpacking…

Setting up…

Unbox successful. Sweet!



commands:


   Compile:          truffle compile
   
   Migrate:           truffle migrate
 
   Test contracts: truffle test

myTokenTest ~$ ls

contracts         migrations              test                   truffle.config.js         truffle.js

npmでプロジェクトをinitし、openzeppelin-solidityをインストールします。
myTokenTest ~$ npm init -y
myTokenTest ~$ npm install -E openzeppelin-solidity

[GitHub – OpenZeppelin/openzeppelin-solidity]

(https://github.com/OpenZeppelin/openzeppelin-solidity)

ICOスマートコントラクトの作成


mytokenというトークンを定義するスマートコントラクトを作成します。
myTokenTest ~$ touch contracts/mytoken.sol
myTokenTest ~$vim contracts/mytoken.sol
## mytoken.sol

pragma solidity ^0.4.23;


import ‘openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol’;

contract mytoken is MintableToken{
    string public name = MY TOKEN”;
    string public symbol = “MT”;
    uint public decimals = 18;
}

以上のスマートコントラクトは一見に簡単に思われますが、実はMintableToken.sol、 StandardToken.sol 、BasicToken.sol などの内容を見ると、TokenのTransfer Approval などの機能が組み込まれています。

そして、ここは重要なポイントなのですが、トークンの機能などにおいてBTCとETHでは生成される仕組みが異なり、スマートコントラクトにより定義されているのがここでわかりました。次はcrowdsale コントラクトを作成します。
myTokenTest ~$ touch contracts/mytokenCrowdsale.sol
myTokenTest ~$ vim contracts/mytokenCrowdsale.sol
## mytokenCrowdsale.sol
pragma solidity ^0.4.23;

import './mytoken.sol';
import 'openzeppelin-solidity/contracts/crowdsale/emission/MintedCrowdsale.sol';
import 'openzeppelin-solidity/contracts/crowdsale/validation/TimedCrowdsale.sol';

contract mytokenCrowdsale is TimedCrowdsale, MintedCrowdsale{
  function  mytokenCrowdsale(
      uint256 _openingTime,
      uint256 _closingTime,
      uint256 _rate,
      address _wallet,
      MintableToken _token
    )
    public
    Crowdsale(_rate, _wallet, _token)
    TimedCrowdsale(_openingTime, _closingTime){
    }
}

truffle と ganache-cli を用いて、プライベートネットワークにデプロイ
コンパイルしてコントラクトのJSONファイルを生成します。
myTokenTest ~$ ganache-cli

Available Accounts
…
Private Keys
…
HD Wallet
==================
Mnemonic:      sand walnut sauce absent chair force potato version assume slab can timber
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

新たなコマンドラインを立ち上げる

macOSをご利用の方でtreeコマンドをインストールしていない方はhomebrewでイ  ンストールしましょう
$ tree 
├── build
│   └── contracts
│       ├── BasicToken.json
│       ├── Crowdsale.json
│       ├── ERC20.json
│       ├── ERC20Basic.json
│       ├── Migrations.json
│       ├── MintableToken.json
│       ├── MintedCrowdsale.json
│       ├── Ownable.json
│       ├── SafeERC20.json
│       ├── SafeMath.json
│       ├── StandardToken.json
│       ├── TimedCrowdsale.json
│       ├── mytoken.json
│       └── mytokenCrowdsale.json


デプロイjsファイルを作成 します。

myTokentest ~$ touch migrations/2_deploy_contracts.js
## 2_deploy_contracts.js
const mytokenCrowdsale = artifacts.require('./mytokenCrowdsale.sol');
const mytoken = artifacts.require('./mytoken.sol');

module.exports = function(deployer, network, accounts) {
  const openingTime = web3.eth.getBlock('latest').timestamp + 2;
  const closingTime = openingTime + 86400 * 20;
  const rate = new web3.BigNumber(1000);
  const wallet = accounts[1];

  return deployer
    .then(() => {
      return deployer.deploy(mytoken);
    })
    .then(() => {
      return deployer.deploy(
        mytokenCrowdsale,
        openingTime,
        closingTime,
        rate,
        wallet,
        mytoken.address
      );
    });
}

ここで初期の入力引数をスマートコントラクトのconstructor関数に渡すことができます。

truffle.jsを編集し、接続先のネットワーク情報を設定します。

ここでは先ほどGanacheで立ち上げたプライベートネットワークを指定します。
## truffle.js
module.exports = {
  networks:{
    development:{
      host: "localhost",
      port: 8545,
      network_id: "*",
      gas: 4500000,
      gasPrice: 10000000000
    }
  }
};


作成したコードをコンパイルします。

myTokenTest ~$cd migrations/
migrations ~$ truffle compile
migrations ~$ truffle console

$ truffle(development)> migrate
Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0xdfe00a40bcb089eb5703ad319b34d4a96123910c731a5b5adbebd7dc66700025
  Migrations: 0x62e2818f17fb5f473f46c82aef08c9e892ed17f5
Saving successful migration to network...
  ... 0xadd27dd598f6c41a2c9dcafabe897ac370c171351157f8d455227e3ca99f7b90
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Running step...
  Deploying mytoken...
  ... 0x5d1cdbde84437df02c552287c1349cfa4a728d3236fd48a6a038caf79fc4dfc3
  mytoken: 0x5f236e4aad4ca882566c506989f7d6e1dc65c808
  Deploying mytokenCrowdsale...
  ... 0xc2542561ba26cdd83a9a02b92aa2186e59ff7edd3a3195294d8cd1eb46a0e2a5
  mytokenCrowdsale: 0x5f298ef785fdfb1ed8052c9b9a6f93998b7d1c89
Saving successful migration to network...
  ... 0xde87301a8dc6b247416ba824c70eff4b7b8df44a7eadccc279e3b719524bdb3a
Saving artifacts…

 
mytoken と mytokenCrowdsale 両方ともデプロイ出来ていれば成功です。です。

truffle console で送金やトークン発行のコントラクトを使ってみます。

//トークン購入者をaccount[2]に設定
truffle(development)> purchaser = web3.eth.accounts[2]
‘0x17699c127b516c567f4f75ebbaf0870bb2308166'
truffle(development)> mytokenCrowdsale.deployed().then(inst => {crowdsale = inst})
undefined
truffle(development)> mytoken.deployed().then(inst => {token = inst})
undefined
//購入者の残高を確認
truffle(development)> token.balanceOf(purchaser)
BigNumber { s: 1, e: 0, c: [ 0 ] }
//ICO実施者に購入者の残高を変更する権利を付与する。
truffle(development)> token.transferOwnership(crowdsale.address)
  logs:
   [ { logIndex: 0,
       transactionIndex: 0,
       transactionHash:
        '0xb107eb56df56e2c24357d283db1cef84115f27c79e1e2d4e628c51209c97061f',
       blockHash:
        '0x67736a0595f74806d32b57d7e145da0dcdfb5cb2fc21ac0d3e4e798d14173aa8',
       blockNumber: 6,
       address: '0x5f236e4aad4ca882566c506989f7d6e1dc65c808',
       type: 'mined',
       event: 'OwnershipTransferred',
       args: [Object] } ] }
truffle(development)> mytokenCrowdsale.deployed().then(inst => inst.sendTransaction({from: purchaser, value: web3.toWei(5, "ether")}))
{ tx:
   '0x3be3543ef830d164d4866de25692d88f49102199a69f5fb4a24682927c95c443',
  receipt:
   { transactionHash:
      '0x3be3543ef830d164d4866de25692d88f49102199a69f5fb4a24682927c95c443',
     transactionIndex: 0,
     blockHash:
      '0xfb917f8fe0195a5239aa148dcfb30f27b89897042aa2a26ffb3a549db439064e',
     blockNumber: 7,
     gasUsed: 99508,
     cumulativeGasUsed: 99508,
     contractAddress: null,
     logs: [ [Object], [Object], [Object] ],
… 
 logs:
   [ { logIndex: 2,
       transactionIndex: 0,
       transactionHash:
        '0x3be3543ef830d164d4866de25692d88f49102199a69f5fb4a24682927c95c443',
       blockHash:
        '0xfb917f8fe0195a5239aa148dcfb30f27b89897042aa2a26ffb3a549db439064e',
       blockNumber: 7,
       address: '0x5f298ef785fdfb1ed8052c9b9a6f93998b7d1c89',
       type: 'mined',
       event: 'TokenPurchase',
       args: [Object] } ] }
truffle(development)> token.balanceOf(purchaser).then(value => {balance = value})
undefined
//weiで残高を確認
truffle(development)> balance
BigNumber { s: 1, e: 21, c: [ 50000000 ] }
//etherでの残高表記に変更
truffle(development)> web3.fromWei(balance, "ether")
BigNumber { s: 1, e: 3, c: [ 5000 ] }
truffle(development)> web3.fromWei(web3.eth.getBalance(purchaser), "ether")
BigNumber { s: 1, e: 1, c: [ 94, 99004920000000 ] }

まとめ


OpenZepplinのライブラリーを使えば、このように非常にシンプルなICOスマートコントラクトを簡単に開発できるようになりました。

以上のコードはあくまでも使い方のデモンストレーションのためのもので、実際にICOを行う場合は、細かいタイムセールやオーナーシップの設定が必要となります。

免責事項


本記事に掲載されている記事の内容につきましては、正しい情報を提供することに務めてはおりますが、提供している記事の内容及び参考資料からいかなる損失や損害などの被害が発生したとしても、弊社では責任を負いかねます。実施される際には、法律事務所にご相談ください。

技術・サービス・実装方法等のレビュー、その他解説・分析・意見につきましてはblock-chani.jp運営者の個人的見解です。正確性・正当性を保証するものではありません。本記事掲載の記事内容のご利用は読者様個人の判断により自己責任でお願いいたします。

ブロックチェーン学習に最適の書籍の紹介


図解即戦力 ブロックチェーンのしくみと開発がこれ1冊でしっかりわかる教科書

本書は、ブロックチェーン技術に興味を持ったエンジニアや、その仕組みを学び、自分の仕事に活かしたいビジネスパーソンを対象にして、ブロックチェーンのコア技術とネットワーク維持の仕組みを平易な言葉で解説しています。この本を読んだうえで、実際にコードを書くような専門書、ブロックチェーンビジネスの解説書を読むことで、理解度が飛躍的に高まるでしょう。(はじめにより)

会社紹介


弊社(コンセンサス・ベイス株式会社)は、2015年設立の国内で最も古いブロックチェーン専門企業です。これまでに、大手企業の顧客を中心に、日本トップクラスのブロックチェーンの開発・コンサルティング実績があります。ブロックチェーンに関わるビジネスコンサル・システム開発・教育・講演などご希望でしたら、お気軽にお問い合わせください。

会社ホームページ

https://www.consensus-base.com/

ブロックチェーンの専門企業で働いてみませんか?

当サイトを運営するコンセンサス・ベイス株式会社では、エンジニア、プロジェクトマネージャー、ライターなど、様々なポジションで一緒に働いてくださる仲間を募集しています。

ブロックチェーン業界にチャレンジしてみたいあなたのご応募をお待ちしております!

イーサリアム(Ethereum)カテゴリの最新記事