[Solidityのはじめ方] Part27: ERC-20, ERC-721, ERC-1155 の理解と実装

Solidity

はじめに:トークン標準とは? 🤔

Ethereumブロックチェーン上でスマートコントラクトを使って様々な「トークン」を作成できます。トークンは、仮想通貨、ポイント、会員権、ゲーム内アイテム、デジタルアートなど、様々な価値を表現するために使われます。

しかし、開発者がそれぞれ独自のルールでトークンを作ってしまうと、ウォレットや取引所などのサービスが個々のトークンに対応するのが大変になります。そこで重要になるのがトークン標準です。トークン標準は、トークンが持つべき機能やインターフェース(関数の名前や引数など)を定めた共通のルールセットです。これにより、標準に準拠したトークンは互換性を持ち、様々なアプリケーションで簡単に扱えるようになります。

このステップでは、Ethereumで最も広く使われている3つのトークン標準、ERC-20ERC-721ERC-1155について学び、それぞれの特徴と簡単な実装方法を見ていきましょう!🚀

ERCとは “Ethereum Request for Comments” の略で、イーサリアムに関する技術的な提案や仕様のことです。トークン標準以外にも様々なERCが存在します。

ERC-20: 代替可能なトークン (Fungible Tokens) 💰

ERC-20は、代替可能 (Fungible) なトークンのための標準規格です。代替可能とは、どのトークンも同じ価値を持ち、互いに交換可能であることを意味します。例えば、日本円の100円玉は、誰が持っているものでも同じ価値があり、区別なく交換できますよね。ERC-20トークンもこれと同じ性質を持ちます。

仮想通貨(例:USDT, DAI)、ガバナンストークン、ユーティリティトークンなど、多くのプロジェクトで利用されています。

主な機能

ERC-20で定義されている主な関数とイベントは以下の通りです。

  • name(): トークンの名前(例: “MyToken”)を返す。
  • symbol(): トークンのシンボル(例: “MTK”)を返す。
  • decimals(): トークンの小数点以下の桁数を返す(通常は18)。
  • totalSupply(): トークンの総供給量を返す。
  • balanceOf(address account): 指定されたアカウントのトークン残高を返す。
  • transfer(address recipient, uint256 amount): 指定されたアドレスにトークンを送金する。
  • allowance(address owner, address spender): `owner`が`spender`に送金を許可した残りのトークン量を返す。
  • approve(address spender, uint256 amount): `spender`が自分の代わりに`amount`までのトークンを送金することを許可する。
  • transferFrom(address sender, address recipient, uint256 amount): `sender`から`recipient`へトークンを送金する(`approve`で許可されている必要がある)。
  • event Transfer(address indexed from, address indexed to, uint256 value): トークンが送金されたときに発行されるイベント。
  • event Approval(address indexed owner, address indexed spender, uint256 value): `approve`が成功したときに発行されるイベント。

簡単な実装例(インターフェース)

ERC-20のインターフェースは以下のようになります。実際にトークンを作成する際は、これらの関数とイベントを実装します。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    // Optional functions
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
}

実際にERC-20トークンを実装する際は、ゼロから書くのではなく、OpenZeppelin Contractsのような信頼性の高いライブラリを利用するのが一般的です。これにより、安全で標準に準拠したトークンを簡単に作成できます。

ERC-721: 代替不可能なトークン (Non-Fungible Tokens – NFTs) 🖼️

ERC-721は、代替不可能 (Non-Fungible) なトークン、いわゆるNFTのための標準規格です。代替不可能とは、各トークンがユニークであり、他のトークンと区別されることを意味します。例えば、一点物のアート作品や、シリアルナンバー付きの限定グッズなどがこれにあたります。

NFTは、デジタルアート、コレクターズアイテム、ゲーム内アイテム(武器、キャラクターなど)、不動産所有権、会員権など、ユニークな価値を持つものをブロックチェーン上で表現するために広く使われています。

主な機能

