---
updatedAt: 2026-09-18T09:50:40.000Z
agentTools:
  projectIndex: https://developers.tron.network/llms.txt
---

# TRC-721

TRC-721 is the TRON standard for non-fungible tokens (NFTs), fully compatible with ERC-721.

> 📘 **Prerequisites**
>
> * [Writing & compiling Solidity](https://developers.tron.network/docs/creating-and-compiling)
> * [Token standards overview](https://developers.tron.network/docs/token-standards-overview)

TRC-721 is the TRON standard for non-fungible tokens (NFTs). It defines a common interface every NFT contract must expose — balance queries, ownership lookups, transfers, and approval management — so that wallets, marketplaces, and explorers can interact with any TRC-721 contract without custom integration work.

TRC-721 is fully compatible with the Ethereum ERC-721 standard at the interface level. The only practical differences are the receiver-hook return value (see below) and the compiler and runtime environment (TVM instead of EVM). If you have an ERC-721 contract, porting it to TRON is mostly a matter of recompiling with the TRON toolchain.

## Required interfaces

Every TRC-721 compliant contract must implement both the `TRC-721` and `TRC-165` interfaces. `TRC-165` is the introspection interface — it lets a caller ask the contract "do you support this other interface?" without having to make a call that might revert.

```solidity
pragma solidity 0.5.10;

interface TRC721 {
    // Returns the number of NFTs owned by the given account
    function balanceOf(address _owner) external view returns (uint256);

    // Returns the owner of the given NFT
    function ownerOf(uint256 _tokenId) external view returns (address);

    // Transfer ownership of NFT (safe variant with data)
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;

    // Transfer ownership of NFT (safe variant)
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    // Transfer ownership of NFT (unsafe variant — caller is responsible for the destination)
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    // Grants the `_approved` address control of the NFT `_tokenId`
    function approve(address _approved, uint256 _tokenId) external payable;

    // Grants or revokes control of every NFT owned by the caller to `_operator`
    function setApprovalForAll(address _operator, bool _approved) external;

    // Returns the address currently approved for `_tokenId`
    function getApproved(uint256 _tokenId) external view returns (address);

    // Returns true if `_operator` is approved for all NFTs owned by `_owner`
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);

    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
}

interface TRC165 {
    // Returns true if the contract implements the interface defined by `interfaceID`
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
```

## Receiving contracts must implement `TRC721TokenReceiver`

A wallet, broker, or auction contract that wants to accept safe transfers (that is, receive NFTs via `safeTransferFrom`) must implement the receiver interface:

```solidity
interface TRC721TokenReceiver {
    // Called whenever a safeTransferFrom sends an NFT to this contract.
    // The return value must match keccak256("onTRC721Received(address,address,uint256,bytes)")
    // or the transfer reverts.
    function onTRC721Received(
        address _operator,
        address _from,
        uint256 _tokenId,
        bytes calldata _data
    ) external returns (bytes4);
}
```

> 🚧 TRON-specific return value
>
> The TRC-721 hash differs from the Ethereum ERC-721 version. Use `0x5175f878` (the TRON hash), not `0x150b7a02` (the Ethereum hash). Returning the Ethereum value causes the transfer to revert.

## Optional: metadata extension

The metadata extension exposes a human-readable name, a symbol, and a URI pointer to off-chain metadata for each token. Most NFT contracts implement this — wallets and marketplaces use it to render the token.

```solidity
interface TRC721Metadata {
    // Returns the name of this NFT collection
    function name() external view returns (string memory _name);

    // Returns a short symbol for the collection
    function symbol() external view returns (string memory _symbol);

    // Returns the URI of an external metadata file for `_tokenId`
    function tokenURI(uint256 _tokenId) external view returns (string memory);
}
```

The `tokenURI` points to a JSON file describing the individual token. The expected JSON schema is:

```json
{
  "title": "Asset Metadata",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Identifies the asset this NFT represents"
    },
    "description": {
      "type": "string",
      "description": "Describes the asset this NFT represents"
    },
    "image": {
      "type": "string",
      "description": "A URI pointing to an image representing the asset"
    }
  }
}
```

Host the metadata JSON and image on IPFS, BTFS, or a CDN you control. Once an NFT is minted with a URI, changing that URI is only possible if the contract exposes an update function.

## Optional: enumeration extension

The enumeration extension lets callers list every token in the collection and walk the tokens owned by an address. This makes your contract discoverable — explorers and analytics tools can enumerate holdings without scanning logs.

```solidity
interface TRC721Enumerable {
    // Returns the total number of NFTs in the collection
    function totalSupply() external view returns (uint256);

    // Returns the tokenId at a global index
    function tokenByIndex(uint256 _index) external view returns (uint256);

    // Returns the tokenId at the per-owner index for `_owner`
    function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}
```

Implementing this extension costs extra Energy on mints and transfers because the contract must maintain index arrays. Skip it if your collection never needs enumeration — callers can always reconstruct ownership from `Transfer` events.

***

## Related resources

* [TRC-721 protocol interfaces](https://developers.tron.network/docs/trc-721-protocol-interface) — full interface reference
* [Issuing a TRC-721 token](https://developers.tron.network/docs/trc-721-token-issuance) — end-to-end deployment walkthrough
* [TRC-721 contract interaction](https://developers.tron.network/docs/trc-721-contract-interaction) — read and write from a deployed TRC-721
* [TRC-1155](https://developers.tron.network/docs/trc-1155) — multi-token standard