前回のDApps学習ではRailsからスマートコントラクトを動作させるサンプルアプリを作成しました。
その際OpenZeppelinのようなライブラリの使い方がわからなかったため、今回はOpenZeppelinを利用して独自トークンを発行し、送金が行えるサンプルアプリをRailsで作ってみました!
基本的なスマートコントラクトのフォーマットを提供するライブラリ群。導入する事で少ない作業量で実装ができるようになります。
今回作ったコードはこちらにおいています。
サンプルアプリでは独自トークンを自動で発行して、別のアドレスに送金する。までができます。
やっていることはとてもシンプルだけど色々ハマった・・。その代わり、その分学びがあったはず。
Contents
本記事の前提
RailsとSolidityでサンプルアプリを作っているため、両方の基礎知識がない方は理解が難しいかもしれません。
動作環境
動作させた環境は以下になります。
- Rails v5.1.6
- ethreum.rb v2.2
- npm v5.6.0
- OpenZeppelin v1.9.0
- truffle v4.1.7
- Ganache v1.1.0
サンプルアプリを動作確認してみる
まずは動作させて動きを見てみましょう。
といっても独自トークンを発行して送金するだけなので、だいたい想像つく方は飛ばして実装ポイントだけ見てもらえばと思います。
動作方法
ソースのクローンしてからgemのインストール
1 |
$ git clone git@github.com:Matsushin/ethereum-token-sample.git |
1 |
$ bundle install |
.env.sampleファイルをコピーして.envファイルの作成。適宜変えてご利用ください。
1 2 3 4 5 |
DATABASE_USERNAME=username # DBのユーザ名 DATABASE_PASSWQRD=password # DBのパスワード DEFAULT_TOKEN_NAME=MatsushinToken # トークン名 DEFAULT_TOKEN_SYMBOL=MS # トークンのシンボル DEFAULT_TOKEN_TOTAL_SUPPLY=10000 # トークンの供給量 |
データベースの作成とテーブルの作成
1 2 |
$ bundle exec rake db:create $ bundle exec rake db:migrate |
アプリを起動する
1 |
$ bundle exec rails s |
Ganacheを起動する
最後にトップ画面(http://localhost:3000)にアクセス。エラーが発生しなければOK!
それでは次に動作を確認していきましょう。
動作確認
トップ画面にアクセスすると2アドレス分のトークン/ETH残高が表示されます。
画面アクセス時にデフォルトのトークンが発行されアドレス1に全て割り当てられます。
アドレス1はGanacheが自動で発行する最初のアドレスで、アドレス2が2番目のアドレスになります。またアドレス1 が独自トークンの発行を行います。
「アドレス2に送金する」をクリックして送金画面へ。送金量を入力して送金実行!
アドレス1のトークン残高が減り、アドレス2のトークン残高が送金した分増えていればOK!
動作が確認できたので次は自分が実装をしていく中で色々ハマったので、その辺りを整理していきます。
実装ポイント
OpenZeppelinの導入
前回の記事でも少し書きましたが、Railsからコントラクトを実行する場合にライブラリの import ができず困っていました。
色々試したのですが、 Truffleでコンパイルする事で解決できましたので手順を解説します。
- Railsプロジェクト直下にtruffleフォルダを作成(フォルダ名は何でも良い)
- truffleフォルダ配下に移動して「truffle init」 してtruffleプロジェクトを作成
- コントラクトのコード(import含む)を書く(※1)
- OpenZeppelinをインストール(※2)
- コントラクトをコンパイル(「truffle compile」)
※1 AppToken.sol
zeppelin-solidityライブラリのStandardToken.sol をimportして継承したクラスを少し実装しているだけのとてもシンプルなコードです。何とこれだけでERC20に準拠のトークンが作れちゃうから便利ですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
pragma solidity ^0.4.23; import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; contract AppToken is StandardToken { string public name; string public symbol; uint8 public decimals = 4; function AppToken(string _name, string _symbol, uint _totalSupply) public { totalSupply_ = _totalSupply; balances[msg.sender] = _totalSupply; name = _name; symbol = _symbol; } } |
※2 OpenZeppelinのインストール
下記コマンドを実行していインストールを行う。
1 2 |
$ npm init -f $ npm install zeppelin-solidity |
truffle利用時のコントラクトの連携方法
Rails側でのコントラクトとの連携部分のコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
class EthereumApi TRUFFLE_PATH = "#{Dir.pwd}/truffle/" # truffleで作成したプロジェクトのパス GANACHE_URL = 'HTTP://127.0.0.1:7545' # GANACHEを起動しているサーバー attr_accessor :contract, :client def initialize self.client = Ethereum::HttpClient.new(GANACHE_URL) create_default_token! if Token.count.zero? token = Token.first self.contract = Ethereum::Contract.create(name: "AppToken", truffle: { paths: [ TRUFFLE_PATH ] }, client: @client, address: token.token_address) # 保管したコントラクトのアドレスを再利用する end def create_default_token! self.client = Ethereum::HttpClient.new(GANACHE_URL) self.contract = Ethereum::Contract.create(name: "AppToken", truffle: { paths: [ TRUFFLE_PATH ] }, client: @client) # 画面に初回アクセス時に新しくコントラクトを作成 token = Token.new( name: Token::DEFAULT_NAME, symbol: Token::DEFAULT_SYMBOL, total_supply: Token::DEFAULT_TOTAL_SUPPLY ) address = contract.deploy_and_wait(token.name, token.symbol, token.total_supply) # デプロイ! token.token_address = address # コントラクトのアドレスを保管しておく token.save! end def get_accounts client.eth_accounts['result'] end def transfer(address, amount) begin contract.transact_and_wait.transfer(address, amount) # 送金処理 rescue => e Rails::logger::debug("Error token transfer. message: #{e.message}") false end end end |
ポイントは2点。デプロイは1度だけ行うこと。あとはデプロイ時に発行されたコントラクトアドレスを使い回す。
そうしないとトークンの保管が初期化されてしまいます。
もう1つはコントラクト作成時(Ethereum::Contract.create)にtruffleオプションを使うこと。そうすることでtruffleで作成したプロジェクトを利用できるようになります。
仕組みを考えるとデプロイは1度だけとか当たり前なのだけど・・実装することで理解が深まる。
モデル名について
Rails側のモデル名とコントラクトのクラス名を同じにしてはいけません。
詳しくはよくわかっていないのですが、同じにしてしまうと挙動がおかしくなってしまいました。
モデルのインスタンスを作るとコントラクト側のクラスのインスタンスができてしまうような感じ?
これめっちゃハマった。。
実装のポイントについては以上です!
まとめ
さて、今回は独自トークンの発行と送金の処理をRailsで行ってみました。
昨日丸一日かけてRailsから、truffleを使って作成した独自トークンを送金するサンプルアプリを作って学んだこと。
後でブログにまとめていこう・truffleで作成したプロジェクトのethereum.rbからの連携方法
・独自トークン送金の実装方法
・ERC20の仕様#DApps開発 #solidity— まつしん@仮想通貨プログラマ (@matsushin11)
Solidity側のコードはほとんど書いていないため、Solidity力はあまり上がりませんでした。ですがスマートコントラクトの連携部分を多少書いて動かしたので、仕組みの理解に繋がりました。あとライブラリのコードリーディングをしたりしたのでERC20の仕様の理解が進んだのがよかったです。
また今回のサンプルアプリはトークンの発行者から決まったアドレスに送金するだけでしたが、トークンの保有者なら誰でも送金できるようにしたいです。例えば今回のサンプルアプリではアドレス1からアドレス2に送金した後、次はアドレス2からまた別のアドレスに送金するような。この辺り実装しようとしてうまくいかなかったのですよね。。
具体的にいうと StandardToken.solにあるtransferFromメソッドがうまく使えませんでした。
後まだローカル環境でしか動作させていないので、本番環境の手前のテスト環境で動かして見たいです。
この辺り知りたいですね〜。
transferFromの問題について、実装の方は解決できましたでしょうか?
もし既知であれば無視していただいて構わないのですが、僕もローカルで試してみてうまく動かなくて色々ためした結果、1つの解が見つかったのでお伝えしようかと思いまして。。。