スマートコントラクトに興味があるのでEthereumを使ったWebアプリを開発するために、Ethereum Pet Shopというチュートリアルをやりました。

【概要】

【開発手順】

1.事前準備インストール

node.js https://qiita.com/jaxx2104/items/2277cec77850f2d83c7a

npm

Git https://qiita.com/megu_ma/items/e459e62821bdd6dbaccb

2.Truffleのインストール

下記のコマンドをターミナルで実行すればインストールされます。

npm install -g truffle

インストール終了したら、確認のため下記コマンドを実行します。

truffle version

以下のように表示されればインストールが完了しています。

3.『ETHEREUM PET SHOP』のダウンロード

事前準備は完了したのでチュートリアルをやっていきます。

まずは好きなところにTruffleのプロジェクトファイル(アプリケーション)を保存するディレクトリ(フォルダ)を作ります。

ここではdesktopに『pet-shop-tutorial』という名前でディレクトリを作成しました。

cd desktop
mkdir pet-shop-tutorial
cd pet-shop-tutorial

次に、このチュートリアル専用にpet-shopというTruffle boxを開梱します。

truffle unbox pet-shop

すると、pet-shop-tutorialディレクトリ内にダウンロードされます。

4.スマートコントラクトを書く

バックエンドロジックおよびストレージとして機能するスマートコントラクトを作成してDappを開始します。

まずはcontracts/ディレクトリにAdoption.solファイルを作成します。

cd contracts
touch Adoption.sol

次に、作成したファイルAdoption.solを開き下記コードを入力します。

pragma solidity ^0.5.0;
contract Adoption {
}

変数設定

次の行に下記変数を追加します

address[16]public adopters;

最初の機能:ペットの採用

下記のコードを入力

// Adopting a pet
function adopt(uint petId) public returns (uint) {
 require(petId >= 0 && petId <= 15);
 adopters[petId] = msg.sender;
 return petId;
}

2番目の機能:アダプターの取得

// Retrieving the adopters
function getAdopters() public view returns (address[16] memory) {
  return adopters;
}

ここで、Parser Errorが表示されることがあります。

以下のように入力されていることを確認してください。

この時に{ }で囲われていなかったため、気づくまでに時間をかなり取られました。

5.スマートコントラクトのコンパイルと移行

スマートコントラクトを作成したので、それをコンパイルして移行します。

編集

ターミナルでルートディレクトリにいることを確認して下記コードを実行します。

truffle compile

次のような出力が表示されます。

移行

契約を正常にコンパイルできたので、今度は契約をブロックチェーンに移行します。

migrations/ディレクトリに2_deploy_contracts.jsという名前で新しいファイルを作成します。

cd migrations
touch 2_deploy_contracts.js

次に、2_deploy_contracts.jsファイルに下記コードを入力します。

var Adoption = artifacts.require("Adoption");
module.exports = function(deployer) {
 deployer.deploy(Adoption);
};

契約をブロックチェーンに移行する前に、ブロックチェーンを実行する必要があります。

このチュートリアルではGanacheを使用します。

Ganacheをダウンロードしてください。

アプリを開いたら、QUICKSTARTを押します。

初期画面は以下のようになっています。

画面左上のCURRENT BLOCKが「0」であることを確認しましょう。

ターミナルに戻り、契約をブロックチェーンに移行します。

下記コマンドを実行します。

truffle migrate

次のような出力が表示されます。

順番に実行されている移行を確認でき、その後、各移行に関連する情報が表示されます。

先ほど「0」であることを確認したCURRENT BLOCKが「4」になっています。

これは、元々100ETHありましたが、移行のトランザクションコストにより、減ったことを意味しています。

これで最初のスマートコントラクトを作成し、ローカルで実行中のブロックチェーンに展開しました。

次に、スマートコントラクトとやり取りして、目的通りに動作することを確認します。

6.スマートコントラクトのテスト

Truffleではスマートコントラクトのテストに関して柔軟です。

テストはJavaScriptかSolidityのいずれかで作成できます。

今回はSolidityでテストを作成します。

はじめに、test/ディレクトリにTestAdoption.solファイルを作成します。

次にTestAdoption.solファイルに下記コードを入力します。

pragma solidity ^0.5.0;
import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";
contract TestAdoption {
 // The address of the adoption contract to be tested
 Adoption adoption = Adoption(DeployedAddresses.Adoption());
 // The id of the pet that will be used for testing
 uint expectedPetId = 8;
 //The expected owner of adopted pet is this contract
 address expectedAdopter = address(this);
 // Testing the adopt() function
 function testUserCanAdoptPet() public {
   uint returnedId = adoption.adopt(expectedPetId);
   Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
 }
 // Testing retrieval of a single pet's owner
 function testGetAdopterAddressByPetId() public {
   address adopter = adoption.adopters(expectedPetId);
   Assert.equal(adopter, expectedAdopter, "Owner of the expected pet should be this contract");
 }
 // Testing retrieval of all pet owners
 function testGetAdopterAddressByPetIdInArray() public {
   // Store adopters in memory rather than contract's storage
   address[16] memory adopters = adoption.getAdopters();
   Assert.equal(adopters[expectedPetId], expectedAdopter, "Owner of the expected pet should be this contract");
 }
}

先ほどのAdoption.solと同様にParser Errorに気をつけてください。

テストを実行

ターミナルで以下のコマンドを実行してください

truffle test

うまくいくと以下のように表示されます。

