akash11

ERC-1155: The Multi-Token Standard for Ethereum

A single smart contract that can manage unlimited types of tokens (fungible, non-fungible, or semi-fungible) with batch operations and reduced gas costs.

Published on 21 November, 2025

Categories:

SolidityToken

What ERC-1155 Actually Solves

Before ERC-1155, if you wanted to create a game with both currency tokens and unique character NFTs, you needed two separate smart contracts. One following ERC-20 for the currency, another following ERC-721 for the characters. Each deployment costs gas. Each interaction with different token types requires separate transactions.
ERC-1155 changes this. One contract manages everything.

The Core Concept

Think of ERC-1155 as a warehouse inventory system. Instead of having separate warehouses for coins, swords, and shields, you have one warehouse with different storage bins. Each bin has an ID number, and you track quantities in each bin.
Warehouse (ERC-1155 Contract)
├── Bin #0: Gold Coins (fungible, quantity: 1000000)
├── Bin #1: Legendary Sword #1 (NFT, quantity: 1)
├── Bin #2: Legendary Sword #2 (NFT, quantity: 1)
├── Bin #3: Common Shield (semi-fungible, quantity: 500)
└── Bin #4: Rare Potion (semi-fungible, quantity: 50)

Basic Implementation

Here's a minimal ERC-1155 contract:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; contract GameItems is ERC1155 { uint256 public constant GOLD = 0; uint256 public constant SWORD = 1; uint256 public constant SHIELD = 2; constructor() ERC1155("https://game.example/api/item/{id}.json") { _mint(msg.sender, GOLD, 10000, ""); _mint(msg.sender, SWORD, 1, ""); _mint(msg.sender, SHIELD, 100, ""); } }
The URI string contains {id} which gets replaced with the actual token ID. So token 0 points to item/0.json, token 1 to item/1.json, and so on.

Key Functions

Minting tokens:
function mint(address to, uint256 id, uint256 amount) public { _mint(to, id, amount, ""); }
Batch minting (this is where gas savings happen):
function mintBatch( address to, uint256[] memory ids, uint256[] memory amounts ) public { _mintBatch(to, ids, amounts, ""); }
Instead of 5 transactions to mint 5 different tokens, you do it in one transaction.
Transferring tokens:
function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public
Batch transfer:
function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public
Checking balance:
function balanceOf(address account, uint256 id) public view returns (uint256)
Batch balance check:
function balanceOfBatch( address[] memory accounts, uint256[] memory ids ) public view returns (uint256[] memory)

How Approvals Work

ERC-1155 uses operator approval, not per-token approval:
// Alice approves Bob to manage ALL her tokens in this contract function setApprovalForAll(address operator, bool approved) public // Check if Bob is approved function isApprovedForAll(address account, address operator) public view returns (bool)
This is all-or-nothing. Bob either controls all of Alice's tokens or none. No middle ground.

Real World Example: Gaming Inventory

