ブロックチェーンSymbolに記録されたフルオンチェーンNFTのNFT-Driveの画像をデコードする

ブロックチェーンSymbolに記録されたフルオンチェーンNFTのNFT-Driveの画像をデコードする方法を紹介します。

公式のデコード手順についてはこちらを参照してください。

【NFT-Drive】フルオンチェーンNFTのデコード手順(概要)を公開します
https://nft-drive.localinfo.jp/posts/23874701

https://qiita.com/bootarouapp/items/912c4b33b8638414be27

# 検証環境の構築

まずは検証環境を構築してください。

https://qiita.com/nem_takanobu/items/e88d5ea76cd609e76bbf


# NGリストの読み込み

最初にNFT-Drive公式からNGリストを取得します。
デコードしてほしくないNFTのID一覧が取得できます。

```js
nglist = [];
fetch('https://nft-drive-data-explorer.tk/black_list/',)
.then((response) => {
    return response.text().then(function(text) {
        nglist = JSON.parse(text);      
        console.log(text);
    });
})
```

公式からの公認デコーダを開発しようとしている人は、ここのロジックを精査されるので組み込んでおきましょう。

# モザイク作成者特定

デコードしたいNFTのIDからモザイクを作成したアカウントを特定します。
NFT-Driveではモザイク作成アカウントのトランザクションにデータの実体が記録されています。

```js
mosaicId = new sym.MosaicId("7A6DF07CC8FD3C19");
mosaicRepo.getMosaic(mosaicId)
.pipe(
	op.filter(x=>{
		return !nglist.find(elem => elem[1] === x.id.toHex())
	})
)
.subscribe(x=>{
    console.log(x);
    ownerAddress = x.ownerAddress;
});

//> ownerAddress: Address {address: 'NAESZDBIRBCD5L77YQWA642Z65AY4IP3ELAOPYA', networkType: 104}
```

今回はMosaicID:7A6DF07CC8FD3C19をデコードします。
NGリストに登録されている場合は情報取得できません。
どんな画像が出てくるでしょうか？お楽しみに！
NFTを作成したアカウントはNAESZDBIRBCD5L77YQWA642Z65AY4IP3ELAOPYAのようです。

# モザイク譲渡履歴

ここで閑話休題。
NFTがどんなアカウントを経由してあなたの元に届いたのか、以下のスクリプトで調べることができます。

```js
txRepo.search({transferMosaicId:mosaicId,group:sym.TransactionGroup.Confirmed,embedded:true})
.subscribe(x=>{console.log(x.data)})
```

このスクリプトは時間がかかることがあります。
(モザイク数が1でない場合は複数人の取引がまとめて出力されます)

# NFTデータ取得

さて、データの実体をブロックチェーンから取得しにいきます。
アグリゲートトランザクションの配列を用意し、詰め込んでいきます。

```js
aggTxes = [];
txRepo.search({
	type:[
		sym.TransactionType.AGGREGATE_COMPLETE,
		sym.TransactionType.AGGREGATE_BONDED
	],
	address:ownerAddress,group:sym.TransactionGroup.Confirmed,pageSize:100})
	.subscribe(async x=>{
		for (let idx = 0; idx < x.data.length; idx++) {
			aggTx = await txRepo.getTransaction(x.data[idx].transactionInfo.hash,sym.TransactionGroup.Confirmed).toPromise();
			if(aggTx.innerTransactions.find(elem => elem.type === 16724)){
	        console.log(aggTx);
	        aggTxes.push(aggTx);
	    }
	}
})
```

今回はアグリゲートトランザクション100件までに収まるデータを対象としました。
100件を超える場合はtxRepo.searchの出力値lastPage属性がfalseのはずなので、page指定を1増分して再度取りに行きましょう。
データ取得対象のトランザクションにはモザイク作成トランザクションなども含まれるのでそれらは取らないようにします。
また、NFT-Driveの場合はトークン作成後、関係の無いトランザクションを受信できないようにしているので、最後までデータを取り切って大丈夫です。

# データソート

ヘッダー情報を参考にデータを正しい順番に並べ替えます。

```js
sotedAggTxes = aggTxes.sort(function(a, b) {

    if (Number(a.innerTransactions[0].message.payload) > Number(b.innerTransactions[0].message.payload)) {
      return 1;
    } else {
      return -1;
    }
})
```

ソートが必要なのは、ブロックチェーンにNFT画像を記録する際にノードの遅延等から記録順序が入れ替わってしまう場合があるためです。

# データ結合

ばらばらに取得したデータを結合してBase64形式の画像データに仕上げていきます。

```js
nftData = "";
header = 15;
for (let aggTx of sotedAggTxes) {

	for(let idx = 0 + header; idx < aggTx.innerTransactions.length;idx++){
		console.log(aggTx.innerTransactions[idx].message.payload);
		nftData += aggTx.innerTransactions[idx].message.payload;
	}
	header = 1;
}

console.log(nftData);
```
各アグリゲートトランザクションの1件目に記録されているヘッダー情報に注意する必要があります。
最初のアグリゲートトランザクションだけ頭15件、
次からは頭1件がヘッダ情報となっているため、その部分は除外して結合していきましょう。


# データ出力

最後にブラウザに出力して完成です。

```js
(tag= document.createElement('img')).src = nftData;
document.getElementsByTagName('body')[0].appendChild(tag);
```


お疲れ様でした。