はじめに
「Solidity とは何か? 開発環境を構築する!」で、Solidity の基礎を理解し、スマートコントラクトプロジェクトを作成する事が出来ました。
Dapps開発のフレームワークである「Truffle」の公式サイトで紹介されている「ETHEREUM PET SHOP」のチュートリアルをやってみて、実際にウェブ上に分散型アプリケーションを作成しましたので、備忘録としてまとめます。
「Truffle」以外にも Hardhat を使用して Dapps開発をする方法があります。
参考にした本
- マスタリング・イーサリアム ―スマートコントラクトとDAppの構築
- SolidityとEthereumによる実践スマートコントラクト開発 ―Truffle Suiteを用いた開発の基礎からデプロイまで
- 詳解 ビットコイン ―ゼロから設計する過程で学ぶデジタル通貨システム
- ブロックチェーン dapp&ゲーム開発入門 Solidityによるイーサリアム分散アプリプログラミング
ETHEREUM PET SHOP
ペットの「adopt」というボタンを押すとMetaMaskが起動し、表示された金額と手数料を確認してトランザクションが作成されETHで支払うことができます。
Truffle Box を使った Truffleプロジェクトの作成
pet-shop-tutorialというディレクトリを作り、このディレクトリ内で作業していきます。
$ mkdir pet-shop-tutorial
$ cd pet-shop-tutorial
今回はチュートリアルのためTruffle Boxというあらかじめ用意されたプロジェクトから作成します。
$ truffle unbox pet-shop
スマートコントラクトを書く
contracts/
ディレクトリの中に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;
}
// Retrieving the adopters
function getAdopters() public view returns (address[16] memory) {
return adopters;
}
}
truffle compile
を実行します。
マイグレーション
migrations/
ディレクトリの中に2_deploy_contracts.js
というマイグレーションファイルを作成します。
var Adoption = artifacts.require("Adoption");
module.exports = function(deploy) {
deploy.deploy(Adoption);
}
プライベートネットを作るツールとして、Ganache を利用します。
「Ganache」を起動した状態にして、ターミナルで truffle migrate
を実行します。
スマートコントラクトのテストコードの作成
「test」ディレクトリ内に「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");
}
}
truffle test
を実行します。
スマートコントラクトと連動したUIの作成
フロントエンド部分のロジックは src ディレクトリで、スマートコントラクトの連携する部分を実装する為に、 /src/js/app.js
ファイルを編集します。
App = {
web3Provider: null,
contracts: {},
init: async function() {
// Load pets.
$.getJSON('../pets.json', function(data) {
var petsRow = $('#petsRow');
var petTemplate = $('#petTemplate');
for (i = 0; i < data.length; i ++) {
petTemplate.find('.panel-title').text(data[i].name);
petTemplate.find('img').attr('src', data[i].picture);
petTemplate.find('.pet-breed').text(data[i].breed);
petTemplate.find('.pet-age').text(data[i].age);
petTemplate.find('.pet-location').text(data[i].location);
petTemplate.find('.btn-adopt').attr('data-id', data[i].id);
petsRow.append(petTemplate.html());
}
});
return await App.initWeb3();
},
initWeb3: async function() {
// Modern dapp browsers...
if (window.ethereum) {
App.web3Provider = window.ethereum;
try {
// Request account access
await window.ethereum.request({ method: "eth_requestAccounts" });;
} 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);
return App.initContract();
},
initContract: function() {
$.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();
});
return App.bindEvents();
},
bindEvents: function() {
$(document).on('click', '.btn-adopt', App.handleAdopt);
},
markAdopted: function() {
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);
});
},
handleAdopt: function(event) {
event.preventDefault();
var petId = parseInt($(event.target).data('id'));
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);
});
});
}
};
$(function() {
$(window).load(function() {
App.init();
});
});
MetaMaskとブロックチェーンを接続する
- 右上の丸いボタンをクリックし、マイアカウントの「設定」を選択する。
- 「ネットワーク」を選択する。
- 「ネットワーク追加」をクリックする。
- ネットワーク名とRPC URLに
http://127.0.0.1:7545
を入れる。 - チェーンIDに
1337
を入れる。 - 通貨記号に
ETH
を入れ「保存」を押す。 - 右上のXをクリックし設定を終了する。
- Ganacheのアカウント1の残高が反映されたことを確認する。
Dappの完成
npm run dev
を実行します。
Dappがブラウザ上で表示できます。「adopt」ボタンをクリックすると、MetaMaskによりトランザクションが送られ、ETH払いでペットを購入することができます。
まとめ
この記事では、truffle unbox pet-shop
で作成される、Truffleのチュートリアルで ETHEREUM PET SHOP の流れにそって、実際にウェブ上に分散型アプリケーションを作成する事が出来るようになりました。
今後は以下の内容をまとめる予定です。
公開されているテストネットワーク Görli testnet にデプロイする。