前回はnem2-sdkをブラウザに使用できるようにして、簡単な情報を取得してみました。
今回は特定のアカウントからトランザクションデータを取得してみます。
その時の肝となる技術がRxJSです。
RxJS を学ぼう #1 - これからはじめる人のための導入編
https://tech.recruit-mp.co.jp/front-end/rxjs-intro/
Catapultからデータを取ってきた場合、基本的には以下のような処理を経て画面上に出力します。
accountHttp
.getAccountInfo(address)
.subscribe(
accountInfo => {
$("#account_pubkey").text(accountInfo.publicKey);
},
err => console.error(err)
);
accountHttpでノードに接続。getAccountInfoでアカウント情報を取りに行き、取れたらsubscribe内が処理されます。accountInfoに情報が入るので、適切な形に加工して画面に表示します。
では、取得したデータがリスト形式の場合はどんな方法で処理していくのでしょうか?
さきほどのソースコードは以下のような書式に書き換えることができます。
accountHttp
.outgoingTransactions(originAccount)
.subscribe(
function(transactions){
console.log(transactions);
},
err => console.error(err)
);
少し見慣れた形になりましたね。今回はトランザクションデータを取得したので出力されたデータが配列で入ってきますので、同一項目のデータにコンバートをかけていくには、中でさらにループ処理を施す必要があります。
accountHttp
.outgoingTransactions(originAccount)
.subscribe(
function(transactions){
transactions.some(function(tx){
console.log(tx);
});
},
err => console.error(err)
);
someの部分がループ処理になります。この内部にロジックを記述していくことで、どんな出力形式にでも対応していくことが可能です。
ただ、一括で変換をかけたいとき、あるいは処理対象でないレコードを除外したい場合は、RxJSのオペレータコマンドを利用して操作することが可能です。
以下のようにsubscribeの前にpipe()というセクションを作るとその中でmap、filter、などで一括操作が行えます。
accountHttp
.outgoingTransactions(originAccount)
.pipe(
rxjs.operators.mergeMap((_) => _),
rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER),
rxjs.operators.toArray(),
)
.subscribe(
transactions => {
$('#items2-ctrl ul').append('<li>' + transactions.recipient.address + '</li>');
},
err => console.error(err)
);
順に説明していきます。
rxjs.operators.mergeMap((_) => _),
mergeMapは配列[a,b,c] と格納されていたデータを[a],[b],[c]と置き換え、後続のオペレーションを適用しやすく変換します。
rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER),
filter により、 transaction内部のtypeがTRANSFERのものに絞り込み、次の処理に通します。
rxjs.operators.toArray(),
toArrayをかけると[a],[b],[c]を再び[a,b,c]という配列に戻します。
これらの操作により、subscribeへはTRANSFER以外のデータが除去されて届きます。
最後に指定namespaceIdを持つ送金の合計値を計算するサンプルを2例示しておきます。
最初はfilter内でやってしまう方法、もう一つはsubscribe内でやってしまう方法です。
//一つ目
const namespaceId = new nem.NamespaceId([3294802500,2243684972]);
accountHttp
.outgoingTransactions(originAccount)
.pipe(
rxjs.operators.mergeMap((_) => _),
rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER),
rxjs.operators.map(function(tx){
var amount = 0;
tx.mosaics.some(function(mosaic) {
if (mosaic.id.toString() == namespaceId.toString()) {
amount = mosaic.amount.compact() / Math.pow(10, divisibility);
return true;
}
});
return amount;
}),
rxjs.operators.toArray(),
rxjs.operators.map((_) => _.reduce((a, b) => a + b, 0))
)
.subscribe(
total => {
$('#account_transfer').text(total);
},
err => console.error(err)
);
//二つ目
accountHttp
.outgoingTransactions(originAccount)
.pipe(
rxjs.operators.mergeMap((_) => _),
rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER),
rxjs.operators.toArray(), // Add all mosaics amounts into one array
)
.subscribe(
_ => {
var amount = 0;
_.some(function(tx) {
tx.mosaics.some(function(mosaic) {
if (mosaic.id.toString() == namespaceId.toString()) {
amount += mosaic.amount.compact() / Math.pow(10, divisibility);
return true;
}
});
});
},
err => console.error(err)
);
次回はnem2-sdkを利用したアカウント作成について説明します。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
<title>011_account_sample</title>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
<script src="nem2-sdk-0.11.6.js"></script><!-- Catapult Cow -->
<script>
$(function() {
const nem = require("/home/ec2-user/node_modules/nem2-sdk");
const NODE_URL = "http://13.114.200.132:3000"; // nf-testnet
const accountHttp = new nem.AccountHttp(NODE_URL);
const originPublicKey = 'AC1A6E1D8DE5B17D2C6B1293F1CAD3829EEACF38D09311BB3C8E5A880092DE26';
const originAccount = nem.PublicAccount.createFromPublicKey(originPublicKey, nem.NetworkType.MIJIN_TEST);
const recipientAddress = 'SB2Y5ND4FDLBIO5KHXTKRWODDG2QHIN73DTYT2PC';
const address = nem.Address.createFromRawAddress(recipientAddress);
const divisibility = 6; // Replace with mosaic divisibility
$('#account_address').text(recipientAddress);
$('#account_pubkey').text(originPublicKey);
accountHttp
.outgoingTransactions(originAccount, new nem.QueryParams(100))
.pipe(
rxjs.operators.mergeMap((_) => _), // Transform transaction array to single transactions to process them
rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER), // Filter transfer transactions
)
.subscribe(
transactions => {
console.log(transactions.message.payload);
$('#items2-ctrl ul').append('<li>' + transactions.recipient.address + '</li>');
},
err => console.error(err)
);
const namespaceId = new nem.NamespaceId([3294802500,2243684972]);
accountHttp
.outgoingTransactions(originAccount, new nem.QueryParams(100))
.pipe(
rxjs.operators.mergeMap((_) => _),
rxjs.operators.filter((_) => _.type === nem.TransactionType.TRANSFER),
rxjs.operators.map(function(tx){
var amount = 0;
tx.mosaics.some(function(mosaic) {
if (mosaic.id.toString() == namespaceId.toString()) {
amount = mosaic.amount.compact() / Math.pow(10, divisibility);
return true;
}
});
return amount;
}),
rxjs.operators.toArray(),
rxjs.operators.map((_) => _.reduce((a, b) => a + b, 0))
)
.subscribe(
total => {
$('#account_transfer').text(total);
},
err => console.error(err)
);
});
</script>
</head>
<body>
<div class="container">
<h1>011_account_transfer</h1>
<div id="items-ctrl">
<ul>
<li>[ADDRESS]:<span id="account_address"></span></li>
<li>[PUBLIC_KEY]:<span id="account_pubkey"></span></li>
<li>[OUTGOING_SUM]:<span id="account_transfer"></span></li>
</ul>
</div>
<h1>transactions</h1>
<div id="items2-ctrl">
<ul>
</ul>
</div>
</div>
</body>
</html>