ERC-1155で実現するフルオンチェーン×マルチトークン~ファンジブルトークンとNFTをスマートに管理する~

 本記事では、Ethereumのトークン規格である「ERC-1155」を使って、Ethereumのテストネットである「Sepolia」でオリジナルのフルオンチェーンマルチトークンを発行する手順を解説します。

 こんにちは。大和総研デジタルソリューション研究開発部の幹です。普段は、大和証券向けにRAGなど生成AIを用いた業務支援ツールの開発・検証に携わっています。過去にはセキュリティ・トークンを扱うためのウォレット(注1)と呼ばれるシステムの開発も担当していました。
 金融フロントシステム部の佐藤です。普段は、地方銀行向けの投資信託窓口販売システムを開発・保守しています。

 大和総研ではEthereumなどのパブリックブロックチェーンを活用したWeb3の広がりを見据え、Web3分野の研究開発を行う専門のプロジェクト「Web3 Lab」を発足しました。社内のさまざまな部署から公募で選ばれたメンバーがWeb3 Labに参加しており、私たちもその一員として日々の業務と兼務しながら研究開発に取り組んでいます。
 Web3 Labではスマートコントラクトを使った開発も行っており、今回はEthereumのトークン規格「ERC-1155」を使ってオリジナルのトークンを発行する手順について解説します。本記事が、スマートコントラクトを使った開発を検討している皆さまの参考になれば幸いです。

1 ERC-1155とは

 ERC-1155のERCとはEthereum Request for Commentの略で、Ethereumの機能やプロセスの改善に関する提案が書かれたドキュメントであるEIP(Ethereum Improvement Proposal)において、スマートコントラクトなどのアプリケーションに関する提案がまとめられたカテゴリの名称です。
 ERC-1155はマルチトークン(Multi Token)を実装するためのスマートコントラクトに関する規格で、Witek Radomski氏やAndrew Cooke氏らによって提案されました(注2)。ERC-1155には、マルチトークンのスマートコントラクトを開発する際に実装すべきメソッドやその引数、戻り値などが定義されています。1155という数字はGitHubのissue番号が1155だったことが由来となっています。

2 マルチトークンとは

 マルチトークンは、FT(Fungible Token:ファンジブルトークン)とNFT(Non-Fungible Token:ノンファンジブルトークン)の両方を一元的に扱えるトークンのことです。
 例えば、ゲーム内で使用する通貨と、唯一無二の希少な武器アイテムをトークンとして扱うケースを考えてみましょう。従来の単一規格のトークンを用いる場合、ゲーム内で使用する通貨をFTであるERC-20を使って発行し、武器アイテムをNFTであるERC-721を使って発行する必要があり、それぞれ別のスマートコントラクトを用いることになるため管理コストや取引のコストが大きくなってしまいます。
 マルチトークンでは、一つのスマートコントラクトで複数のタイプのトークンを同時発行および同時移転できるため、取引プロセスの効率化や取引コストの削減が期待できます。

図1. マルチトークンについて
出所:大和総研作成

3 フルオンチェーンとは

 「フルオンチェーン」とは、NFTなどのデジタルコンテンツのすべてのデータをブロックチェーン上に直接記録・保存することを指します。多くのNFTでは、画像や動画などのデータはブロックチェーン外に保管され、チェーン上にはその参照情報(URI)のみが記録されます。NFTなどのデジタルコンテンツをブロックチェーン外に保管する場合、保管先のサービスの停止などでNFTの中身にアクセスできなくなり、今まで持っていたNFTの価値がなくなってしまうリスクがあります。これに対しフルオンチェーンのNFTでは、画像・テキストを含む全データがブロック内にあり、ブロックチェーンが稼働し続ける限りコンテンツが失われる心配がありません。
 一方で、フルオンチェーンNFTは、デジタルコンテンツ自体をブロックチェーン上に書き込むため、データ量が大きくなり、取り扱う際のコスト(トランザクション手数料)が高額になるといったデメリットがあります。
 フルオンチェーンNFTについてはERC-721の解説記事もご参照ください。

