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へアクセスする場合、用途に応じてAccountHttpTransactionHttpMosaicHttpなどの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を利用します。

AccountHttpgetMosaicOwnedByAddressで所有モザイクの一覧を取得し、MosaicHttpgetMosaicDefinitionメソッドでそれぞれのモザイクの定義情報を取得します。 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
  }
]

トランザクション

トランザクションを参照するには、allTransactionsincomingTransactionsunconfirmedTransactionsなどのメソッドを利用します。 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