21
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Blockchain(ブロックチェーン)/ TokenEconomy(トークンエコノミー)Advent Calendar 2020

Day 9

【初めてのDApps開発①】ReactでシンプルなDAppsを作ってみよう!~コントラクト編~

Last updated at Posted at 2020-12-07

#はじめに
私はつい最近までブロックチェーンって何?なんか面白そう!ってゆー感じだったDApps開発初心者です。
この記事は、Ethereumで簡単なDappsを作ってDapps開発の流れを掴んでいくのが目的です。
自分が初めてDAppsを作る際にあったらよかったなあ〜と感じたロードマップを書きました。

この開発では以下の3つの項目を行っていきます。
3項目の内この記事では、1と2を行なっていきます。3については、【初めてのDApps開発②】の記事を参照して下さい。

1. Remixを用いたスマートコントラクト開発
2. Truffleを用いたスマートコントラクトのデプロイ

  1. Reactを用いたフロントエンド開発

#作っていくアプリケーション

今回作成するアプリケーションはブロックチェーン上に自分の氏名、年齢、趣味をブロックチェーン上に記録するだけのシンプルなものです。
URL: https://diarydapp-2a69c.web.app

完成するアプリケーションは、以下のものなります。

  1. 左側のフォームでユーザーの氏名、年齢、趣味を入力。入力された情報は、ブロックチェーン上に記録。
  2. 右側のフォームでユーザー情報の検索を行う。検索したいユーザーのアドレスを入力。
  3. 入力されたアドレスを持つユーザーの情報を表示。

スクリーンショット 2020-11-29 11.29.27.png

本記事では、スマートコントラクトの開発からデプロイまでを行っていきます!
では、一緒に開発を進めていきましょう!!

#Remixでスマートコントラクト開発

Remixはブラウザ上でスマートコントラクトを作成し、テストができるツールです。
リンクからRemixを開き、
左上の+マークをクリックしてファイルを作成し、コントラクトを書いていきます。

今回扱う言語Solidityについては、こちらを参考にしてください。
【Solidity入門】まず最初にSolidityで覚えるべき基本構文まとめ
Solidity公式ドキュメント

###コントラクト作成
今回使用するコントラクトは以下のものを使います。

Resister.sol
// コンパイラのバージョン指定(0.5.0以上)
pragma solidity ^0.5.0;

contract Resister {
    
    // アカウント情報
    struct Data {
        string name; // 名前
        uint256 age; // 年齢
        string hobby; // 趣味
    }
   
    address[] public users; // 全ユーザーのアドレスを格納

    mapping(address => Data) accounts;
    
    // アカウントを登録する関数
    function registerAccount(string memory _name, uint256 _age, string memory _hobby)
        public
        returns (bool)
    {
        //アカウントが登録されていなければ新規会員登録をする
        if (!isUserExist(msg.sender)) {
            users.push(msg.sender);
        }
        accounts[msg.sender].name = _name;
        accounts[msg.sender].age = _age;
        accounts[msg.sender].hobby = _hobby;
        return true;
    }

    // アカウントが登録されているかどうかを確認
    function isUserExist(address user) public view returns (bool) {
        for (uint256 i = 0; i < users.length; i++) {
            if (users[i] == user) {
                return true;
            }
        }
        return false;
    }

    // アカウント情報を読み込む
    function viewAccount(address user) public view returns (string memory, uint256, string memory) {
        string memory _name = accounts[user].name;
        uint256 _age = accounts[user].age;
        string memory _hobby = accounts[user].hobby;

        return (_name, _age, _hobby);
    }   
}

一つずつ見ていきましょう。

struct Data {
        string name; 
        uint256 age; 
        string hobby; 
    }

これはデータの構造体です。氏名、年齢、趣味を記録します。stringは文字列を指定します。uint256 はデータの型で256bitまでの符号なし整数(自然数)を指定します。

address[] public users; 

mapping(address => Data) accounts;

上は配列です。氏名、年齢、趣味を記録したユーザーのETHアドレスを保存します。 solidityにはaddress型というデータ型が存在します。

配列の下はデータとデータを紐付けるためのものです。例えば, accounts[ETHアドレス] とすると上で定義した任意のData構造体にアクセスできます。

function registerAccount(string memory _name, uint256 _age, string memory _hobby)
        public
        returns (bool)
    {
        if (!isUserExist(msg.sender)) {
            users.push(msg.sender);
        }
        accounts[msg.sender].name = _name;
        accounts[msg.sender].age = _age;
        accounts[msg.sender].hobby = _hobby;
        return true;
    }

これは、アカウント情報(氏名、年齢、趣味)の登録を行う関数です。
msg.sender はトランザクションの送信者のアドレスです。ユーザーがすでに配列に含まれているかを確認して, users[]に追加します。
accounts[msg.sender].name = _nameのようにしてアドレスに紐づく構造体にデータを追加します。

function isUserExist(address user) public view returns (bool) {
        for (uint256 i = 0; i < users.length; i++) {
            if (users[i] == user) {
                return true;
            }
        }
        return false;
    }

これは、ユーザーが配列(users[])に含まれているかどうかを調べる関数です。

function viewAccount(address user) public view returns (string memory, uint256, string memory) {
        string memory _name = accounts[user].name;
        uint256 _age = accounts[user].age;
        string memory _hobby = accounts[user].hobby;

        return (_name, _age, _hobby);
    }   

アドレスに紐づくデータをみるための関数です。
string memory _name = accounts[user].name のようにして, 構造体に記録されているデータを取得します。最後に氏名、年齢、趣味を返り値として返します。

###コントラクトのテスト
次は、コントラクトのテストを行っていきます。
まず最初に、画面左のSOLIDITY COMPILERタブに移動。COMPILERを以下の画像で選択されている0.5.0+commit. 1d4f565aに選択。それ以上のバージョンなら問題ないと思います。

