見出し画像

Lisk SDK (lisk-client)を使いながらJavaScriptを勉強してみない? - その4 -

こんにちは!
久々に風邪をひいて週末熱を出して寝ていた万博おじです😨
(子どもから貰った様子)

ということで、今回は5回目、内容は「ループ処理:APIからトランザクション情報を取得」です。

  1. 準備

  2. JavaScriptの基本:パスフレーズを生成

  3. 文字列操作:パスフレーズからアドレスを取得

  4. 非同期処理:APIからアカウント情報を取得

  5. ループ処理:APIからトランザクション情報を取得

  6. トランザクションの生成と送信

  7. トランザクション手数料の取得

はじめに

ループ処理。大事です。
だいたいどんな機能でも複数の情報を並べて表示とかしますしね!
で、このループ処理、JavaScriptだけではないですがいろんな書き方があります。
お勉強用ソースコードには1種類だけですが、参考に記載するので覚えていってくださいね😉

前回

今回のお勉強用ソースコード

<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="utf-8"/>
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>画面タイトルです</title>
		<script src="https://js.lisk.com/lisk-client-5.2.2.min.js" defer></script>
		<style>
			html { font-size: 10px; }
			
			body { font-size: 1.6rem; }

			input[type="text"],
			input[type="number"],
			input[type="password"],
			textarea,
			button {
				font-size: 1.6rem;
				padding: 5px;
			}
		</style>
	</head>
	<body>
		<div>
			<input type="password" id="enter-passphrase" style="width: 300px;" placeholder="パスフレーズを入力してください" oninput="checkPassphrase(this.value)" />
		</div>
		<div>
			<button type="button" id="btn-login" style="width: 150px;" onclick="login()" disabled="true">ログイン</button>
			<button type="button" style="width: 150px;" onclick="createAccount()">アカウントを作成</button>
		</div>
		<div>
			<a href="https://testnet-faucet.lisk.com/" target="_blank" rel="noopener noreferrer">テストネット用のLSKを受け取ります</a>
		</div>
		<hr>
			<h4>テスト用パスフレーズ:</h4>
			<div>
				abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
			</div>
		<hr>
		<!-- (1)パスフレーズの表示場所 -->
		<h4>パスフレーズ:</h4>
		<div id="lisk-passphrase"></div>

		<!-- (2)アドレスの表示場所 -->
		<h4>アドレス:</h4>
		<div id="lisk-address"></div>
		<div id="lisk-bufferAddress"></div>

		<!-- (3)公開鍵の表示場所 -->
		<h4>公開鍵:</h4>
		<div id="lisk-publicKey"></div>
		
		<!-- (4)残高の表示場所 -->
		<h4>残高:</h4>
		<div id="lisk-balance"></div>
		
		<!-- (5)残高の表示場所 -->
		<h4>トランザクション(直近10件):</h4>
		<div id="lisk-transactions"></div>

		<script>
			/*
			 * アカウント情報初期化
			 */
			function clearAccountInfo() {
				document.querySelector("#lisk-address").innerHTML = "";
				document.querySelector("#lisk-balance").innerHTML = "";
				document.querySelector("#lisk-passphrase").innerHTML = "";
				document.querySelector("#lisk-bufferAddress").innerHTML = "";
				document.querySelector("#lisk-publicKey").innerHTML = "";
				document.querySelector("#lisk-transactions").innerHTML = "";
			}

			/*
			 * アカウント作成処理
			 */
			function createAccount() {
				// (0)アカウント情報初期化
				clearAccountInfo();

				// (1)パスフレーズを生成して画面に表示
				const mnemonic = lisk.passphrase.Mnemonic.generateMnemonic();
				document.querySelector("#lisk-passphrase").innerHTML = mnemonic;

				// (2)アドレスと公開鍵を取得して画面に表示
				const addressAndPublicKey = lisk.cryptography.getAddressAndPublicKeyFromPassphrase(mnemonic);
				const bufferAddress = addressAndPublicKey.address;
				const publicKey = addressAndPublicKey.publicKey;
				document.querySelector("#lisk-bufferAddress").innerHTML = `(${bufferAddress.toString("hex")})`;
				document.querySelector("#lisk-publicKey").innerHTML = publicKey.toString("hex");

				// (3)アドレスを取得して画面に表示
				const address = lisk.cryptography.getLisk32AddressFromAddress(bufferAddress);
				document.querySelector("#lisk-address").innerHTML = address;

				// (4)残高は0LSKとする
				document.querySelector("#lisk-balance").innerHTML = "0LSK";
			}

			/*
			 * ログイン 
			 */
			async function login() {
				// (0)アカウント情報初期化
				clearAccountInfo();

				// (1)入力されたパスフレーズを取得
				const passphrase = document.querySelector("#enter-passphrase").value;

				// (2)パスフレーズからlsk始まりのアドレスを取得
				const address = lisk.cryptography.getLisk32AddressFromPassphrase(passphrase);

				// (3)Lisk Service API の accounts を使用してアカウント情報を取得
				const response = await fetch(`https://testnet-service.lisk.com/api/v2/accounts?address=${address}`);
				const json = await response.json();

				// (4)見つからなかった場合は終了
				if (json.error) {
					alert("アカウントが見つかりませんでした。");
					return;
				}

				// (5)見つかった場合は画面に表示
				const account = json.data[0];
				document.querySelector("#lisk-address").innerHTML = account.summary.address;
				document.querySelector("#lisk-balance").innerHTML = `${lisk.transactions.convertBeddowsToLSK(account.summary.balance)}LSK`;
				document.querySelector("#lisk-passphrase").innerHTML = "ひみつ";
				
				// (6)公開鍵とバッファアドレスはパスフレーズから取得して表示
				const addressAndPublicKey = lisk.cryptography.getAddressAndPublicKeyFromPassphrase(passphrase);
				document.querySelector("#lisk-bufferAddress").innerHTML = `(${addressAndPublicKey.address.toString("hex")})`;
				document.querySelector("#lisk-publicKey").innerHTML = addressAndPublicKey.publicKey.toString("hex");

				// (7)トランザクション情報取得して表示
				showTransactions(address);
			}

			/*
			 * トランザクション情報表示
			 */
			async function showTransactions(address) {
				// (1)指定のアドレスで送信または受信したトランザクション情報を取得
				const response = await fetch(`https://testnet-service.lisk.com/api/v2/transactions?address=${address}&offset=0&limit=10`);
				const json = await response.json();

				// (2)見つからなかった場合は終了
				if (json.error || json.data.length === 0) {
					return;
				}
				
				let html_transactions = "";
				for (data of json.data) {
					html_transactions += `
						<div>ID:${data.id}</div>
						<div>タイプ:${data.moduleAssetName}</div>
						<div>送信者:${data.sender.address === address? "あなた": data.sender.address}</div>
						${data.asset.recipient === undefined? "":
							`<div>受信者:${data.asset.recipient.address === address? "あなた": data.asset.recipient.address}</div>`
						}
						${data.asset.amount === undefined? "":
							`<div>${data.sender.address === address? "送信":"受信"}枚数:${lisk.transactions.convertBeddowsToLSK(data.asset.amount)}LSK</div>`
						}
						<div>手数料:${lisk.transactions.convertBeddowsToLSK(data.fee)}LSK</div>
						${data.asset.data === undefined? "":
							`<div>データ:${data.asset.data}</div>`
						}
						<hr>
					`;
				}
				document.querySelector("#lisk-transactions").innerHTML = html_transactions;
			}

			/*
			 * パスフレーズチェック
			 */
			function checkPassphrase(val) {
				// (1)現在の入力値をチェック
				const ret = lisk.passphrase.Mnemonic.validateMnemonic(val);

				// (2)パスフレーズが正しくない場合はログインボタンを入力不可、正しい場合は入力可に変更
				document.querySelector("#btn-login").disabled = !ret;
			}
		</script>
	</body>
