Final ERC standard. A single contract holds many token IDs, each with its own supply — fungible, non-fungible, or semi-fungible — with native batch transfer support. Originally designed for game items.
- 01game items and equipment
- 02edition-based NFT drops
- 03semi-fungible tickets / vouchers
- 04batch airdrops
- 05fractional or tiered access tokens
- pnpm add @openzeppelin/contracts
Implement ERC-1155 by extending `ERC1155` from `@openzeppelin/contracts/token/ERC1155/ERC1155.sol` (or Solady's `ERC1155`). Core interface (`IERC1155`): `balanceOf(address,uint256)`, `balanceOfBatch(address[],uint256[])`, `safeTransferFrom(from,to,id,amount,data)`, `safeBatchTransferFrom`, `setApprovalForAll`, `isApprovedForAll`. Metadata via `uri(uint256 id)` — convention is to embed `{id}` placeholder so a single base URI serves all IDs. Mint with `_mint(to, id, amount, data)` and `_mintBatch`. Recipient contracts MUST implement `IERC1155Receiver` (`onERC1155Received` / `onERC1155BatchReceived`) or transfers revert. Always implement `supportsInterface` (ERC-165).
- ⚑There is no per-token `decimals()` and no enforcement of fungible vs non-fungible — treat ID semantics as a contract convention, not a guarantee.
- ⚑Approval is all-or-nothing across every ID via `setApprovalForAll` — there's no per-ID approval like ERC-721's `approve`.
- ⚑Sending to an EOA always works, but sending to a contract that doesn't implement `IERC1155Receiver` reverts. Many legacy multisigs and timelocks are not 1155-aware.
- ⚑The `{id}` substitution in `uri()` expects 64-character lowercase hex with no `0x` prefix — easy to get wrong and break marketplaces.
- ⚑Batch transfers are atomic: one bad ID or insufficient balance reverts the whole batch. Validate inputs before calling on-chain.