文系プログラマがdAPPs開発ハンズオン with NEM(Catapult - Cowアップデート済)

#あいさつ
教育学部一年です。
東京の会社にリモートでインターンをしてます。

今回はその業務関連で勉強したことをまとめます。
多くのバグに衝突し、その対処法を逐次書くのでテンポは非常に悪いです。必要なところだけを適宜読み抜くようにしてください。

ほんと、バグがいっぱいで辛かった……
#やること
[NEMの公式チュートリアル](https://nemtech.github.io/getting-started/what-is-nem.html)の冒頭から、TypeScriptのサンプルコードを実行するところまでです。

#環境（執筆時）
Windows 10
TypeScript Version 3.3.4000
Node.js v8.11.3
nem2-cli @0.11.0
nem2-sdk @0.11.1
npm @6.9.0
ts-node @8.0.3

#環境設定
Node.jsはインストールしておいてください。

```sh
npm i npm
npm init
npm i -g nem2-cli nem2-sdk typescript rxjs js-joda ts-node utf8
```

#アカウント作成
まず、NEMアカウントを作成します。
今回は会社の上司がオンラインでNodeを建ててくれたので、そちらを使います。
（通常はローカルで環境を作りそこでテストするのが王道のようです。）

```console
>nem2-cli profile create
```

しかし、まずここからエラーに衝突します。幸先悪いですね。
##Error1: アカウント作成エラー
`Error Provide a valid NEM2 Node URL. Example: http://localhost:3000`
エラーに記述があるとおりのURLや、Private KEYなど様々確認するのですが、どうやら違う様子。

###解決
```
Profile stored correctly
default->
        Network:        MIJIN_TEST
        Url:            http://~
        Address:        SCJY~
        PublicKey:      63FD~
        PrivateKey:     783E~
```

端的に言って、これはいつの間にか解決していました。
しかし、エラーの原因をいろいろ探していた経験を鑑みると、ホームディレクトリに`.nem2rc.json`ファイルが存在しなかったのが第一の原因のように思います。

同様のことにぶち当たった方は、ホームディレクトリに`.nem2rc.json`ファイルを作成し、`{}`とだけ記入して保存してみてください。

話をチュートリアルに戻し、アカウント情報を取得してみます。

```console
>nem2-cli account info

Account:        SCJY~
-------------------------------------------------------

Address:        SCJY~
at height:      1

PublicKey:      0000~
at height:      0

Importance:     0
at height:      0

Mosaics
650b00d30620b86a:       409090909

```
MosaicIDが650b~のモザイクがすでに付与されていますね。

ここで、チュートリアルを進めるため、別アカウントも作成しておきます。

```console
>nem2-cli account generate --network MIJIN_TEST --save --url http://~ --profile buyer

New Account:    SAXS~
Public Key:     8CF5~
Private Key:    8BF8~

Stored buyer profile
```
#Monitor類の作成
モニタリング用のコマンドプロンプトを３つ用意し、それぞれ`nem2-cli monitor status`, `nem2-cli monitor unconfirmed`, `nem2-cli monitor confirmed`を実行しておきます。

最初のコマンドはエラーを表示してくれます。二番目のが「正常に予約された処理」。三番目のは「正常に終了した処理」を表示します。

#モザイク作成
チュートリアル通りです。
すでにトークンが付与されていましたが、新しく作ってみます。

```console
>nem2-cli transaction mosaic --amount 1000000 --supplymutable --divisibility 0 --duration 1000

Do you want mosaic to be transferable? [y/n]: n
Do you want mosaic to have levy mutable? [y/n]: y
Transaction announced correctly
Hash:    A2D3~
Signer:  63FD~
Your mosaic id is:
Hex:  10a5abae76de8603
Uint64: [ 1994294787 279292846]
```

#モザイク送信
これもチュートリアル通り。
最初に登録したdefaultアカウントから、buyerアカウントへモザイクを送信しています。

まずは、もともと作ってあったモザイクの方から。

```console
>nem2-cli transaction transfer --recipient SAXS~ --mosaics @cat.currency::10 --message "Hello NEM" --profile default

Transaction announced correctly
Hash:    969E65EBFF15827A34B2C949D9AC6F7299A62DA04F372FB272952A6DA8AAD0BE
Signer:  63FD~
```
buyerアカウント情報を取得してみましょう。

```console
>nem2-cli account info --profile buyer

Account:        SAXS~
-------------------------------------------------------

Address:        SAXS~
at height:      16561

PublicKey:      0000~
at height:      0

Importance:     0
at height:      0

Mosaics
650b00d30620b86a:       0.00001
```
モザイクは正常に送信されていますね。

作った方のモザイクも送信してみます。

```console
>nem2-cli transaction transfer --recipient SAXS~ --mosaics 10a5abae76de8603::10 --message "Hello buyer" --profile default

Transaction announced correctly
Hash:    B830~
Signer:  63FD~
```
同じく、defaultアカウントからbuyerアカウントへの送信です。
アカウント情報を確認してみます。

```console
>nem2-cli account info --profile buyer

Account:        SAXS~
-------------------------------------------------------

Address:        SAXS~
at height:      16561

PublicKey:      0000~
at height:      0

Importance:     0
at height:      0

Mosaics
10a5abae76de8603:       10
650b00d30620b86a:       0.00001
```
650b~以外のモザイクが取得できていますね！

#TypeScriptサンプルコードの実行
さて、ここからがひどい泥沼だったのですが……
まず、NEMアプリを記述したTSコードを実行するには、Node.js上で実行する必要があります。
手段は二通り。

1. `~.ts`ファイルを直接`ts-node`コマンドで実行
2. `~.ts`ファイルを`~.js`ファイルにコンパイルし、`node`コマンドで実行

結論から言うと、1は断念し、2はうまくいきました。

こちらがソースコード。

```test.ts
import {
    Account, Address, Deadline, UInt64, NetworkType, PlainMessage, TransferTransaction, Mosaic, MosaicId,
    TransactionHttp
} from 'nem2-sdk';

const transferTransaction = TransferTransaction.create(
    Deadline.create(),
    Address.createFromRawAddress('SC7A~'),
    [new Mosaic(new MosaicId([ 1994294787, 279292846 ]), UInt64.fromUint(1))], // Replace with your mosaicId
    PlainMessage.create('enjoy your ticket'),
    NetworkType.MIJIN_TEST
);

const privateKey = process.env.PRIVATE_KEY;

const account = Account.createFromPrivateKey(privateKey, NetworkType.MIJIN_TEST);

const signedTransaction = account.sign(transferTransaction);

const transactionHttp = new TransactionHttp('http://~');

transactionHttp.announce(signedTransaction).subscribe(
    x => console.log(x),
    err => console.log(err)
);
```
チュートリアルサイトのコードを貼り合わせてMosaicIDとかを変更しただけですね。

実行前に、環境変数に`PRIVATE_KEY`を登録しておくのもお忘れなく。

では方針1「`ts-node`で直接実行」を試してみます。

```console
>ts-node ./~/test.ts

C:\~\index.ts:226
    return new TSError(diagnosticText, diagnosticCodes)
           ^
TSError: ⨯ Unable to compile TypeScript:
~/test.ts:4:8 - error TS2307: Cannot find module 'nem2-sdk'.

4 } from 'nem2-sdk';
         ~~~~~~~~~~
~/test.ts:14:20 - error TS2580: Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.

14 const privateKey = process.env.PRIVATE_KEY;
                      ~~~~~~~

    at createTSError (C:~\index.ts:226:12)
    at getOutput (C:~\index.ts:335:40)
    at Object.compile (C:~\index.ts:368:11)
    at Module.m._compile (C:~\index.ts:414:43)
    at Module._extensions..js (module.js:663:10)
    at Object.require.extensions.(anonymous function) [as .ts] (C:~\index.ts:417:12)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
```
だめみたいですね。
じゃあ方針2「一度コンパイルしてから実行」も試してみます。

```console
>tsc ./~/test.ts

~/MultisigAccountGraphInfo.d.ts(8,48): error TS2583: Cannot find name 'Map'. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.
~/Observable.d.ts(89,59): error TS2585: 'Promise' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.
~/types.d.ts(41,84): error TS2304: Cannot find name 'Iterable'.
~/types.d.ts(45,6): error TS2585: 'Symbol' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.
C:~\index.ts(14,20): error TS2580: Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.
```
あらら……

##Error2: TSファイルが実行できない
エラーコード通りにtsconfigを設定するも解決せず。
まぁエラーの一部は.tsファイルの存在するディレクトリで`npm i nem2-sdk`やってなかったとかいう超初歩的なものだったのですが…笑

tsconfig設定とかをいろいろ調べ回った挙げ句、原因は「型定義ファイルがインストールされていない」、「管理者権限でないとコンパイラが環境変数を読みにいけない」ということでした。

##解決
TSファイルのあるディレクトリで`npm i nem2-sdk @types/core-js @types/node`を実行します。
次に、管理者モードでコマンドプロンプトを開き、`tsc`コマンドでTSファイルをコンパイル。
そうしたら、非管理者モードのコマンドプロンプトに戻って`node ~.ts`を実行。

管理者モードではPOSTメソッドが許可されていないらしいので、実行そのものは非管理者モードでないといけないようです。（おそらく、ts-nodeがうまくいかないのはこのため……）

```console
>node ./~/test.js

TransactionAnnounceResponse {
  message: 'packet 9 was pushed to the network via /transaction' }
```
以上、TSサンプルコードの実行は見事達成できました。

##終わりに
以下蛇足ですが感想など。

前半からエラー（しかも環境依存ぽい）に苦しまされ続けたので、非常に疲れました。右も左もわからない中でもがくハメになり、NEMコミュニティの皆様の助言の数々は（直接の解決には至らずとも）貴重なマイルストーンとなり、とても感謝しています。
ともかくも良い勉強になりましたね。またできることが一つ増えた。

次回からはNEMの新機能などに深く突っ込んだ記事を書いていく所存です。

最後になりましたが、いろいろご指導くださった @daoka さんに感謝申し上げます。これからも頑張っていきます。