4 ERC-1155を用いたNFTの事例

 ERC-1155を使用した「Enjin」と「Into The Metaverse」の二つの事例を紹介します。

4.1 Enjin

 まずは、ERC-1155を提案したEnjin社の事例です。Enjin社はシンガポールに拠点を置く企業で、ブロックチェーン技術が登場する以前からゲーム内アイテムの販売や管理を行うプラットフォームを提供していました。2017年にブロックチェーンへ本格参入しましたが、当時主流だったERC-721は、唯一無二のアイテムのトークンと、大量発行されるコインのようなアイテムのトークンが混在するゲーム開発において、トランザクション手数料や運用負荷の面で課題がありました。この課題を解決するためにEnjinのCTOであるWitek Radomski氏は、複数のトークンを1つのコントラクトで扱える新規格ERC-1155を提唱しました。
 この規格を活用した代表的なプロダクトが、2020年にサービスインした「Enjin Platform」です。このプラットフォームでは、Web上で誰でもブロックチェーン資産を発行・管理できるツール群が提供されており、ERC-1155にも対応しています。

4.2 Into The Metaverse

 次にadidas社の「Into The Metaverse」です。2021年12月に開始されたadidas初のNFTコレクション「Into The Metaverse」では、Ethereum上で3万点のNFTがERC-1155規格を用いて発行されました。Bored Ape Yacht ClubやPUNKS Comicといった人気NFTプロジェクトとのコラボレーションということで注目され、発売されたNFTは即完売しています。
 このNFTプロジェクトの特長は、NFT保有者に特別な権利が付与されたという点です。NFT保有者はNFTと引き換えることで、メタバース内で着用できるデジタルウェアや物理的な限定商品を入手できました。
 このプロジェクトでは、ERC-1155を採用することで数万点規模のNFTを単一のスマートコントラクトで効率よく発行・管理し、NFTの発行や移転に係るトランザクション手数料を抑えるための工夫が行われています。

図2. Into The Metaverseについて
出所:大和総研作成

5 ERC-1155の規格の概要

 ここからは、ERC-1155の具体的な仕組みについてご紹介します。ERC-1155で定義されているメソッドのうち主なものは以下の通りです。

  • transferFrom:トークンIDを指定された数量分別のアドレス宛てに移転するメソッド。
function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
  • safeBatchTransferFrom:複数のトークンIDを一度に別のアドレス宛てに移転するメソッド。
function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;  
  • balanceOf:指定したアドレスが所有するトークンの数を取得するメソッド。ERC-1155は複数のトークンタイプ(FT/NFT)を同時に扱えるため、トークンIDの指定が必須。
function balanceOf(address _owner, uint256 _id) external view returns (uint256);  
  • balanceOfBatch:指定した複数のアドレスが所有するトークンの数を一度に取得するメソッド。
function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);  
  • mint:指定したアドレスにトークンを発行するメソッド。
function mint(address to, uint256 id, uint256 value, bytes memory data) internal  
  • mintBatch:指定したアドレスに複数のトークンを発行するメソッド。通常の「mint」と異なり、複数のトークンを一度に発行できる。
function mintBatch(address to, uint256[] memory ids, uint256[] memory values, bytes memory data) internal  

6 テストネットでオリジナルトークンを発行してみる

 それでは、実際にEthereumのテストネットである「Sepolia」でERC-1155を使用してオリジナルトークンを発行します。
 今回は、発行時に指定した色の円の中に指定したテキストが描かれたSVG画像データを持つトークンを発行します。画像データも含めてすべてのデータがブロックチェーン上に記録される「フルオンチェーンマルチトークン」として発行します。
 Ethereumではトランザクションを実行するにはガス代をEther(略称:ETH。以降、ETHと表記します。)で支払う必要があります。手数料は、スマートコントラクトのデプロイとトークンの発行を実行するアカウントが支払う必要があります。そのため、あらかじめアカウントにETHを保有させておく必要があることに注意してください。SepoliaのSepolia ETHは以下のFaucetというサービスを使って無料で入手できます。
