DApps開発ギルドでデプロイしたアプリを1から作ってみる
先日、オオキマキさん(@ookimaki_JP)が主催するDApps開発ギルドの勉強会に参加してきました。
勉強会は2時間という限られた時間だったので、今回作ったアプリを復習がてら自分で1から作成してみました。
勉強会で行った内容はこちら。
それでは作ってみます。
開発環境
MacBook Pro(macOS High Sierra 10.13.6)
MetaMask 4.8.0
Ganache 1.2.1
$ npm -v 6.1.0 $ node -v v10.7.0 $ truffle version Truffle v4.1.13 (core: 4.1.13) Solidity v0.4.24 (solc-js)
環境構築が済んでいない方はこちらへ。
プロジェクト初期化
はじめにプロジェクト用のフォルダを作成し移動しましょう。
$ cd ~/Document $ mkdir ~/dapps $ mkdir ~/dapps/turotial/ $ cd ~/dapps/tutorial/
今回はtutorial
というフォルダで作業を行います。
プロジェクトフォルダに移動したら、truffleというフレームワークを初期化します。
$ truffle init Downloading... Unpacking... Setting up... Unbox successful. Sweet! Commands: Compile: truffle compile Migrate: truffle migrate Test contracts: truffle test
tutorial
直下にファイルが作成されていることを確認しましょう。
コントラクトを作成
contracts
フォルダの下にSimpleStore.sol
を作成します。
ファイルの中身は次のコードを記載します。
pragma solidity ^0.4.23; contract SimpleStore { string value; function set(string _value) public { value = _value; } function get() public view returns (string) { return (value); } }
Migrationsを作成
先ほど書いたコントラクトをデプロイするためのmigrationを作成します。
migrations
フォルダに2_deploy_contracts.js
を作成します。
ファイルの中身は次のコードを記載します。
var SimpleStore = artifacts.require('../contracts/SimpleStore.sol'); module.exports = function(deployer) { deployer.deploy(SimpleStore); };
ネットワーク設定
次にデプロイするネットワークを設定ファイルに追記します。
truffle.js
に次のコードを記載します。
module.exports = { // See <http://truffleframework.com/docs/advanced/configuration> // to customize your Truffle configuration! networks: { development: { host: '127.0.0.1', port: 8545, network_id: '*' } } };
host: 127.0.0.1
、port: 8545
という接続先はプライベートチェーンGanacheを指しています。
※Ganacheのポート番号を8545に変更しています。Ganacheの設定画面から変更可能です。
デプロイ
準備が整ったのでデプロイします。コマンドを実行する前にGanacheを起動しておきましょう。
$ truffle migrate Compiling ./contracts/Migrations.sol... Compiling ./contracts/SimpleStore.sol... Writing artifacts to ./build/contracts Using network 'development'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0x55a8c1d05804136fb1670127c8bf4f08b6832a74a44e8a0006b8108ffb355d46 Migrations: 0x562f587d7241ca995b50476adbbdc4bf3b7fa75f Saving successful migration to network... ... 0xa1d9bb5b7594aea5b49ea110e2648dc5859c49c18b05aad555fab6c4dc2b6658 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying SimpleStore... ... 0x4dd1bdb65839e6eaade2cb3a45b9d63fa403bf66f8609d014d54eb09eaf07c13 SimpleStore: 0x15a40f91de5f6797721266c6a6daf83758d10e00 Saving successful migration to network... ... 0xe1b4d2a0a3aba6861b6e2c79fa5285af636d04d2ce030a0fa85db53a250ac229 Saving artifacts...
SimpleStore: 0x15a40f91de5f6797721266c6a6daf83758d10e00
がコントラクトのアドレスになります。
重要なのでメモっておきましょう。※これって後で確認したいときどうするんでしょう?
無事にデプロイできたらbuild
というフォルダが作成されているはずです。
フロントアプリを作成
コントラクトがデプロイできたので、これを利用するためのフロントアプリを作成します。
tutorial
直下にsrc
フォルダを作成し、下記3つのファイルを追加していきます。
web3.min.js
は下ページから最新バージョンをダウンロードし、dist/web3.min.js
を所定の位置に配置します。
contract_abi.js
は新規に作成し、下記コードを記載しましょう。
変数の中身はbuild/SimpleStore.json
のabiから取ってきたものです。
var contractABI = [ { // build/SimpleStore.jsonのabiをコピペ constant: false, inputs: [ { name: '_value', type: 'string' } ], name: 'set', outputs: [], payable: false, stateMutability: 'nonpayable', type: 'function' }, { constant: true, inputs: [], name: 'get', outputs: [ { name: '', type: 'string' } ], payable: false, stateMutability: 'view', type: 'function' } ];
index.html
の中身は最低限動くレベルで書いておきます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>DApps Dev Guild Tutorial</title> <script language="javascript" type="text/javascript" src="./js/web3.min.js"></script> <script language="javascript" type="text/javascript" src="./js/contract_abi.js"></script> <script> var contract; var userAccount; function startApp() { var contractAddress = "0xdc30da2490373416e7bf7467bb297bae624287b4"; contract = new web3js.eth.Contract(contractABI, contractAddress); var accountInterval = setInterval(function () { web3.eth.getAccounts((error, accounts) => { if (accounts[0] !== userAccount) { userAccount = accounts[0]; } }); }, 100); } window.addEventListener('load', function () { // Checking if Web3 has been injected by the browser(Mist/MetaMask) if (typeof web3 !== 'undefined') { // Use Mist/MetaMask's provider web3js = new Web3(web3.currentProvider); } else { // Handle the case where the user doesn't have Metamask installed // Probably show them a message prompting them to install Metamask } // Now you can start your app & access web3 freely; startApp(); }); function set() { var _val = document.getElementById("val").value; contract.methods.set(_val) .send({ from: userAccount }) .on("transactionHash", function (txhash) { alert("Txhash: " + txhash); }) .on("receipt", function (receipt) { console.log(receipt); }) .on("error", function (error) { console.log(error); }); } function get() { contract.methods.get().call().then(function (_val) { alert(_val); }); } </script> </head> <body> <h1>DApps Dev Guild Tutorial</h1> <input type="text" name="val" id="val"> <input type="button" value="Set" onclick="set()"> <input type="button" value="Get" onclick="get()"> </body> </html>
MetaMaskにethを用意する
アプリを実行するためにはMetaMaskにethがないと利用できないので、Ganacheで用意されているアドレスをインポートしましょう。
適当なアドレスのshow keys
をクリックします。
プライベートキーが表示されるのでクリップボードにコピーします。
MetaMaskの人マークをクリックすると、アカウントメニューが表示されるのでImport Account
をクリックします。
先程コピーしたプライベートキーを入力し、Import
をクリックするとアカウントが追加されます。
動作確認
動作確認するためにlive-server(ローカルWebサーバ)を利用します。
導入されていない方はnpm install
でインストールしましょう。
$ npm install -g live-server
エディタのプラグインで提供されている場合もあるので、そっちを使っても大丈夫です。
src
フォルダに移動して実行します。
$ cd src $ live-server Serving "/Users/hoge/Document/dapps/tutorial/src" at http://127.0.0.1:8080 Ready for changes
Chromeでhttp://127.0.0.1:8080
にアクセスするとフロントアプリの画面が表示されます。
MetaMaskを立ち上げネットワークLocalhost 8545
を選択します。
実際に動かしてみます。
無事に動きました!お疲れ様でした!!
おまけ
ちょっと見た目が野暮ったいので、機能は変えずにデザインをオシャレにアレンジしてみます。
MaterializeというCSSフレームワークを利用します。
ファイルをダウンロードし、materialize.min.css
とmaterialize.min.js
を配置します。
index.htmlをゴリゴリ修正します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>DApps Dev Guild Tutorial</title> <script language="javascript" type="text/javascript" src="./js/web3.min.js"></script> <script language="javascript" type="text/javascript" src="./js/contract_abi.js"></script> <script language="javascript" type="text/javascript" src="./js/materialize.min.js"></script> <!--Import Google Icon Font--> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <!--Import materialize.css--> <link type="text/css" rel="stylesheet" href="./css/materialize.min.css" media="screen,projection" /> <script> var contract; var userAccount; function startApp() { var contractAddress = "0xdc30da2490373416e7bf7467bb297bae624287b4"; contract = new web3js.eth.Contract(contractABI, contractAddress); var accountInterval = setInterval(function () { web3.eth.getAccounts((error, accounts) => { if (accounts[0] !== userAccount) { userAccount = accounts[0]; } }); }, 100); } window.addEventListener('load', function () { // Checking if Web3 has been injected by the browser(Mist/MetaMask) if (typeof web3 !== 'undefined') { // Use Mist/MetaMask's provider web3js = new Web3(web3.currentProvider); } else { // Handle the case where the user doesn't have Metamask installed // Probably show them a message prompting them to install Metamask } // Now you can start your app & access web3 freely; startApp(); }); function set() { var _val = document.getElementById("val").value; contract.methods.set(_val) .send({ from: userAccount }) .on("transactionHash", function (txhash) { alert("Txhash: " + txhash); }) .on("receipt", function (receipt) { console.log(receipt); }) .on("error", function (error) { console.log(error); }); } function get() { contract.methods.get().call().then(function (_val) { alert(_val); }); } </script> </head> <body> <nav> <div class="nav-wrapper blue-grey "> <a href="" class="brand-logo">DApps Dev Guild Tutorial</a> </div> </nav> <div class="container"> <input type="text" name="val" id="val"> <div class="center"> <a class="waves-effect waves-light btn" onclick="set()"> <i class="material-icons left">file_upload</i>Set</a> <a class="waves-effect waves-light btn" onclick="get()"> <i class="material-icons right">file_download</i>Get</a> </div> </div> </body> </html>
まとめ
デプロイするまでDApps開発に敷居を高く感じていましたが、手順を教えてもらい一通りできるようになったので取っ掛かりは掴めたかなって感じです。 所々理解できていない部分もありますが、今は教えられたことを全部飲み込むフェーズにし、アプリの開発をドシドシやろうと思います。ではまた。