---
eip: -
title: Asset Collections
author: Nathan Sala (@3esmit)
discussions-to: 
status: Draft
type: Standards Track
category: ERC
created: 12-06-2020
requires: 
---

## Simple Summary

An extension for ERC-1155 to support collections of fungible and non-fungbile tokens.

## Abstract

This proposal attempts to rationalize the co-existence of fungible and non-fungible tokens
(of potentially different collections) within a single `ERC-1155` contract.


We consider that there 3 types of identifiers:
 * Fungible Token identifiers, each representing a set of Fungible Tokens,
 * Non-Fungible Collection identifiers, each representing a set of Non-Fungible Tokens,
 * (c) Non-Fungible Token identifiers.

[EIP-721]
[EIP-1155]
[EIP-1178]
[EIP-1203]
Some contracts are designed to be Singletons which have the same address no matter what chain they are, which means that should exist one instance for all, such as [EIP-1820] and [EIP-2429]. These contracts are usually deployed using a method known as [Nick]'s method, so anyone can deploy those contracts on any chain and they have a deterministic address.
This standard proposes the creation of a CREATE2 factory using this method, so other projects requiring this feature can use this factory in any chain with the same setup, even in development chains.    

## Motivation

While ERC1155 is capable of supporting both fungible and non-fungible tokens, it falls short of usability when it comes to non-fungible tokens management. Firstly, the standard doesn't provide a way to tell whether a particular token is fungible or non-fungible. This information is important for interacting with the standard efficiently as fungibles and non-fungibles represent different concepts and some applications need to represent them differently (for example a marketplace).



## Specification

### Asset Collections

> This is an exact copy of the code of the [ERC2470 factory smart contract].

```solidity
pragma solidity 0.6.8;

interface IERC1155Inventory {
    /**
     * @dev Returns whether or not an ID represents a Fungible Token.
     * @param id The ID to query.
     * @return bool true if id represents a Fungible Token, false otherwise.
     */
    function isFungible(uint256 id) external view returns (bool);

    /**
     * @dev Returns the parent collection ID of a Non-Fungible Token ID.
     * This function returns either a Fungible Token ID or a Non-Fungible Collection ID.
     * This function SHOULD NOT be used to check the existence of a Non-Fungible Token.
     * This function MAY return a value for a non-existing Non-Fungible Token.
     * @param id The ID to query. id must represent an existing/non-existing Non-Fungible Token, else it throws.
     * @return uint256 the parent collection ID.
     */
    function collectionOf(uint256 id) external view returns (uint256);

    /**
     * @dev Returns the owner of a Non-Fungible Token.
     * @param nftId The identifier to query. MUST represent an existing Non-Fungible Token, else it throws.
     * @return owner address currently marked as the owner of the Non-Fungible Token.
     */
    function ownerOf(uint256 nftId) external view returns (address owner);

    /**
     * @dev Checks the existence of an Non-Fungible Token
     * @return bool true if the token belongs to a non-zero address, false otherwise
     */
    function exists(uint256 nftId) external view returns (bool);
}
```

### Deployment Transaction

Below is the raw transaction which MUST be used to deploy the smart contract on any chain.

```
0xf9016c8085174876e8008303c4d88080b90154608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c634300060200331b83247000822470
```

The strings of `2470`'s at the end of the transaction are the `r` and `s` of the signature.
From this deterministic pattern (generated by a human), anyone can deduce that no one knows the private key for the deployment account.

### Deployment Method

