0から作るDapps入門 コントラクト編

Junki Yuasa
Acompany
Published in
16 min readMay 23, 2019

--

はじめに

みなさん、こんにちは株式会社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 に成功すると以下のように表示されます.

次に関数をテストしていきます.
まず, writeData180, 78 と入力し, writeData をクリックします.

関数の呼び出しが成功します. 次に users0 を入力し, 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 をクリックします. KEYSENDPOINTRINKEBY を選択します. このエンドポイントはあとで使用します.

次にメタマスクのニーモニックを取得します. メタマスク を起動して右上のアカウントマークをクリック, 設定, 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.js
3 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 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0.04106076 ETH

最後に

今回のコントラクト編ではスマートコントラクトの開発, Rinkebyネットワークへのデプロイを行いました. フロントエンド編ではReactを用いてフロントを作っていきます!

今後も,Acompanyからブロックチェーンに関する記事を投稿していきますので,ぜひfollowしていただけると嬉しいです.

Happy Hacking 😎!

--

--