jQueryとSymbol SDKを用いたブロックチェーンのプログラミング
この記事はSymbol SDKを用いてブロックチェーンのプログラミングを掲載したものです。
また、これは2019年11月に執筆されたものを2022年11月対応版として書いたものなので、ここに記載されているコードは2022年11月において動作確認済みです。配布されているSymbol SDKのバージョン(symbol-sdk-2.0.3.js)が大きく変わるとそのままで動く保証はない事から、最新のSymbol SDKを各自調べた上でプログラミングをしてください。
あくまで、これは参考記事として留めておいてください。
最後に、この記事は暗号資産Symbolのウォレット「Arcana」の開発者であるしゅうさんの監修で書かれたものですので、しゅうさんに対して感謝の意を表します。
Symbol SDKの資料の見つけ方について
Symbolの機能を使う際、どんな機能が用意されているのかは、以下ページを読む必要があります。
まず、このページに入ってみます。
はじめてのアプリケーションを作成に入ります。
ここに、サーバー側から送るSymbol CLI(Command Line Interface)やサンプルコードが書かれています。
具体的には、自分の目で見てください。
ガイドにはSymbolの各機能が紹介されています。
ここでは、アカウントの作成、マルチシグ、ノードの立ち上げ方法等が紹介され、具体的にSymbolの機能が紹介されています。
プログラミングをする時は、Referenceの「SDK」の項目を見る事が大事になります。
そのためには、「アカウント→アカウントの作成→方法 #02: SDK を使用する」と進みます。
このように、アカウントの作成方法が紹介されています。
プログラミングをするときはSDK、サーバー関連ならばCLIを使う事になります。
なのでSDKでは、どのようにプログラミングを組めば、アカウント作成できるのかを調べる事ができます。
またSDKの中身をさらにくわしく知りたい方は、これから述べる以下の方法で調べる事になります。
ここで、SDKとは「Software Developper Kit」の略称で、開発に必要なプログラムをひとまとめにしたパッケージです。
また、Symbolはオープンソースなので、どんな言語でもSDKに移植することができます。
SDKについては「リファレンス→SDK」に進むと、SDKについて調べられます。
リポジトリにはSDKのソースが掲載され、ドキュメントにはSDKを使う事でどんな機能があるのかを確認できます。
ここから、バージョンを選んで、SDKの機能を調べられます。
このように、Symbolの機能を見る事ができます。
例えば、accountHTTPとはどんな機能なのかを調べたいとします。
ここで、AccountHttpに入ります。
すると、以下の画面になります。
一例として、getAccountinfoを見つけてみます。
すると、以下のようになっているようです。
パラメータはaddressであり、返ってくるのがAccountinfoであるのが、ここでわかります。
ここで、Accountinfoをクリックすると、以下のようになっています。
ここで、mosaicsをクリックすると、mosaicというオブジェクトがあるのがわかります。
ここで、Mosaicをクリックすると・・・
下に行くと・・・
ここを見ると、プロパティにamountとidがあるのがわかります。
じゃあ。amountとidとは・・・
このようにして、プログラミングをするときにSymbolの機能をこの資料を確認しながら組み立てていくことになります。
ブラウザ環境でのSymbolの実行環境の構築
ブラウザ環境でSymbolの機能を使えるようにするには、browserifyを用いてまとめたファイルをダウンロードすることで、ブラウザ環境でもSymbol SDKを使えるようになります。このまとめたファイルをバンドルファイルといいます。なお、このファイルを使う際は、Node.jsをインストールする必要はありません。
これは、takanobuさん(https://twitter.com/xembook)がGitHUBにて以下のページにてまとめてくれています。
https://github.com/xembook/nem2-browserify
このページから最新版をダウンロードすることになります。
2020年11月1日現在の最新版は「symbol-sdk-0.21.0.js」なので、それをクリックします。
今後、最新版が出ると思いますので、それをダウンロードしてください。
(注釈:2022年11月現在の最新版は「symbol-sdk-2.0.3.js」です)
クリックすると・・・
「Download」をクリックすると、ソースコードが表示されます。
このソースコードをダウンロードしてください。ここで、右クリックすると、ダウンロードすることができます。
拡張子が.jsでのダウンロードができない場合は、ダウンロード後に.jsに拡張子を変更することも可能です。
このファイルをバンドルファイルといいます。
ダウンロードしたら、適用したいファイルとSymbol SDKを同じフォルダに入れることで、Symbol SDKを使えるようになります。適用するファイルはHTMLファイルであったりします。
このようにダウンロードしてローカル環境でパスを通してSDKを使うのもいいですが、HTMLの中に以下のようにSymbol SDKがあるリポジトリのURLを記述することでSymbol SDKを使う事もできます。
<script src="https://xembook.github.io/nem2-browserify/symbol-sdk-0.21.0.js"></script>
そうすることで、ネットワーク上からSDKを読み込んで使うことができます。
初心者はこの方法を利用して試すのがいいですが、実際にビジネスとして利用するのならば、Symbol SDKをダウンロードして、パスとしてつなげてください。
ここでは初心者向けにとりあえず動くものを作るものとし、以降は以上のコードを用いてSymbol SDKを読み込む事とします。
アドレスとプライベートキーの発行プログラム
ここでは、Symbol SDKを使ってアドレスとプライベートキーの発行をするプログラムについて説明します。ここでは、以下のコードについて説明をします。
<html lang="ja">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/css/uikit.min.css" />
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit-icons.min.js"></script>
<script src="https://xembook.github.io/nem2-browserify/symbol-sdk-2.0.3.js"></script>
<title>Symbolアドレス生成</title>
</head>
<body style="margin:30px;">
<h1>Symbolを触ってみる</h1>
<h2>アドレスを取得しよう</h2>
<div>
Address:<span id="address">Nothing</span>
</div>
<div>
Private Key:<span id="private">Nothing</span>
</div>
<a id="address_get" class="uk-button uk-button-primary" style="margin-top:10px;">
アドレス取得</a>
<script>
const symbol=require("/node_modules/symbol-sdk");
$('#address_get').click(function(){
let wallet = symbol.Account.generateNewAccount(symbol.NetworkType.TEST_NET);
$('#address').text(wallet.address.plain());
$('#private').text(wallet.privateKey);
});
</script>
</body>
</html>
このコードの完成版が以下になります。
このことについて説明します。
head要素の中身
まず、head要素をみてみます。
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
</script>
この要素で、jQueryを読み込みます。
以下の3行でCSSのフレームワークであるUIKitを読み込んでいます。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/css/uikit.min.css" />
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit-icons.min.js"></script>
body要素の中身
ここからbody要素の中身をみていきます。
以下のコードでWEBページのレイアウトを表示します。
<h1>Symbolを触ってみる</h1>
<h2>アドレスを取得しよう</h2>
<div>
Address:<span id="address">Nothing</span>
</div>
<div>
Private Key:<span id="private">Nothing</span>
</div>
<a id="address_get" class="uk-button uk-button-primary" style="margin-top:10px;">
アドレス取得</a>
この実際のレイアウトが以下のようになります。
それぞれについて説明します。
以下の要素で、見出しをを表示させます。
<h1>Symbolを触ってみる</h1>
<h2>アドレスを取得しよう</h2>
この要素で、addressとプライベートキーを表示します。
<div>
Address:<span id="address">Nothing</span>
</div>
また、この要素でプライベートキーを表示します。
<div>
Private Key:<span id="private">Nothing</span>
</div>
以下の要素で、「アドレス取得」と書かれている実行ボタンを実装します。
<a id="address_get" class="uk-button uk-button-primary" style="margin-top:10px;">
アドレス取得</a>
Script要素の中身
ここから、script要素をみていきます。
以下の1行で、Symbol SDKを呼び出す宣言になります。
const symbol=require("/node_modules/symbol-sdk");
このように書く事で、symbolという定数にSymbol SDK内の情報を代入することになります。ここで、requireはnode.jsで定義されるコマンドです。こうすることで、定数symbolはオブジェクトになります。
それ以降、symbolという定数を使う事で、Symbol SDKの中身を使えるようになります。この1行は基本的にコピペでOKです。また、ここではsymbolを定数名としましたが、定数名はお好みでOKです。
以下の行はjQueryで書かれていて、IDをaddress_getとした要素(「アドレス取得」と書かれたボタン)をクリックすると、
$('#address_get').click(function(){
以降に書かれているコードを実行するというものです。
そのコードが以下のものになります。
let wallet = symbol.Account.generateNewAccount(symbol.NetworkType.TEST_NET);
$('#address').text(wallet.address.plain());
$('#private').text(wallet.privateKey);
この3行について説明します。
let wallet = symbol.Account.generateNewAccount(symbol.NetworkType.TEST_NET);
walletという変数を用意し、定数symbolの中のAccount.generateNewAccount命令を使えば、アドレスとプライベートキーが発行されます。ここで、generateNewAccountの引数は、ネットワークの種類を表し、ここではsymbol.NetworkType.TEST_NETとすることで、テストネットとしています。
また、テストネットは152番のネットワークとしているので、
let wallet = symbol.Account.generateNewAccount(152);
としても構いません。
メインネットの場合、ここをこのように書き換えるだけでOKです。
let wallet = symbol.Account.generateNewAccount(symbol.NetworkType.MAIN_NET);
以上の一文は基本的にコピペで構いません。
この命令を実行すると、アドレスやプライベートキーといった情報が変数walletに代入されます。
次に
$('#address').text(wallet.address.plain());
と書く事で、発行されたアドレスがID「address」とした要素にアドレスが出力されます。
つまり、以下のコードに書き出すこととなります。
<div>
Address:<span id="address">Nothing</span>
</div>
また、
$('#private').text(wallet.privateKey);
と書く事で、発行されたプライベートキーがID「private」とした要素にプライベートキーが出力されます。
つまり、以下のコードに書き出すこととなります。
<div>
Private Key:<span id="private">Nothing</span>
</div>
すると「アドレス取得」と書かれたボタンをクリックすることで実行すると、以下のようになります。
XYMとモザイクの残高表示プログラム
XYMとモザイクの残高表示プログラムを作ります。ここでは、以下のコードについて説明します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/css/uikit.min.css" />
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit-icons.min.js"></script>
<script src="https://xembook.github.io/nem2-browserify/symbol-sdk-2.0.3.js"></script>
<title>Symbol残高照会</title>
</head>
<body style="margin:30px;">
<h1>Symbolを触ってみる</h1>
<h2>残高の確認</h2>
<div style="margin-top:10px;">
Address:<input type="text" id="address" class="uk-input">
</div>
<div style="margin-top:10px;">
Balance:<span id="balance">0</span> xym
</div>
<div id="balance_get" class="uk-button uk-button-primary" style="margin-top:10px;">残高取得</div>
<script>
const symbol=require("/node_modules/symbol-sdk");
let address;
const nodeAddress='http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000';
const networkGenerationHash="1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B";
const repositoryFactory=new symbol.RepositoryFactoryHttp(nodeAddress);
const accountHttp=repositoryFactory.createAccountRepository();
$('#balance_get').click(function(){
address=symbol.Address.createFromRawAddress($('#address').val());
accountHttp.getAccountInfo(address)
.subscribe(function(accountInfo){
$('#balance').text(accountInfo.mosaics[0].amount.compact()/1000000);
console.log(accountInfo.mosaics[0].id.toHex())
}, err => console.error(err));
});
</script>
</body>
</html>
このプログラムの完成版は以下になります
これについて説明します。
head要素について
以下の要素で、jQueryを読み込みます。
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
</script>
以下の3行でCSSのフレームワークであるUIKitを読み込んでいます。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/css/uikit.min.css" />
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit-icons.min.js"></script>
body要素について
input要素を使う事で、アドレスの入力フォームをつくることができます。
<div style="margin-top:10px;">
Address:<input type="text" id="address" class="uk-input">
</div>
ここでは、UIKITを使っているので入力枠を見やすくしていますが、最低限、これだけでもOKです。
<input type="text">
また、以下のコードで残高(Balance)を表示させます。ここでは初期値として0としています。
Balance:<span id="balance">0</span> xym
以下のコードで、「残高取得」と書かれたボタンを実装しています。このボタンをクリックすることで実行できます。
<div id="balance_get" class="uk-button uk-button-primary" style="margin-top:10px;">残高取得</div>
script要素について
以下の1行で、Symbol SDKを呼び出す宣言になります。
const symbol=require("/node_modules/symbol-sdk");
このように書く事で、symbolという定数にSymbol SDK内の情報を代入することになります。ここで、requireはnode.jsで定義されるコマンドです。
それ以降、symbolという定数を使う事で、Symbol SDKの中身を使えるようになります。この1行は基本的にコピペでOKです。また、ここではsymbolを定数としましたが、定数名はお好みでOKです。
また、アドレスを入れておく事を確保するための変数をここで用意しておきます。
let address
以下のコードでどこのノードアドレスに情報を問い合わせるのかを定数nodeAddressにて設定します。
const nodeAddress='http://api-01.eu-central-1.096x.symboldev.network:3000';
ノードアドレスを調べたい場合は、Symbol Walletにて、赤い四角で囲んだところをクリックして
ノードのアドレスを確認できます。ここから、ノードを選んでください。
ノードを選んだら、それに対応するネットワークジェネレーションハッシュが必要になります。
const networkGenerationHash="1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B";
このジェネレーションハッシュは以下のように書かれています。
つまり、ノードアドレスが、
http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000
の場合は
http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/node/info
ということになります。このURLで開いてみると・・・
networkGenerationHashSeedに、ネットワークジェネレーションハッシュが書かれています。これをコードに書きます。
ノードのURLとネットワークジェネレーションハッシュは一対一に結びついているので、ノードURLを決めたら、必ずネットワークジェネレーションハッシュを見つけてください。
次に、このコードを見ます。
const repositoryFactory=new symbol.RepositoryFactoryHttp(nodeAddress);
これは、Symbolの中身の仕組みを使うのに、nodeAddressを引数としたrepositoryFactoryを定数としておいたものです。(何をしているのかはわかりません)
次に、このコードを見ます。
const accountHttp=repositoryFactory.createAccountRepository();
定数 accountHttpは、先ほど書いたrepositoryFactoryをcreateAccountRepositoryで読んで来いというものです。
ここまで読んだ以下のコード
const symbol=require("/node_modules/symbol-sdk");
let address;
const nodeAddress='http://api-01.eu-central-1.096x.symboldev.network:3000';;
const networkGenerationHash="1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B";
const repositoryFactory=new symbol.RepositoryFactoryHttp(nodeAddress);
const accountHttp=repositoryFactory.createAccountRepository();
は、基本的にコピペでOKで、変えるべきなのは、ノードアドレスと、そのジェネレーションハッシュということになります。
次に、このコードについて
$('#balance_get').click(function(){
これはIDがbalance_getの要素(「残高取得」と書かれたボタン)をクリックしたら、以下の処理を実行するというものです。
address=symbol.Address.createFromRawAddress($('#address').val());
accountHttp.getAccountInfo(address)
.subscribe(function(accountInfo){
$('#balance').text(accountInfo.mosaics[0].amount.compact()/1000000);
console.log(accountInfo.mosaics[0].id.toHex())
}, err => console.error(err));
以上のコードについて、それぞれ説明します。
まず、以下のコードについて説明します。
address=symbol.Address.createFromRawAddress($('#address').val());
これは、input要素のIDを「address」と指定することで、そのinput要素から取得したデータと.valを読んでくる事ができます。それをcreateFromRawAddressで、Symbol SDKの中で使うアドレスに変換されたものを、変数addressに代入するという意味です。
この変数addressの中身は、アドレスとネットワークのタイプ(152番)がオブジェクトデータとして入っているようです。実際にそうなっている事を調べるには、このコードのあとに、
alert(JSON.stringify(address));
として、input要素をクリック(残高収得をクリック)するとわかります。
調べてみると、確かにそうなっています!!
このコードは基本的にコピペでOKで、もしアドレスを固定で指定したい場合は
address=symbol.Address.createFromRawAddress($('TADAFIKEA2DCTO6CJPNQHT3W543W2RBMJ6QITPI').val());
として、直接アドレスを書いてもOKです。
今度は、このコードを見てみます。
accountHttp.getAccountInfo(address)
.subscribe(function(accountInfo){
$('#balance').text(accountInfo.mosaics[0].amount.compact()/1000000);
console.log(accountInfo.mosaics[0].id.toHex())
getAccountInfo(address)でaddressの情報を取ってきて、それを非同期処理(sunscribe)(結果が返ってきた処理から順番に処理すること)により、addressに入っている情報が入ってきたら、入ってきた順番に処理をしていくことになります。その結果がaccountInfoに返ってきます。返ってきたら、IDを「balance」とした要素にテキストとして、モザイクの0番目の量をコンパクト化したものを100万で割ったものを表示することになります。
Balance:<span id="balance">0</span> xym
すると、0として最初は表示していたのが、実際の残高として表示されます。
ここで、このコードをaccountInfoを見てみます。そのためには、(accountinfo)の後にconsole.log(accountInfo)を書きます。つまり、コードを以下のようにするということです。
accountHttp.getAccountInfo(address).subscribe(function(accountInfo)
console.log(accountInfo);
実行してみて、Google Chromeのデベロッパーツールのconsoleを見てみると・・・
この中身を見てみます。
ここで、mosaicsの中身をみると・・・
0番目のモザイクを見てみると・・・
赤い四角で囲んだものをcompactという関数で組み合わせると、残高が表示されるようになります。
けど、表示されるときは、μXYMで表示されているので、XYMで表示するために、100万で割っています。
ここで、0番目のモザイクはXYMの事を指していますが、1番目と2番目はXYM以外のモザイクを表します。
実際にウォレットを見てみます。
SymbolではXYMとモザイクの送信方法は、IDを指定するだけで同じようにできます。
つまり、XYMの場合のIDは「5B66E76BECAD0860」であり、自作のモザイクのID「7DC393D57931CA69」や「519F7A167869851C」を送信するとしてやると、XYMもモザイクも同じように送信することができます。
accountHttp.getAccountInfo(address).subscribe(function(accountInfo){$('#balance').text(accountInfo.mosaics[1].amount.compact()/1000000);}, err => console.error(err));
とすると、モザイクの残高が表示されるようになります。
つまり、
accountHttp.getAccountInfo(address).subscribe(function(accountInfo){ }, err => console.error(err));
は、コピペでOKで、{ }の中に残高を表示するプログラムを書くとなると、
accountHttp.getAccountInfo(address).subscribe(function(accountInfo){
$('#balance').text(accountInfo.mosaics[0].amount.compact()/1000000);
}, err => console.error(err));
となるわけです。基本的に、この一文はコピペでOKであり、XYM以外のモザイクを表示したいときに、accountinfo.mosaics[1]とかにします。
となるわけです。基本的に、この一文はコピペでOKであり、XYM以外のモザイクを表示したいときに、accountinfo.mosaics[1]とかにします。
最後に、このコードについて見てみます。
console.log(accountInfo.mosaics[0].id.toHex())
これは、モザイクのIDを見る事ができます。
ここで、lower、higherが分かれているので、結合するために、toHEX()とすることで、16進数にして合体させる処理をしています。
ここで、実行させてみると・・・
このように、IDが表示されています。ここでは0番目のモザイクのIDを指定しているので、XYMのIDが表示されています。
ここで、
console.log(accountInfo.mosaics[1].id.toHex()
として、1番目のモザイクのIDを調べてみると、以下のようになります。
先ほどのウォレットと比較してみると・・・
これで、1番目のモザイクは何なのかがわかりました。
このようにして、何番目のモザイクなのかを確認することができます。
これは、モザイクのIDを調べたいとき、必要に応じて書いてください。
最後に・・・
では、XYMとモザイクを一気に残高照会をしたときは、このように書きます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/css/uikit.min.css" />
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit-icons.min.js"></script>
<script src="https://xembook.github.io/nem2-browserify/symbol-sdk-2.0.3.js"></script>
<title>Symbol残高照会</title>
</head>
<body style="margin:30px;">
<h1>Symbolを触ってみる</h1>
<h2>残高の確認</h2>
<div style="margin-top:10px;">
Address:<input type="text" id="address" class="uk-input">
</div>
<div style="margin-top:10px;">
Balance:<span id="balance0">0</span> xym
Balance:<span id="balance1">0</span> xym
</div>
<div id="balance_get" class="uk-button uk-button-primary" style="margin-top:10px;">残高取得</div>
<script>
const symbol=require("/node_modules/symbol-sdk");
let address;
const nodeAddress='http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000';
const repositoryFactory=new symbol.RepositoryFactoryHttp(nodeAddress);
const accountHttp=repositoryFactory.createAccountRepository();
$('#balance_get').click(function(){
address=symbol.Address.createFromRawAddress($('#address').val());
accountHttp.getAccountInfo(address)
.subscribe(function(accountInfo){
for(i=0;i<accountInfo.mosaics.length;i++){
$('#balance'+i).text(accountInfo.mosaics[i].amount.compact()/1000000);
}
console.log(accountInfo.mosaics[0].id.toHex())
}, err => console.error(err));
});
</script>
</body>
</html>
つまり、
$('#balance').text(accountInfo.mosaics[0].amount.compact()/1000000)
に関して、accountInfo.mosaicsに入っている配列の長さ分を繰り返せばよいので、accountInfo.mosaics.lengthと書くことになるので、for文は
for(i=0;i<accountInfo.mosaics.length;i++){$('#balance'+i).text(accountInfo.mosaics[i].amount.compact()/1000000);}
となります。また、それを画面に表示させたいので、
<div style="margin-top:10px;"> Balance:<span id="balance0">0</span> xym Balance:<span id="balance1">0</span> mosaic </div>
と書く事になります。
トランザクションの発行プログラム
Symbol SDKを使ってトランザクションの発行するプログラムについて解説します。トランザクションを発行することで、XYMやモザイクを送金することができます。ここでは以下のプログラムについて説明します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/css/uikit.min.css" />
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit-icons.min.js"></script>
<script src="https://xembook.github.io/nem2-browserify/symbol-sdk-2.0.3.js"></script>
<title>Symbolを送金してみる</title>
</head>
<body style="margin:30px;">
<h1>Symbolを触ってみる</h1>
<h2>送金してみる</h2>
<div style="margin-top:10px;">
Receiver Address:<input type="text" id="receiver_address" class="uk-input">
</div>
<div style="margin-top:10px;">
Sender Private Key:<input type="text" id="pk" class="uk-input">
</div>
<div id="issue_tx" class="uk-button uk-button-primary" style="margin-top:10px;">1xym送金</div>
<script>
const symbol=require("/node_modules/symbol-sdk");
let privateKey;
const nodeAddress='http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000';
const networkGenerationHash="6C1B92391CCB41C96478471C2634C111D9E989DECD66130C0430B5B8D20117CD";
const repositoryFactory=new symbol.RepositoryFactoryHttp(nodeAddress);
const accountHttp=repositoryFactory.createAccountRepository();
$('#issue_tx').click(function(){
privateKey=$('#pk').val();
let tx = symbol.TransferTransaction.create(
symbol.Deadline.create(),
symbol.Address.createFromRawAddress($('#receiver_address').val()),
[new symbol.Mosaic (new symbol.MosaicId('5E62990DCAC5BE8A'),symbol.UInt64.fromUint(100))],
symbol.PlainMessage.create('Send Symbol'),
symbol.NetworkType.TEST_NET,
symbol.UInt64.fromUint(100000)
);
const account = symbol.Account.createFromPrivateKey(privateKey,symbol.NetworkType.TEST_NET);
let signedTx = account.sign(tx,networkGenerationHash);
console.log(signedTx);
new symbol.TransactionHttp(nodeAddress).announce(signedTx)
.subscribe(function(x){
alert("Txが発行されました!");
console.log(x);
},function(x){
alert("エラーが発生しました!");
console.error(err)
});
});
</script>
</body>
</html>
このコードの完成版は、以下のようになります。
このことについて、説明します。
head要素について
以下の要素で、jQueryを読み込みます。
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
</script>
以下の3行でCSSのフレームワークであるUIKitを読み込んでいます。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/css/uikit.min.css" />
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit-icons.min.js"></script>
body要素について
ここで、このプログラムでは2つの入力枠を用意しています。
以下のコードがRecever Addressの入力枠となるコードです。
<div style="margin-top:10px;">
Receiver Address:<input type="text" id="receiver_address" class="uk-input">
</div>
次に、以下のコードでSender Private Keyの入力枠となるコードです。
<div style="margin-top:10px;">
Sender Private Key:<input type="text" id="pk" class="uk-input">
</div>
そして、以下のコードがボタンとなるものになります。このボタンをクリックすると、実行となります。
<div id="issue_tx" class="uk-button uk-button-primary" style="margin-top:10px;">1xym送金</div>
script要素について
以下の1行で、Symbol SDKに入っている情報を変数symbolに代入します。
const symbol=require("/node_modules/symbol-sdk");
つまり、Symbol SDKを呼び出す宣言になります。
また、プライベートキーを入れておく事を確保するために、変数をここで用意しておきます。
let privateKey
以下のコードでどこのノードアドレスに情報を問い合わせるのかを定数nodeAddressにて設定します。
const nodeAddress='http://api-01.eu-central-1.096x.symboldev.network:3000';
ノードアドレスを調べたい場合は、赤い四角で囲んだところをクリックしてノードのアドレスを確認できます。
ここから、ノードを選んでください。
ノードを選んだら、それに対応するネットワークジェネレーションハッシュが必要になります。
const networkGenerationHash="1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B";
このジェネレーションハッシュは以下のように書かれています。
つまり、ノードアドレスが、
http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000
の場合は
http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/node/info
ということになります。このURLで開いてみると・・・
networkGenerationHashSeedに、ネットワークジェネレーションハッシュが書かれています。これをコードに書きます。
ノードのURLとネットワークジェネレーションハッシュは一対一に結びついているので、ノードURLを決めたら、必ずネットワークジェネレーションハッシュを見つけてください。
次に、このコードを見ます。
const repositoryFactory=new symbol.RepositoryFactoryHttp(nodeAddress);
これは、Symbolの中身の仕組みを使うのに、nodeAddressを引数としたrepositoryFactoryを定数としておいたものです。(何をしているのかは筆者にはわかりません)
次に、このコードを見ます。
const accountHttp=repositoryFactory.createAccountRepository();
定数 accountHttpは、先ほど書いたrepositoryFactoryをcreateAccountRepositoryで読んで来いというものです。
ここまで読んだ以下のコード
const symbol=require("/node_modules/symbol-sdk");
let address;
const nodeAddress='http://api-01.eu-central-1.096x.symboldev.network:3000';;
const networkGenerationHash="1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B";
const repositoryFactory=new symbol.RepositoryFactoryHttp(nodeAddress);
const accountHttp=repositoryFactory.createAccountRepository();
は、基本的にコピペでOKで、変えるべきなのは、ノードアドレスと、そのジェネレーションハッシュということになります。
以下のコードについて、IDをissue_txで定義したボタン(「1XYM送金」と書かれたボタン)をクリックすると、
$('#issue_tx').click(function(){
の文以降の処理が発生します。
privateKey=$('#pk').val();
let tx = symbol.TransferTransaction.create(
symbol.Deadline.create(),
symbol.Address.createFromRawAddress($('#receiver_address').val()),
[new symbol.Mosaic (new symbol.MosaicId('5E62990DCAC5BE8A'),symbol.UInt64.fromUint(100))],
symbol.PlainMessage.create('Send Symbol'),
symbol.NetworkType.TEST_NET,
symbol.UInt64.fromUint(100000)
);
const account = symbol.Account.createFromPrivateKey(privateKey,symbol.NetworkType.TEST_NET);
let signedTx = account.sign(tx,networkGenerationHash);
console.log(signedTx);
new symbol.TransactionHttp(nodeAddress).announce(signedTx)
.subscribe(function(x){
alert("Txが発行されました!");
console.log(x);
},function(x){
alert("エラーが発生しました!");
console.error(err)
});
このコードについて見ていきます。
以下のコードで、入力枠に入力したプライベートキーと.val()を変数PrivateKeyに代入したものになります。
privateKey=$('#pk').val();
以下のコードで、トランザクションを発行するコードになり、基本的にコピペでOKです。
let tx = symbol.TransferTransaction.create(
symbol.Deadline.create(),
symbol.Address.createFromRawAddress($('#receiver_address').val()),
[new symbol.Mosaic (new symbol.MosaicId('5E62990DCAC5BE8A'),symbol.UInt64.fromUint(100))],
symbol.PlainMessage.create('Send Symbol'),
symbol.NetworkType.TEST_NET,
symbol.UInt64.fromUint(100000)
);
以下のコードは、トランザクションの有効期間になります。
symbol.Deadline.create(),
この書き方で、デフォルトで設定された期間になります。
以下のコードで入力したアドレスを取得しています。
symbol.Address.createFromRawAddress($('#receiver_address').val()),
次に、以下のコードで、送るモザイクの種類とその量を決めます。
[new symbol.Mosaic (new symbol.MosaicId('5E62990DCAC5BE8A'),symbol.UInt64.fromUint(100))],
5E62990DCAC5BE8A:モザイクのID
fromUint(100):送る量。単位はμXYM。この時は100μXYMを送る量としています。
以下のコードで送るメッセージを設定します。
symbol.PlainMessage.create('Send Symbol'),
このコードでは、メッセージを「Send Symbol」としています。
以下のコードで、テストネット上で送るように指示したものになります。
symbol.NetworkType.TEST_NET,
以下のコードで、送金手数料の最大値を書いています。
symbol.UInt64.fromUint(100000)
次に、以下のコードを見ていきます。
const account = symbol.Account.createFromPrivateKey(privateKey,symbol.NetworkType.TEST_NET);
let signedTx = account.sign(tx,networkGenerationHash);
console.log(signedTx);
new symbol.TransactionHttp(nodeAddress).announce(signedTx)
.subscribe(function(x){
alert("Txが発行されました!");
console.log(x);
},function(x){
alert("エラーが発生しました!");
console.error(err)
});
このコードで、どのアカウントでサインするのかを定義します。
const account = symbol.Account.createFromPrivateKey(privateKey,symbol.NetworkType.TEST_NET);
ここでは、プライベートキーからアカウントを作ります。ここでは、引数privateKyeに入っているプライベートキーと使うSymbolのネットワークタイプ(ここではテストネット)を引数privateKeyを変える事で、アカウントが変わります。さっき作ったトランザクションを、このアカウントでサインします。
次に、signTxという変数に対して
let signedTx = account.sign(tx,networkGenerationHash);
と書く事で、トランザクションデータとネットワークジェネレーションハッシュを使うことで、サインしたトランザクションができます。
ここで、sighedTxの中身を見てみます。
console.log(signedTx);
すると、Google Chromeのディベロッパーツールのconsoleを見ると、以下のようになっています。
payloadが、サインされたトランザクションを表します。これを作れる仕組みを考えれば、他のプログラミング言語にも移植することができます。(これがややこしいみたいです)
このpayroadは表示されていると、トランザクションを発生させたことに成功したことになります。
そして、次のコードで、ネットワークにXYMやモザイクを送金します。
new symbol.TransactionHttp(nodeAddress).announce(signedTx)
.subscribe(function(x){
alert("Txが発行されました!");
console.log(x);
},function(x){
alert("エラーが発生しました!");
console.error(err)
});
ここで、TransactionHttp(nodeAddress)で使うノードを決め、announce(signedTx)でサインしたトランザクションをネットワークにてアナウンスし、
.subscribe(function(x){
alert("Txが発行されました!");
console.log(x);
},function(x){
alert("エラーが発生しました!");
console.error(err)
});
でアナウンスした結果が画面に表示されるようになります。
最後に・・・
ここで、for文を用いて
とすると、複数回のトランザクションを1回のクリックで発生することもできます。
さらに面白い事を考えると・・・
ユーザーアドレスを配列として定義し、for文を配列の長さまで実行するように定義すれば、
定義したアドレス全員に1回のみで配る事ができます。
また、モザイクをランダムに配るプログラムを乱数を使ってできる可能性もあります・・・
その時は、for文以下の内容をどう考えていくのかということになるかもしれません。
可分性を考慮したSymbolプログラミング
可分性(divisibility)とは小数点以下の桁数のことです。
XYMの場合、可分性は6です。つまり、小数点以下は6けたです。つまり、小数点以下の0の数は6つあることになりますので、
1,000,000μXYM(マイクロXYM) = 1XYM
ということになります。
もし、可分性が3の場合、
1,000 mMosaic(ミリモザイク) = 1 Mosaic
になります。
ここで、可分性に関する部分に注目して、以下のコードを見ていきます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/css/uikit.min.css" />
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/uikit@3.5.8/dist/js/uikit-icons.min.js"></script>
<script src="https://xembook.github.io/nem2-browserify/symbol-sdk-2.0.3.js"></script>
<title>Symbol mosaicの可分性を確認する</title>
</head>
<body style="margin:30px;">
<h1>Symbolを触ってみる</h1>
<h2>可分性(モザイクが小数点以下何桁で表現されるか)</h2>
<div style="margin-top:10px;">Mosaic ID:<input type="text" id="mosaic_id" class="uk-input"></div>
<div style="margin-top:10px;">可分性:<span id="divisibility">0</span>桁</div>
<div id="issue_tx" class="uk-button uk-button-primary" style="margin-top:10px;">可分性確認</div>
<script>
const symbol=require("/node_modules/symbol-sdk");
const nodeAddress='http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000';
const mosaicHttp = new symbol.MosaicHttp(nodeAddress);
$('#issue_tx').click(function(){
mosaicHttp.getMosaic(new symbol.MosaicId($('#mosaic_id').val()))
.subscribe(mosaicInfo=>$('#divisibility').text(mosaicInfo.divisibility),err=>console.error(err));
});
</script>
</body>
</html>
まず、以下のコードを見ます。
const mosaicHttp = new symbol.MosaicHttp(nodeAddress);
MosaicHttpというオブジェクトを定数mosaicHttpに代入します。
この定数を用いて以下のコードを考えます。
mosaicHttp.getMosaic(new symbol.MosaicId($('#mosaic_id').val()))
.subscribe(mosaicInfo=>$('#divisibility').text(mosaicInfo.divisibility),err=>console.error(err));
mosaicHttpの中にあるgetMosaic(new symbol.MosaicId($('#mosaic_id').val()))に入力したモザイクIDを入れます。
すると、mosaicInfoというやつに、mosaicHttp.getMosaic(new symbol.MosaicId($('#mosaic_id').val()))を実行した結果が非同期処理(subscribe)として返ってきて、そして、
mosaicInfoの中のdivisibilityをIDがdivisibilityと名付けた以下の要素内のテキストに書き込みなさいという事になります。
<div style="margin-top:10px;">可分性:<span id="divisibility">0</span>桁</div>
なぜ可分性を考えるのか
XYMの場合は可分性は6と決められているので、それに合わせてコードを組めばいいのですが、実際のモザイクは可分性が必ずしも6とは限りません。そこで可分性を考慮した柔軟なプログラムを組む必要があり、以上のコードはその時に役に立ちます。
しゅうさんについて
しゅうさんは以下の開発で実績を残しています。
仮想通貨NEMを使ったブログサイト「NEMLOG」の開発
仮想通貨NEMを使った写真投稿サイト「nemgraph」の開発
仮想通貨Symbolのウォレット「Arcana」の開発
ゲーミングコントローラ「MH-Delta」の制作
興味がある方は、しゅうさんに連絡してみると良いかと思っています。
この記事が気に入ったらサポートをしてみませんか?