nem-sdkのモザイク送金のサンプルコードを自動で手数料計算するように変更してみる

[「NEMを使ってYoのパロディサイトを(略」のスピンオフです](https://qiita.com/xiaca/items/87d6a985bb6bc843b89c)

前々から書きたかったnem-sdkを使ったモザイク送金についての記事。
手数料計算時にハマり解決したけどイマイチ自身がないので記事にします。



# 変更前のソースコードについて
[変更前のソースコードはこちら](
https://github.com/QuantumMechanics/NEM-sdk/blob/master/examples/nodejs/mosaicTransfer.js)

リンク先のサンプルだと手数料(fee)を自分で設定しないかぎり送金に失敗します。

send直前でトランザクションオブジェクトの状態をみると以下の通り手数料(fee)がぬるぽになっています。よって__「FAILURE_INSUFFICIENT_FEE」エラー__になります。一度はハマるかと思います。

````json
{
  "type": 257,
  "version": -1744830462,
  "signer": "d914bf22b9d070f9a93b43d6c0141ccdb4e5387388197c0ecde135dc02e0d757",
  "timeStamp": 97423640,
  "deadline": 97427240,
  "recipient": "TAO5MJ7J35XXZFTW6V5BJOAHTORFS7WOE2VKJBFQ",
  "amount": 1000000,
  "fee": null,
  "message": {
    "type": 1,
    "payload": "48656c6c6f"
  },
  "mosaics": [
    {
      "mosaicId": {
        "namespaceId": "nem",
        "name": "xem"
      },
      "quantity": 1000000
    },
    {
      "mosaicId": {
        "namespaceId": "greeting",
        "name": "ohayo"
      },
      "quantity": 1
    }
  ]
}

````



# 変更後のソースコード
以下が手数料を自動計算してくれるように変更したソースコードです。

主な変更点はトランザクションオブジェクト送信前の「モザイクの供給量を取得」部分です。
ここで、送りたいモザイクの供給量を取得します。これをmosaicDefinitionMetaDataPair変数のsupplyフィールドに設定しないと手数料がぬるぽになってしまうんですね。

````javascript
// nem-libraryを読み込む。
var nem = require("../../build/index.js").default;

// endpointオブジェクトの作成
var endpoint = nem.model.objects.create("endpoint")(nem.model.nodes.defaultTestnet, nem.model.nodes.defaultPort);

// commonの作成
var common = nem.model.objects.create("common")("", "ウォレットの秘密鍵");

// モザイクメタデータを保持する変数を作成する。手数料計算のために必要(xemは既に定義済み)
var mosaicDefinitionMetaDataPair = nem.model.objects.get("mosaicDefinitionMetaDataPair");

// モザイク送金のためのトランザクションオブジェクトを作成する。amountを1に設定しないと送金できても入金に反映されなかった。なぜ...。しかもこれを1にしてもモザイク送金時はxemを送金したことにならない。なぜ...。
var transferTransaction = nem.model.objects.create("transferTransaction")("TAO5MJ7J35XXZFTW6V5BJOAHTORFS7WOE2VKJBFQ", 1, "Hello");

/**
 * XEMモザイクの貼付け
 *
 * xemに関してはモザイクのメタデータを取得する必要はないです(nem-sdkが用意してくれているため　)
 */

// モザイク添付オブジェクトの作成
var mosaicAttachment = nem.model.objects.create("mosaicAttachment")("nem", "xem", 1000000);

// トランザクションオブジェクトのモザイクフィールドに追加
transferTransaction.mosaics.push(mosaicAttachment);

/**
 * 別のモザイクの貼付け
 *
 * com.requestsを使用してモザイクのメタデータを取得する必要があります。
 */

// モザイク添付オブジェクトの作成
var mosaicAttachment2 = nem.model.objects.create("mosaicAttachment")("greeting", "ohayo", 10); // ohayoトークン

// トランザクションオブジェクトのモザイクフィールドに追加
transferTransaction.mosaics.push(mosaicAttachment2);

//手数料計算に「greeting:ohayo」のモザイクのメタデータが必要なので、ネットワークから取得します。
nem.com.requests.namespace.mosaicDefinitions(endpoint, mosaicAttachment2.mosaicId.namespaceId).then(function(res) {

	//リクエスト応答から「greeting:ohayo」のメタデータを探します
	var neededDefinition = nem.utils.helpers.searchMosaicDefinitionArray(res.data, ["ohayo"]);
	
	// オブジェクトキー用にモザイク名を設定します（{namespaceId:"greeting",name:"ohayo"}→"greeting:ohayo"）
	var fullMosaicName  = nem.utils.format.mosaicIdToName(mosaicAttachment2.mosaicId);

	// モザイクが見つかったかを検証します
	if(undefined === neededDefinition[fullMosaicName]) return console.error("Mosaic not found !");

	// ohayoモザイクのメタデータをmosaicDefinitionMetaDataPairに設定します
	mosaicDefinitionMetaDataPair[fullMosaicName] = {};
	mosaicDefinitionMetaDataPair[fullMosaicName].mosaicDefinition = neededDefinition[fullMosaicName];

    /**
     * ★★モザイクの供給量を取得★★
     *
     * 手数料計算に必要なモザイクの供給量を取得する
     */
    // ohayoモザイクの供給量を取得します
    nem.com.requests.mosaic.supply(endpoint, fullMosaicName).then(function(res) {
        // 供給量をmosaicDefinitionMetaDataPairに設定する。
        mosaicDefinitionMetaDataPair[fullMosaicName].supply = res.supply;
        // 転送トランザクションオブジェクトを準備する
        var transactionEntity = nem.model.transactions.prepare("mosaicTransferTransaction")(common, transferTransaction, mosaicDefinitionMetaDataPair, nem.model.network.data.testnet.id);

        // 転送トランザクションをシリアル化してNISに送る
        nem.model.transactions.send(common, transactionEntity, endpoint).then(function(res){
        }, function(err) {
            console.error(err);
        });
    }, function(err) {
        console.error(err);
    });

}, 
function(err) {
	console.error(err);
});

````



## 余談
nem.com.requests.namespace.mosaicDefinitionsでもモザイクの供給量っぽいものが取得できますがそれは__「初期供給量」__です。モザイク発行総量は後から変更することが可能ですが、変更してもこの初期供給量は変わりません。手数料計算には現在の発行総量が必要なためnem.com.requests.mosaic.supplyを使う必要があるようです。


## もうひとつ余談
NEM-libraryだとinitialSupplyで手数料計算しているので、発行総量を変更したら__FAILURE_INSUFFICIENT_FEEエラー__がでるんじゃないかと予想している。またnem-library-tsを触り始めたら試してみます。
[ソースコード](https://github.com/aleixmorgadas/nem-library-ts/blob/d8df40c5a1f8b2cc858e416d7c0f9801d21e6d2d/src/models/transaction/TransferTransaction.ts#L209)

## 追記
本記事のsupply周りについては下のissueです。
https://github.com/QuantumMechanics/NEM-sdk/issues/36

上記issueは私がぬるぽになる件をissueとしてNEM-sdkにあげたときに開発者からかえってきた返答です。改めてみてみるとNanoWalletもnem.com.requests.mosaic.supplyを使って手数料計算してました。

送金周りを作る時処理は何を見れば良いのかわからず...ということが多いですが、とりあえずNanoWalletのソースコードを読むのが一つの手段かと思います。
https://github.com/NemProject/NanoWallet