Sepolia PoW Faucet
Chainlink Ethereum Sepolia Faucet

 今回はMetamaskであらかじめ作成したアカウントを使ってデプロイと発行を行います。秘密鍵はMetamaskからエクスポートして使用します。

6.1 スマートコントラクトの作成

 今回は開発環境としてHardhat (Ver 2.25.0) (注3)と、スマートコントラクトのライブラリであるOpenZeppelin(Ver5.3.0)(注4)を利用して、ERC-1155トークンのスマートコントラクトを実際に作成します。両者とも、Ethereum(および互換性のあるブロックチェーン)のスマートコントラクト開発においてよく利用されるツールとライブラリです。

 Hardhatはスマートコントラクトの開発ツールです。Solidityで記載されたスマートコントラクトに対して、テスト、コンパイル、デプロイなどが実行できます。

 OpenZeppelinはスマートコントラクトのライブラリです。ERC-1155などのトークン規格に沿ったスマートコントラクトを効率的に開発できます。その他、セキュリティ機能や数学的な演算を効率よく行う機能など、スマートコントラクト開発で役立つさまざまなユーティリティ機能も提供しています。

 具体的な開発方法は以下の通りです。

① Hardhat プロジェクトの作成:
 任意のディレクトリを作成し、npxコマンドでHardhat プロジェクトをセットアップします。

$ npx hardhat init

コマンドを実行すると質問が表示されるので、それぞれ以下のように回答します。

・Ok to proceed? (y) y  
・What do you want to do? · Create a TypeScript project   
・Hardhat project root: 何もせずEnterキー  
・Do you want to add a .gitignore? (Y/n) ‣ y  
・Do you want to install this sample project's dependencies with npm (hardhat @nomicfoundation/hardhat-toolbox-viem)? (Y/n) ‣ y  

② OpenZeppelin ライブラリのインストール:
 Hardhatをインストールできましたので、次にプロジェクトの依存関係として OpenZeppelin をインストールします。

 プロジェクト直下でnpm installコマンドを実行し、ライブラリをインストールします。

$ npm install @openzeppelin/contracts

 node_modules配下に@oppenzeppelin/contractsフォルダが生成され、ライブラリが使用可能になりました。
 環境が整いましたので、実際にERC1155規格のトークンをデプロイするスマートコントラクトを作成します。
 今回は、作成・編集した以下3ファイルごとに解説します。
・プロジェクト直下のhardhat.config.ts
・contracts配下のerc1155.sol
・ignition/modules配下のerc1155.ts

 なお、プロジェクト全体のディレクトリ内の主要なファイルの構成は以下のようになります。

図3. ディレクトリ構成(主要なファイルのみ抜粋)
出所:大和総研作成

 まず、hardhat.config.tsについて解説します。このファイルでは、以下の設定を行っています。
● solidityのバージョンの設定
● デプロイ先のネットワークの設定(EthereumのテストネットであるSepoliaノードの接続先の設定)
● デプロイの際に使用するアカウントの秘密鍵の設定

hardhat.config.ts(大和総研作成)
 import { HardhatUserConfig } from "hardhat/config";
 import "@nomicfoundation/hardhat-toolbox";

 const PRIVATE_KEY= {Metamaskからエクスポートしたアカウントの秘密鍵をここで読み込む環境変数の利用を推奨};

 const config: HardhatUserConfig = {
   solidity: "0.8.28",
   networks: {
     sepolia: {
       url: “Ethereum SepoliaノードのURL”,
       accounts: [PRIVATE_KEY!],
     },
   },
 };

 export default config;

 次に、contracts配下のerc1155.solについて解説します。コントラクトとして MyOnChainMultiToken クラスを定義し、ライブラリであるOpenZeppelinのERC1155と ERC1155URIStorageとOwnableを継承しました。ソースコードは以下の通りです。

