# OpenZeppelin ERC-1155

## EIP-1155

{% embed url="<https://eips.ethereum.org/EIPS/eip-1155>" %}
EIP-1155
{% endembed %}

### Simple Summary

EIP-1155 is a standard interface for contracts that manage <mark style="color:red;">**multiple token types**</mark>. A single deployed contract may include any combination of fungible tokens, non-fungible tokens or other configurations (e.g. semi-fungible tokens).

### Abstract

This standard outlines a smart contract interface that can represent any number of fungible and non-fungible token types. Existing standards such as ERC-20 require deployment of separate contracts per token type. The ERC-721 standard's token ID is a single non-fungible index and the group of these non-fungibles is deployed as a single contract with settings for the entire collection. <mark style="color:red;">**In contrast, the ERC-1155 Multi Token Standard allows for each token ID to represent a new configurable token type, which may have its own metadata, supply and other attributes.**</mark>

The `_id` argument contained in each function’s argument set indicates a specific token or token type in a transaction.

### Motivation

Tokens standards like ERC-20 and ERC-721 require a separate contract to be deployed for each token type or collection. This places a lot of redundant bytecode on the Ethereum blockchain and limits certain functionality by the nature of separating each token contract into its own permissioned address. With the rise of blockchain games and platforms like Enjin Coin, game developers may be creating thousands of token types, and a new type of token standard is needed to support them. However, ERC-1155 is not specific to games and many other applications can benefit from this flexibility.

New functionality is possible with this design such as transferring multiple token types at once, saving on transaction costs. Trading (escrow / atomic swaps) of multiple tokens can be built on top of this standard and it removes the need to “approve” individual token contracts separately. It is also easy to describe and mix multiple fungible or non-fungible token types in a single contract.

## OpenZeppelin ERC1155 Doc

{% embed url="<https://docs.openzeppelin.com/contracts/3.x/erc1155>" %}
ERC1155
{% endembed %}

