Protocol Standard

Simple Summary

A standard interface for contracts that manage multiple token types. A single deployed contract may include any combination of fungible tokens, non-fungible tokens or other configurations (e.g. semi-fungible tokens).

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.

Abstract

Smart contracts implementing the VRC-1155 standard MUST implement all of the functions in the VRC1155 interface.

Smart contracts implementing the VRC-1155 standard MUST implement the VRC-165 supportsInterface function and MUST return the constant value true if 0xd9b67a26 is passed through the interfaceID argument.

MUST implement

Interfaces

Smart contracts implementing the VRC-1155 standard MUST implement all of the functions in the VRC1155 interface.

Smart contracts implementing the VRC-1155 standard MUST implement the VRC-165 supportsInterface function and MUST return the constant value true if 0xd9b67a26 is passed through the interfaceID argument.

pragma solidity ^0.5.9;

interface VRC1155 /* is VRC165 */ {

    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 _owner, address indexed _operator, bool _approved);
    event URI(string _value, uint256 indexed _id);

    function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;

    function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external;

    function balanceOf(address _owner, uint256 _id) external view returns (uint256);

    function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);

    function setApprovalForAll(address _operator, bool _approved) external;

    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

VRC-1155 Token Receiver

Smart contracts MUST implement all of the functions in the VRC1155TokenReceiver interface to accept transfers. See "Safe Transfer Rules" for further detail.

Smart contracts MUST implement the VRC-165 supportsInterface function and signify support for the VRC1155TokenReceiver interface to accept transfers. See "VRC1155TokenReceiver VRC-165 rules" for further detail.

pragma solidity ^0.5.9;

interface VRC1155TokenReceiver {
 
    function onVRC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4);
    function onVRC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4);       
}

check the token receive must implement the flow interface.

function supportsInterface(bytes4 interfaceID) external view returns (bool) {
    return  interfaceID == 0x01ffc9a7 ||    // VRC-165 support (i.e. `bytes4(keccak256('supportsInterface(bytes4)'))`).
            interfaceID == 0xd37fbeb15;      // VRC-1155 `VRC1155TokenReceiver` support (i.e. `bytes4(keccak256("onVRC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onVRC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`).
}

Safe Transfer Rules

safeTransferFrom rules:

  • Caller must be approved to manage the tokens being transferred out of the _from account (see "Approval" section).
  • MUST revert if _to is the zero address.
  • MUST revert if balance of holder for token _id is lower than the _value sent to the recipient.
  • MUST revert on any other error.
  • MUST emit the TransferSingle event to reflect the balance change (see "TransferSingle and TransferBatch event rules" section).
  • After the above conditions are met, this function MUST check if _to is a smart contract (e.g. code size > 0). If so, it *MUST call onVRC1155Received on _to and act appropriately (see "onVRC1155Received rules" section).
  • The _data argument provided by the sender for the transfer MUST be passed with its contents unaltered to the onVRC1155Received hook function via its _data argument.

Metadata

The URI value allows for ID substitution by clients. If the string {id} exists in any URI, clients MUST replace this with the actual token ID in hexadecimal form. This allows for a large number of tokens to use the same on-chain string by defining a URI once, for that large number of tokens.

  • The string format of the substituted hexadecimal ID MUST be lowVRCase alphanumeric: [0-9a-f] with no 0x prefix.
  • The string format of the substituted hexadecimal ID MUST be leading zero padded to 64 hex characters length if necessary.

Example of such a URI: https://token-cdn-domain/{id}.json would be replaced with https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json if the client is referring to token ID 314592/0x4CCE0.

Metadata Extensions

pragma solidity ^0.5.9;
interface VRC1155Metadata_URI {
    function uri(uint256 _id) external view returns (string memory);
}

VRC-1155 Metadata URI JSON Schema

This JSON schema is loosely based on the "VRC721 Metadata JSON Schema", but includes optional formatting to allow for ID substitution by clients. If the string {id} exists in any JSON value, it MUST be replaced with the actual token ID, by all client software that follows this standard.

{
    "title": "Token Metadata",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "description": "Identifies the asset to which this token represents",
        },
        "decimals": {
            "type": "integer",
            "description": "The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation.",
        },
        "description": {
            "type": "string",
            "description": "Describes the asset to which this token represents",
        },
        "image": {
            "type": "string",
            "description": "A URI pointing to a resource with mime type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive.",
        },
        "properties": {
            "type": "object",
            "description": "Arbitrary properties. Values may be strings, numbers, object or arrays.",
        },
    }
}