まだ色々と議論中ではありますが、ERC1155を使ったDAppを作ってみました。(リンクはここです)。
ERC1155の仕様が変わる可能性もあるので、2019.03.24今現在ここで公開されているものの仕様になります。明日は同じか分かりません。
ERC1155とは
ERC1155はERC20とERC721の両方の性質を兼ね備えたハイブリッドな規格と説明されていることが多いです。
ただERC721トークンとは違って、同じものを複数作ることができます。
正確にはあるNFTにインデックスを付与してユニークな番号がついた同じ種別のNFTを複数作れるといった感じです。
トークンを作ったり付与したりするファンクションは下記のようになっています。(コメントとかあまり関係ない処理は省略してます。)
ERC1155MixedFungibleMintable.solはNFTとFTを扱うファイルです。
// トークンの作成
function create(
string calldata _uri, //トークンの情報を入れるURIを記述します。
bool _isNF) // NFTだったらTrueです。
external returns(uint256 _type) {
// nonceはトークンの通し番号です。最初のトークンは1になります。
// _typeはトークンのIDを表してます。
// つまりここでは通し番号のビットを左に128個分シフトしてます。
_type = (++nonce << 128);
// 一番上のビットをNFTかFTかのフラグにする
if (_isNF)
_type = _type | TYPE_NF_BIT; // TYPE_NF_BIT = 1 << 255
}
// NFTの付与
function mintNonFungible(uint256 _type,
address[] calldata _to) // NFTを渡すアドレスを配列で渡す。
external creatorOnly(_type) {
require(isNonFungible(_type));
// 今回付与するNFTのインデックスの開始を前回付与した時のプラス1にする
uint256 index = maxIndex[_type] + 1;
for (uint256 i = 0; i < _to.length; ++i) {
address dst = _to[i]; // NFTを渡すアドレスを配列から1つとる。
uint256 id = _type | index + i; // 上128ビットはNFTの種類に使って、下128ビットはそのNFTのインデックスに使ってる。
nfOwners[id] = dst; // あるNFTのオーナーを指定
}
maxIndex[_type] = _to.length.add(maxIndex[_type]); // 現在のインデックスの最大値は前回の最大値とNFTを付与したアドレスの数の合計
}
// FTの付与
function mintFungible(uint256 _id, address[] calldata _to, //付与するアドレス
uint256[] calldata _quantities) // 各アドレスに付与する量
external creatorOnly(_id) {
require(isFungible(_id));
for (uint256 i = 0; i < _to.length; ++i) {
address to = _to[i]; // 付与するアドレスを配列から1つとる
uint256 quantity = _quantities[i]; // 付与するトークン量
balances[_id][to] = quantity.add(balances[_id][to]); // あるアドレスの保持トークン量
}
}
FTは結構簡単です。ただ、アドレスと付与するトークン量を渡すだけです。
NFTはちょっと分かりづらくて、256ビットのIDの最初の数字をNFTのフラグにし、次の127文字をトークン特有のIDに使って、後ろ128ビットをそのトークンのインデックスに使っています。
FFのポーションは使う分には同じですが、10個持っていたら後ろ128ビットを使ってそれぞれのIDはインデックスの分異なるといった感じですね。ですのでERC1155を使ったFFがあったらポーションは約300澗しか持てません。
実装
作ったDAppは単純に作ったNFTをFTでやりとりするだけのもので、下記のように実装しました。
// NFTを作ってコントラクト作成者に渡すファンクション
function initiatePictures() onlyOwner() public {
string memory uri1 = "あるURI1";
string memory uri2 = "あるURI2";
string memory uri3 = "あるURI3";
uint type1 = this.create(uri1, true); // 2つ目の引数はNFTであることを表す
uint type2 = this.create(uri2, true);
uint type3 = this.create(uri3, true);
address[] memory _address = new address[](1); // mintNonFungibleにはアドレスの配列を渡さなければいけないので、要素が1つの配列を作る。
_address[0] = msg.sender; // コントラクト作成者を指定
this.mintNonFungible(type1, _address); // コントラクト作成者にtype1というNFTを付与する。
this.mintNonFungible(type2, _address);
this.mintNonFungible(type3, _address);
}
本当はこれをコンストラクタでやりたかったのですが、mintNonFungibleがexternalのファンクションなので色々つまりました。その中で学んだのが、
1. externalファンクションは子コントラクトから継承して呼び出せないので、呼び出すときはthisを使ってそのコントラクトが呼び出した様にする。
2. コンストラクタ内ではthis.externalFunction(今回でいうmintNonFungibleの様な親コントラクトのファンクション)を使えない。これはデプロイ時にコンストラクタが呼ばれる時に親コントラクトのexternalのコントラクトのデプロイが終わっているか分からないから的な説明をちらっとどこかで見かけました。その理論で行くとpublicもinternalも使えなさそうなのですが、この辺はちょっと分かりません。
まとめ
まだ議論中ということもありますし、自分の勉強不足もあり、よく分からないところや使い勝手が悪くて勝手にERC1155自体の中身を変えてしまったところもありましたが、今後使ってくる人が増えれば良い規格だなと思いました。ネット上でもほとんど情報はなかったのでERC1155を使ったサンプルコードや解説しているサイトがあったら教えてください