見出し画像

ERC Token Standards 【OpenZeppelin】

ERC1155に準拠したトークンを用いたアプリケーションを作成するにあたりこれまでのERC20, 721, 777についても学び直したかったためOpenZeppelinのドキュメントから各種の特徴をまとめてみます。

ERC-20

概要

代替可能なトークン(Fungible Token)に関する規格。全てのトークンがあらゆる面で等しいため、通貨交換・投票権・ステーキング等の媒体として機能する。

作成

// contracts/GLDToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract GLDToken is ERC20 {
    constructor(uint256 initialSupply) public ERC20("Gold", "GLD") {
        _mint(msg.sender, initialSupply);
    }
}

ERC20("name", "symbol")でどのようなトークンを作成するのか指定しています。

小数について

SolidityとEVM(Ethereum Virtual Machine)では小数を扱うことが出来ません。上記を例にとると、そのままでは1.5GLDを誰かに送ることが出来なくなってしまいます。

そこでERC20ではdecimals fieldを提供しこの問題を解決しています。小数以下をいくらまで扱うか指定することで、残高15を1.5GLDとして扱うと言った処理が可能になります。
ただし内部的には全ての演算が整数で行われているため、表示する際には注意が必要です。多くのERC20 Tokenでは小数点以下18位までが標準であるため、実際のトークン価値を得るには10*18で割る必要があります。

ERC-721

概要

ERC20によるトークンとは異なり、一つ一つがユニークであり代替できないもの(不動産、アート作品、免許証etc…)を表現するにはERC721規格を使用します。
具体的にはERC721はNon Fungible Token(NFT)の所有権を表現するためのものです。

作成

// contracts/GameItem.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract GameItem is ERC721 {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() public ERC721("GameItem", "ITM") {}

    function awardItem(address player, string memory tokenURI)
        public
        returns (uint256)
    {
        _tokenIds.increment();

        uint256 newItemId = _tokenIds.current();
        _mint(player, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
}

ERC20と異なり、トークンを個別に認識する必要があるためtokenIdやtokenURIでどのトークンが何の情報を持っているか指定してきます。
(tokenURIの参照先は例えばIPFSなどがあります。)

ERC-777

概要

ERC20で使いづらかった部分を改良したものがERC777になります。
ERC20に比べた時の変更点としていくつか挙げてみると

  • 小数に関する混乱の解決

  • 適切なイベントによるミント(作成)とバーン(廃棄)

  • receive hooksの実装

特に最後の機能が重要な機能追加になります。
ERC20 tokenの問題点として、誤った送信先にトークンを送ってしまった場合修正不可能である点があります。この問題をERC777ではコントラクトアドレスもトークンの送信・受信が行うことができるようにすることによって解決を図っています。
もちろんERC20との互換性も備えているため、すでにERC20を導入してしまったものも置換することが出来ます。

作成

// contracts/GLDToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC777/ERC777.sol";

contract GLDToken is ERC777 {
    constructor(uint256 initialSupply, address[] memory defaultOperators)
        public
        ERC777("Gold", "GLD", defaultOperators)
    {
        _mint(msg.sender, initialSupply, "", "");
    }
}

defaultOperatorsでトークンを操作することができるアドレスと指定します。

ERC-1155

概要

NFT(ERC721 Token)は唯一無二の対象を証明するために活用されていますが、会員証など複数枚発行する必要があるものに対しては一枚一枚発行するとトランザクション費用などコストが余分にかかってしまいます。

そこで一つのスマートコントラクトで複数のトークンを発行できるようにしたものがERC1155になります。ERC20とERC721のいいとこ取りをして生まれました。
同じNFTを複数枚発行できる、と言ったイメージです。
単一のスマートコントラクトで管理するため、複数のトークンに対しての操作が容易になります。
(複数枚の残高照会、転送など)

作成

// contracts/GameItems.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";

contract GameItems is ERC1155 {
    uint256 public constant GOLD = 0;
    uint256 public constant SILVER = 1;
    uint256 public constant THORS_HAMMER = 2;
    uint256 public constant SWORD = 3;
    uint256 public constant SHIELD = 4;

    constructor() public ERC1155("https://game.example/api/item/{id}.json") {
        _mint(msg.sender, GOLD, 10**18, "");
        _mint(msg.sender, SILVER, 10**27, "");
        _mint(msg.sender, THORS_HAMMER, 1, "");
        _mint(msg.sender, SWORD, 10**9, "");
        _mint(msg.sender, SHIELD, 10**9, "");
    }
}

このコード例では発行するトークンをコントラクターが最初に決めていますが後から追加することも可能です。
トークンを転送するには、単体であれば

safeTransferFrom("from", "to", "id", "amount", "bytes data")

複数枚やり取りしたい場合は

safeBatchTransferFrom("from", "to", "ids([])", "amounts([])", "bytes data")

配列として指定することで容易に取引を行うことが出来ます。

まとめ

まとめて見てみると以下のように整理できました。

ERC各規格のイメージ

ERC777がFungible Token規格なのが少し紛らわしいです。なぜ2桁にしてくれなかったのか…

この記事が気に入ったらサポートをしてみませんか?