erc1155.sol(大和総研作成)
pragma solidity ^0.8.22;

import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import {ERC1155URIStorage} from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155URIStorage.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Base64.sol";

contract MyOnChainMultiToken is ERC1155, ERC1155URIStorage, Ownable {
    // コンストラクタ関数
    // コントラクトがデプロイされる際に一度だけ実行される
    constructor() ERC1155("") Ownable(msg.sender) {}  // 親コントラクトOwnableのコンストラクタを呼び出し、デプロイ時に利用したアカウントをトークンの所有者(管理者、owner)に設定

    // 指定したアドレスにトークンを発行するメソッド
    // この関数はコントラクトの所有者 (管理者、owner) のみが実行できる
    function mint(
        address recipient,    // トークンを発行するアドレス
        uint256 tokenId,      // トークンID
        uint256 amount,       // 発行する数量
        string memory _text,  // 円の中に描くテキスト
        string memory _color  // 円の色
    ) public onlyOwner {
        _mint(recipient, tokenId, amount, "");  // 指定されたアドレス 'recipient' に指定されたトークンID 'tokenId' のトークンを指定された量 'amount' 発行する
        _setURI(tokenId, generateTokenURI(_text, _color, tokenId));  // 指定されたトークンID 'tokenId'にコンテンツを紐づける
    }

    // 指定したアドレスに複数のトークンを発行するメソッド
    // この関数はコントラクトの所有者 (管理者、owner) のみが実行できる
    function mintBatch(
        address recipient,          // トークンを発行するアドレス
        uint256[] memory tokenIds,  // トークンID(配列形式で指定)
        uint256[] memory amounts,   // 発行する数量(配列形式で指定)
        string[] memory _text,      // 円の中に描くテキスト(配列形式で指定)
        string[] memory _color      // 円の色(配列形式で指定)
    ) public onlyOwner {
        _mintBatch(recipient, tokenIds, amounts, "");  // 指定されたアドレス 'recipient' に指定されたトークンID 'tokenId' のトークンを指定された量 'amount' 発行する
        for (uint256 i = 0; i < tokenIds.length; i++) {
            _setURI(
                tokenIds[i],
                generateTokenURI(_text[i], _color[i], tokenIds[i])
            );  // 指定されたトークンID 'tokenId'にコンテンツを紐づける
        }
    }

    // トークンに紐付けるコンテンツを作成するメソッド
    function generateTokenURI(
        string memory _text,   // 円の中に描くテキスト
        string memory _color,  // 円の色
        uint256 _tokenId
    ) private pure returns (string memory) {
        string memory svg = generateSVG(_text, _color);  // SVG形式で指定した色の円の中に指定したテキストを描く
        
        // JSON形式でトークン名やSVG画像などが含まれたメタデータを作成
        string memory json = string(
            abi.encodePacked(
                '{"name": "OnChain Multi Token #',
                Strings.toString(_tokenId),
                '", "description": "This is a full on-chain Multi Token.", "image": "data:image/svg+xml;base64,',
                Base64.encode(bytes(svg)),
                '"}'
            )
        );
        return
            // Base64エンコードしてData URI形式に変換
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(bytes(json))
                )
            );
    }

    // SVG形式で指定した色の円の中に指定したテキストを描くメソッド
    function generateSVG(
        string memory _text,  // 円の中に描くテキスト
        string memory _color  // 円の色
    ) private pure returns (string memory) {
        // SVGのベースとなる文字列を作成
        string
            memory baseSvg = '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 300 300">';
        // 円のスタイルを指定
        string memory circleSvg = string(
            abi.encodePacked(
                '<circle cx="150" cy="150" r="100" fill="',
                _color,
                '" />'
            )
        );
        // テキストのスタイルを指定
        string memory textSvg = string(
            abi.encodePacked(
                '<text x="150" y="150" font-size="40" text-anchor="middle" dominant-baseline="middle" fill="white">',
                _text,
                "</text>"
            )
        );
        // 閉じタグを付与
        string memory endSvg = "</svg>";

        return string(abi.encodePacked(baseSvg, circleSvg, textSvg, endSvg));
    }

    // 指定したトークンIDに紐付くコンテンツを取得するメソッド
    function uri(
        uint256 tokenId
    ) public view override(ERC1155, ERC1155URIStorage) returns (string memory) {
        return ERC1155URIStorage.uri(tokenId);
    }
}

 最後に、ignition/module配下のerc1155.tsです。先ほどのerc1155.solの MyOnChainMultiToken コントラクトのコンストラクタを使って、Hardhat Ignitionを利用してデプロイができます。ソースコードは以下の通りです。