contract RPGItems is ERC1155 { uint256 private _currentTokenID = 0; mapping(uint256 => uint256) public tokenSupply; mapping(uint256 => bool) public isNFT; constructor() ERC1155("https://rpg.game/items/{id}") {} function createToken(uint256 supply, bool nft) external returns (uint256) { uint256 id = _currentTokenID; _currentTokenID++; isNFT[id] = nft; tokenSupply[id] = supply; _mint(msg.sender, id, supply, ""); return id; } function craft(uint256[] memory materials, uint256 result) external { // Burn materials _burnBatch(msg.sender, materials, getAmounts(materials)); // Mint result _mint(msg.sender, result, 1, ""); } function getAmounts(uint256[] memory ids) private pure returns (uint256[] memory) { uint256[] memory amounts = new uint256[](ids.length); for (uint256 i = 0; i < ids.length; i++) { amounts[i] = 1; } return amounts; } }
This contract lets you define items on the fly and implement crafting systems where multiple items combine into one.

The Transfer Flow

User initiates transfer
         |
    Has approval?
    /          \
  yes           no
   |            |
Check balance  Revert
   |
Enough tokens?
  /         \
yes          no
 |           |
Update       Revert
balances
 |
Call receiver
(if contract)
 |
Receiver accepts?
  /         \
yes          no
 |           |
Done        Revert
            (undo)

Receiving Tokens Safely

If your contract needs to receive ERC-1155 tokens, implement this:
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; contract ItemVault is IERC1155Receiver { function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4) { // Do something with the tokens return this.onERC1155Received.selector; } function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4) { return this.onERC1155BatchReceived.selector; } function supportsInterface(bytes4 interfaceId) external pure returns (bool) { return interfaceId == type(IERC1155Receiver).interfaceId; } }
Without these functions, your contract cannot receive ERC-1155 tokens safely.

Gas Comparison Example

Transferring 5 different tokens to someone:
Using separate ERC-20/721 contracts:
  • Transaction 1: Transfer token A (21,000 base + execution)
  • Transaction 2: Transfer token B (21,000 base + execution)
  • Transaction 3: Transfer token C (21,000 base + execution)
  • Transaction 4: Transfer token D (21,000 base + execution)
  • Transaction 5: Transfer token E (21,000 base + execution)
  • Total: ~105,000 gas just for base transaction costs
Using ERC-1155 batch transfer:
  • Transaction 1: Transfer all 5 tokens (21,000 base + batch execution)
  • Total: ~21,000 gas base + slightly higher execution cost
The savings are substantial, especially when gas prices spike.

Metadata Structure

The JSON metadata looks like this:
{ "name": "Fire Sword", "description": "A legendary blade forged in dragon fire", "image": "https://game.example/images/fire-sword.png", "properties": { "damage": 150, "element": "fire", "rarity": "legendary" } }
One URI template serves all tokens. The contract replaces {id} with the token ID when anyone queries it.

Comparing Token Standards

ERC-20 vs ERC-1155

ERC-20 (Fungible tokens only):
contract Token is ERC20 { // One type of token per contract function transfer(address to, uint256 amount) public function balanceOf(address account) public view returns (uint256) }
Each token type needs its own contract. If you have 10 different currencies, you deploy 10 contracts.
ERC-1155 (Multiple token types):
contract Tokens is ERC1155 { // Unlimited token types in one contract function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public function balanceOf(address account, uint256 id) public view returns (uint256) }
One contract handles everything. The ID parameter distinguishes token types.

ERC-721 vs ERC-1155

ERC-721 (Non-fungible tokens):
contract NFT is ERC721 { // Each token is unique function ownerOf(uint256 tokenId) public view returns (address) function transferFrom(address from, address to, uint256 tokenId) public }
Designed specifically for unique items. Cannot handle quantities greater than 1. Every transfer is a single-token operation.
ERC-1155 (Can be NFT or fungible):
contract MultiToken is ERC1155 { // Can mint quantity 1 for NFTs, quantity N for fungible function balanceOf(address account, uint256 id) public view returns (uint256) }
Flexible. Mint quantity 1 for an NFT, mint quantity 1000 for a currency. Same contract handles both.

Direct Comparison Table

Feature               | ERC-20  | ERC-721 | ERC-1155
---------------------|---------|---------|----------
Token types per      | 1       | 1       | Unlimited
contract             |         |         |
Fungibility          | Yes     | No      | Both
Batch transfers      | No      | No      | Yes
Gas efficiency       | Medium  | Medium  | High
Metadata per token   | No      | Yes     | Yes
Approval mechanism   | Amount  | Token   | Operator
Use case            | Currency| Unique  | Everything
                     |         | assets  |

When to Use What

Use ERC-20 when:
  • You only need one type of fungible token
  • Simple payment or governance token
  • Maximum compatibility with existing DeFi
Use ERC-721 when:
  • Every single token must be unique
  • Art, collectibles, domain names
  • You need standard NFT marketplace support
Use ERC-1155 when:
  • Managing multiple token types
  • Gaming items (mix of currencies and uniques)
  • Need batch operations for gas savings
  • Semi-fungible tokens (limited edition items)

Gas Cost Reality Check

Minting 100 different tokens:
ERC-20/721 separate:  Deploy 100 contracts + 100 mint transactions
ERC-1155:             Deploy 1 contract + 1 batch mint transaction
The difference is massive. ERC-1155 can save 90%+ on gas for multi-token operations.

Code Size Comparison

Creating a game with currency and items:
ERC-20 + ERC-721 approach:
contract Gold is ERC20 { ... } // ~200 lines contract Weapons is ERC721 { ... } // ~250 lines contract Armor is ERC721 { ... } // ~250 lines // Total: 3 contracts, ~700 lines, 3 deployments
ERC-1155 approach:
contract GameAssets is ERC1155 { ... } // ~150 lines // Total: 1 contract, ~150 lines, 1 deployment

The Tradeoff

ERC-1155 sacrifices some simplicity for flexibility. The balance mapping becomes two-dimensional (account + token ID), and you lose some fine-grained control that ERC-721's per-token approvals provide. But for most multi-asset applications, these tradeoffs are worth it.

Conclusion

ERC-1155 consolidates what previously required multiple contracts into one efficient system. It handles fungible tokens, NFTs, and semi-fungible tokens with batch operations that significantly reduce gas costs. While ERC-20 and ERC-721 remain optimal for single-purpose applications, ERC-1155 dominates scenarios requiring multiple token types, particularly in gaming and complex digital asset ecosystems.

Copyright © 2025

Akash Vaghela