7.スマートコントラクトのやり取りをするためのUIを作成

web3のインスタンス化

/src/js/app.jsのファイルを開きます。

次に、initWeb3のコメントを削除し下記コードを入力

// Modern dapp browsers...
if (window.ethereum) {
  App.web3Provider = window.ethereum;
  try {
    // Request account access
    await window.ethereum.enable();
  } catch (error) {
    // User denied account access...
    console.error("User denied account access")
  }
}
// Legacy dapp browsers...
else if (window.web3) {
  App.web3Provider = window.web3.currentProvider;
}
// If no injected web3 instance is detected, fall back to Ganache
else {
  App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
}
web3 = new Web3(App.web3Provider);

契約のインスタンス化

initContractのコメントを削除し、下記のコードを入力

$.getJSON('Adoption.json', function(data) {
   // Get the necessary contract artifact file and instantiate it with truffle-contract
   var AdoptionArtifact = data;
   App.contracts.Adoption = TruffleContract(AdoptionArtifact);

   // Set the provider for our contract
   App.contracts.Adoption.setProvider(App.web3Provider);

   // Use our contract to retrieve and mark the adopted pets
   return App.markAdopted();
 });

採用(adopt)されたペットの入手とUIの更新

markAdoptedのコメントを削除し、下記コードを入力

var adoptionInstance;
 App.contracts.Adoption.deployed().then(function(instance) {
   adoptionInstance = instance;

   return adoptionInstance.getAdopters.call();
 }).then(function(adopters) {
   for (i = 0; i < adopters.length; i++) {
     if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
       $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
     }
   }
 }).catch(function(err) {
   console.log(err.message);
 }); 

accept()関数の処理

handleAdoptのコメントを削除し、下記コードを入力

var adoptionInstance;
 web3.eth.getAccounts(function(error, accounts) {
   if (error) {
     console.log(error);
   }

   var account = accounts[0];

   App.contracts.Adoption.deployed().then(function(instance) {
     adoptionInstance = instance;

     // Execute adopt as a transaction by sending account
     return adoptionInstance.adopt(petId, {from: account});
   }).then(function(result) {
     return App.markAdopted();
   }).catch(function(err) {
     console.log(err.message);
   });
 });

これで、Dappを使用する準備が整いました。

8.MetaMaskのインストールと構成

こちらはEHT独自コイン発行の記事で紹介しますので、インストールされていることを前提とします。

9.Meta Mask ✖️ Ganache連携

まず、MetaMaskのトップ画面を開きます。

次に右上の「Ehereumメインネット」をクリック

カスタムRPCを選択します

設定画面に移ります。

Network Name:Ganache

New RPC URL:http://127.0.0.1:7545

とそれぞれ入力して「保存」します。

「Gnache」を再び開き、鍵のマークをクリックします。

PRIVATE KEYをコピーします。

MetaMaskの画面に移ります。

「アカウントのインポート」をクリック

赤枠の所に先ほどコピーした秘密鍵(PRIVATE KYE)をペーストし、追加します。

MetaMaskとGanacheのアカウントを比較して、「ADDRESS」と「BALANCE」に間違いがないか確認します。

10.dappを使用する

最後にローカルWebサーバーを起動します。

下記コマンドを実行してください。

npm run dev

以下のような画面が表示されれば成功です。

さらにどれかの犬を「Adopt」してみます。

【メイン参考記事】

こじりょーの研究所 Dapps作成手順をイチから学べる!Truffleの「イーサリアム・ペットショップ」

ETHEREUM PET SHOP — YOUR FIRST DAPP

※英語のサイトなので下記の検索バー横のGoogle翻訳を使いながら進めました

※Docker使用 【Truffleチュートリアル】Ethereum Pet Shopを作成してみた

【サブ参考記事】

Meta Mask + Ganache(https://qiita.com/perfCat/items/6a259723dd023587c844

Meta MaskのUIが新しくなっていたため、Ganacheとの連携時、入力画面が変化している。

【エラー解決方法】

npm installでError:EACCESがでたら

Truffleのテストが実行できないエラーに対応する

Parser Error(参照

コントラクトを記入している時に2度エラーが発生しました。

原因は{}括弧が一致していないことにあります。

エラー表示が出たら、行数を確認して括弧が一致しているか確認してください。

3.コマンド入力

【ターミナルコマンド】

ターミナルよく使うコマンド

・node.jsを終了:process.exit();

・実行中画面終了:conytol+C

・mv

・touch

・cd

4.学習

【検索ワード】

・Dapps

・ERC20

・My Ether Wallet (MEW)

・Meta Mask

・AirDrop

・solidity

・truffle

・コンパイル

・バイトコード

仮想的なコンピュータ(VM:Virtual Machine、仮想マシン)のための命令コードの体型

・EVM(Ethereum Virtual Machine)

ETHの仮想マシン

・単一障害点

その単一箇所が働かないと、システム全体が障害となるような箇所を指す。

・ルートディレクトリ

ファイル階層における最初または最上位のディレクトリのこと。

全ての枝の開始点である木の幹に例えることができる。

・assertion(アサーション)

プログラミング言語などの仕様・機能の一つで、プログラムの前提として満たされるべき条件を記述し、実行時にそれが満たされていない場合にエラーや例外を発生させたり、メッセージを表示して処理を中断したりする機能をアサーションチェックあるいは略してアサーションという。プログラム中のバグや不具合、論理の矛盾を発見するのに使われる。

【参考記事】

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です