Create your own
Lesson illustration

Building an ERC-1155 Multi-Token Contract

Hello! Welcome back to your course on Ethereum development.

In our last lesson, we built our first NFT using the ERC-721 standard, which is perfect for creating unique, one-of-a-kind digital assets. We saw that each token has a unique ID and is non-fungible. But what if a project needs more flexibility? Imagine a blockchain game that needs thousands of identical "Gold Coin" tokens, hundreds of "Health Potion" tokens, and only one unique "Legendary Sword" token. Deploying separate ERC-20 and ERC-721 contracts for this would be inefficient and costly.

This is where the ERC-1155 multi-token standard comes in.

Introduction

This lesson introduces ERC-1155, a powerful and gas-efficient standard that allows a single smart contract to manage an entire portfolio of fungible, non-fungible, and semi-fungible tokens.

Learning Outcome: By the end of this lesson, you will be able to implement an ERC-1155 multi-token contract for combined fungible and non-fungible assets.

We'll explore the core concepts that make ERC-1155 so versatile, use OpenZeppelin to build a contract, and implement practical features like a public mint with supply caps and payment processing.

Caption: This image illustrates the core benefit of ERC-1155. Instead of deploying multiple contracts for different tokens, a single ERC-1155 contract can manage various token types, significantly reducing complexity and deployment costs.

1. The Core Concepts of ERC-1155

ERC-1155 achieves its flexibility by changing how token ownership and identity are tracked. Instead of having separate standards, it introduces a unified framework where each token type is identified by an id. The nature of the token—fungible or non-fungible—is determined simply by its supply.

To get a solid conceptual foundation, please watch the following video.

ERC 1155: Multi-tokens

This video from BlockchainBob provides an excellent explanation of the ERC-1155 standard, its internal data structure, and its common use cases.

Watch the first three sections of the video (from 00:00 to 04:51). Pay close attention to: How ERC-1155 combines ideas from ERC-20 and ERC-721. The nested mapping used for tracking balances: mapping(id => mapping(owner => balance)). The gaming item example, which clearly illustrates how one contract can manage tokens with vastly different supplies.

As the video explained, the key ideas are:

  • A single contract manages multiple token types.
  • Each token type has a unique uint256 id.
  • A token id with a supply of 1 is effectively a non-fungible token (NFT).
  • A token id with a supply greater than 1 is a fungible token.

This is all managed through a central data structure, a nested mapping that looks like this in Solidity:
mapping(uint256 => mapping(address => uint256)) internal _balances;

To find out how many tokens of a certain type an address holds, you query _balances[tokenId][ownerAddress].

2. Efficiency Through Batch Operations

One of the most significant advantages of ERC-1155 is its gas efficiency, primarily achieved through batch operations. Since all tokens live in one contract, you can transfer multiple different token types to a recipient in a single transaction.

ERC 1155: Multi-tokens

Let's return to the BlockchainBob video to see how batch transfers work and why they are so efficient.

Watch the section on batch transfers (from 04:38 to 06:23). Notice how safeBatchTransferFrom allows you to pass arrays of token IDs and amounts, consolidating what would have been multiple transactions into one.

This is a game-changer for applications like games or marketplaces where users might buy or trade a "shopping cart" of different items at once. Instead of paying gas for five separate transfer calls, they pay for one safeBatchTransferFrom call.

3. Building an ERC-1155 Contract with OpenZeppelin

As with ERC-721, we'll use OpenZeppelin's audited contracts as our foundation. The primary contract is ERC1155.sol.

Let's start by looking at a canonical example from the OpenZeppelin documentation.

ERC-1155 Documentation

The OpenZeppelin documentation provides a clear and concise example of an ERC-1155 contract for game items. This is the best place to start for a code-level understanding.

Please read the section 'Constructing an ERC-1155 Token Contract'. Study the GameItems.sol code example. Focus on: How uint256 public constant is used to create readable names for token IDs. The constructor, which passes a metadata URI and then calls _mint to create both fungible (GOLD, SWORD) and non-fungible (THORS_HAMMER) tokens.

The GameItems.sol contract demonstrates the core structure:

  1. Inherit from ERC1155.
  2. Set the base URI in the constructor. Note the {id} placeholder. This works just like it did for ERC-721, but the contract automatically substitutes the id of the token being queried. We'll touch on this again later.
  3. Mint tokens using _mint(to, id, amount, data). You can mint a large amount for fungible tokens or an amount of 1 for an NFT.

