どうもこじりょー(@kojiryoinvestor)です。
この記事ではERC721トークンの作成方法を解説していきます。
この記事は最低でもERC20トークンの作成を学習した人に向けて書いています。
初心者の方はまず下記のチュートリアルから、Ethereum開発の環境の構築とトークンの作成を学習してみてください。
【Truffle5.0対応】シンプルなERC20トークンを作成しよう!
このチュートリアルからは主に以下のことを学習することができます。
- OpenZeppelinを利用したERC721トークンの作成方法
- Rinkebyテストネットへのデプロイ手順
- 「OpenSea」で作成したトークンの確認
またこの記事のチュートリアルは以下の開発環境で実装していきます。
- Truffle@v5.0.0
- openzeppelin-solidity@v2.1.1
- ganache(GUI)@v1.2.2
- solidity@v0.5.0
ERC721とは?
「代替不可能なもの」の所有権をEthereumで扱うための規格(ルール)がERC721です。
「代替不可能なもの」とは、例えば著作権等で保護されるようなコンテンツ(写真や絵に音楽)などが当てはまります。
ERC721を利用することで「代替不可能なもの」の所有権をトークン「Non Fungible Token(NFT)」として扱うことができます。
特に、ある主体が発行した代替不可能なもの(ゲームのアイテムなど)で移管・交換の性質を持つものとの相性が良いです。
ERC721の実装
EIP721
ERC721トークンの実装とは、EIP721の仕様で定められらた関数を実装することです。
EIP721は以下のリンクから確認することができます。
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
仕様を確認してみると実装する関数も多く脆弱性なく作るのは大変です。
そこでここでは「OpenZeppelin」で提供されているERC721のコントラクトを再利用することでERC721トークンを実装していきます。
OpenZeppelinのインストール:
$ npm i openzeppelin-solidity
コントラクト作成
それではERC721のコントラクトを実装していきます。
「OpenZeppelin」では、ERC721トークンを用途に合わせて拡張することができるようになっています。
https://github.com/OpenZeppelin/openzeppelin-solidity/tree/master/contracts/token/ERC721

ERC721の用途に合わせて必要なコントラクトを継承することで拡張していくことができます。
ここでは特に特別なことはせず、ERC721トークンとして最低限利用できるものを目指します。
それではコントラクトファイルを作成しましょう。
ここでは「Asset.sol」と名前をつけて作成しています。

続いて、ERC721トークンとして最低限必要なロジックを実装した「ERC721Full.sol」を継承します。
「Asset.sol」は以下のようになります:
コントラクト解説
「Asset.sol」はコントラクトがデプロイされる際に、以下のパラメーターをコンストラクタで受け取ります。
- name: トークンの名前
- symbol: トークンの単位
- tokenId: アセットを識別する数字(例えば、クリプトゾンビで生成したゾンビには一体一体に固有の数字が付いている)
- tokenURI: トークンの詳細情報を示すJSONファイルのパス(詳細後述)
その後「name」と「symbol」を「ERC721Full.sol」のコンストラクタに渡します。

「ERC721Full.sol」は受け取った「name」と「symbol」を「ERC721Metadata.sol」に渡します。

続いて「ERC721.sol」の「_mint関数」を呼び出して、tokenId(アセットを識別する数字)に対してアカウントアドレス(トークンの所有者)を関連づけます。

その後「ERC721Enumerable.sol」の「_addTokenToOwnerEnumeration関数」と「_addTokenToAllTokensEnumeration関数」を呼び出し、アカウントアドレスに対して「tokenId」の配列を関連づけます。
その結果、アカウントアドレスから「tokenId」を直接取得できるようになっています。

アドレスとアセットの関連付けが完了した後は、「ERC721Metadata.sol」の「_setTokenURI関数」を呼び出し、「tokenId」と「tokenURI」の関連づけを行います。

「tokenURI」はERC721トークンに「名前」「詳細」「画像」といった情報をつけたJSONファイルのことです。
{"name":"kojiryo","description":"JSONTSET","image":"https://ipfs.io/ipfs/QmZZD344VHrsLuiaKMjbmdvRBhW8n5rZhqS62g3DCUde8y"}
「tokenURI」の設定はあくまでオプションですが、JSONを返すAPIサーバーのURLを渡すことでトークンの詳細な情報を伝えることできます。
例えば以下は上記の「tokenURI」を設定したERC721トークンを、アセットの取引所である「OpenSea(テストネット版)」で表示させたデモです。

「tokenURI」を返すAPIサーバーを構築するのが面倒であったため、ここでは分散ファイルストレージ「IPFS」を、画像とJSONを返すAPIサーバーとして利用しています。
https://ipfs.io/ipfs/QmYMYdJqSrTNB3iwXzmYfGdjAymuXvHAJuum9zzT7RV7N1
マイグレーションの作成
コントラクトを作成したらマイグレーションを作成していきます。
ここでは「2_deploy_asset.js」と名前をつけて作成します。
2_deploy_asset.js:
「2_deploy_asset.js」はERC721トークンに必要なパラメーターを定義してコントラクトをデプロイします。
「tokenURI」はIPFSを利用して作成するツールを用意しましたので、よければご利用ください!
https://kojimaro.github.io/tokenURI-creator/
ERC725トークンを勉強で作るときに、tokenURIを用意するのが手間だったので作成するツールを突貫工事で作りました。
— こじりょー (@kojiryoinvestor) 2019年1月6日
まず画像をIPFSにアップロードし、その後、入力したname・descriptionと一緒にJSON化してmetadataをIPFSへアップロードしています。https://t.co/dTHXsB43IC pic.twitter.com/3dktxjq22i
テストの作成
作成したコントラクトの動作を確認するためにテストを作成します。
ここでは「Asset.js」と名前をつけて作成します。
Asset.js :
コントラクトをデプロイした後、トークンの名前とtokenIdに紐づいたアカウントアドレスを取得します。
その後、こちらが指定したトークンの名前と、tokenIdに紐づいたアドレスがトークン作成者のアカウントアドレスと一致しているかを検証しています。
続いてプライベートネットへ接続する設定を「truffle-config.js」に書き込んでおきます。
ここではGanacheの環境に合わせています。
truffle-config.js:
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*",
},
Ganacheを起動したら下記のコマンドを実行します。
$ truffle test
以下のように表示されればコントラクトは正常に動いています。

