0から作るDapps入門 コントラクト編
はじめに
みなさん、こんにちは株式会社Acompanyの湯浅です。先日ブロックチェーン関係のイベントを開催したのですが,「Dappsを作ってみたい!」という方が結構いたような気がしました。そこで今回はEthereumで簡単なDappsを作ってDapps開発の流れを掴んでいただきたいなと思っております.
このチュートリアルで行うことは以下の3つです.
・remixを用いたスマートコントラクト開発
・truffleを用いたスマートコントラクトのデプロイ
・reactを用いたフロントエンド開発
今回のコントラクト編では上のうち2つを行います.
作るアプリケーション
今回作成するアプリケーションはブロックチェーン上に自分の身長と体重を記録するだけのシンプルなものです.アプリケーションに凝らずにDapps開発の流れを掴むことを目的としています.
remixでスマートコントラクト開発
Remixはブラウザ上でスマートコントラクトを作成し、テストができるツールです. 左上の+マークをクリックしてファイルを適当な名前で作成し、コントラクトを書いていきます.
コントラクト作成
pragma solidity ^0.5.0;contract Recorder {
struct Data {
uint256 weight;
uint256 height;
uint256 time;
}
address[] public users;
mapping (address => Data) userToData;
function writeData(uint256 weight, uint256 height) public returns(bool) {
uint256 time = block.timestamp;
if(!isUserExist(msg.sender)) {
users.push(msg.sender);
}
userToData[msg.sender].weight = weight;
userToData[msg.sender].height = height;
userToData[msg.sender].time = time;
return true;
}
function viewData(address user) public view returns(uint256, uint256, uint256) {
uint256 weight = userToData[user].weight;
uint256 height = userToData[user].height;
uint256 time = userToData[user].time;
return (weight, height, time);
}
function isUserExist(address user) public view returns(bool) {
for(uint i = 0; i < users.length; i++) {
if(users[i] == user) {
return true;
}
}
return false;
}
}
一つずつみていきましょう.
struct Data {
uint256 weight;
uint256 height;
uint256 time;
}
これはデータの構造体です. 身長, 体重, 記録した時間を記録します. uint256
はデータの型で256bitまでの符号なし整数(自然数)を指定します.
address[] public users;
mapping (address => Data) userToData;
上は配列です. 身長と体重を記録したユーザーのETHアドレスを保存します. solidityにはaddress型というデータ型があります.
配列の下はデータとデータを紐付けるためのものです. 例えば, userToData(ETHアドレス)
とすると上で定義したData構造体にアクセスできます.
function writeData(uint256 weight, uint256 height) public returns(bool) {
uint256 time = block.timestamp;
if(!isUserExist(msg.sender)) {
users.push(msg.sender);
}
userToData[msg.sender].weight = weight;
userToData[msg.sender].height = height;
userToData[msg.sender].time = time;
return true;
}
これは身長と体重, 記録した時間を記録する関数です. block.timestamp
でブロックに刻まれている現在時刻を取得できます. msg.sender
はトランザクションの送信者のアドレスです. ユーザーがすでに配列に含まれているかを確認して, ユーザー一覧の配列に追加します. userToData[msg.sender].weight = weight
という感じでアドレスに紐づく構造体にデータを追加します.
function isUserExist(address user) public view returns(bool) {
for(uint i = 0; i < users.length; i++) {
if(users[i] == user) {
return true;
}
}
return false;
}
上はユーザーが配列に含まれているかどうかを調べる関数です.
function viewData(address user) public view returns(uint256, uint256, uint256) {
uint256 weight = userToData[user].weight;
uint256 height = userToData[user].height;
uint256 time = userToData[user].time;
return (weight, height, time);
}
アドレスに紐づくデータをみるための関数です. userToData[user].weight
という感じで, 構造体に記録されているデータを取得します. 最後に身長, 体重, 記録した時間を返します.
コントラクトのテスト
まず, 右上にCompile, Run, Analysis…などの項目が並んでいると思います. Compileを選択し, Select new compiler version
から以下の画像で選択されている”0.5.0+commit. 1d4f565a”を選択します. それ以上のバージョンなら問題ないと思います.
選択したら, 自動でコンパイルしてくれます. 次に上の項目からRunを選択します. Environment
を JavaScriptVMに設定, コントラクトはRecorderを選択し, Deploy
をクリックします.
Deploy
に成功すると以下のように表示されます.
次に関数をテストしていきます.
まず, writeData
に 180, 78
と入力し, writeData
をクリックします.
関数の呼び出しが成功します. 次に users
に 0
を入力し, users
をクリックします. これは users
の配列の0番目の要素を取得しています. すると先ほど writeData
を実行したアドレスが表示されます. そのアドレスをコピーして viewData
に入力します. そして viewData
をクリックすると登録したデータが呼び出されます.
ここではテストは以上としておきますが, 色々試してみてください.
Truffleでスマートコントラクトをデプロイ
Truffleはスマートコントラクトをテストしたり, デプロイしたりできるツールです. 今回はrinkebyテストネットにコントラクトをデプロイするためだけに使います.
nodeとnpmはインストールされていることを前提に話を進めます.
私のバージョン
・node — v10.14.2
・npm — 6.9.0
インストールされていない方はこちらの記事などを参考にしてください.
まずTruffleのコマンドラインツールをインストールします.
$ npm install -g truffle
ディレクトリを作って移動し,truffle init
します. このコマンドで色々ファイルが生成されます. その後 truffle-hdwallet-provider
をインストールしてください.
$ mkdir dapps-sample
$ cd dapps-sample
$ truffle init
$ npm i --save truffle-hdwallet-provider
infuraエンドポイント& ニーモニック取得
次にinfuraでプロジェクトを作ります. infuraはEthereumノードを提供してくれるツールです. infuraを利用することでEthereumブロックチェーンに接続できるようになります.
ユーザー登録を済ませたらプロジェクトを作成し, VIEW PROJECT
をクリックします. KEYS
の ENDPOINT
で RINKEBY
を選択します. このエンドポイントはあとで使用します.
次にメタマスクのニーモニックを取得します. メタマスク を起動して右上のアカウントマークをクリック, 設定, Security & Privacyを選択. パスフレーズを表示をクリックし, ニーモニックを取得します.
[注意!]ニーモニックの流出は秘密鍵の流出を意味するので, 絶対にGithubなどにそのまま公開しないようにしましょう!
いろいろファイル作成 & 編集
dapps-sample
ディレクトリ下の truffle-config.js
を編集します.
以下をコピペして先ほど取得したメタマスク のニーモニックとinfuraのエンドポイントを指定の場所に貼ります.
var HDWalletProvider = require("truffle-hdwallet-provider");var mnemonic = 'メタマスクのニーモニック';var accessToken = 'infuraのエンドポイント';const gas = 4000000;const gasPrice = 1000000000 * 60;module.exports = {
networks: {
rinkeby: {
provider: function () {
return new HDWalletProvider(
mnemonic,
accessToken
);
},
network_id: 4,
gas: gas,
gasPrice: gasPrice,
skipDryRun: true
}
},
compilers: {
solc: {
version: "0.5.2",
}
}
};
次に dapps-sample
下の migrations
ディレクトリに 2_deploy_contract.js
というファイルを作成してください. 以下のように編集します.
var contract = artifacts.require('Recorder');module.exports = function(deployer) {
deployer.deploy(contract)
};
最後にdapps-sample
下の contract
ディレクトリに Recorder.sol
というファイルを作成し, Remixで作成したコントラクトを貼ります.
pragma solidity ^0.5.0;contract Recorder {
struct Data {
uint256 weight;
uint256 height;
uint256 time;
}
address[] public users;
mapping (address => Data) userToData;
function writeData(uint256 weight, uint256 height) public returns(bool) {
uint256 time = block.timestamp;
if(!isUserExist(msg.sender)) {
users.push(msg.sender);
}
userToData[msg.sender].weight = weight;
userToData[msg.sender].height = height;
userToData[msg.sender].time = time;
return true;
}
function viewData(address user) public view returns(uint256, uint256, uint256) {
uint256 weight = userToData[user].weight;
uint256 height = userToData[user].height;
uint256 time = userToData[user].time;
return (weight, height, time);
}
function isUserExist(address user) public view returns(bool) {
for(uint i = 0; i < users.length; i++) {
if(users[i] == user) {
return true;
}
}
return false;
}
}
最終的にこのようなファイル構成になっています.
.
├── contracts
│ ├── Migrations.sol
│ └── Recorder.sol
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contract.js
├── test
└── truffle-config.js3 directories, 5 files
Rinkebyネットワークへのデプロイ
ではコントラクトをデプロイします. rinkebyのETHを持っていないとデプロイ時にGAS代が足りずにはじかれるので、ある程度持っていない人は以下から補充しましょう.
補充したら以下のコマンドを実行します. 極度testnet(デプロイしなさい)!
$ truffle compile
$ truffle migrate --network rinkeby --reset
このように返ってきたらデプロイ成功です!
あなたが作成したスマートコントラクトがブロックチェーンにデプロイされました!
Deploying 'Recorder'
--------------------
> transaction hash: 0x93ec8eab47ddc572f4a153fbb892d36a84a5dec73aa988528c489b90bbab6bf5
> Blocks: 0 Seconds: 9
> contract address: 0x685B279Fcdd9Fc637512ab05B7857aBCCa0EbEF7
> account: 0x9C95Be0EC32C4Da4Aba2e765593F697a899098ff
> balance: 19.767089046
> gas used: 411184
> gas price: 60 gwei
> value sent: 0 ETH
> total cost: 0.02467104 ETH> Saving artifacts
-------------------------------------
> Total cost: 0.02467104 ETHSummary
=======
> Total deployments: 2
> Final cost: 0.04106076 ETH
最後に
今回のコントラクト編ではスマートコントラクトの開発, Rinkebyネットワークへのデプロイを行いました. フロントエンド編ではReactを用いてフロントを作っていきます!
今後も,Acompanyからブロックチェーンに関する記事を投稿していきますので,ぜひfollowしていただけると嬉しいです.
Happy Hacking 😎!