ブロックチェーンゲームで大注目のNFTをERC721を利用して発行してみた

ブロックチェーンを利用したゲーム開発などで必須として利用されるNFTですが、なかなかイメージが湧きにくいものです。今回は、NFTを定義している主要な標準であるERC721を利用して実際に発行してみて、どのようなものかみていきましょう。
# ERC721とは？
イーサリアムでは、トークンやコントラクト開発に対してさまざまな標準が設けられており、ERCとして整理されています。ERC721はNFT（Non-Fungible Token：非代替トークン）の標準を定義しています。

NFT（非代替トークン）とは、デジタルなアイテムを、コピーできるコモディティではなく、唯一の価値を持つデジタルアセットとして扱えるようにするものです。この性質はゲームと非常に相性がよく、ゲーム内でアイテムやキャラクターを獲得したり、育てたりしたことがそのまま資産になっていくことになります。

ずっとプレイして育てたキャラクターがそのゲーム内だけでなく、別のアプリケーションでも扱えたり交換できる・・・なんだか聞いただけでワクワクしませんか？！

ERC721で具体的に定義されているのは、以下のメソッドとイベントです。同じくトークンを発行する際の標準の一つであるERC20との大きな違いは、発行数に関する定義がないことだと言えます。

```
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function approve(address _approved, uint256 _tokenId) external payable;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
function supportsInterface(bytes4 interfaceID) external view returns (bool);
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
```

なんだか多いですね…
でも、大丈夫！OpenZeppelinというライブラリを使えば簡単にこれらのメソッドやイベントを実装できます！

#NFTを発行する！
では、NFTを発行してみましょう。今回はなるべく簡単かつ早く発行するために、OpenZeppelinというライブラリを利用します。OpenZeppelinはコントラクトを実装する上で、下敷きとなるライブラリでゼロから書くよりも簡単に安全なコントラクトを実装することができます。

また、Truffleを使ってテストやコンパイル、デプロイを行うのもいいのですが、簡単な結果だけ欲しいのでブラウザ版のRemixを利用します。

今回の環境は以下の通りです。特に、Solidityはバージョンが変わると記法が変わったり、コンパイルできなかったり、といったことが起こるので注意してください。今回はTruffle4.1.15をインストールして導入しています。

- Truffle:4.1.15
- OpenZeppelin：1.12.0

## コントラクトのコード
今回は、個性豊かな住民が暮らす村にプレイヤーが移り住み、住人たちとの交流ながら生活をするゲームを開発する場面を想定しましょう。（あの任天堂さんがリリースしているタイトルにも似たゲームがありますね。）

この手のゲームでは、まず拠点となる自分の家を持つことになり、その家に置く家具も商店で買って模様替えをしたりできますね。ひとまずは、この「家具」をNFTとして扱えるようにしましょう。

家具にも色々な要素がありますが、シンプルに「縦、横、奥行き」のサイズで定義することとしました。トークンの名前を`FurnitureAsset`、シンボルを`FAS`としています。

このような前提で以下のようにコントラクトを定義します。なお、サイズに関しては便宜上時間（now）を使って表現しています。

```Solidity:assets.sol
pragma solidity^0.4.24;

import 'zeppelin-solidity/contracts/token/ERC721/ERC721Token.sol';

contract Assets is ERC721Token {
  constructor() ERC721Token("FurnitureAsset", "FAS") public {}

  struct Size {
    uint8 width;
    uint8 height;
    uint8 depth;
  }

  Size[] sizes;

  function mint() public {
    Size memory _size = Size(uint8(now), uint8(now-1000), uint8(now-2000));
    uint _id = sizes.push(_size) - 1;
    _mint(msg.sender, _id);
  }

  function getSizeFromId(uint id) public view returns(uint8, uint8, uint8) {
    return (sizes[id].width, sizes[id].height, sizes[id].depth);
  }
}
```
さて、このコントラクトをデプロイをしたいのですが、簡単に結果だけ欲しいのでブラウザ版のRemixを利用します。[Remix](https://remix.ethereum.org/#optimize=false&evmVersion=null&version=soljson-v0.5.1+commit.c8a2cb62.js)にアクセスして以下のようにコードをコピー＆ペーストで貼り付けます。
<img width="1390" alt="スクリーンショット 2019-07-31 9.58.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454589/f88993f2-770e-3edc-9af6-2b1b1a6dc870.png">
ここで注意が必要なのは、Remix上でOpenZeppelinなどのライブラリを利用するには、import文でGitHubのリンクを指定することです。今回であれば、以下のようになります。
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v1.12.0/contracts/token/ERC721/ERC721Token.sol

# 結果
コンパイルして問題なければ、[DEPLOY & RUN TRANSACTIONS]をクリックします。左下にnamesymbolの欄がないかをみてみましょう。nameとsymbolの欄がそれぞれ、コントラクトで定義をした`FurnitureAsset`と`FAS`になっていることを確認できます。

<img width="369" alt="name.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454589/ef49df1e-823f-ba81-ed53-1c785f18a56b.png">

<img width="364" alt="symbol.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454589/02d71b11-1d5d-3ace-6b16-ab677c47c86a.png">
その後、左下のmintを2〜3回クリックしましょう。mintとはトークンを鋳造（発行）することです。getSizeFromIdのidを1や2に変えてみると、以下のように3つの数字の組み合わせが変わっていることが分かると思います。

<img width="408" alt="スクリーンショット 2019-07-31 9.51.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454589/f4a0876f-58e7-2255-7b36-d002f8fcc033.png">
<img width="372" alt="スクリーンショット 2019-07-31 9.51.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454589/ec543659-2551-2929-9bd9-2656d59dc1ab.png">
<img width="364" alt="スクリーンショット 2019-07-31 9.54.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/454589/3e31602e-a752-7638-37c8-491ad3a0523c.png">

このidの違うそれぞれのトークンは、サイズがそれぞれ異なる家具のことを表していて、それぞれがNFTということになります。

#まとめ
今回は家具のサイズをお題にしてトークン化してみました。

実はOpenZeppelinのERC721には便利なフレームが他にもいくつかあり、発行されたトークンにIDを振ったり、APIを利用することで画像をはじめとするより複雑なデータを組み込んだりすることが可能です。こうすることでゲーム内におけるアイテムやキャラクターの性質をより明確に作り込むことが可能になります。これがNFTがゲームで多用される理由です。