Rinkebyへのデプロイ
テストネット「Rinkeby」用のEtherを取得する
コントラクトを作成したらテストネットのひとつである「Rinkeby」へデプロイしていきます。
コントラクトのデプロイにはGasがかかるため、デプロイ用にEtherを入手しておく必要があります。
ここでは受け取るウォレットに「MetaMask」を利用していきます。
「Rinkeby」用のEtherは下記のサイトから無料で入手することができます。
サイトにアクセスしたら「Twitter」「Facebook」のいずれかで投稿します。
ここでは「Twitter」を利用していきます。

投稿の内容にはEtherを受け取るアドレスを記載します。
後々コントラクトのデプロイの際に、一番最初に作成したアカウントアドレスの残高からGasが引き落とされるため、受け取るアドレスはMetaMaskで一番最初に作成したアカウントアドレスにしておいてください。
Requesting faucet funds into 0xcf99020C27039339B130669cF8E87fc63B2c5ADb on the #Rinkeby #Ethereum test network.
— こじりょー (@kojiryoinvestor) 2019年1月4日
次に投稿へのリンクをコピーしてフォームに貼り付け、欲しいEtherの量を選択します。

しばらく待つと記載したアドレスにEtherが送られます。

MetaMaskのネットワークをRinkebyに変更して確認してみてください!

INFURAへの登録
テストネットやメインネットへのデプロイするためには、イーサリアムクライアントを同期させる必要があります。
しかしイーサリアムクライアントを利用してブロックチェーンを同期すると、ディスク容量が大量に消費されます。
そこでイーサリアムクライアントを利用しなくてもコントラクトのデプロイができるサービス「INFURA」を使います。
無料で利用できますので、「GET STARTED FOR FREE」をクリックして必要事項を入力して登録をしましょう。
登録が完了したら「CREATE NEW PROJECT」をクリックしてプロジェクトを作成します。
プロジェクトを作成すると「PROJECT ID」が表示されます。
これはデプロイの際に必要となりますのでコピーしておきましょう。
Rinkebyへの接続設定
Rinkebyへデプロイするための設定を「truffle-config.js」に書いていきます。
まずはRinkebyへ接続するために必要な2つのキーを用意しましょう。
まずはプロジェクトディレクトリ直下に「.infurakey」と「.mnemonic」と名前をつけた2つのファイルを作成します。

「.infurakey」の中身は「INFURA」の「PROJECT ID」を貼り付けておきます。
「.mnemonic」の中身は「MetaMask」でアカウントを作成した際に表示された単語の羅列(パスフレーズ)をコピーして貼り付けておきます。
パスフレーズは「MetaMask」を開き「設定」→「パスフレーズを表示」で確認することが可能です。
Git等でソースコードを管理する際には、これらのファイルをGitから取り除いておくことでアクセスキーの漏洩を防ぐことができます。
続いて以下のコマンドを実行して「truffle-hdwallet-provider」をインストールします。
$ npm install truffle-hdwallet-provider
テストネットデプロイに必要なものが揃ったら「truffle-config.js」を以下のように修正します。
truffle-config.js :
まずテストネットへのデプロイに必要な格アクセスキーや「truffle-hdwallet-provider」の読み込みをする処理を追加します。

続いて「INFURA」のノードを利用して「Rinkeby」へデプロイする設定を記述しています。

それでは「Rinkeby」にアクセスできているか、コントラクトのテストを実行して検証してみましょう。
$ truffle test --network rinkeby
少し時間がかかりますが以下のように表示されれば「Rinkeby」への接続に成功しています。

デプロイ
「Rinkeby」への接続の成功が確認できたので、コントラクトをデプロイしていきましょう。
以下のコマンドを実行することで「Rinkeby」へデプロイすることができます。
$ truffle migrate --network rinkeby
特にエラーもなくデプロイに成功したら、トークンを確認するためにコントラクトアドレスをコピーしましょう。

ERC721トークンの確認
MetaMask
「MetaMask」を開き、ネットワークを「Rinkeby」へ変更します。
トークンは一番最初に作成したアドレスに付与されていますので、アカウントはそちらを選択しましょう。
次に左端のメニューアイコンをクリックします。

「トークンを追加」をクリックします。

「カスタムトークン」と表示された場所をクリックします。

「トークンアドレス」にデプロイ時に表示されたコントラクトアドレスを入力します。

最後に「次へ」をクリックすればトークンが確認できます。

OpenSea
ERC721トークンに設定した「metadata」も確認してみましょう。
「Rinkeby」テストネットで公開されている「OpenSea」を利用することで確認することができます。
以下のURLで「コントラクトアドレス」と「tokenId」を指定することで確認できます。
https://rinkeby.opensea.io/assets/コントラクトアドレス/tokenId
例: https://rinkeby.opensea.io/assets/0x993b7099d751e7744f2fde046c073c90dbfb3be9/1

おわりに
以上、ERC721トークン作成のチュートリアルでした。
OpenZeppelinを利用することでERC721トークンにも「mint」や「burn」などを追加することができますので、ぜひ色々試してみてください!
コメントを残す