NEM NanoWalletのServicesをnpmモジュール化する
前回に続いて、NEM NanoWalletの中身をnpmモジュール化していきます。
前回はUtilsをnpmモジュール化したので、今回はそのモジュールを利用した各種Servicesの移植に進みます。
Services
まずはServicesの中身を整理します。
- Alert
alert.service.js
エラーメッセージに関するサービス。
- Connector
connector.service.js
SockJSを利用してWebSocket APIに接続するサービス。
- DataBridge
dataBridge.service.js
NetworkRequestsやConnectorを利用してデータを取得、保持するサービス。
- NetworkRequests
networkRequests.service.js
各種APIへ接続するサービス。
- Transactions
transactions.service.js
各種トランザクションを作成するサービス。
- Wallet
wallet.service.js
カレントアドレスと接続ノードなどを設定するサービス。
- WalletBuilder
walletBuilder.service.js
アドレスを生成するサービス。
WebSocketでリアルタイムにデータを取得したいのであれば、ConnectorとDataBridgeが必要になりますが、トランザクションを生成してブロードキャストするだけなら、NetworkRequestsとTransactionsがあれば事足りるので、今回はこの二つのファイルを対象とします。
対象のファイル2ファイルは、上記のDataBridge、Walletを参照しています。
DataBridgeについては、NISとの時刻の同期を行っている部分を参照しているだけなので、今回は時刻同期無しでDataBridgeは無視します。
Walletについては、今回は定数としてしか使わないので、定数を保持するだけのクラスとして実装します。本来はWalletのインスタンスをコンストラクタで受ける形になるかと思います。
また、以下のconfigフォルダにあるAppConstantsも参照しています。
AppConstantsはデフォルトのポート番号や、有効な言語の一覧などが入っている設定ファイルなので、スタティックな変数だけ持つクラスとして実装します。
Typescriptで実装する
せっかくなのでTypescriptで書き換えていきます。(と言っても片っ端からanyで型指定していくことに何の意味があるのかは知りませんがw)
まずは、TypeScriptのファイルと、生成されるJavaScriptのファイルをそれぞれ管理する必要があるので、少し環境を整えます。
srcがソース置き場、libが実体置き場ということにしてディレクトリを分けます。
npmには実体だけアップしたいので、.npmignoreに除外するsrcフォルダを指定します。
.npmignore
src/
また、生成されたJavaScriptのファイルをgitで管理したくないので、.gitignoreに除外するlibフォルダを指定します。
.gitignore
lib/
srcディレクトリに移動してtscの--initオプションでtsconfig.jsonを生成し、outDirで出力先を設定します。
tsconfig.json
{ "compilerOptions": { "module": "commonjs", "target": "es5", "noImplicitAny": false, "sourceMap": false, "outDir": "../lib" } }
最終的なディレクトリ構成は以下のようになります。
nem-services
│ .gitignore
│ .npmignore
│ LICENSE
│ package.json
│ README.md
│
├─lib
│ appConstants.js
│ index.js
│ networkRequests.js
│ transactions.js
│ wallet.js
│
└─src
appConstants.ts
index.ts
networkRequests.ts
transactions.ts
tsconfig.json
wallet.ts
それぞれのサービスの実装
あとはそれぞれのファイルの、元のコードのままでは動かない部分について修正しつつ、狂ったように型指定していきます。
基本的にはそのままですが、AngularJSのhttp部分をisomorphic-fetchで書き換えました。
fetchした時に帰ってくるPromiseをObservableにしていじくりまわしたのでrxjsも必要になりました。
なぜここでRxJSかというと、それってモダンじゃね?ぐらいの感覚で良く分かってませんw
package.json
"dependencies": { "rxjs": "^5.1.0", "isomorphic-fetch": "^2.2.1", "nem-utils": "0.0.3" }
NetworkRequests
httpアクセス部分は以下のような感じです。
正直、Observableの使い方、使いどころが正しいのか良く分かりませんが、動いたので正しいということにしておきます。
import * as fetch from "isomorphic-fetch"; import { Observable } from 'rxjs'; import 'rxjs/add/operator/map'; getHeight(host: string): Observable<number> { let url: string = "http://" + host + ":" + this.getPort() + "/chain/height"; return Observable.fromPromise(fetch(url)) .flatMap(response => response.json()) .map(json => json.height); }
Transactions
DataBridgeが担っていた部分は、以下のように書き換えました。
// let d = new Date(); // let timeStamp = Math.floor(this._DataBridge.networkTime) + Math.floor(d.getSeconds() / 10); let timeStamp: number = helpers.createNEMTimeStamp();
その他、jQueryを使っている部分をNativeなコードに直したり、型に関しては結構ゆるい作り(数値型っぽいところに突然エラーメッセージ配列を放り込む)になっていたので、その辺を微調整しました。
実行
以下のようにしてインポートして利用します。 プライベートキーは伏せてありますが、実際は正しいものを利用します。
import { Transactions } from "nem-services"; let common = { privateKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", password: "" } let dummyTransaction = { recipient: "NDLHY5KMQTATAR7IBRBF32MAQWDK7333VNI2MD5W", recipientPubKey: "099132a49ed0c15936a464cf6ef43120f01fa88835803593571882feea6161db", amount: 0, message: "sooooooooooooooooooooooooooooon", mosaics: null, fee: 12000000, innerFee: 0, due: 60, isMultisig: false, multisigAccount: "" } let tx = new Transactions(); let entity = tx.prepareTransfer(common, dummyTransaction, null); tx.serializeAndAnnounceTransaction(entity, common) .subscribe(s => console.log(s));
以下のレスポンスが返ってきました。
{
innerTransactionHash: Object,
code: 1,
type: 1,
message: "SUCCESS",
transactionHash: Object
}
そして無事トランザクションが取り込まれました。
http://chain.nem.ninja/#/transfer/aa530b1189fa5401101df71232fa0cf0f883a5d25364e552ea95d986a2c215c4
※筆者のモチベーション向上のため、以下NEMアドレスへxemなりシットトークンなりの寄付を受け付けています。
NDY4RH-UZ3CZO-Z53O5H-NEXTEM-7UF5X3-MMDGH4-IMAD