</html>

前回からの変更点

HTMLで変わったのは以下の通りです

  • トランザクション情報を表示する場所を追加(lisk-transactions)

  • テスト用パスフレーズの表示

JavaScriptで変わったのは以下の通りです

  • アカウント情報クリア処理(clearAccountInfo)を追加

  • トランザクション情報表示処理(showTransactions)を追加

  • アカウント作成処理でアカウント情報クリア処理を呼び出すように変更

  • ログイン処理でアカウント情報クリア処理とトランザクション情報表示処理を呼び出すように変更

画面を開くとこんな感じ

変更後の画面
パスフレーズを入力して「ログイン」ボタン押下後

ソースコードの説明:HTML

新しく出てくる内容はありません🙂

ソースコードの説明:JavaScript(lisk-client)

新しく出てくる内容はありません🙂

ソースコードの説明:JavaScript

function clearAccountInfo

画面に表示されているアカウント情報をクリアする処理です。
innerHTML = "" とすることで画面を初めて開いた状態と同じ状態にしています。

function showTransactions

渡されたlsk始まりのアドレスからトランザクション情報を取得し画面にその情報を表示する処理です。
ログイン処理の最後に実行されます。

ℹ️この処理自体は非同期処理ですが、ログイン処理からはawaitをあえて付与せずに呼び出しています。
これはトランザクション情報の表示自体は表示できる状態となったときに表示すれば問題はなく、トランザクション情報が表示できる状態となるまでログイン処理を待ち状態にしておく必要がないためです。
非同期処理については前回の内容をご覧ください。