erc1155.ts(大和総研作成)
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";  
// import { ethers } from "hardhat";  
  
export default buildModule("erc1155", (m) => {  
  const erc1155 = m.contract("MyOnChainMultiToken");  
  
  return { erc1155 };  
});  

 ソースコードが完成したので、npx コマンドを使ってhardhatでコンパイルを実行します。コンパイルが通ればデプロイの準備は完了です。

$ npx hardhat compile

6.2 スマートコントラクトのデプロイしオリジナルトークンを発行する

 作成したスマートコントラクトをデプロイします。デプロイはnpxコマンドでネットワークにsepoliaを指定することで実行できます。

$ npx hardhat ignition deploy ignition/modules/erc1155.ts --network sepolia

 コマンドを実行し、デプロイが成功すると以下のようなメッセージが表示され、デプロイしたトークンのコントラクトアドレスが表示されます。このコントラクトアドレス(ここでは0xb829...)は、後述のトークン発行処理と MetaMaskへのインポートの際に使用するため、控えておきます。

トークンデプロイ結果の確認(大和総研作成)

✔ Confirm deploy to network sepolia (11155111)? … yes
Hardhat Ignition 🚀

Deploying [ erc1155 ]

Batch #1 
  Executed erc1155#MyOnChainMultiToken

[ erc1155 ] successfully deployed 🚀

Deployed Addresses

erc1155#MyOnChainMultiToken - 0xb82939eDB5A47343369E963C0e6428Eb8420d3f8

6.3 デプロイしたトークンをMetaMaskで扱えるようにする

 いよいよトークンをMetaMaskのアカウントアドレスに発行します。MintBatch関数に発行先アドレス、トークンID、数量、テキスト、色を引数として設定し、発行処理を実行します。

mintBatch.ts (大和総研作成)
import { ethers } from "hardhat";

