Symbolアカウントに登録したメタデータを取得するバックエンドをNapkinで自作する

#はじめに
本記事は前回の記事([Symbolアカウントにメタデータ登録するバックエンドをNapkinで自作する](https://qiita.com/Toshi_ma/items/b3a5633f081b676d7e79))で作成したAPIにアクセスし、武器データを取得するためのAPIをNapkinで作成する方法の解説記事です。

主にマルチシグアカウントのメタデータ取得の方法なので、そのあたりに関することをしたい人には役に立つかと思います。たまたま初めて本記事にたどり着いた方は事前知識として前回記事を一度ご覧いただくことをおすすめします。

https://qiita.com/Toshi_ma/items/b3a5633f081b676d7e79

#準備するもの

■Symbolデスクトップウォレット
こちらの記事を参考にお持ちでない場合はインストールしてください
https://note.com/nembear/n/n56d2c9a28e8a
※ただし、本番稼働するまではテストネットで進めましょう

テストネット上で、
・Gameアカウント
・Playerアカウント
をそれぞれ準備しておきましょう。

■Napkinアカウント
こちらを参考に作成しておいてください
https://paiza.hatenablog.com/entry/2021/07/21/130000

モジュールと環境変数を準備しておいてください

[モジュール]
・symbol-sdk@1.0.2 // 最新版(12/23/2021/現在)1.0.3だとエラーが出ます
・request-promise-native

[環境変数]
・APIKEY
こちらは前回と同じです
・SOURCEADDRESS
こちらはゲームアカウントのアドレスです。そのアドレスが署名をしているメタデータであればゲーム内で使用できるという判別のためです。（ここでのアドレスとはウォレットから取得できるアドレスです。）


#ソースコード解説

以下全文です。後ほどブロックごとに解説します。

```javascript
import requestPromise from "request-promise-native";
import * as symbol from 'symbol-sdk-1.0.2';

const SOURCEADDRESS = process.env.SOURCEADDRESS;
const addressUint8Array = symbol.RawAddress.stringToAddress(SOURCEADDRESS);
const hexAddress = symbol.Convert.uint8ToHex(addressUint8Array);

const getDataFromSymbol = async (NODE, param) => {
  const result = await requestPromise(`${NODE}${param}`);
  return result;
};

const isValidJson = (value) => {
  try {
    JSON.parse(value);
  } catch (e) {
    return false;
  }
  return true;
};

export default async (req, res) => {
    if(req.query.apikey ==! process.env.apikey) {
        throw TypeError("Wrong APIKEY ERROR");
    } else {
        let NODE = "https://sym-main-01.opening-line.jp:3001/";
        if (req.query.mode == "TEST_NET") {
            NODE = "https://sym-test-01.opening-line.jp:3001/";
        }
        const address = req.query.address;
        const param1 = "account/" + address + "/multisig";
        const maltisigData = await getDataFromSymbol(NODE,param1);
        const maltisigDataJson = JSON.parse(maltisigData);
        const multisigAddresses = maltisigDataJson.multisig.multisigAddresses;
        const rawAddresses = [];
        for (const multisigAddresse of multisigAddresses) {
            const unit8address = symbol.Convert.hexToUint8(multisigAddresse);
            const rawAddress = symbol.RawAddress.addressToString(unit8address);
            rawAddresses.push(rawAddress);
        }

        const metaDataValues = [];
        for (const rawAddress of rawAddresses) {
            let isAcquiredMetaData = false;
            let count = 1;
            while (isAcquiredMetaData == false) {
                const param2 = "metadata?pageNumber=" + count + "&targetAddress=" + rawAddress;
                const metaDataBase = await getDataFromSymbol(NODE, param2);
                const metaDatas = JSON.parse(metaDataBase);
                if (metaDatas.data.length === 0) {
                    isAcquiredMetaData = true;
                    break;
                }
                for (const metaData of metaDatas.data) {
                    if (metaData.metadataEntry.sourceAddress == hexAddress) {
                        const metaValue = Buffer.from(metaData.metadataEntry.value, "hex").toString("utf-8");
                        if (isValidJson(metaValue)) {
                            metaDataValues.push(JSON.parse(metaValue));
                            isAcquiredMetaData = true;
                        }
                    }
                }
                count++;
            }
        }
        const data = {
            "data":
            metaDataValues,
        };
        res.json(data);
    }
}
```


```javascript
import requestPromise from "request-promise-native";
import * as symbol from 'symbol-sdk-1.0.2';

const SOURCEADDRESS = process.env.SOURCEADDRESS;
const addressUint8Array = symbol.RawAddress.stringToAddress(SOURCEADDRESS);
const hexAddress = symbol.Convert.uint8ToHex(addressUint8Array);

const getDataFromSymbol = async (NODE, param) => {
  const result = await requestPromise(`${NODE}${param}`);
  return result;
};

const isValidJson = (value) => {
  try {
    JSON.parse(value);
  } catch (e) {
    return false;
  }
  return true;
};
```
ゲームアカウントのアドレスはウォレットから取得できるRawAddressを後ほどSymbol APIでデータを取得するために16進数のアドレスにするためごにょごにょします。

あとは、ノードからデータを取得するための関数 `getDataFromSymbol `と
メタデータがJSON形式かどうか判別するための関数`isValidJson `です。

```javascript
if(req.query.apikey ==! process.env.apikey) {
        throw TypeError("Wrong APIKEY ERROR");
    } else {
        let NODE = "https://sym-main-01.opening-line.jp:3001/";
        if (req.query.mode == "TEST_NET") {
            NODE = "https://sym-test-01.opening-line.jp:3001/";
        }
        const address = req.query.address;
        const param1 = "account/" + address + "/multisig";
        const maltisigData = await getDataFromSymbol(NODE,param1);
        const maltisigDataJson = JSON.parse(maltisigData);
        const multisigAddresses = maltisigDataJson.multisig.multisigAddresses;
        const rawAddresses = [];
        for (const multisigAddresse of multisigAddresses) {
            const unit8address = symbol.Convert.hexToUint8(multisigAddresse);
            const rawAddress = symbol.RawAddress.addressToString(unit8address);
            rawAddresses.push(rawAddress);
        }
```

例のごとくAPIKEYが正しいかどうか判別し、正しければテストネットかメインネットなのか。なお、テストネット、メインネットともにオープニングライン社のノードをお借りしています。

`maltisigData `最初にまずはプレイヤーのアドレスが署名者のマルチシグアカウントを調べます。
`maltisigDataJson`JSON形式にし`multisigAddresses`そのプレイヤーアカウントが署名者のマルチシグアカウントを全て取得。
それぞれ、RawAddressにコンバート

```javascript
const metaDataValues = [];
        for (const rawAddress of rawAddresses) {
            let isAcquiredMetaData = false;
            let count = 1;
            while (isAcquiredMetaData == false) {
                const param2 = "metadata?pageNumber=" + count + "&targetAddress=" + rawAddress;
                const metaDataBase = await getDataFromSymbol(NODE, param2);
                const metaDatas = JSON.parse(metaDataBase);
                if (metaDatas.data.length === 0) {
                    isAcquiredMetaData = true;
                    break;
                }
                for (const metaData of metaDatas.data) {
                    if (metaData.metadataEntry.sourceAddress == hexAddress) {
                        const metaValue = Buffer.from(metaData.metadataEntry.value, "hex").toString("utf-8");
                        if (isValidJson(metaValue)) {
                            metaDataValues.push(JSON.parse(metaValue));
                            isAcquiredMetaData = true;
                        }
                    }
                }
                count++;
            }
        }
        const data = {
            "data":
            metaDataValues,
        };
        res.json(data);
```

取得したRawAddressが持つメタデータを取得します。その際に、そのメタデータの署名者がゲームアカウントであるかを確認。そうであれば配列に格納していく。を繰り返します。

これで、プレイヤーアカウントに紐付いているこのゲームに関するメタデータが取得できましたので、それをJSONで返して終わりです。

あとは、例えばUnityなどでこのAPIに接続してデータ取得。そのデータに沿って武器を格納庫に入れる。こんな感じでゲームとSymbolの連携が完了です。

繰り返しますが、メタデータ取得のためには事前に登録しておく必要があるので、事前にこちらの記事をお読みください。


https://qiita.com/Toshi_ma/items/b3a5633f081b676d7e79