ERC-721で定義されている主な関数とイベントは以下の通りです。

  • balanceOf(address owner): 指定されたアドレスが所有するNFTの数を返す。
  • ownerOf(uint256 tokenId): 指定された`tokenId`のNFTを所有するアドレスを返す。
  • transferFrom(address from, address to, uint256 tokenId): `from`から`to`へ指定された`tokenId`のNFTを転送する。
  • safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data): `transferFrom`に加えて、転送先がNFTを受け取れるコントラクトかどうかのチェックを行う安全な転送。
  • approve(address to, uint256 tokenId): 特定の`tokenId`のNFTの操作(転送など)を`to`アドレスに許可する。
  • getApproved(uint256 tokenId): 特定の`tokenId`のNFTに対して操作許可を得ているアドレスを返す。
  • setApprovalForAll(address operator, bool approved): 自分の所有する全てのNFTに対する操作権限を`operator`アドレスに与える(または剥奪する)。
  • isApprovedForAll(address owner, address operator): `operator`が`owner`の全てのNFTに対する操作権限を持っているかどうかを返す。
  • event Transfer(address indexed from, address indexed to, uint256 indexed tokenId): NFTが転送されたときに発行されるイベント。
  • event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId): `approve`が成功したときに発行されるイベント。
  • event ApprovalForAll(address indexed owner, address indexed operator, bool approved): `setApprovalForAll`が成功したときに発行されるイベント。

簡単な実装例(インターフェース)

ERC-721のコアインターフェースは以下のようになります。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IERC721 {
    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);

    function balanceOf(address owner) external view returns (uint256 balance);
    function ownerOf(uint256 tokenId) external view returns (address owner);
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
    function setApprovalForAll(address operator, bool _approved) external;
    function getApproved(uint256 tokenId) external view returns (address operator);
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    // Optional: Metadata Extension
    // function name() external view returns (string memory);
    // function symbol() external view returns (string memory);
    // function tokenURI(uint256 tokenId) external view returns (string memory);

    // Optional: Enumerable Extension
    // function totalSupply() external view returns (uint256);
    // function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
    // function tokenByIndex(uint256 index) external view returns (uint256);
}

ERC-721も、OpenZeppelin Contractsなどのライブラリを使って実装することが推奨されます。メタデータ(名前、説明、画像など)を扱うための拡張機能なども用意されています。

ERC-1155: マルチトークン標準 🎮

ERC-1155は、複数のトークンタイプを単一のコントラクトで管理できる、より効率的なトークン標準です。ERC-20のような代替可能なトークンと、ERC-721のような代替不可能なトークン(NFT)の両方を、同じコントラクト内で扱うことができます。さらに、「半代替可能 (Semi-Fungible)」なトークン(例:ゲーム内で使用すると消費されるアイテム)も表現できます。

ERC-1155の大きな利点は、バッチ処理が可能であることです。複数のトークン(異なる種類でも可)を一度のトランザクションで転送できるため、ガス代を大幅に節約できます。これは特に、多数のアイテムが存在するブロックチェーンゲームなどで非常に有効です。

主な特徴と機能

  • 単一コントラクト、複数トークン: 1つのコントラクトで、異なるIDを持つ複数のトークン(代替可能、代替不可能)を管理します。
  • バッチ転送: 複数のトークンIDと数量を一度に転送できます (safeBatchTransferFrom)。
  • バッチ残高確認: 複数のアカウントとトークンIDの組み合わせについて、一度に残高を確認できます (balanceOfBatch)。
  • バッチ承認: ERC-721と同様のsetApprovalForAllによるオペレーターへの一括承認が可能です。(個別トークンIDごとの`approve`はありません)
  • IDごとの残高: 残高確認 (balanceOf) は、アカウントアドレスとトークンIDの両方を指定します。
  • 安全な転送規則: ERC-721と同様に、トークンを受け取れないコントラクトへの誤送信を防ぐためのonERC1155Receivedフックがあります。
  • 主な関数: balanceOf, balanceOfBatch, safeTransferFrom, safeBatchTransferFrom, setApprovalForAll, isApprovedForAll
  • 主なイベント: TransferSingle, TransferBatch, ApprovalForAll, URI

