💻
[Blockchain]DApps開発を通してEthereum開発の基礎知識を洗い出してみた
Ethereumの概要
- Ethereum
- BitcoinなどのBlockchainの一種
- EthereumのBlockchainで流通できるEtherが存在する
- Public/Privateなどの種類がある
- Public:全世界のnodeからアクセスできる
- Main Net:Etherに金銭的な価値がある(取引所などで扱える)
- Test Net:Etherに金銭的な価値がない(取引所などで扱えない)
- ※技術的にはMain Netと全く同じ
- Private:特定のnodeのみで構成したり、ローカルで起動したりする
- Public:全世界のnodeからアクセスできる
Contractの概要
- Contract(狭義)
- Ethereum Blockchainで起動できる単純なプログラム
- Ethereum Blockchainにアドレスを持って存在することができる、データ(状態)とコード(関数)の集合体
- Solidityという高級言語で開発できる
- Object指向言語でいう
Singleton Object
的なあれ- 「Contract = Ethereum Blockchain上で唯一の
Singleton Object
」のイメージ
- 「Contract = Ethereum Blockchain上で唯一の
Layerとは何か?
- Layer0
- 異なるBlockchain ネットワークを接続するプロトコル
- 互換性のないBlockchain(EthereumとBitcoinなど)同士の通信(データの送受信)などが可能
- Layer1
- Bitcoin、Ethereum、Solanaなどの基盤となるBlockchainネットワーク
- 他のネットワークを必要とせず、取引を検証し確定することが可能
- Layer2
- トランザクションをLayer1外部(オフチェーン)で処理することで、スケーラビリティ(拡張性)問題を解消するソリューション
- 例えばLayer1であるEthereumが約10rpsなのに対し、Layer2のPolygonは約5000rpsと言われる
- Layer3
- Blockchian基盤上で動作するSmart Contractを使用したアプリケーション(DApps)
- ERC20(暗号資産)やERC721(NFT)も含まれる
引用元:https://medium.com/@nick.5montana/blockchain-layers-l0-l1-l2-l3-in-a-diagram-569162398db
ホスティングサービスとは何か?
- PublicなBlockchain(MainnetとTestnet)に対し、Contractのデプロイ/トランザクションの確認などを行うためのBlockchain Nodeをホストするサービス
- 自前でクラウドサービス(AWSやGCP)にBlockchain Node用の仮想サーバーを立てる労力を省くことができる
- Testnetにデプロイして動作確認をするくらいであれば、無料で使用できる
- 以下が主要なサービス
Testnetとは何か
- 流通通貨に価値はないが、Publicに公開されているBlockchain基盤
- アルゴリズムやBlockchain自体のソースコードに違いは(ほぼ)ないが、Testnet上の流通通貨に価値はない(市場で取引がされていない)
- Ethereumには、Ropsten/Kovan/Rinkeby/Goerliなど複数のTestnetが存在する
- Testnet上の流通通貨は、Faucetと呼ばれる発行体から流通通貨を得る必要がある
- ex) RinkebyのFaucetサイト
開発で使用するツールなど
(情報古め)
-
Ganache
- ローカルで小規模にPrivate Ethereumブロックチェーンを構築できるツール
- Solidityで開発したContractを、ローカルで実行する際に必要
-
Truffle
- SolidityによるEthereum開発フレームワーク
- Solidityのコンパイル、テスト、デプロイを数コマンドで実行できる
- RubyのRails的なやつ
-
OpenZeppelin
- Ethereum開発で必要なInterfaceなどを提供してくれるライブラリ
- ERC20(仮想通貨)やERC721(NFT)のInterfaceが定義されている
- OpenZeppelinを使用すると、ERC20の実装が簡単にできる(※後述)
-
Metamask
- Ethereumブッロクチェーンのアカウント管理ウォレット
- Ethereumブッロクチェーンに関連する仮想通貨(ERC20)を管理できるChrome拡張アプリ
- Ethereumブロックチェーンに関連するDAppsを使用する際に必要となる
作った目的
- DApps開発を通して、Solidityの「設計→実装→デプロイ」までの一連の開発フローを学習する
- Solidityに関連する開発環境/各種ツール/技術仕様を大雑把に理解する
- Ethereumを通して、Blockchain技術で何が実現できるのかを大雑把に把握する
デモ
"sample0"アカウントと"sample1"アカウントのOppay Token(OTK)残高
"sample0"アカウントから"sample1"アカウントに100Oppayを送金
"sample0"アカウントのOpapy残高が800Oppayに減少
"Sample1"アカウントのOppai残高が200Oppayに増加
やったこと
ざっくり
- Ethereumと互換性のあるOppay Tokenを実装した
- WebからOppay Tokenを送金/残高確認できる機能を開発した
詳細
- Solidityで実装したContractを、ローカルで起動したPrivate Ethereum NetowrkであるGanacheにデプロイした
- ERC20規格に準じたContractを実装し、トークンを新規発行した
- Front End(React)にweb3.jsを導入し、EthereumにデプロイされたContractと連携させた
使用したツール
-
Ganache
- ローカルで小規模にPrivate Ethereumブロックチェーンを構築できるツール
- Solidityで開発したContractを、ローカルで実行する際に必要
-
Truffle
- SolidityによるEthereum開発フレームワーク
- Solidityのコンパイル、テスト、デプロイを数コマンドで実行できる
- RubyのRails的なやつ
-
OpenZeppelin
- Ethereum開発で必要なInterfaceなどを提供してくれるライブラリ
- ERC20(仮想通貨)やERC721(NFT)のInterfaceが定義されている
- OpenZeppelinを使用すると、ERC20の実装が簡単にできる(※後述)
-
Metamask
- Ethereumブッロクチェーンのアカウント管理ウォレット
- Ethereumブッロクチェーンに関連する仮想通貨(ERC20)を管理できるChrome拡張アプリ
- Ethereumブロックチェーンに関連するDAppsを使用する際に必要となる
アプリ全体像
構成
AWSでホストされているBack Endアプリが、Ethereum Blockchain上のContractに代替されたイメージ
機能
- 残高照会機能
- Metamaskでログインしているアカウントの、Oppai Token残高を照会する
- 送金機能
- 入力されたアドレスに、入力された量のOppay Tokenを送金する
- 残高以上の量のOppay Tokenが送金された場合は、トランザクションを失敗させる
開発フロー
※開発ツール(ganache等)が既にインストールされている前提
- DAppsアプリ用のディレクトリ作成
mkdir dapps-sample
cd ./dapps-sample
- truffleフレームワークで用意された
React Box
で、Reactアプリを作成- Boxとは、truffleで予め用意されたサンプルアプリケーション
- Angular、Vueなどのサンプルアプリなどが用意されている
truffle unbox react
- SolidityコードとReactコードを微修正
-
dapps-sample/contracts
配下にOppayToken.solを作成し、以下のように実装する- 独自の仮想通貨を発行するのに必要な実装は、このたった9行のみ
-
dapps-sample/migrations
配下の2_deploy_contracts.jsを以下のように編集する -
dapps-sample/client/src
配下のApp.jsを以下のように編集する
-
pragma solidity >=0.4.21 <8.10.0;
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
contract OppayToken is ERC20 {
constructor() ERC20("Oppay Token", "OTK") {
_mint(msg.sender, 1000*10**18);
}
}
var OppayToken = artifacts.require("./OppayToken.sol");
module.exports = function(deployer) { deployer.deploy(OppayToken);};
import React, { Component } from "react";
import IrieTokenContract from "./contracts/IrieToken.json";
import getWeb3 from "./getWeb3";
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import "./App.css";
class App extends Component {
state = {
storageValue: 0,
web3: null,
accounts: null,
contract: null,
accountId: '',
itk: 0,
balance: 0
};
componentDidMount = async () => {
try {
const web3 = await getWeb3();
const accounts = await web3.eth.getAccounts();
const networkId = await web3.eth.net.getId();
const deployedNetwork = IrieTokenContract.networks[networkId];
const instance = new web3.eth.Contract(
IrieTokenContract.abi,
deployedNetwork && deployedNetwork.address,
);
const response = await instance.methods.balanceOf(accounts[0]).call();
this.setState({ web3, accounts, contract: instance, balance: response / (10 * 10 ** 17) });
} catch (error) {
alert(`Failed to load web3, accounts, or contract. Check console for details.`);
}
};
handleChangeInputAccountId = (e) => {
this.setState({ accountId: e.target.value });
}
handleChangeInputItk = (e) => {
this.setState({ itk: e.target.value });
}
onClickSendButton = async () => {
const { accounts, contract, accountId, itk } = this.state;
await contract.methods.transfer(accountId, (itk * 10 ** 18).toString()).send({ from: accounts[0] });
}
render() {
if (!this.state.web3) {
return <div>Loading Web3, accounts, and contract...</div>;
}
return (
<div className="App">
<h1>Dapps Sample</h1>
<p>Balance Of Your Oppay Token(OTK): <strong>{this.state.balance}</strong></p>
<br />
<p>Send <strong>Oppay</strong> </p>
<TextField
placeholder="Account ID"
value={this.state.accountId}
onChange={this.handleChangeInputAccountId}
required
/>
<br />
<br />
<TextField
label="OTK"
placeholder="OTK"
type="number"
value={this.state.itk}
onChange={this.handleChangeInputItk}
required
/>
<br />
<br />
<Button
variant="contained"
color="primary"
size="medium"
onClick={this.onClickSendButton}
>
send
</Button>
</div>
);
}
}
export default App;
- Ethereum Blockchainとの接続情報を修正する
-
dapps-sample
配下のtruffle-config.jsを以下のように修正する
-
const HDWalletProvider = require("truffle-hdwallet-provider");
const path = require("path");
module.exports = {
contracts_build_directory: path.join(__dirname, "client/src/contracts"),
networks: {
develop: {
host: "127.0.0.1",
port: 7545,
network_id: "5777"
}
},
compilers: {
solc: {
version: "0.8.13"
}
}
};
- Ganache GUIを起動し、サンプルアプリと連動させる
- Ethereum Blockchainに接続し、Solidityのコンパイルとデプロイを行う
truffle develop
※以下はGanacheによりローカルで起動したEthereum Blockchainに接続した状態
truffle(develop)> compile
truffle(develop)> migrate
- MetamaskにGanacheで作成したアカウントを登録する
- 一番上のアカウントと、その他のアカウントの秘密鍵を取得する
- 取得した秘密鍵により、Metamaskでアカウントをインポートする
- Metamaskが管理しているNetworkに、Localで起動しているPrivate Ethereum Blockchainを登録する
- MetamaskにOppay Tokenを認識させる
- GanacheからOppai TokenのContract Addressを取得する
- Metamaskに上記のAddressを登録し、Oppay Tokenを管理できるように設定する
- Reactアプリを起動する
-
dapps-sample/client
配下に移動し、Reactを起動する
-
cd ./dapps-sample/client
npm run start
何が凄いのか
- たった60分ほどで、グローバルで非中央集権的な送金システムが構築できる
- Blockchain技術を用いることで、グローバルに"価値"をやりとりできるシステムが容易に構築できる
- ContractはBlockchainに参加している全てのNodeに保存されるため、システムがとまることがない
- たった60分ほどで、Ethereumに準拠した独自のトークンが発行できる
- 独自の機能を有したトークンや、特定のコミュニティのみでやりとりできるトークンが容易に構築できる
- トークンを媒介とすることで、Web2.0で成し遂げられなかった、インターネット上でのエコノミー構築が実現可能になる
次回予告
- Oppay Tokenで売買できるOppay NFTを作成する
- Public Ethereum Blockchain(Ropsten Test Net)にOppai Token/NFT関連のDAppsをデプロイする
- AWS Managed Blockchain or INFURAを使用してデプロイしてみる
Discussion