ERC1155 is a novel token standard that aims to take the best from previous standards to create a [**fungibility-agnostic**](https://docs.openzeppelin.com/contracts/3.x/tokens#different-kinds-of-tokens) and **gas-efficient** [token contract](https://docs.openzeppelin.com/contracts/3.x/tokens#but_first_coffee_a_primer_on_token_contracts).

### Multi Token Standard <a href="#multi-token-standard" id="multi-token-standard"></a>

<mark style="color:red;">**The distinctive feature of ERC1155 is that it uses a single smart contract to represent multiple tokens at once.**</mark> This is why its [`balanceOf`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#IERC1155-balanceOf-address-uint256-) function differs from ERC20’s and ERC777’s: it has an additional `id` argument for the identifier of the token that you want to query the balance of.

This is similar to how ERC721 does things, but in that standard a token `id` has no concept of balance: each token is non-fungible and exists or doesn’t. The ERC721 [`balanceOf`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC721#IERC721-balanceOf-address-) function refers to *how many different tokens* an account has, not how many of each. On the other hand, in ERC1155 accounts have a distinct balance for each token `id`, and non-fungible tokens are implemented by simply minting a single one of them.

This approach leads to massive gas savings for projects that require multiple tokens. <mark style="color:red;">**Instead of deploying a new contract for each token type, a single ERC1155 token contract can hold the entire system state, reducing deployment costs and complexity.**</mark>

### Batch Operations <a href="#batch-operations" id="batch-operations"></a>

Because all state is held in a single contract, it is possible to operate over multiple tokens in a single transaction very efficiently. The standard provides two functions, [`balanceOfBatch`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#IERC1155-balanceOfBatch-address---uint256---) and [`safeBatchTransferFrom`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#IERC1155-safeBatchTransferFrom-address-address-uint256---uint256---bytes-), that make querying multiple balances and transferring multiple tokens simpler and less gas-intensive.

In the spirit of the standard, we’ve also included batch operations in the non-standard functions, such as [`_mintBatch`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#ERC1155-_mintBatch-address-uint256---uint256---bytes-).

### Constructing an ERC1155 Token Contract <a href="#constructing_an_erc1155_token_contract" id="constructing_an_erc1155_token_contract"></a>

We’ll use ERC1155 to track multiple items in our game, which will each have their own unique attributes. We mint all items to the deployer of the contract, which we can later transfer to players. Players are free to keep their tokens or trade them with other people as they see fit, as they would any other asset on the blockchain!

For simplicity we will mint all items in the constructor but you could add minting functionality to the contract to mint on demand to players.

Here’s what a contract for tokenized items might look like:

```solidity
// contracts/GameItems.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";

contract GameItems is ERC1155 {
    uint256 public constant GOLD = 0;
    uint256 public constant SILVER = 1;
    uint256 public constant THORS_HAMMER = 2;
    uint256 public constant SWORD = 3;
    uint256 public constant SHIELD = 4;

    constructor() public ERC1155("https://game.example/api/item/{id}.json") {
        _mint(msg.sender, GOLD, 10**18, "");
        _mint(msg.sender, SILVER, 10**27, "");
        _mint(msg.sender, THORS_HAMMER, 1, "");
        _mint(msg.sender, SWORD, 10**9, "");
        _mint(msg.sender, SHIELD, 10**9, "");
    }
}
```

Note that for our Game Items, Gold is a fungible token whilst Thor’s Hammer is a non-fungible token as we minted only one.

The [`ERC1155`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#ERC1155) contract includes the optional extension [`IERC1155MetadataURI`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#IERC1155MetadataURI). That’s where the [`uri`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#IERC1155MetadataURI-uri-uint256-) function comes from: we use it to retrieve the metadata uri.

Also note that, unlike ERC20, ERC1155 lacks a `decimals` field, since each token is distinct and cannot be partitioned.

Once deployed, we will be able to query the deployer’s balance:

```solidity
> gameItems.balanceOf(deployerAddress,3)
1000000000
```

We can transfer items to player accounts:

```solidity
> gameItems.safeTransferFrom(deployerAddress, playerAddress, 2, 1, "0x0")
> gameItems.balanceOf(playerAddress, 2)
1
> gameItems.balanceOf(deployerAddress, 2)
0
```

We can also batch transfer items to player accounts and get the balance of batches:

```solidity
> gameItems.safeBatchTransferFrom(deployerAddress, playerAddress, [0,1,3,4], [50,100,1,1], "0x0")
> gameItems.balanceOfBatch([playerAddress,playerAddress,playerAddress,playerAddress,playerAddress], [0,1,2,3,4])
[50,100,1,1,1]
```

The metadata uri can be obtained:

```solidity
> gameItems.uri(2)
"https://game.example/api/item/{id}.json"
```

The `uri` can include the string `{id}` which clients must replace with the actual token ID, in lowercase hexadecimal (with no 0x prefix) and leading zero padded to 64 hex characters.

For token ID `2` and uri `https://game.example/api/item/{id}.json` clients would replace `{id}` with `0000000000000000000000000000000000000000000000000000000000000002` to retrieve JSON at [`https://game.example/api/item/0000000000000000000000000000000000000000000000000000000000000002.json`](https://game.example/api/item/0000000000000000000000000000000000000000000000000000000000000002.json).

The JSON document for token ID 2 might look something like:

```solidity
{
    "name": "Thor's hammer",
    "description": "Mjölnir, the legendary hammer of the Norse god of thunder.",
    "image": "https://game.example/item-id-8u5h2m.png",
    "strength": 20
}
```

For more information about the metadata JSON Schema, check out the [ERC-1155 Metadata URI JSON Schema](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md#erc-1155-metadata-uri-json-schema).

{% hint style="info" %}
**Note**\
you’ll notice that the item’s information is included in the metadata, but that information isn’t on-chain! So a game developer could change the underlying metadata, changing the rules of the game!
{% endhint %}

### Sending Tokens to Contracts <a href="#sending-to-contracts" id="sending-to-contracts"></a>

A key difference when using [`safeTransferFrom`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#IERC1155-safeTransferFrom-address-address-uint256-uint256-bytes-) is that token transfers to other contracts may revert with the following message:

```
ERC1155: transfer to non ERC1155Receiver implementer
```

This is a good thing! It means that the recipient contract has not registered itself as aware of the ERC1155 protocol, so transfers to it are disabled to <mark style="color:red;">**prevent tokens from being locked forever**</mark>. As an example, [the Golem contract currently holds over 350k `GNT` tokens](https://etherscan.io/token/0xa74476443119A942dE498590Fe1f2454d7D4aC0d?a=0xa74476443119A942dE498590Fe1f2454d7D4aC0d), worth multiple tens of thousands of dollars, and lacks methods to get them out of there. This has happened to virtually every ERC20-backed project, usually due to user error.

In order for our contract to receive ERC1155 tokens we can inherit from the convenience contract [`ERC1155Holder`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#ERC1155Holder) which handles the registering for us. Though we need to remember to implement functionality to allow tokens to be transferred out of our contract:

```solidity
// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC1155/ERC1155Holder.sol";

contract MyContract is ERC1155Holder {
}
```

We can also implement more complex scenarios using the [`onERC1155Received`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#IERC1155Receiver-onERC1155Received-address-address-uint256-uint256-bytes-) and [`onERC1155BatchReceived`](https://docs.openzeppelin.com/contracts/3.x/api/token/ERC1155#IERC1155Receiver-onERC1155BatchReceived-address-address-uint256---uint256---bytes-) functions.

### Preset ERC1155 contract <a href="#presets" id="presets"></a>

A preset ERC1155 is available, [`ERC1155PresetMinterPauser`](https://docs.openzeppelin.com/contracts/3.x/api/presets#ERC1155PresetMinterPauser). It is preset to allow for token minting (create) - including batch minting, stop all token transfers (pause) and allow holders to burn (destroy) their tokens. The contract uses [Access Control](https://docs.openzeppelin.com/contracts/3.x/access-control) to control access to the minting and pausing functionality. The account that deploys the contract will be granted the minter and pauser roles, as well as the default admin role.

This contract is ready to deploy without having to write any Solidity code. It can be used as-is for quick prototyping and testing, but is also suitable for production environments.

## OpenZeppelin Implementation

{% embed url="<https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.2/contracts/token/ERC1155/ERC1155.sol>" %}
ERC1155 - OpenZeppelin
{% endembed %}

{% hint style="warning" %}
**Note**\
ERC1155 does not have `transferFrom()`. Only `safeTransferFrom()` exists.
{% endhint %}

### safeTransferFrom()

```solidity
    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function _safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();
        uint256[] memory ids = _asSingletonArray(id);
        uint256[] memory amounts = _asSingletonArray(amount);

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;

        emit TransferSingle(operator, from, to, id, amount);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);
    }
```

`_beforeTokenTransfer()`, `_afterTokenTransfer()` and `_doSafeTransferAcceptanceCheck()` are called <mark style="color:red;">**"hooks"**</mark>. These are empty functions waiting for developers to implement.

### safeBatchTransferFrom()

```solidity
    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function _safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        address operator = _msgSender();

        _beforeTokenTransfer(operator, from, to, ids, amounts, data);

        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);

        _afterTokenTransfer(operator, from, to, ids, amounts, data);

        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);
    }
```

This `require` statement:

```solidity
require(ids.length == amounts.length);
```

checks if both arrays have the same length so that the loop works. Otherwise there will be out-of-bound read in `ids` or `amounts`. If this restriction is missing then it is an audit finding. The rest is just the logic of `_safeTransferFrom()` wrapped in a loop.

Note that every operation has its corresponding batch version in this contract and the "loop logic" is similar.