[変数] += [値]

変数に設定された値にさらに値を加算する方法です。
変数名 = 変数名 + 値 と同じです。

ℹ️変数に格納されている値と加算する値が数値の場合は合計値が、文字列の場合は文字列結合した結果が変数に格納されます。

例:let hoge = 1;
hoge += 2;
⇒hogeには「3」が格納

例:let hoge = "あい";
hoge += "うえお";
⇒hogeには「あいうえお」が格納

[値1] === [値2]

型も含めて一致する場合にtrueになります。

ℹ️== は型の判断は行われません。
厳密にチェックするには === を使用しましょう。

例: 1 == "1"
 ⇒ true ※1と"1"は等しいと判断

例: 1 === "1"
 ⇒ false ※1と"1"は等しくないと判断

[値1] !== [値2]

型または値が一致しない場合にtrueになります。

ℹ️!= は型の判断は行われません。
厳密にチェックするには !== を使用しましょう。

例: 1 != "1"
 ⇒ false ※1と"1"は等しくないと判断(1は数値、"1"は文字列)

例: 1 !== "1"
 ⇒ true ※1と"1"は等しくないと判断

undefined

値が設定されていない変数、戻り値がない関数の結果などがundefinedとして扱われます。

⚠️nullとは別ものです。
判定方法に注意してください。

例:null == undefined
 ⇒ true ※nullとundefinedは等しいと判断

例:null === undefined
 ⇒ false ※nullとundefinedは等しくないと判断

[真偽判定]? [真の場合]: [偽の場合]

三項演算子といいます。
?の前の判定が真(true)となる場合はコロン(:)の左側、偽(false)となる場合は右側が適用されます。

ℹ️お勉強用ソースコードでは以下のように使用しています。
{data.asset.recipient === undefined? "":
    `<div>受信者:…..</div>`
}
これはトランザクション情報内のassetのrecipientがundefinedの場合は空文字("")、そうでなければ受信者として表示ということをしています。

他にも受信者のアドレスが自分のアドレスなら「あなた」と表示したり、データフィールドがあるかどうか判断して、あったら表示するようにしたりもしています。

for ([ループ内変数] of [配列変数])

配列変数の内容をループ処理する際に使用します。
ループ処理中の配列変数の1要素についてはループ内変数に格納されます。

ℹ️お勉強用ソースコードでは以下のように使用しています。
for (data of json.data) {
    ….
}
LiskサービスAPIで取得したトランザクション情報には複数の情報が格納されているので、これを1件ずつループを回して処理を行います。
トランザクション情報1件の情報は data という変数に格納されます。

