NEM Libraryを使ってみる(1) 初期設定と各種アカウント情報
以前試してみたNEM-sdkよりも、明らかに使いやすそうなNEM LibraryをAngularで動かしてみます。
TypeScriptできれいに書かれていて、RxJSが使われているのでAngularとも相性が良く、モダンJavaScriptプログラミングのいい教材になると思います。
また、NISからは配列で返ってきて操作しにくいモザイクのpropertiesがちゃんとプロパティになっていたり、ややこしいことで有名なdivisibility周辺も勝手にうまいことやってくれるので、NISの変な癖みたいな部分をきれいに吸収してくれています。
さらにはMultisigAggregateModificationTransactionなどにも対応しているので、機能的にも十分な機能を備えています。
初期設定
npmでパッケージをインストールします。
$ npm install nem-library --save
NEM Libraryはメインネットでもテストネットでも使えるので、app.module.ts
あたりでどちらか指定しておきます。
app.module.ts
import { NEMLibrary, NetworkTypes } from "nem-library"; NEMLibrary.bootstrap(NetworkTypes.MAIN_NET);
ちなみに自分の環境(@angular/cli: 1.3.1
)では以下のエラーが出ました。
In ambi ent enum declarations member initializer must be constant expression.
これはpackage.json
にあるdevDependencies
部分のtypescript
のバージョンを最新にしてnpm install
し直したところ解決しました。
Angular CLIを使って普通にプロジェクト生成した場合のデフォルトのバージョンが2.3.3でしたが、2.4以降が必要なんだと思います。
NISへの接続
NEM LibraryでNISへアクセスする場合、用途に応じてAccountHttp
、TransactionHttp
、MosaicHttp
などのHttpEndpoint
を継承したクラスを利用します。
HttpEndpoint
を継承したクラスはデフォルトでConnection Poolを使用しているので、引数無しで呼べば接続に成功するまで自動的にNISノードのリストに次々接続していきます。
(NanoWalletもこのような仕組みにすれば、開いたら残高がゼロなんです!!みたいな質問もなくなっていいんじゃないかと思うんですがどうなんでしょう。)
Account情報
AccountHttp
を利用するとアカウントの様々な情報を参照出来ます。
xemの残高や公開鍵などの情報を参照するには、getFromAddress
メソッドを利用します。
ちなみに一度もトランザクションを発行していないアドレスだと、Not a valid public key
のエラーが出ます。
let accountHttp: AccountHttp = new AccountHttp(); let address: Address = new Address("NDLHY5-KMQTAT-AR7IBR-BF32MA-QWDK73-33VNI2-MD5W"); accountHttp.getFromAddress(address) .subscribe(accountInfoWithMetaData => { console.log("getFromAddress", accountInfoWithMetaData); });
以下のようなAccountInfoWithMetaDataオブジェクトが返ります。
{ "balance":{ "balance":32799996, "vestedBalance":32796162, "unvestedBalance":3834 }, "importance":0, "publicAccount":{ "address":{ "value":"NDLHY5KMQTATAR7IBRBF32MAQWDK7333VNI2MD5W", "networkType":104 }, "publicKey":"099132a49ed0c15936a464cf6ef43120f01fa88835803593571882feea6161db" }, "harvestedBlocks":0, "status":"LOCKED", "remoteStatus":"INACTIVE", "cosignatoryOf":[], "cosignatories":[] }
パブリックキーからAccountInfoWithMetaData
を参照するには、getFromPublicKey
メソッドを利用します、
let publicKey: string = "099132a49ed0c15936a464cf6ef43120f01fa88835803593571882feea6161db"; accountHttp.getFromPublicKey(publicKey) .subscribe(accountInfoWithMetaData => { console.log("getFromPublicKey", accountInfoWithMetaData); });
モザイク情報
モザイクの情報を取得するにはMosaicHttp
を利用します。
AccountHttp
のgetMosaicOwnedByAddress
で所有モザイクの一覧を取得し、MosaicHttp
のgetMosaicDefinition
メソッドでそれぞれのモザイクの定義情報を取得します。
xemはgetMosaicDefinition
でエラーになったのでnemネームスペースでフィルターしました。
※中の人のアドバイスを受けて、RxJSを全力で活用したいい感じのコードに修正しました。
accountHttp.getMosaicOwnedByAddress(address) .flatMap(_ => _) .filter(mosaic => mosaic.mosaicId.namespaceId !== "nem") .flatMap(mosaic => { return mosaicHttp.getMosaicDefinition(mosaic.mosaicId) .map(mosaicDefinition => <any>{ mosaicOwnedByTheUser: mosaic, mosaicDefinition: mosaicDefinition }) }) .subscribe(mosaicInformation => { console.log(mosaicInformation.mosaicDefinition.id, mosaicInformation.mosaicDefinition); console.log(mosaicInformation.mosaicDefinition.id, mosaicInformation.mosaicOwnedByTheUser.quantity / (10 ^ mosaicInformation.mosaicDefinition.properties.divisibility)); });
MosaicDefinition
は以下のようなオブジェクトが返ります。
{ "creator":{ "address":{"value":"NDY4RHUZ3CZOZ53O5HNEXTEM7UF5X3MMDGH4IMAD","networkType":104}, "publicKey":"506e25d76aba0bc36dcf28127626a61f1d49ed45352f64fafaa72243c3e9e0ba" }, "id":{"namespaceId":"nextem.ex","name":"photon"}, "description":"Let there be light.", "properties":{ "initialSupply":1000000, "supplyMutable":true, "transferable":false, "divisibility":0 }, "metaId":75 }
アドレスが所有するモザイクの定義と残高を一度に取得するにはAccountOwnedMosaicsService
を利用します。
let accountOwnedMosaics = new AccountOwnedMosaicsService(new AccountHttp(), new MosaicHttp()); accountOwnedMosaics.fromAddress(address) .subscribe(mosaics => { console.log("accountOwnedMosaics", mosaics); });
以下のようなMosaicTransferableオブジェクト配列が返ります。
[ { "mosaicId":{"namespaceId":"nem","name":"xem"}, "properties":{ "initialSupply":8999999999, "supplyMutable":false, "transferable":true, "divisibility":6 }, "amount":32.799996 }, { "mosaicId":{"namespaceId":"nextem.ex","name":"higgs"}, "properties":{ "initialSupply":1000000, "supplyMutable":true, "transferable":true, "divisibility":6 }, "levy":{ "type":2, "recipient":{ "value":"NDY4RHUZ3CZOZ53O5HNEXTEM7UF5X3MMDGH4IMAD", "networkType":104 }, "mosaicId":{"namespaceId":"nem","name":"xem"}, "fee":10000 }, "amount":95.999999 } ]
トランザクション
トランザクションを参照するには、allTransactions
、incomingTransactions
、unconfirmedTransactions
などのメソッドを利用します。
pageSize
を指定しないとデフォルトでは10になります。最小5、最大100です。
accountHttp.allTransactions(address, undefined, undefined, 100) .subscribe(transaction => { console.log("allTransactions", transaction); });
以下のようなTransactionオブジェクト配列が返ります。
[ { "type":257, "version":1744830466, "timeWindow":{ "deadline":"2017-08-26T23:49:09", "timeStamp":"2017-08-26T21:49:09" }, "signature":"f5d861c62a2abd11a1517911a74123a4bf5a9ec749500bd0f14dcd8059ff3ec5814025969ddf3d79fbd0cbbec81d2f9519bf5c96fcccf76155c67450c86ee20b", "signer":{ "address":{ "value":"NDLHY5KMQTATAR7IBRBF32MAQWDK7333VNI2MD5W", "networkType":104 }, "publicKey":"099132a49ed0c15936a464cf6ef43120f01fa88835803593571882feea6161db" }, "transactionInfo":{ "height":1257790, "id":1006473, "hash":{ "data":"39d46a2fcbea6fed97518e0e77fae424c6dc5200bfa1169a76110ba0c84269ce" } }, "fee":200000, "recipient":{ "value":"NCS5BIMFLIOP5TMLKMINCN5C4PQ5VKYXK7BBVGYX", "networkType":104 }, "amount":1000000, "message":{ "payload":"" }, "mosaics":[ {"mosaicId":{"namespaceId":"nem","name":"xem"},"quantity":1000000}, {"mosaicId":{"namespaceId":"nextem.ex","name":"higgs"},"quantity":1000000}, {"mosaicId":{"namespaceId":"nextem.ex","name":"photon"},"quantity":1}, {"mosaicId":{"namespaceId":"nextem","name":"nex"},"quantity":1} ] } ]
もうこれ完全に暗号通貨の知識いらないですね。
※筆者のモチベーション向上のため、以下NEMアドレスへxemなりシットトークンなりの寄付を受け付けています。
NDY4RH-UZ3CZO-Z53O5H-NEXTEM-7UF5X3-MMDGH4-IMAD