Skip to content

NEP-11: Non-Fungible Tokens

Back to Standards and Contracts

NEP-11 is Neo's non-fungible token standard. Compared to ERC-721, it requires tokensOf() and properties() methods, uses bytes32 token IDs, and supports both indivisible and divisible (fractional ownership) NFTs in a single standard.

Spec: NEP-11 Proposal

Required Interface

MethodParametersReturnSafeDescription
symbolStringYesToken symbol
decimalsIntegerYes0 for indivisible, >0 for divisible
totalSupplyIntegerYesTotal minted tokens
balanceOfHash160 ownerIntegerYesToken count for owner
ownerOfByteArray tokenIdHash160YesToken owner
transferHash160 to, ByteArray tokenId, Any dataBooleanNoTransfer NFT
tokensOfHash160 ownerIteratorYesEnumerate owner's tokens
propertiesByteArray tokenIdMapYesToken metadata

Required event:

Transfer(Hash160 from, Hash160 to, Integer amount, ByteArray tokenId)

NEP-11 Transfer has 4 parameters

Unlike NEP-17's 3-parameter Transfer event, NEP-11 includes the tokenId as a fourth parameter. The amount field is 1 for indivisible NFTs.

Divisible vs Indivisible

NEP-11 supports two modes in a single standard:

IndivisibleDivisible
decimals()Returns 0Returns > 0
ownerOf(tokenId)Single Hash160Iterator of Hash160 (multiple owners)
transfertransfer(to, tokenId, data)transfer(from, to, amount, tokenId, data)
Use caseUnique collectibles, artFractional real estate, shared assets

The devpack provides the INEP11Divisible interface for divisible NFTs:

solidity
interface INEP11Divisible is INEP11 {
    function balanceOf(address owner, bytes32 tokenId) external view returns (uint256);
    function transfer(address from, address to, uint256 amount, bytes32 tokenId, bytes calldata data)
        external returns (bool);
    function ownersOf(bytes32 tokenId) external view returns (address[] memory);
}

Token IDs

ERC-721 (Ethereum)NEP-11 (Neo)
Typeuint256ByteArray (mapped to bytes32 in Solidity)
Neo ABI typeHash256 / ByteArray
GenerationSequential counter or hashSequential counter, hash, or arbitrary bytes
solidity
// ERC-721: uint256 token IDs
function ownerOf(uint256 tokenId) public view returns (address) { ... }

// NEP-11: bytes32 token IDs
function ownerOf(bytes32 tokenId) public view returns (address) { ... }

Implementation Example

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "standards/NEP11.sol";
import "libraries/Runtime.sol";

/// @title MyNFT — A production NEP-11 non-fungible token
contract MyNFT is NEP11 {
    using Runtime for *;

    constructor()
        NEP11(
            "My NFT Collection",  // name
            "MNFT",               // symbol
            0,                    // decimals (0 = indivisible)
            "https://api.example.com/nft/",  // base URI
            10000,                // max supply
            false                 // not divisible
        )
    {}

    /// @dev Mint a new NFT with metadata properties
    function mintNFT(
        address to,
        bytes32 tokenId,
        bytes memory props
    ) external onlyMinter {
        require(Runtime.checkWitness(msg.sender), "MyNFT: unauthorized");
        mint(to, tokenId, props);
    }

    /// @dev NEP-11 3-parameter transfer with witness authorization
    function transfer(
        address to,
        bytes32 tokenId,
        bytes calldata data
    ) public override returns (bool) {
        address tokenOwner = ownerOf(tokenId);
        require(Runtime.checkWitness(tokenOwner), "MyNFT: unauthorized");
        return super.transfer(to, tokenId, data);
    }

    /// @dev Required: enumerate tokens owned by address
    function tokensOf(address owner) public view override returns (bytes32[] memory) {
        return super.tokensOf(owner);
    }

    /// @dev Required: return token metadata
    function properties(bytes32 tokenId) public view override returns (bytes memory) {
        return super.properties(tokenId);
    }

    /// @dev Callback for receiving NEP-11 tokens
    function onNEP11Payment(
        address from,
        uint256 amount,
        bytes32 tokenId,
        bytes calldata data
    ) external {
        // Handle incoming NFT
    }
}

ERC-721 Migration Checklist

  1. Replace uint256 tokenId with bytes32
  2. Replace transferFrom(from, to, tokenId) with transfer(to, tokenId, data) (3 params)
  3. Remove approve(), setApprovalForAll(), getApproved() — use witness model
  4. Add required tokensOf(owner) method returning token ID array
  5. Add required properties(tokenId) method returning serialized metadata
  6. Add decimals() returning 0 for indivisible NFTs
  7. Add onNEP11Payment(from, amount, tokenId, data) callback for receiving NFTs
  8. Verify manifest shows supportedstandards: ["NEP-11"]

MIT Licensed