ERC-1155 is the Ethereum multi-token standard that allows a single smart contract to manage an unlimited number of fungible and non-fungible token types simultaneously. Proposed by Witek Radomski (co-founder of Enjin) in June 2018 via EIP-1155, it was designed to solve the inefficiency of deploying separate contracts for each token type. Whether you need in-game currencies, unique weapons, edition-based art, or event tickets, ERC-1155 handles them all from one contract with dramatically lower gas costs through native batch operations.
Why Was ERC-1155 Created?
Before ERC-1155, blockchain game developers faced a painful choice. If they wanted fungible in-game currency, they needed an ERC-20 contract. For unique items like legendary swords, they needed an ERC-721 contract. A game with 100 different item types would require 100 separate smart contract deployments, each consuming gas and adding complexity.
ERC-1155 eliminates this problem by introducing a multi-token architecture. A single contract can define token ID 1 as a fungible gold coin (with millions of copies), token ID 2 as a semi-fungible health potion (with 10,000 copies), and token ID 3 as a unique legendary item (with exactly one copy). All live in the same contract, share the same interface, and can be transferred together in a single transaction.
Beyond gaming, ERC-1155 has found widespread adoption in music NFT platforms (where artists sell multiple editions), event ticketing systems, membership passes, and DeFi protocols that need to track multiple position types.
ERC-20 vs ERC-721 vs ERC-1155
Understanding how the three major Ethereum token standards compare is essential for choosing the right one for your project:
| Feature | ERC-20 | ERC-721 | ERC-1155 |
|---|---|---|---|
| Fungibility | Fungible only | Non-fungible only | Both fungible and non-fungible |
| Tokens per contract | 1 token type | 1 collection (unique IDs) | Unlimited token types |
| Batch transfers | Not supported | Not built-in | Native support |
| Balance query | balanceOf(address) | balanceOf(address) / ownerOf(id) | balanceOf(address, id) |
| Gas efficiency | Moderate | Higher per transfer | Lowest (batch optimized) |
| Metadata | name, symbol, decimals | tokenURI per token | URI with {id} substitution |
| Best for | Currencies, governance | 1-of-1 art, PFPs | Gaming, editions, mixed |
| EIP number | EIP-20 (2015) | EIP-721 (2018) | EIP-1155 (2018) |
Batch Transfers and Gas Efficiency
The headline feature of ERC-1155 is batch operations. When you need to transfer 50 different items to another player in a game, ERC-721 would require 50 separate transactions, each paying a base gas cost of 21,000 gas plus the transfer logic. ERC-1155 does it in one transaction with a single base gas cost.
The gas savings are substantial. In benchmarks, transferring 10 different token types via ERC-1155 safeBatchTransferFrom costs roughly 50-80% less gas compared to 10 individual ERC-721 safeTransferFrom calls. This makes a meaningful difference when gas fees are high on Ethereum mainnet.
Gas comparison example: Transferring 20 tokens individually via ERC-721 costs approximately 1,400,000 gas. The same 20 tokens via ERC-1155 batch transfer costs approximately 350,000 gas — a 75% reduction.
The ERC-1155 Interface
Every ERC-1155 contract must implement these core functions and events:
Balance Functions
// Get the balance of a specific token ID for an address
function balanceOf(address account, uint256 id)
external view returns (uint256);
// Get balances for multiple account/token pairs in one call
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external view returns (uint256[] memory);The balanceOf function takes both an address and a token ID, since a single contract manages many token types. The balanceOfBatch function lets you query multiple balances in one call, avoiding multiple RPC requests.
Transfer Functions
// Transfer a single token type
function safeTransferFrom(
address from, address to,
uint256 id, uint256 amount, bytes calldata data
) external;
// Transfer multiple token types in one transaction
function safeBatchTransferFrom(
address from, address to,
uint256[] calldata ids, uint256[] calldata amounts,
bytes calldata data
) external;Both transfer functions include a data parameter for passing arbitrary bytes to the receiver, and both check that the recipient implements IERC1155Receiver if it is a contract. Unlike ERC-721, there is no "unsafe" transfer variant — all ERC-1155 transfers are safe by default.
Approval Functions
// Approve or revoke an operator for ALL token types
function setApprovalForAll(address operator, bool approved) external;
// Check if an operator is approved
function isApprovedForAll(address account, address operator)
external view returns (bool);ERC-1155 simplifies approvals compared to ERC-721. There is no per-token approval — operators are either approved for all token types or none. This is a deliberate design choice that reduces complexity and gas costs, though it means you cannot grant fine-grained permissions for individual token IDs.
Required Events
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);URI Metadata Pattern
ERC-1155 uses a URI-based metadata system with a powerful substitution mechanism. Instead of storing a separate URI per token (as ERC-721 does), ERC-1155 can use a single template URI with an {id} placeholder:
// The URI function returns metadata URL for a given token ID
function uri(uint256 id) external view returns (string memory);
// Example URI template:
// https://api.example.com/tokens/{id}.json
//
// For token ID 42 (hex: 0x000...002a), this resolves to:
// https://api.example.com/tokens/000...002a.json
//
// The {id} is substituted with the lowercase hex token ID,
// zero-padded to 64 characters (uint256 in hex)The metadata JSON for each token follows this schema:
{
"name": "Gold Coin",
"description": "In-game currency for purchasing items",
"image": "https://example.com/tokens/gold-coin.png",
"decimals": 18,
"properties": {
"type": "currency",
"rarity": "common"
}
}The optional decimals field is particularly useful for fungible token types, telling clients how to display the balance (similar to ERC-20 decimals). For non-fungible tokens where the supply is 1, the decimals field is typically omitted or set to 0.
Real-World Use Cases
Gaming Items
ERC-1155 was born from the gaming world, and it remains the dominant standard for blockchain games. A single game contract can manage gold coins (fungible, high supply), health potions (semi-fungible, limited supply), and legendary weapons (non-fungible, supply of 1). Players can trade bundles of items in one transaction, making marketplace experiences smooth and affordable.
Mixed Collections and Editions
Artists and creators use ERC-1155 to sell edition-based works. An artist might create token ID 1 as a "Gold Edition" print with 10 copies, token ID 2 as a "Silver Edition" with 100 copies, and token ID 3 as a "1-of-1 Original." All exist in the same contract, simplifying management and reducing deployment costs.
Semi-Fungible Tokens
Some tokens start as fungible and become non-fungible after an event. Concert tickets are a classic example: before the show, all floor section tickets are interchangeable. After the show, each ticket becomes a unique collectible tied to the specific event experience. ERC-1155 handles this transition naturally — the token ID remains the same, but the application layer can treat it differently based on state.
DeFi Position Tokens
Some DeFi protocols use ERC-1155 to represent liquidity positions, option contracts, or other financial instruments where multiple position types need to coexist in one contract. This avoids the overhead of deploying separate ERC-20 contracts for each position type.
ERC-1155 Implementation Example
Here is a practical ERC-1155 contract using OpenZeppelin that demonstrates a gaming item system with multiple token types:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract GameItems is ERC1155, Ownable {
// Token IDs
uint256 public constant GOLD = 0;
uint256 public constant SILVER = 1;
uint256 public constant SWORD = 2;
uint256 public constant SHIELD = 3;
uint256 public constant LEGENDARY_CROWN = 4;
// Track supply for limited items
mapping(uint256 => uint256) public maxSupply;
mapping(uint256 => uint256) public totalMinted;
constructor() ERC1155("https://game.example/api/item/{id}.json")
Ownable(msg.sender) {
// Fungible currencies - mint initial supply
_mint(msg.sender, GOLD, 10_000_000 * 1e18, "");
_mint(msg.sender, SILVER, 1_000_000 * 1e18, "");
// Set max supply for limited items
maxSupply[SWORD] = 10000;
maxSupply[SHIELD] = 5000;
maxSupply[LEGENDARY_CROWN] = 1; // Unique item
}
function mint(address to, uint256 id, uint256 amount)
external onlyOwner
{
require(
maxSupply[id] == 0 ||
totalMinted[id] + amount <= maxSupply[id],
"Exceeds max supply"
);
totalMinted[id] += amount;
_mint(to, id, amount, "");
}
function mintBatch(
address to,
uint256[] memory ids,
uint256[] memory amounts
) external onlyOwner {
for (uint256 i = 0; i < ids.length; i++) {
require(
maxSupply[ids[i]] == 0 ||
totalMinted[ids[i]] + amounts[i] <= maxSupply[ids[i]],
"Exceeds max supply"
);
totalMinted[ids[i]] += amounts[i];
}
_mintBatch(to, ids, amounts, "");
}
}The ERC-1155 Receiver Interface
Any smart contract that wants to receive ERC-1155 tokens must implement the IERC1155Receiver interface. This prevents tokens from being permanently locked in contracts that do not know how to handle them:
interface IERC1155Receiver {
function onERC1155Received(
address operator, address from,
uint256 id, uint256 value, bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator, address from,
uint256[] calldata ids, uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}Important: Both callback functions must return their respective function selectors (bytes4(keccak256("onERC1155Received(...)"))) to confirm successful receipt. Returning any other value or reverting will cause the transfer to fail.
Frequently Asked Questions
What is the difference between ERC-1155 and ERC-721?
ERC-721 creates one unique token per ID with a single owner, ideal for one-of-a-kind NFTs. ERC-1155 allows each token ID to have multiple copies (editions), supports both fungible and non-fungible tokens in a single contract, and includes native batch transfer operations that are significantly more gas efficient.
Why is ERC-1155 more gas efficient than ERC-721?
ERC-1155 achieves gas savings through batch operations. Instead of calling transfer once per token (as ERC-721 requires), safeBatchTransferFrom can move hundreds of different tokens in a single transaction. The contract also uses a more compact storage layout with a single mapping for balances across all token IDs.
What is a semi-fungible token?
A semi-fungible token starts as fungible (interchangeable with others of the same ID) but can become non-fungible after a certain event. For example, a concert ticket token might be fungible before the event but becomes a unique collectible after use. ERC-1155 naturally supports this pattern.
Can ERC-1155 replace both ERC-20 and ERC-721?
Technically yes, ERC-1155 can represent both fungible and non-fungible tokens in a single contract. However, ERC-20 and ERC-721 remain widely used because of their simplicity, tooling maturity, and universal support across wallets, exchanges, and DeFi protocols. ERC-1155 is best for applications needing multiple token types.
How does ERC-1155 metadata work?
ERC-1155 uses a URI-based metadata system where a single uri(uint256 id) function returns the metadata URL for any token ID. The standard supports a {id} substitution pattern in the URI string, allowing a single base URL to serve metadata for all tokens by replacing the placeholder with the hex-encoded token ID.
Explore ERC-1155 On-Chain
Use our ABI Encoder / Decoder to encode and decode ERC-1155 function calls like balanceOfBatch and safeBatchTransferFrom, or inspect raw transaction data with the Calldata Decoder.
Related Guides and Tools
- What is ERC-20? — The fungible token standard for cryptocurrencies
- What is ERC-721? — The non-fungible token standard for unique assets
- What is a Smart Contract? — Understand the foundation behind all token standards
- ABI Encoder / Decoder — Encode and decode ERC-1155 function calls
- Keccak256 Hash Generator — Compute function selectors and event signatures