TRC-1155 protocol interfaces

Full reference for every function and event in the TRC-1155 standard, plus the receiver and metadata extensions.

📘

Prerequisites

This page is the function-and-event reference for TRC-1155. TRC-1155 lets a single contract represent and manage many fungible, non-fungible, and semi-fungible tokens, with batch operations for transfers and approvals.

📘

ERC-1155 compatibility

TRC-1155 uses the same selectors as Ethereum's ERC-1155: 0xf23a6e61 for onERC1155Received and 0xbc197c81 for onERC1155BatchReceived. ERC-1155 contracts can be ported to TRON without changing the receiver hook return values. (This is different from TRC-721, which uses TRON-specific hash 0x5175f878.)

Required interfaces

A TRC-1155 contract must implement the TRC-1155 and TRC-165 interfaces:

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

    // Required functions
    function setApprovalForAll(address _operator, bool _approved) external;
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
    function balanceOf(address _owner, uint256 _id) external view returns (uint256);
    function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);
    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;
}

interface ITRC165 {
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

Function reference

FunctionPurpose
setApprovalForAll(address _operator, bool _approved)Authorize or revoke _operator to transfer every token under this contract on behalf of the caller. Emits ApprovalForAll.
isApprovedForAll(address _owner, address _operator)Returns true if _operator is currently approved to manage every token of _owner.
balanceOf(address _owner, uint256 _id)Returns the balance of token _id held by _owner.
balanceOfBatch(address[] _owners, uint256[] _ids)Returns balances for many (owner, id) pairs in one call. The two arrays must be the same length, or the call reverts.
safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes _data)Transfer _value of token _id from _from to _to. If _to is a contract, calls onERC1155Received and checks the return value.
safeBatchTransferFrom(address _from, address _to, uint256[] _ids, uint256[] _values, bytes _data)Batch version of safeTransferFrom. Calls onERC1155BatchReceived if _to is a contract.

Event reference

EventFires when
TransferSingle(_operator, _from, _to, _id, _value)A single-token transfer succeeds. Also fires on mint (_from = 0x0) and burn (_to = 0x0).
TransferBatch(_operator, _from, _to, _ids, _values)A batch transfer succeeds.
ApprovalForAll(_owner, _operator, _approved)setApprovalForAll toggles the operator.
URI(_value, _id)The metadata URI for token _id is updated to _value.

Receive hooks

A contract receiving TRC-1155 tokens via safeTransferFrom or safeBatchTransferFrom must implement TRC1155TokenReceiver:

interface TRC1155TokenReceiver {
    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);
}

onERC1155Received(...)

Called when safeTransferFrom sends tokens to a contract. The contract must return:

0xf23a6e61  // = bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))

Returning any other value causes the transfer to revert.

onERC1155BatchReceived(...)

Called when safeBatchTransferFrom sends tokens to a contract. The contract must return:

0xbc197c81  // = bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))

TRC-165 supportsInterface

A contract implementing TRC1155TokenReceiver should also implement TRC-165 introspection:

function supportsInterface(bytes4 interfaceID) external view returns (bool) {
    return interfaceID == 0x01ffc9a7   // TRC-165 itself
        || interfaceID == 0x4e2312e0;  // TRC1155TokenReceiver
}
interfaceIDWhat it identifies
0x01ffc9a7TRC-165 — bytes4(keccak256("supportsInterface(bytes4)"))
0x4e2312e0TRC1155TokenReceiver — XOR of the two hook selectors

Metadata extension (optional)

TRC-1155 contracts may expose a per-token metadata URI through the URI event and an optional uri(uint256) function. The URI string can include the token id substitution pattern {id}, which clients replace with the lowercase hex representation of the token id (zero-padded to 64 characters).

interface ITRC1155Metadata_URI {
    function uri(uint256 _id) external view returns (string memory);
}

The metadata JSON pointed to by the URI follows a schema similar to TRC-721 metadata — name, description, image, and any custom attributes.


Reference implementation snippets (non-standard, OpenZeppelin-derived)

The standard above only specifies the protocol interface. The contract logic that backs these interfaces — including mint helpers, internal balance accounting, and the safe-transfer acceptance checks — is implementation-defined. The snippets below are common patterns drawn from OpenZeppelin's reference TRC-1155 implementation.

Internal storage layout

contract TRC1155 is ITRC1155, ITRC165 {
    // id => (owner => balance)
    mapping (uint256 => mapping(address => uint256)) internal balances;

    // owner => (operator => approved)
    mapping (address => mapping(address => bool)) internal operatorApproval;
}

setApprovalForAll implementation

function setApprovalForAll(address _operator, bool _approved) external {
    operatorApproval[msg.sender][_operator] = _approved;
    emit ApprovalForAll(msg.sender, _operator, _approved);
}

safeTransferFrom with receiver-hook check

📘

Note

The following code demonstrates the core transfer and minting flows; it is not a complete, independently compilable contract. It depends on storage mappings, access modifiers, and helper functions from the full implementation, including isContract, isFungible, isNonFungible, and _doSafeTransferAcceptanceCheck.

bytes4 constant public TRC1155_ACCEPTED = 0xf23a6e61;
bytes4 constant internal TRC1155_BATCH_ACCEPTED = 0xbc197c81;

function safeTransferFrom(
    address _from,
    address _to,
    uint256 _id,
    uint256 _value,
    bytes calldata _data
) external {
    require(_to != address(0x0), "_to cannot be the zero address");
    require(_from == msg.sender || operatorApproval[_from][msg.sender] == true, "Need operator approval");

    balances[_id][_from] = balances[_id][_from] - _value;
    balances[_id][_to]   = balances[_id][_to]   + _value;

    emit TransferSingle(msg.sender, _from, _to, _id, _value);

    // Call onERC1155Received if the destination is a contract.
    if (isContract(_to)) {
        require(
            TRC1155TokenReceiver(_to).onERC1155Received(msg.sender, _from, _id, _value, _data) == TRC1155_ACCEPTED,
            "contract returned an unknown value from onERC1155Received"
        );
    }
}

mintFungible helper (non-standard extension)

mintFungible is not part of the TRC-1155 standard — minting is up to the implementer. The pattern below is a common one for fungible-token mints under TRC-1155:

function mintFungible(uint256 _id, address[] calldata _to, uint256[] calldata _quantities) external creatorOnly(_id) {
    require(isFungible(_id));

    for (uint256 i = 0; i < _to.length; ++i) {
        address to = _to[i];
        uint256 quantity = _quantities[i];

        balances[_id][to] = quantity + balances[_id][to];

        // 0x0 source address indicates a mint.
        emit TransferSingle(msg.sender, address(0x0), to, _id, quantity);

        if (isContract(to)) {
            _doSafeTransferAcceptanceCheck(msg.sender, msg.sender, to, _id, quantity, '');
        }
    }
}

mintNonFungible helper (non-standard extension)

mintNonFungible is similarly an implementer's pattern — it mints one NFT for each address in the list:

function mintNonFungible(uint256 _type, address[] calldata _to) external creatorOnly(_type) {
    require(isNonFungible(_type));

    uint256 index = maxIndex[_type] + 1;
    maxIndex[_type] = _to.length + maxIndex[_type];

    for (uint256 i = 0; i < _to.length; ++i) {
        address dst = _to[i];
        uint256 id  = _type | index + i;

        nfOwners[id] = dst;

        emit TransferSingle(msg.sender, address(0x0), dst, id, 1);

        if (isContract(dst)) {
            _doSafeTransferAcceptanceCheck(msg.sender, msg.sender, dst, id, 1, '');
        }
    }
}

Related resources