複数トークン規格と言われている ERC 1155 ですが、下記部分はモヤっていたので調べてみました。
マルチトークンの扱い方
NFT と FT はどう区別されるか
規格詳細は下記 EIP ページ参照
openzeppelin の実装はこちら
保持しているデータは下記 2 種類になっています。
// Mapping from token ID to account balances
mapping(uint256 => mapping(address => uint256)) private _balances;
// Mapping from account to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
_balances
の token ID
の値は、コントラクトアドレス + ERC 721 の index
のような役割になります
トークンを表す際に、ERC 20 の場合、コントラクトアドレスのみでしたが、ERC 721 の場合は、コントラクトアドレスと index の2階層になっています
なので、ERC 1155 はマルチトークンをサポートするなら少なくとも2階層の構造が必要ですが、その2階層をこの1つの id
で表すように設けられています
例をあげましょう
アカウントA 0xa…1
が下記のようにトークンを持っているとします。
GameToken
ERC20:99 枚
SwordNFT
ERC721 #2
SwordNFT
ERC721 #5
ShieldNFT
ERC721 #1
これらのトークンが全部 ERC 1155 で管理しているなら、各 id の値は下記の形で表すことができます
{
"0000000100000000": { "0xa...1": 99 },
"1000000100000002": { "0xa...1": 1 },
"1000000100000005": { "0xa...1": 1 },
"1000000200000001": { "0xa...1": 1 },
}
つまり、256 ビットの id を分割して
前半の 128 ビット(16 進数の場合 8 桁)で トークン
の id を表す
後半の 128 ビット(16 進数の場合 8 桁)で index
を表す
ERC20 タイプの場合 index
使わないため、全部 0
を指定しています
ただ、ややこしいのは、このやり方は、EIP 1155 規約の最後の `Usage` 節に例として記載され、 The standard does NOT mandate how an implementation must do this.
となって、必須の実装ではありません
1. マルチトークンの扱い方
については、上記通りでした
2. NFT と FT はどう区別されるか
は、意外な結果ですが、EIP 1155 標準のインファフェースでは、区別できません
ownerOf
関数がない
_owners
mapping があるので上記関数で調べられますが、ERC 1155 の場合そもそもこの mapping がないし、関数もありませんname
/ symbol
もない
EIP 1155 の Metadata Choices
節にかかれてありますが、意図的にこの2つの関数を削除したようです
symbol
は通貨取引以外であんまり使われてないので、一般的に有用なデータではない、かつ、衝突の可能性があるため、削除されました
name
は、メタデータで表せばよいので、重複を減らすため削除されました
mint と転送系のメソッド(safeTransferFrom / safeBatchTransferFrom) に _data というパラメータがあります
このパラメータは、コントラクトの処理中には使われていません
EIP には 転送するために送信者によって提供された情報を変更せずに受信者にそのまま転送するように実装する必要があります
と書かれてあります
よって、コントラクトの動作確認などの場合は、その値を無視して問題ございません
ちなみに、remix などで動作確認する場合は、0x0
を入力すればチェックを通れます
ERC 1155 コントラクトで定義しているトークンであれば、複数の転送を一括で1つのトランザクションで処理できます
SwordNFT#2
とShieldNFT#1
とアカウトBに転送したい、その2つの NFT は ERC 721 である場合、2つの NFT コントラクトの転送関数を別々呼び出して2つのトランザクションになりますが、ERC 1155 で定義されている場合、下記のように一括転送関数を1回の呼び出しで完結しますsafeBatchTransferFrom(
'0xa...1', // from
'0xb...2', // to
[1000000100000002, 1000000200000001], // token ids
[1,1], // token amount
"" // オプションデータ
)
複数トークン
を扱う一番のメリットじゃないかと思われますねERC 1155 1つのコントラクトで複数のトークンを扱うことは確かにできますが、インタフェースは、ERC 20 と ERC 721 と異なっているため、互換性はないようにみえます。
特に、FT / NFT を1つのコントラクトで管理できるのに、区別するインタフェースが設けられていないのは意外でした。
また、1つの id
で複数トークンを表現するのは、gas 節約する目的があるのは理解できていますが、ルールが決められていないところも結構意外でした。結局このルールは、コントラクトを実装する際だけではなく、コントラクトを利用する側もそのルールを把握して使う必要があるので、若干使いにくいと感じられますね。