4. Hands-On: Implementing a Public Mint with Payments

The GameItems example is great, but the owner mints everything at deployment. A more common real-world scenario is allowing users to mint tokens themselves, often by paying for them.

The following video provides a step-by-step guide in Remix to build out these features. We'll walk through it together.

How to create ERC 1155 Multi NFT Smart Contract

This video by Shobhit (Web3 Club) is a practical, hands-on tutorial that starts with the OpenZeppelin Wizard and progressively adds features to an ERC-1155 contract.

We will go through this video in stages. You can follow along in Remix IDE if you wish.

Step 1: Basic Setup and Owner-Only Minting

First, watch parts 0 and 1 of the video (01:18 - 05:04). This shows how to generate a base contract with OpenZeppelin Wizard and demonstrates the default mint function, which is restricted to the contract owner by the onlyOwner modifier. This is useful for pre-minting or airdropping tokens.

Step 2: Implementing a Public Mint with Supply Caps

Now, watch part 2 (05:54 - 08:45). This is where things get interesting. The contract is modified to allow anyone to mint. The key changes are:

  • Removing onlyOwner: The mint function is made public.
  • Tracking Supply: A minted mapping or array is added to keep track of how many tokens of each id have been created.
  • Enforcing Limits: A supplies array is created to define the maximum supply for each token id. A require statement ensures minted[id] + amount <= supplies[id].

This pattern is fundamental for managing public sales of tokens with different rarities or edition sizes.

Step 3: Adding Payments

Next, watch part 4 (12:35 - 15:04). To charge for minting, we introduce two changes:

  • payable function: The mint function is marked as payable, allowing it to receive Ether.
  • Value Check: A rates array stores the price per token for each id. A require statement checks that msg.value (the Ether sent with the transaction) is sufficient: require(msg.value >= amount * rates[id]).

Step 4: Withdrawing Funds

Finally, watch part 5 (15:04 - 17:49). The Ether paid for mints is now held by the contract. To retrieve it, a withdraw function is added. It's critical that this function is protected with onlyOwner to ensure only the designated owner can access the funds.

By following these steps, you've built a robust ERC-1155 contract that can handle a public sale for multiple token types with different supplies and prices.

5. Metadata and Other Considerations

Metadata URI
Just like in our previous lesson, the uri(tokenId) function points to a JSON metadata file. The OpenZeppelin ERC-1155 implementation handles this elegantly. The URI you set in the constructor, e.g., https://my-api.com/items/{id}.json, will have the {id} part automatically replaced with the hexadecimal representation of the token ID being queried.

How to Create and Deploy an ERC-1155 NFT

This Quicknode guide provides a good refresher on metadata and shows a practical example of overriding the uri function if needed.

Skim the section 'Creating & Deploying the ERC1155 Contract'. You don't need to follow the deployment steps, but notice the code block where the uri function is overridden. This is sometimes necessary to match the exact format expected by marketplaces like OpenSea, which may not handle the {id} replacement correctly.

Token Enumeration
A key trade-off with ERC-1155's gas efficiency is that it's difficult to enumerate (or list) all the token IDs an owner possesses directly on-chain. The standard was designed to keep storage light. Discovering which tokens a user owns typically requires indexing Transfer events off-chain, using services like The Graph or third-party APIs.

Conclusion

Today we've explored the ERC-1155 standard, a versatile and efficient tool for any developer's toolkit. It elegantly solves the problem of managing diverse token types within a single, unified system.

Key Takeaways:

  • ERC-1155 is a multi-token standard that manages both fungible (supply > 1) and non-fungible (supply = 1) assets in one contract.
  • It uses a mapping(id => mapping(owner => balance)) structure to track ownership, making it highly flexible.
  • Batch operations like safeBatchTransferFrom provide significant gas savings for multi-item transactions.
  • Implementing features like public mints with supply caps and payments involves adding on-chain logic with require statements and tracking state in mappings or arrays.
Caption: A concise summary of the features and ideal use cases for ERC-721 and ERC-1155. Choosing the right standard depends on your project's specific needs.

Next Steps:

Now that you can create contracts for public mints, a common next requirement is to manage a pre-sale or an "allowlist" for a select group of users. In our next lesson, "Implement an NFT mint with an allowlist using Merkle proofs," we will explore a powerful and gas-efficient cryptographic technique to achieve just that.

Can't find a good explanation? Sign up and we'll make it for you

Sign up