This contract is going to be deployed using the keyless deployment method---also known as [Nick]'s method---which relies on a single-use address.
(See [Nick's article] for more details). This method works as follows:

1. Generate a transaction which deploys the contract from a new random account.
  - This transaction MUST NOT use [EIP-155] in order to work on any chain.
  - This transaction MUST have a relatively high gas price to be deployed on any chain. In this case, it is going to be 100 Gwei.

2. Forge a transaction with the following parameters:
    ```js
    {
        nonce: 0,
        gasPrice: 100000000000,
        value: 0,
        data: '0x608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c63430006020033',
        gasLimit: 247000,
        v: 27,
        r: '0x247000',
        s: '0x2470'
    }
    ```
    > The `r` and `s` values, made of starting `2470`, are obviously a human determined value, instead of a real signature.

3. We recover the sender of this transaction, i.e., the single-use deployment account.

    > Thus we obtain an account that can broadcast that transaction, but we also have the warranty that nobody knows the private key of that account.

4. Send exactly 0.0247 ether to this single-use deployment account.

5. Broadcast the deployment transaction.

    > Note: 247000 is the double of gas needed to deploy the smart contract, this ensures that future changes in OPCODE pricing are unlikely to cause this deploy transction to fail out of gas. A left over will sit in the address of about 0.01 ETH will be forever locked in the single use address. 

The resulting transaction hash is `0x803351deb6d745e91545a6a3e1c0ea3e9a6a02a1a4193b70edfcd2f40f71a01c`.

This operation can be done on any chain, guaranteeing that the contract address is always the same and nobody can use that address with a different contract.


### Single-use Factory Deployment Account

![](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAB0UlEQVR4nO3asW1CQRBAQdpyCa6CIpxTjgujDGTJNEC2QqvjTbDx33c3P7vL79f1fzLf98dobn8/o5nuP53p/tPzm+5/AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4CMBnH6B0/23L2AbEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ8JYPsCtw+w3g9AvB+AeD8A8X4A4v0AxPsBiPcDEO8HIN4PQLwfgHg/APF+AOL9AMT7AYj3AxDvP/5ByOkApt/PvwgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgJYDtA9w+gO0fYHsAAGB/CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAdALYfNExnun+9H4B4PwDxfgDi/QDE+wGI9wMQ7wcg3g9AvB+AeD8A8X4A4v0AxPsBiPcDEO8HIN4/fhCy/aDidADb5wcAAGcHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO8CsH2ApwPY/j4Ah+8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPB6nlegoDNgrfyiAAAAAElFTkSuQmCC)

`0xBb6e024b9cFFACB947A71991E386681B1Cd1477D`

This account is generated by reverse engineering it from its signature for the transaction. 
This way no one knows the private key, but it is known that it is the valid signer of the deployment transaction.

> To deploy the registry, 0.0247 ether MUST be sent to this account *first*.

### Factory Contract Address
![](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABn0lEQVR4nO3coRECMRRF0S2GutCUQzd4WqAMLB4qQGWYP+EecXXeZo/OcTrf35Ndbq+l7F/rmB6w+wXuvh+A+H4A4vsBiO8HIL4fgPh+AOL7AYjvByC+H4D4fgDi+wGI7wcgvh+A+H4A4vuXAUxfwPX5GG33+wMAgL0/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPgGYHrA9A+cbhoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wlgesD0+bvvXz0fgM33AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8ATB9gZoNgHgAxAMgHgDxAIgHQDwA4gEQD4B4AMQDIB4A8QCIB0A8AOId0w8caK3V/wfA5gEQD4B4AMQDIB4A8QCIB0A8AOIBEA+AeADEAyAeAPEAiAdAPADiARAPgHgAxAMgHgDxAIgHQDwA4gEQD4B4AMQDIB4A8QCItwxg+oECDT8QMT1AAAgAASAABIAAEAACQAAIAAEgAASAANAv+gDxVDRR1CVqRAAAAABJRU5ErkJggg==)

`0xce0042B868300000d44A59004Da54A005ffdcf9f`

The contract has the address above for every chain on which it is deployed.
### ABI for SingletonFactory:
```json
[
    {
        "constant": false,
        "inputs": [
            {
                "internalType": "bytes",
                "name": "_initCode",
                "type": "bytes"
            },
            {
                "internalType": "bytes32",
                "name": "_salt",
                "type": "bytes32"
            }
        ],
        "name": "deploy",
        "outputs": [
            {
                "internalType": "address payable",
                "name": "createdContract",
                "type": "address"
            }
        ],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
    }
]
```

## Rationale

SingletonFactory does not allow sending value on create2, this was done to prevent different results on the created object. 
SingletonFactory allows user defined salt to facilitate the creation of vanity addresses for other projects. If vanity address is not necessary, salt `bytes(0)` should be used.
Contracts that are constructed by the SingletonFactory MUST not use `msg.sender` in their constructor, all variables must came through initialization data. This is intentional, as if allowing a callback after creation to aid initialization state would lead to contracts with same address (but different chains) to have the same address but different initial state.
The resulting address can be calculated in chain by any contract using this formula: `address(keccak256(bytes1(0xff), 0xce0042B868300000d44A59004Da54A005ffdcf9f, _salt, keccak256(_code)) << 96)` or in javascript using https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/README.md#const-generateaddress2.

## Backwards Compatibility

Does not apply as there are no past versions of Singleton Factory being used.

## Test Cases

TBD

## Implementation

https://github.com/3esmit/ERC2470

## Security Considerations

Some contracts can possibly not support being deployed on any chain, or require a different address per chain, that can be safely done by using comparison in [EIP-1344] in constructor.
Account contracts are singletons in the point of view of each user, when wallets want to signal what chain id is intended, [EIP-1191] should be used. 
Contracts deployed on factory must not use `msg.sender` in constructor, instead use constructor parameters, otherwise the factory would end up being the controller/only owner of those. 


## Bibliography
### Related EIPs
 <!-- - [EIP-20: ERC-20 Token Standard (a.k.a. ERC-20)](https://eips.ethereum.org/EIPS/eip-20) -->
 - [EIP-165: Standard Interface Detection](https://eips.ethereum.org/EIPS/eip-165)
 - [EIP-721: Non-Fungible Token Standard(a.k.a. ERC-721)](https://eips.ethereum.org/EIPS/eip-721)
 - [EIP-777: A New Advanced Token Standard](https://eips.ethereum.org/EIPS/eip-777)
 - [EIP-1155: Multi Token Standard(a.k.a. ERC-1155)](https://eips.ethereum.org/EIPS/eip-1155)
 - [EIP-1178: Multi-class Token Standard](https://eips.ethereum.org/EIPS/eip-1178)
 - [EIP-1203: Multi-class Token Standard(ERC-20 Extension)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1203.md)

### Worthnoting Projects
 - [Ethereum DAO: How to build a DEMOCRACY on the blockchain](https://www.ethereum.org/dao)
 - [Carbon Vote](http://carbonvote.com/)
 - [Paper: A Smart Contract for Boardroom Voting with Maximum Voter Privacy](https://eprint.iacr.org/2017/110.pdf) - *Suggested by @aodhgan*
 - [Private Voting for TCR](https://blog.enigma.co/private-voting-for-tcrs-with-enigma-b441b5d4fa7b)
 - [Consensus/PLCR implementation](https://github.com/ConsenSys/PLCRVoting)
 - [Aragon Voting App](https://wiki.aragon.org/dev/apps/voting/)

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

[EIP-155]: https://eips.ethereum.org/EIPS/eip-155
[EIP-1191]: https://eips.ethereum.org/EIPS/eip-1191
[EIP-1344]: https://eips.ethereum.org/EIPS/eip-1344
[EIP-1820]: https://eips.ethereum.org/EIPS/eip-1820
[EIP-2429]: https://gitlab.com/status-im/docs/EIPs/blob/secret-multisig-recovery/EIPS/eip-2429.md
[Nick's article]: https://medium.com/@weka/how-to-send-ether-to-11-440-people-187e332566b7
[Nick]: https://github.com/Arachnid/