簡単な実装例(インターフェース)

ERC-1155のコアインターフェースの主要部分は以下のようになります。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IERC1155 {
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
    event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);
    event URI(string value, uint256 indexed id);

    function balanceOf(address account, uint256 id) external view returns (uint256);
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);
    function setApprovalForAll(address operator, bool approved) external;
    function isApprovedForAll(address account, address operator) external view returns (bool);
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
    function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;

    // Optional: Metadata Extension
    // function uri(uint256 id) external view returns (string memory);
}

ERC-1155の実装も、OpenZeppelin Contractsを利用することで、安全かつ効率的に行うことができます。

各標準の比較まとめ 📊

ERC-20、ERC-721、ERC-1155の主な違いをまとめます。

特徴 ERC-20 ERC-721 ERC-1155
トークンの性質 代替可能 (Fungible) 代替不可能 (Non-Fungible) 代替可能 & 代替不可能 (Multi-Token)
主な用途 仮想通貨, ポイント, 投票権 NFTアート, コレクティブル, ゲームアイテム(ユニーク) ゲーム(複数アイテム), バンドル販売, 効率的な複数トークン管理
キーコンセプト 数量 (Amount) トークンID (Token ID) トークンID (Token ID) & 数量 (Amount)
単位 数量で管理 個別のトークンとして管理 IDごとに数量で管理
バッチ転送 ❌ 不可 ❌ 不可 (一部拡張で可能) ✅ 可能
ガス効率 (複数種類転送時) 低い (個別トランザクション) 低い (個別トランザクション) 高い (バッチ処理)
管理コントラクト数 1トークン種別 = 1コントラクト 1コレクション = 1コントラクト 複数トークン種別 = 1コントラクト

実装上の注意点と推奨事項 💡

  • OpenZeppelinの利用: これらのトークン標準を実装する際は、セキュリティ監査済みで広く使われているOpenZeppelin Contractsライブラリの使用を強く推奨します。これにより、一般的な脆弱性を避け、開発時間を短縮できます。
  • ガス効率: 特にNFT (ERC-721) や、大量のトークンを扱う場合、ガス代は重要な考慮事項です。ERC-1155はバッチ処理によりガス効率が良い場合がありますが、コントラクトの複雑性は増します。ERC-721でも、Enumerable拡張などはガス代が高くなる傾向があるため、必要性を検討しましょう。
  • セキュリティ: トークンコントラクトは資産を扱うため、セキュリティは最優先事項です。OpenZeppelinなどのライブラリを使用することに加え、アクセス制御(誰がミントできるか、など)や、オーバーフロー/アンダーフロー対策(Solidity 0.8以降は標準でチェックされます)などを適切に実装することが重要です。
  • メタデータ管理: 特にNFT (ERC-721, ERC-1155) では、トークンに関連付けられるメタデータ(名前、説明、画像URLなど)の管理方法が重要です。オンチェーンに保存するか、IPFSなどのオフチェーンストレージを利用するかなどを検討します。tokenURI (ERC-721) や uri (ERC-1155) 関数でメタデータへのリンクを提供します。

まとめ 🎉

今回は、Ethereumエコシステムにおける主要なトークン標準であるERC-20、ERC-721、ERC-1155について学びました。

  • ERC-20 は、通貨のような代替可能なトークンの標準です。
  • ERC-721 は、アートやコレクティブルのようなユニークな価値を持つ代替不可能なトークン (NFT) の標準です。
  • ERC-1155 は、代替可能・不可能の両方を単一コントラクトで効率的に扱えるマルチトークン標準で、特にゲームなどで有用です。

これらの標準を理解し、適切に使い分けることで、様々なユースケースに対応した信頼性の高いトークンを作成することができます。OpenZeppelinなどのライブラリを活用し、実際にこれらのトークンコントラクトを作成・デプロイしてみることをお勧めします。次のステップでは、これらの知識を活かして、より実践的な開発に挑戦していきましょう!💪

コメント

タイトルとURLをコピーしました