for ([カウンタ変数] = [初期値]; [ループ範囲]; [最終評価式])

ごく標準的なループ処理です。
配列の件数分ループする場合に使用されます。

ℹ️よく以下のように使われます。
for (let i = 0; i < hoge.length; i++) {
    const data = hoge[i];
}
※hoge:配列を格納した変数
※let i:カウンタ変数(この例では初期値は0)
※i < hoge.length:ループ範囲はカウンタ変数iが配列hogeの件数以上になるまで
※i++:ループ1回終了ごとにカウンタ変数iを1カウントアップ

このループでは配列内の1件(1要素)を取得するために、hoge[ループ回数]のようにする必要があります。(最近はあまり使われないと思います)

[配列].forEach([コールバック関数])

配列を起点にループする場合はこのようにforEachを使用します。

ℹ️コールバック関数とかいきなり出てきてわけわからないと思うので割愛。
書くとしたらこのようになります。

hoge.forEach((data, index) => {
    ….
}
※hoge:配列を格納した変数
※data:配列hogeのループ中の1件を格納する変数
※index:ループ回数を格納する変数(初期値0)

ぶっちゃけこれ使ううくらいなら for … of … を使用するとか他を使用することをおススメします。

ソースコードの説明:Liskサービス API

https://testnet-service.lisk.com/api/v2/transactions

テストネットのトランザクション情報を取得するLisk公式のLiskサービスAPIです。
詳しくはこちらをご覧ください。

ℹ️LiskサービスのAPIは基本的に最新10件のみ取得します。
取得件数を変えたい場合は limit=取得したい件数 とします。
ただし、limitは最大100です。
100件以上取得したい場合は offset を使用します。

例:最新100件取得
~/transactions?limit=100&offset=0

例:最新101件目から100件取得
~/transactions?limit=100&offset=100

例:最新151件目から100件取得
~/transactions?limit=100&offset=150

トランザクション情報が取得できた時は以下のようなJSONが返却されます。

{
    "data": [
        {
            "id": "...",
            "moduleAssetId": "2:0",
            "moduleAssetName": "token:transfer",
            "fee": "210000",
            ....,
            "sender": {
                "address": "lsk.....",
                "publicKey": "...",
            },
            "asset": {
                "amount": "100000000",
                "data": "",
                "recipient": {
                    "address": "lsk...",
                    "publicKey": "..."
                }
            },
            ....
        },
    ]
}

エラー時は以下のようなJSONが返却されます。

{
    "error": true,
    "message": "Not found"
}

⚠️トランザクション情報はトランザクションの種類によってassetの中身が変わります。
常に受信者情報(recipient)やメモ欄で使われるデータフィールド(data)が存在するわけではないことに注意してください。

おわりに

今回はここまで!
ループ処理はプログラムで何かしようとすると頻繁に出てくると思うのでしっかり覚えましょう。
どんなループ方法があるか知りたい人は「JavaScript ループ処理」などで検索してみてね😉
次回はいよいよLSKを送信します。LiskサービスのAPIやlisk-clientをいろいろ使用するのでちょっと難しいかもしれませんがよろしくお願いします🙂
それではお疲れさまでした!

万博おじについて

Liskに関するツールなど開発したりノード管理したりしています。
何かあればTwitter等でご連絡ください。

個人アカウント
Twitter:ys_mdmg
GitHub:lisknonanika
Discord:ys_mdmg#5646
Lisk Explorer:lisk observer, lisk scan

デリゲートアカウント(共同管理)
Twitter:liskcommulab
Discord:CommuLab#0097
Lisk Explorer:lisk observer, lisk scan

管理
ノード:Mainnet / Testnet
Lisk Service:Mainnet / Testnet
デリゲートサイト:Lisk CommuLab

個人やデリゲート宛ての寄付ありがとうございます!
ノード管理や開発資金に充てさせて頂いています😊

この記事が気に入ったらサポートをしてみませんか?