選択したら、画面中部の青いボタンCompile Resister.solを押せばコンパイルしてくれます(⌘+Sでも可能)。
コンパイルが完了すると、画像のようにチェックマークが付きます。

スクリーンショット 2020-11-18 20.15.36.png

コンパイルが完了したら、次にDEPLOY & RUN TRANSACTIONSタブに移動します。
ENVIRONMENT を JavaScriptVMに設定, CONTRACTはResisterを選択し, Deploy をクリックします。

スクリーンショット 2020-11-18 20.35.00.png

次に関数をテストしていきます。
Deployed Contracts欄から
まず, resisterAccount に 適当に氏名、年齢、趣味を入力し, transactボタンをクリックします。

スクリーンショット 2020-11-18 20.37.09.png

以下のように表示されれば関数呼び出し成功です。
スクリーンショット 2020-11-18 21.03.15.png

次に users0 を入力し, callボタン をクリックします。これは usersの配列の0番目の要素を取得しています。すると先ほど resisterAccountを実行したアドレスが表示されます。
そのアドレスをコピーして viewAccount に入力します。そして call ボタンをクリックすると登録したデータが呼び出されます。

スクリーンショット 2020-11-18 21.18.08.png

以上のことが問題なく動作できればテストは完了です。

#Truffleでスマートコントラクトをデプロイ
Truffleはスマートコントラクトをテストしたり, デプロイしたりできるツールです。 今回はRinkebyテストネットにコントラクトをデプロイするためだけに使います。

今後nodeとnpmがインストールされていることを前提に話を進めます。
インストールされていない方はこちらの記事などを参考にしてください。
Mac用:買いたてのMacにNode.jsとnpmをインストール
Windows用:Node.js / npmをインストールする(for Windows)

↓私のバージョンです。

node:v14.2.0
npm:v6.14.8

まずTruffleのコマンドラインツールをインストールします。

$ npm install -g truffle

ディレクトリを作って移動し,truffle init します. このコマンドで色々ファイルが生成されます。その後 truffle-hdwallet-provider をインストールしてください。

$ mkdir dapps-deploy
$ cd dapps-deploy
$ truffle init
$ npm i --save truffle-hdwallet-provider

###infuraエンドポイント& ニーモニック取得
次にinfuraでプロジェクトを作ります。infuraはEthereumノードを提供してくれるツールです。infuraを利用することでEthereumブロックチェーンに接続できるようになります。

スクリーンショット 2020-11-21 15.55.47.png

ユーザー登録を済ませたらプロジェクトを作成。作成したプロジェクトに移動しSETTING画面へ。 KEYSENDPOINTRINKEBY を選択します。このエンドポイントはあとで使用します。

次に、Metamask のニーモニック(12個の英単語)を取得します。
45a7e0137f6e059636572f2b98db9db7.png

まず、Metamaskをインストールしましょう。
インストール後、Metamaskを起動して右上のアカウントマークをクリック→、設定→, Security & Privacyを選択。パスフレーズを表示をクリックし, ニーモニックを取得します。

※[注意]ニーモニックの流出は秘密鍵の流出を意味するので, 絶対にGithubなどにそのまま公開しないように!

###ファイルの作成&編集
dapps-deploy ディレクトリ下の 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 というファイルを作成してください。以下のように編集してください。
.require('ここで.solのコントラクト名を指定')

var contract = artifacts.require('Resister');
module.exports = function(deployer) {
  deployer.deploy(contract)
};

最後にdapps-deploy 下の contract ディレクトリに Resister.sol というファイルを作成し, Remixで作成したコントラクトを貼ります。

Resister.sol
pragma solidity ^0.5.0;

contract Resister {
    
    struct Data {
        string name; 
        uint256 age; 
        string hobby; 
    }
   
    address[] public users; 

    mapping(address => Data) accounts;
    
    function registerAccount(string memory _name, uint256 _age, string memory _hobby)
        public
        returns (bool)
    {
        if (!isUserExist(msg.sender)) {
            users.push(msg.sender);
        }
        accounts[msg.sender].name = _name;
        accounts[msg.sender].age = _age;
        accounts[msg.sender].hobby = _hobby;
        return true;
    }

    function isUserExist(address user) public view returns (bool) {
        for (uint256 i = 0; i < users.length; i++) {
            if (users[i] == user) {
                return true;
            }
        }
        return false;
    }

    function viewAccount(address user) public view returns (string memory, uint256, string memory) {
        string memory _name = accounts[user].name;
        uint256 _age = accounts[user].age;
        string memory _hobby = accounts[user].hobby;

        return (_name, _age, _hobby);
    }
    
}

最終的なファイル構成はこのようになっています。
スクリーンショット 2020-12-02 19.18.51.png

###Rinkebyネットワークへのデプロイ
いよいよコントラクトをデプロイします!
rinkebyのETHを持っていないとデプロイ時にGAS代が足りずにはじかれるので、ある程度ETHを持っている必要があります。
僕は以下の記事を参考にして補充しました。

EtherumのRinkebyテストネットでethを取得する

完了したら以下のコマンドを実行してください。

$ truffle compile
$ truffle migrate --network rinkeby --reset

このように返ってきたらデプロイ成功です!お疲れ様です!
あなたが作成したスマートコントラクトがブロックチェーンにデプロイされました!

スクリーンショット 2020-11-22 20.30.18.png

#最後に
ここまでで、コントラクトのデプロイまでを行うことができるはずです!!
次は、アプリケーションの見た目の部分であるフロントエンドの開発に取り掛かります。
続編となっている【初めてのDApps開発②】の記事も見てもらえると嬉しいです!

21
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?