クリプトゾンビ チャプター 3: コントラクトへのアクセスをやり直す
Web3.jsをプロジェクトに追加
// NPMを使用 npm install web3 // Yarnを使用 yarn add web3 //.jsファイルをgithubからダウンロードした場合 を追加。
Web3.js初期化
イーサリアムは ノード で構成されていて、全ノードが同じデータのコピーをシェアしている。 Web3.jsにおけるWeb3プロバイダの設定は、 どのノード に読み書きを処理させるかを設定すること。 (ウェブアプリでAPIコールをするためにリモートのウェブサーバーのURLを設定するようなもの) 例えばInfuraをWeb3プロバイダとして使うには、次のようにWeb3をセットアップする必要がある。 var web3 = new Web3(new Web3.providers.WebsocketProvider("wss://mainnet.infura.io/ws"));
上記の設定でイーサリアム(一般的なブロックチェーン)にアクセスできるが、トランザクションにデジタル署名をするために公開鍵/秘密鍵の管理が必要になる。 アプリのフロントエンドでユーザーの秘密鍵を自ら管理しようとするのは良い考えではないのでMetamaskを使用する。
infuraとMetamaskの利用設定。
- Infura とは、高速な読み込みのためのキャッシュレイヤーをもつイーサリアム・ノードのセットを保持するサービスで、API経由でこれらノードに無料でアクセスで可能。
- MetamaskはChromeとFirefoxのブラウザ拡張機能で、ユーザーは自分のイーサリアム・アカウントと秘密鍵を安全に管理し、そのアカウントでWeb3.jsを使用しているウェブサイトとやりとりすることが可能
- Metamaskは、InfuraのサーバーをWeb3プロバイダーとして使用しますが、Web3プロバイダーを選択するオプションも提供。
Metamaskのweb3プロバイダの使用
Metamaskは、web3プロバイダをJavaScriptのグローバルオブジェクト web3のブラウザに導入する。なので web3が存在するか、そしてプロバイダとしてweb3.currentProviderを使用しているかをアプリがチェックすることができる。
window.addEventListener('load', function() { // Web3がブラウザにインジェクトされているかチェック (Mist/MetaMask) if (typeof web3 !== 'undefined') { // Mist/MetaMaskのプロバイダの使用 web3js = new Web3(web3.currentProvider); } else { // ユーザーがweb3を持たない場合の対処。 // アプリを使用するためにMetamaskをインストールするよう // 伝えるメッセージを表示。 } // アプリのスタート&Web3.jsへの自由なアクセスが可能に: startApp() })
Web3.jsがコントラクトにアクセスするためにコントラクトの アドレスと ABI が必要
コントラクトアドレス
コントラクトをデプロイすると、永久に有効なイーサリアム上の固定アドレスが与えられる。
ABI
ABIとは、Application Binary Interfaceの略である。 基本的にこれはコントラクトのメソッドをJSON形式で表していて、関数コールをコントラクトが理解できるようフォーマットする方法を、Web3.jsに教える。
Web3.jsコントラクトのインスタンス化
コントラクトのアドレスとABIがわかったら、Web3でインスタンス化できる // web3jsコントラクトをmyContractにインスタンス化する var myContract = new web3js.eth.Contract(myABI, myContractAddress);
コントラクトの関数を呼び出す
インスタンス化したWeb3.jsでコントラクトの関数を呼び出す方法。 Web3.jsは、コントラクトの関数を呼び出すためにcallとsendの2つのメソッドをもつ。
CALLメソッド
callはview関数およびpure関数に使われる。 これはローカルのノードでのみ機能し、ブロックチェーン上のトランザクションを生成しない。 Web3.jsを使って、次のように123をパラメーターにしてスマートコントラクト内のmyMethodという名の関数をcallできる: myContract.methods.myMethod(123).call()
SENDメソッド
sendはトランザクションを生成し、ブロックチェーン上のデータを変更する。 viewまたはpureではない関数には、sendを使う必要がある。 Web3.jsを使って、次のように123をパラメーターにして、スマートコントラクト内のmyMethodという名の関数を呼び出しトランザクションをsendすることができる: myContract.methods.myMethod(123).send()
ここまでのクリプトゾンビのコード
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CryptoZombies front-end</title> <script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script language="javascript" type="text/javascript" src="web3.min.js"></script> <script language="javascript" type="text/javascript" src="cryptozombies_abi.js"></script> </head> <body> <script> var cryptoZombies; function startApp() { var cryptoZombiesAddress = "YOUR_CONTRACT_ADDRESS"; cryptoZombies = new web3js.eth.Contract(cryptoZombiesABI, cryptoZombiesAddress);//web3jsのインスタンス化 }
データ取得
上記の様なpublicで作成したzombies配列のデーターを取り出す。
下記コードはIDを受け取って、そのゾンビをコントラクトでクエリして結果を返す。
function getZombieDetails(id) { return cryptoZombies.methods.zombies(id).call(); } // 関数を呼び出し、その結果を処理する: getZombieDetails(15) .then(function(result) { console.log("Zombie 15: " + JSON.stringify(result)); });【参考】 JSON.stringify() メソッド:あるJavaScript のオブジェクトや値を JSON 文字列に変換する。 cryptoZombies.methods.zombies(id).call() 上記コードはWeb3プロバイダのノードと通信して、コントラクトにあるZombie[] public zombiesからデータとそのインデックスidを返している。 これは外部サーバーへのAPIコールのように非同期です。。 Web3はここでpromiseを返す
このPromiseがリゾルブするとWeb3プロバイダからの応答を受けとったという意味。
result は次のようなJavaScriptオブジェクトとなる
{ "name": "H4XF13LD MORRIS'S COOLER OLDER BROTHER", "dna": "1337133713371337", "level": "9999", "readyTime": "1522498671", "winCount": "999999999", "lossCount": "0" // Obviously. }
Promiseの基本動作
非同期処理を操作できる。
Promise は、JavaScript や Node.js において、非同期処理のコールバック関数をに記述するための仕組み
Promise は、待機(pending)、成功(fulfilled)、失敗(rejected)の3値を持つオブジェクトです。
非同期処理の成功時(resolve)、失敗時(reject)の処理を明示的に書くことが出来る
Promise オブジェクトは then(ok_callback, ng_callback) というメソッドを持ちます。
then() は、Promise が成功または失敗になるまで処理を受け流し、成功時に ok_callback を、失敗時に ng_callback をコールバック関数として呼び出します。
同期処理と非同期処理
同期処理 ↓ 非同期処理 → 結果が返ってきたら処理を続ける ↓ 同期処理(前の処理を待たなくてもOK) ↓ 同期処理
「getDate()」が日付を取得する処理で、「getYear()」がその日付から西暦を抽出する処理になります。
//日付データを取得する function getDate() { var date = new Date; } //日付データを元に西暦を取得する function getYear(data) { var year = data.getFullYear(); }
日付データを取得しないと「getYear()」が実行できない。
そこで、日付データを元にして処理をするためにgetDate()へ「コールバック」を実装する。
function getDate(callback) { callback(new Date); }
「getDate()」の引数にコールバックを設定すれば関数を指定できるようになる。
getDate(function(data) { getYear(data); });
「Promise」の使用と基本的な構文
Promiseオブジェクトを作成することで、簡潔に非同期処理を実現することができる。
newを使ってPromiseのインスタンスを作成し、その「返り値」としてPromiseオブジェクトを取得
var result = new Promise( )
Promise()の引数には関数を設定し、その中で行いたい処理を記述する
非同期処理の成功時(resolve)、失敗時(reject)の処理を明示的に書くことが出来る 非同期処理を平行・直列に実行させることが出来る var result = new Promise(function(resolve) { resolve(new Date); }) 関数の引数「resolve」に取得したい値を設定することで、非同期処理の結果を格納することができる。 変数「result」にはPromiseオブジェクトが格納される
「Promise」の使用と基本的な構文(もう一つの説明例)
Promiseの基本的な書き方1
Promiseはresolveとreject、ふたつの関数を引数に取ります。
resolve:処理が成功したときのメッセージを表示する関数
reject:処理が失敗したときのメッセージを表示する関数
「then」を使ったコールバック処理
Promiseは「then()」を使うことでコールバックのような処理を実現できます。
先ほど作成したPromiseは最終的に変数「result」にPromiseオブジェクトが格納されました。
このPromiseオブジェクトが格納された「result」から、例えば日付データを取得して西暦を取得するには次のよう記述できます
result.then( function(data){ console.log(data.getFullYear()); } );
Promiseの基本的な書き方2