async function main() {
  const contractAddress = "0x1419EeB995beA238205279c85A8e2f7Ca0722e0b"; // デプロイ時に表示されたコントラクトアドレスを入力する。
  const [deployer] = await ethers.getSigners();

  const abi = [
    "function mintBatch(address recipient, uint256[] tokenIds, uint256[] amounts, string[] _text, string[] _color) public"
  ];
 
  const contract = new ethers.Contract(contractAddress, abi, deployer);

  const to = "0x..."; // トークンを発行する先のアドレスを入力する(今回は秘密鍵をエクスポートしたMetaMaskのアカウントアドレス)

  //トークンID、数量、テキスト、カラーを指定
  // ここでは以下のような3つのトークンを作成する
  // id = 1, amount=10, text=”text1”, color=”red”
  // id = 2, amount=20, text=”text2”, color=”green”
  // id = 3, amount=30, text=”text3”, color=”blue”
  const ids = [1, 2, 3];
  const amounts = [10, 20, 30];
  const texts = ["text1", "text2", "text3"];
  const colors = ["red", "green", "blue"];

  // mintBatchを実行
  const tx = await contract.mintBatch(to, ids, amounts, texts, colors);
  console.log("tx hash:", tx.hash);
  await tx.wait();
  console.log("mintBatch completed");
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

 scriptsフォルダにファイル名を「mintBatch.ts」として格納します。次に、以下のコマンドを実行し、ミントバッチ処理を実行します。

$ npx hardhat run scripts/mintBatch.ts --network sepolia

 処理が成功すると、以下のようなログが出力されます。トランザクションハッシュが出力されていることを確認します。

tx hash: 0x8a233265961503fa88941a099846f13501200b778dc4e9384889cbe2be0cf6b2
mintBatch completed

 次に発行したトークンを秘密鍵のエクスポート元であるMetaMaskにインポートし、トークンをMetaMask上で扱えます。インポートすることで、MetaMask上でトークンの確認や移転を行えるようになります。
 MetaMaskを起動し、Sepoliaネットワークに接続した上で、NFTタブに移動し「NFTインポート」を選択します(図4)。

図4. 「NFTをインポート」を選択
出所:Consensys Software Inc.のアプリMetaMaskより引用(2025年12月8日閲覧)

 図5のような画面が表示されますので、「アドレス」「トークンID」欄に先ほどメモしたコントラクトアドレスとトークンIDを入力し、「インポート」を選択します(図6)。

図5. NFTインポート画面(入力前)
出所:Consensys Software Inc.のアプリMetaMaskより引用(2025年12月8日閲覧)

図6. NFTインポート画面(入力後)
出所:Consensys Software Inc.のアプリMetaMaskより引用(2025年12月8日閲覧)

 インポートが完了すると、図7のようにNFT一覧画面にインポートしたトークンが表示されます。

図7. NFT一覧にトークンが追加される
出所:Consensys Software Inc.のアプリMetaMaskより引用(2025年12月8日閲覧)

 追加したトークンをクリックすることで、情報を確認できます(図8)。

図8. トークンの詳細情報を確認
出所:Consensys Software Inc.のアプリMetaMaskより引用(2025年12月8日閲覧)

 「送金」を選択すると他のアドレスに移転することもできます(図9)。

図9. 「送金」を選択するとNFTを他のアドレスに移転できる
出所:Consensys Software Inc.のアプリMetaMaskより引用(2025年12月8日閲覧)

7 おわりに

 本記事では、ERC-1155の規格を用いてオリジナルのトークンをEthereumのSepoliaで発行しました。ERC-1155トークンの発行は今回紹介したHardhatやOpenZeppelinなどのツールやライブラリを用いて、比較的簡単に試せます。興味のある方はぜひチャレンジしてみてください。

(本ブログの内容は2025年12月時点のものです)

8 お問い合わせ先

 大和総研では、ブロックチェーン・Web3分野の研究開発を行う専門のプロジェクトを発足し、ウォレットに関する特許を取得するなど、ブロックチェーン・Web3に関する取り組みを行っています。長年にわたるブロックチェーン・Web3関連の取り組みの実績を活かし、お客様のブロックチェーン・Web3ビジネスの検討やシステムの構築をサポートします。ご要望・ご不明点などがありましたら、ITソリューションサービスサイトよりお問い合わせください。

参考文献

(注1)本邦初の PTS(私設取引システム)取扱セキュリティ・トークンの引受及び、 セキュリティ・トークンウォレット「Crossllet」開発のお知らせ
https://ssl4.eir-parts.net/doc/8601/ir_material3/218042/00.pdf
(注2)ethereum.org 「ERC-1155: Multi Token Standard」
https://eips.ethereum.org/EIPS/eip-1155
(注3)Nomic Foundation 「Hardhat」
https://hardhat.org/
(注4)Zeppelin Group Ltd「OpenZeppelin」
https://www.openzeppelin.com/solidity-contracts