AndroidでNEMのモザイクを送受信する
NEMのAPIをAndroidからいろいろ触ってみてます。今回は、NEMのモザイクの送信をやってみました。
前回の続き的な内容ですので、先に前回記事を読んでいただくとより理解が深まるかと思います。
2017/11/21 追記
この記事の内容を元にライブラリを作成して公開しました。
モザイクって何?
NEM の通貨は XEM という単位ですが、同じように独自の通貨をユーザが定義できるようになっています。
その通貨(トークン)のことをモザイクと呼びます。
モザイクを定義するにあたり、ネームスペースというものが必要になります。
ネームスペースはインターネット上のドメインのようなものですが、このあたりはクリプトストリームさんが詳細に説明されていますので、「モザイクとかネームスペースとか初めて聞いた!」という方は下記記事を見ていただくと良いかと思います。
今回は、このモザイクの送受信を NEM の API を使ってやります。
作ったアプリ
では、今回作ったアプリ。
前回作成したアプリに、モザイク情報の取得、モザイク送信の機能を追加してみました。
コード解説
例の如く、全文はGitHubを参照ください。
保有しているモザイクの取得
保有している XEM 残高はアカウント情報取得の API で取得できましたが、ここで取得した情報には保有しているモザイクの情報は含まれません。
保有しているモザイクの取得は、/account/mosaic/owned
API を使います。クエリで指定したアドレスの保有しているモザイク情報を取得することが出来ます。
例えば、下記のようなURLのリクエストで
1 2 |
http://62.75.251.134:7890/account/mosaic/owned?address=NB...(アドレス)... |
下記のような JSON のレスポンスが得られます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
{ "data": [ { "quantity": 1199985, "mosaicId": { "namespaceId": "nem", "name": "xem" } }, { "quantity": 1000, "mosaicId": { "namespaceId": "ttech", "name": "ryuta1k" } }, { "quantity": 4, "mosaicId": { "namespaceId": "ttech", "name": "ryuta" } } ] } |
この API のレスポンスには XEM が含まれています。XEM は NEM の通貨ですが、nem というネームスペースの、 xem という名前のモザイクと捉えることも出来ますね。
他二つ、いずれも ttech というネームスペースで、ryuta1k と ryuta という名前のモザイクを保有していることがわかります。
quantity の単位はちょっと注意が必要です。
モザイクには可分性 (divisibility) という設定があります。可分性はそのモザイクで小数点以下第何位まで扱えるかという定義で、10^(-divisibility) がそのモザイクの最小単位となります。
例えば、上記の ryuta というモザイクは可分性 0 なので、最小単位は 1 となります。小数点以下は扱わないという定義になっています。
ryuta1k の方は、可分性 3 にしているので、0.001 が最小単位です。
可分性は、NanoWalletでモザイク作成する時に下記画面で設定できます。
さて、quantity の話に戻りますが、実は上記アドレスには 1.0 ryuta1k しか送っていません。しかし、quantity は 1000 で表示されています。
この quantity は、最小単位の何倍かという値になっていて、常に整数です。
可分性 0 の場合はその値をそのまま使っても問題ありませんが、可分性が 1 以上のモザイクであればユーザに値を表示する時には可分性を考慮する必要があるかと思います。
quantity が常に整数なのは、そうすることで内部計算で誤差が発生しないようにしているのかなと推測します。
ちなみに、XEM の可分性は 6 です。XEM 送信時の送信量やトランザクションの手数料をマイクロ単位 (本来のXEMの量 * 10^6 した値) で指定する理由がわかってきたかと思います。
モザイクの定義の取得
モザイクの送信を試したいところですが、後述するモザイクの送信手数料の計算で、モザイクの供給量と可分性の情報が必要になるので、先にその取得方法を書いておきます。
モザイクの定義は、namespace/mosaic/definition/page
で取得できます。クエリでネームスペースを指定します。また、オプションで id、pagesize を指定できます。
この API は、指定した namespace 下に定義されているモザイクの定義を一覧で取得する API のようです。pagesize で一度に取得するモザイク定義の数を指定できます。
また、id で ID を指定すると、その id より若い ID を持つモザイクの定義を取得することができます。
id を指定しない場合は、最新のモザイクの一覧を取得します。一度に全てのモザイクの情報を取得できない場合は、取得したモザイク定義リストの末尾のID を指定して再度 API 呼び出しを繰り返す、というかたちでページングしていくことのできる API のようです。
例えば、下記のような URL のリクエストで
1 2 |
http://62.75.251.134:7890/namespace/mosaic/definition/page?namespace=ttech |
下記のようなJSONが返ってきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
{ "data": [ { "meta": { "id": 507 }, "mosaic": { "creator": "...(作成者の公開鍵)...", "description": "りゅーたがモザイクの徴収を試すために作ったモザイク", "id": { "namespaceId": "ttech", "name": "ryutalevymosaic" }, "properties": [ { "name": "divisibility", "value": "0" }, { "name": "initialSupply", "value": "1000000" }, { "name": "supplyMutable", "value": "true" }, { "name": "transferable", "value": "true" } ], "levy": { "fee": 5, "recipient": "...(徴収の受信アドレス)...", "type": 1, "mosaicId": { "namespaceId": "ttech", "name": "ryuta" } } } }, { "meta": { "id": 506 }, .... (モザイク定義が続く) .... |
divisibility が可分性ですね。また、initialSupply というフィールドで供給量が取得できます。
モザイクの面白い機能の一つとして、モザイク送信時に別のモザイクの徴収を行うことが出来ます。
levy に入ってきているのは、その徴収の情報ですね。このモザイクは、モザイク送信時に手数料とは別に 5 ryuta モザイクが徴収されるように設定しています。
モザイクの送信
モザイクの定義もわかったところで、いよいよモザイクの送信をしてみます。
あらかじめアプリのアドレスの方にモザイクを送信しておいてくださいね。
モザイクの送信は、XEM を送信した時と同じ転送トランザクションを使います。
トランザクションのバイト列を用意し、それを署名して署名バイト列と一緒に送信します。このあたりは前回記事も参考に。
おさらいも兼ねて、モザイク転送のバイト列を書き出すと下記のような形になります。
- トランザクションの種別(4バイト)
- バージョン(メインネットワークか、テストネットワークか。4バイト)
- タイムスタンプ(4バイト)
- 公開鍵の長さ(32固定、4バイト)
- 公開鍵(32バイト)
- 手数料(マイクロNEM単位、8バイト)
- デッドライン(4バイト)
ここまではトランザクション共通です。以下は、送金(転送トランザクション)用のバイト列
- 受信者のアドレス長(40固定、4バイト)
- 受信者のアドレス(UTF-8、40バイト)
- 送信量(8バイト)
- メッセージフィールドの長さ(下3つのバイト数の合計。ここが 0 なら下2つのフィールドは存在しない。4バイト)
- メッセージ種別(平文か、暗号文か。4バイト)
- メッセージ長(4バイト)
- メッセージペイロード(UTF-8)
- モザイク数(4バイト)
以降はモザイク数分繰り返し
- モザイク構造長(下 6 つのフィールドのバイト数の総和, 4バイト)
- モザイクID構造長(下 4 つのフィールドのバイト数の総和, 4バイト)
- ネームスペース長(4バイト)
- ネームスペース(UTF-8)
- モザイク名長(4バイト)
- モザイク名(UTF-8)
- モザイク送信量(8バイト)
注意すべきフィールドを抜粋します。
手数料
モザイク送信時の手数料計算は、XEM送信時とはまた違った計算式になります。
計算式は API ドキュメントの 7.10. Transaction fees にありますが、これは執筆時点では古い情報らしく、この計算値を 20 分の 1 にした値が実際の値だそう。NEM の Tech support に投稿がありました。
APIドキュメント修正されてました。
現在の計算式は API ドキュメントの 7.10. Transaction fees であってそうです。
計算としては、
- 可分性が 0 でかつ供給量が 10,000 以下のモザイクの手数料は 0.05 xem
- それ以外のモザイクの場合
- モザイクをXEM相当の量に換算した時の手数料を求める。XEM相当の量は、XEMの供給量とモザイクの供給量の比率で求める
- 供給量に応じた調整値を減算する
という流れで求めるそうです。供給量に応じた調整値?みたいなものがよくわからなかったのですが、自然対数などを使って求めていました。詳細は API ドキュメントをご確認ください。
kotlin の実装に落としたものが下記。
NanoWalletと計算結果は一致してそうでしたが、間違ってたら誰か教えてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private fun calculateMosaicTransferFee(mosaic: MosaicAttachment): Long { val factor = 50_000L return if ( mosaic.divisibility == 0 && mosaic.supply < 10_000 ) { factor } else { val maxMosaicQuantity = 9_000_000_000_000_000L val totalMosaicQuantity = mosaic.supply * Math.pow(10.0, mosaic.divisibility.toDouble()) val supplyRelatedAdjustment = Math.floor(0.8 * Math.log(maxMosaicQuantity / totalMosaicQuantity)).toLong() val xemEquivalent = (8_999_999_999L * mosaic.quantity) / ( mosaic.supply * Math.pow(10.0, mosaic.divisibility.toDouble()) ) val microNemEquivalentFee = calculateMicroNemTransferFee((xemEquivalent * Math.pow(10.0, 6.0)).toLong()) Math.max(factor, microNemEquivalentFee - factor * supplyRelatedAdjustment) } } |
一度の転送トランザクションで複数のモザイクを同時に送信することが出来ますが、その場合は上記式をモザイク数分繰り返して合計を求めることになります。
また、徴収される分は手数料に含める必要はありません。トランザクションを発行したら勝手に徴収されます。
徴収するモザイクが足りない場合は、”FAILURE_INSUFFICIENT_BALANCE” でエラーになります。
送信量
amount というフィールドで定義されている値で、XEM を送信する時には送信する XEM の量をマイクロ NEM 単位で与えていたものです。
このフィールドは、モザイク送信時には別の意味になり、この値を 1,000,000 で割った値のセット数、添付されているモザイクを送るという意味になります。
非常に混乱しやすいフィールドかと思いますので、設定値には注意が必要です。
amount = 0 としていると、モザイク送信が行われません(エラーにはならず、0 セットのモザイクが送信された状態になる)。
1,000,000 を与えると、設定したモザイクが 1 セット送信されます。3,000,000 と指定すると、3 セット送信されます。
正直なところ、ややこしいのでモザイク送信時には 1,000,000 固定にして運用したほうが良いかと思います。送信するモザイクの量は quantity で制御できるので。
NanoWallet がモザイク送信時に、「量」が 1 XEM で固定されているのはこれが理由かと思います。
NanoWallet って、こういうところ素直に API に与える値を見せる UI になってますね。実際にモザイク送信する時に 1 XEM を送信したりしないので、 API 知らない人にとっては意味不明かもしれませんが。
ちなみに、amount = 1 とか 1,000,000 で割り切れない値を設定すると、 “FAILURE_MOSAIC_DIVISIBILITY_VIOLATED” というエラーが返ってきます。同様のエラーが発生した場合は、amount 設定値をご確認ください。
モザイク送信量
各モザイクを送信する数量を指定するフィールドですが、ここもそのモザイクでの最小単位での指定となります。
可分性 3 のモザイクであれば、送信するモザイク量 * 1000 の値を設定すればよいかと。
署名して送信
トランザクションのバイト列が生成できれば、あとの署名・送信は XEM 送信時と同じです。
あとは前回記事と同じ内容になるので、ここでは省略します。
さいごに
無事、NEM の興味深い機能、モザイクの送信までいくことができました。
ちなみに今回作ったアプリでは ttech というネームスペースの ryuta というモザイク (通称:りゅーたコイン) を送信するようになっています。
え? まだりゅーたコイン持ってないの?
あげます。モザイクの動作検証にお使いください。もちろん、その他の目的で使ってもOK。
今回、モザイクの動作検証用に
- ryuta 可分性 0 のモザイク。徴収なし
- ryuta1k 可分性 3 のモザイク。徴収なし
- ryutalevymosaic 可分性 0 のモザイク。送信時に 5 ryuta を固定で徴収
という謎モザイクを 3つ作っています。
(実はもう一つ、XEM を徴収するモザイクを作ったのですが、それをまくのは気がひけるのでそっとぼくの手元で眠らせておきます。)
tipnem で投げるので、欲しい方は @ryuta461 に DM 送るか、この記事をシェアするついでにでもその旨を伝えていただければと思います。
次は・・・・マルチシグとかちゃんと理解できてないので、その辺を調べようかと思っていたのですが、その前にこれまでの内容をまとめてライブラリ化しようかと思っています。
今後は、調べてわかったことを実装を交えてこのブログで紹介しつつ、その内容を元にライブラリに機能追加するという感じで、ライブラリの機能の拡充もできれば理想かなと。
最後まで読んでいただきありがとうございます。 このブログを「いいな」と感じていただけましたら、Twiter にてフォローいただけるとうれしいです。ブログ更新情報などもお届けします。
Follow @ryuta461
この記事をシェアする