今日は送金部分の実装を行います。簡単なFAUCET(蛇口)アプリとしても使えるので、必要に応じて利用してみてください。
HTML構成はテキストボックスとボタンを配置しておきましょう。
<h3>送信先</h3>
<div id="address"><input type="text" id="recipientAddress" class="form-control"></div>
<button id="button1" class="btn btn-primary" type="button" >送信 </button>
ボタンをクリックしたときに、処理が実行されるようにJSを記述します。
$("#button1").click(
function(){
process();
return false;
}
);
実際の送金に必要な処理は以下の通りです。
const GENERATION_HASH = "DD3AC0E0F93DCC0F101B9314DB181480EA2A39A8A3AB7204CF8CBEC1442B87BF";
const alice = nem.Account.createFromPrivateKey(
'DF1A2101A6BA32F33CXXXXXXXX72170BB0AC5A4E1057FBA706DC2B99XXXXX',
nem.NetworkType.MIJIN_TEST
);
function process(){
const recipientAddress = nem.Address.createFromRawAddress($("#recipientAddress").val());
const tx = nem.TransferTransaction.create(
nem.Deadline.create(), //締め切り
recipientAddress, //送信先
[
new nem.Mosaic(
new nem.MosaicId('6D230A8CE01B4384'),
nem.UInt64.fromUint(10000000)
)
], //送金種類・量
nem.PlainMessage.create('Hello World!'), //メッセージ
nem.NetworkType.MIJIN_TEST, //使用ネットワーク
nem.UInt64.fromUint(100000) //最大手数料
);
const signedTx = alice.sign(tx,GENERATION_HASH);
const txHttp = new nem.TransactionHttp(NODE);
txHttp
.announce(signedTx)
.subscribe();
}
順に説明していきます。
-
GENERATION_HASH
- どのチェーンを使用するかの詳細設定です。将来MIJINネットワークが数万といった単位で使用されるようになったとしても、そのチェーンを生み出したHASH値は被ることが無いため(被らせてはいけない)、ネットワークを一意に特定することができます。これはリプレイ攻撃といって他のネットワークで使用された署名済みトランザクションを再利用されることを防ぐ意味合いもあります。
-
alice
- 今回はこのアカウントから送金を行います。このアカウントには十分な残高があり、送金者はaliceの秘密鍵を知っているものとします。
-
TransferTransaction.create
- 今回の一番重要なコマンドです。送金トランザクションを作成するために使用します。
- 締め切り、送信先、送金種類・量、メッセージ、使用ネットワーク、最大手数料を指定します。
-
alice.sign
- 先ほど作成したトランザクションにアカウントaliceで署名します。送信元はおのずと署名したaliceになります。
-
TransactionHttp.annouce
- 署名したトランザクションをネットワークに通知します。これがブロックに取り込まれれば送金完了となります。
以上が送金に必要な処理の流れになります。しかし、アプリケーションを開発していくためには送金結果を調べることも重要になります。
Catapultではトランザクションのannouce結果がsubscribeには流れてきません。流れてくるのは「受け取りましたよ」というネットワーク的な応答結果のみです。こうすることでネットワークの遅延にブロックチェーンの処理が影響を受けてしまうのを防いでいるようです。
送金結果を調べるための情報をHTMLに追加しましょう。
<h3>承認状態の確認</h3><div id="status"><ul></ul></div>
<h3>承認後に確認</h3>
<div id="confirmed"><ul></ul></div>
<div id="account"><ul></ul></div>
承認状態の確認は署名トランザクションのハッシュ値から調べることができます。strLiというファンクションを自作してブラウザ上にリンクを追加します。
txHttp
.announce(signedTx)
.subscribe(_ => {
$('#signedTx').val(signedTx.payload);
$('#status ul').append(strLi('/transaction/' + signedTx.hash + '/status' ,"トランザクション状態情報"));
}, err => console.error(err));
function strLi(href,text){
return '<li><a target="_blank" href="' + NODE + href + '">' + text + '</a></li>';
}
未承認中の情報はこんな感じで group unconfirmedとなっています。statusはSuccessとなっていますので間違えないようにご注意ください。
{
"group":"unconfirmed",
"status":"Success",
"hash":"4290D6817C70E2F3C1F810F10EC0568CB978E20E88F60E3FCD3E2282A27B11BF",
"deadline":"116396326497",
"height":"0"
}
承認済みとなると group:confirmedとなりブロック高が記録されます。
{
"group":"confirmed",
"status":"Success",
"hash":"4290D6817C70E2F3C1F810F10EC0568CB978E20E88F60E3FCD3E2282A27B11BF",
"deadline":"116396326497",
"height":"3659"
}
送金後はトランザクション情報やアカウント情報からもアクセス可能となります。ここでリスナー機能を利用してみましょう。
const wsEndpoint = NODE.replace('http', 'ws');
const listener = new nem.Listener(wsEndpoint, WebSocket);
listener.open();
listener
.confirmed(alice.address)
.subscribe(
function(_){
$('#confirmed ul').append(strLi('/transaction/' + signedTx.hash ,"トランザクション情報" ));
$('#account ul').append(strLi('/account/' + alice.publicKey ,"アカウント情報" ));
},
err => console.error(err)
);
NEMのRESTノードはWebSocketをサポートしています。必要なエンドポイントにつなぐためにNODE情報を書き換えます。
その後、ブラウザのWebSocket機能を利用してリスナーを起動します。簡単な時代になりまたね。
listener.confirmed(Address)
このように記述すると、Addressにて新規に承認されたトランザクションがあると、以下subscribeにデータが出力されます。
つまり、送金が完了したタイミングが分かるので、トランザクション情報を調べるためのリンクをここで表示します。
まとめ
今までの処理をまとめると以下のようになります。
(現在テストネットが構築途中のようなのでパラメータを実際に使用することはできません)
<!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">
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous">
</head>
<body>
<div class="container">
<h3>送信先</h3>
<div id="address"><input type="text" id="recipientAddress" class="form-control"></div>
<button id="button1" class="btn btn-primary" type="button" >送信 </button>
<h3>署名データ</h3><textarea id="signedTx" rows="8" class="form-control"></textarea>
<h3>承認状態の確認</h3><div id="status"><ul></ul></div>
<h3>承認後に確認</h3>
<div id="confirmed"><ul></ul></div>
<div id="account"><ul></ul></div>
</div>
<script
src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous">
</script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
crossorigin="anonymous">
</script>
<script src="https://s3-ap-northeast-1.amazonaws.com/xembook.net/nem2-sdk/symbol-sdk-0.18.0.js"></script>
<script>
//const NODE = 'https://jp12.nemesis.land:3001';
const NODE = 'https://sym-test.opening-line.jp:3001';
const GENERATION_HASH = "ACECD90E7B248E012803228ADB4424F0D966D24149B72E58987D2BF2F2AF03C4";
const nem = require("/node_modules/symbol-sdk");
//TBPA7RIRR7E24STGJWGKAQCZ3RSNDFWUMFPDZFFK
const alice = nem.Account.createFromPrivateKey('4EA0CFE69AA421BDF4AFED56A79AA3AAA159B4EA517E401A6B2EAE41C15EB83A', nem.NetworkType.TEST_NET);
console.log(alice.publicKey);
const wsEndpoint = NODE.replace('http', 'ws');
const listener = new nem.Listener(wsEndpoint, WebSocket);
listener.open();
function process(){
const recipientAddress = nem.Address.createFromRawAddress($("#recipientAddress").val());
const tx = nem.TransferTransaction.create(
nem.Deadline.create(),
recipientAddress,
[
new nem.Mosaic(
new nem.MosaicId('519FC24B9223E0B4'),
nem.UInt64.fromUint(1000000)
)
],
nem.PlainMessage.create('Hello World!'),
nem.NetworkType.TEST_NET,
nem.UInt64.fromUint(100000)
);
const signedTx = alice.sign(tx,GENERATION_HASH);
const txHttp = new nem.TransactionHttp(NODE);
txHttp
.announce(signedTx)
.subscribe(_ => {
$('#signedTx').val(signedTx.payload);
$('#status ul').append(strLi('/transaction/' + signedTx.hash + '/status' ,"トランザクション状態情報"));
}, err => console.error(err));
listener
.confirmed(alice.address)
.subscribe(
function(_){
$('#confirmed ul').append(strLi('/transaction/' + signedTx.hash ,"トランザクション情報" ));
$('#account ul').append(strLi('/account/' + alice.publicKey ,"アカウント情報" ));
},
err => console.error(err)
);
}
function strLi(href,text){
return '<li><a target="_blank" href="' + NODE + href + '">' + text + '</a></li>';
}
$("#button1").click(
function(){
process();
return false;
}
);
</script>
</body>
</html>
記事一覧
NEM開発事始め(1) HTMLファイルの作成とNEMアカウントの生成
NEM開発事始め(2) NEMアカウント情報の取得と表示
NEM開発事始め(3) NEMトランザクション履歴の取得と表示
NEM開発事始め(4) NEM送金トランザクションの実行