Near Learn

NEP-181: NFT enumeration

NEP-181 NFT enumeration standard on NEAR: nft_total_supply, nft_tokens, nft_supply_for_owner and nft_tokens_for_owner, and how to paginate them safely.

Intermediate5 min read3-question check

The NFT core (NEP-171) can look up one token by ID, but cannot answer “what does Alice own?” or “show me the whole collection”. NEP-181 adds four view methods for that, so wallets and marketplaces can list tokens straight from the contract without running an indexer.

Methods#

MethodKindReturns
nft_total_supply()viewNumber of tokens in the contract, as a string (U128).
nft_tokens(from_index?, limit?)viewA page of Token objects across the whole contract.
nft_supply_for_owner(account_id)viewHow many tokens an account owns, as a string. "0" if none.
nft_tokens_for_owner(account_id, from_index?, limit?)viewA page of the account’s tokens.

Paginating#

Fetch an owner’s tokens page by page
Shell
# how many?
near view nft.example.near nft_supply_for_owner '{"account_id": "alice.near"}'
# → "137"

# first page
near view nft.example.near nft_tokens_for_owner \
  '{"account_id": "alice.near", "from_index": "0", "limit": 50}'

# next pages: advance from_index by the page size
near view nft.example.near nft_tokens_for_owner \
  '{"account_id": "alice.near", "from_index": "50", "limit": 50}'
  • Use the supply call to know when to stop, or stop when a page returns fewer items than limit.
  • View calls are free for the caller but still run under a gas limit on the RPC node. A huge limit — especially with large on-chain metadata per token — can exceed it and fail. Pages of tens of tokens are a safe default.
  • Ordering is defined by the contract’s storage structure, not by mint time. If a token is transferred between your page requests, it can be skipped or seen twice; treat results as a snapshot.
ERC-721 Enumerable vs NEP-181
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

// ERC721Enumerable: one index at a time
interface IERC721Enumerable {
    function totalSupply() external view returns (uint256);
    function tokenByIndex(uint256 index) external view returns (uint256);
    function tokenOfOwnerByIndex(address owner, uint256 index)
        external view returns (uint256);
}
Rust
use near_contract_standards::non_fungible_token::enumeration::NonFungibleTokenEnumeration;
use near_contract_standards::non_fungible_token::Token;
use near_sdk::json_types::U128;
use near_sdk::{near, AccountId};

// pages of full Token objects; delegate to the embedded NonFungibleToken
// (created with an Enumeration storage prefix)
#[near]
impl NonFungibleTokenEnumeration for Contract {
    fn nft_total_supply(&self) -> U128 {
        self.tokens.nft_total_supply()
    }
    fn nft_tokens(&self, from_index: Option<U128>, limit: Option<u64>) -> Vec<Token> {
        self.tokens.nft_tokens(from_index, limit)
    }
    fn nft_supply_for_owner(&self, account_id: AccountId) -> U128 {
        self.tokens.nft_supply_for_owner(account_id)
    }
    fn nft_tokens_for_owner(
        &self,
        account_id: AccountId,
        from_index: Option<U128>,
        limit: Option<u64>,
    ) -> Vec<Token> {
        self.tokens.nft_tokens_for_owner(account_id, from_index, limit)
    }
}
Coming from EVM

tokenOfOwnerByIndex returns one ID per call; NEP-181 returns a page of full Token objects (owner, metadata, approvals) per call — far fewer round trips.

Enumeration costs extra storage (a per-owner token set). The reference NonFungibleToken only maintains it if you pass an enumeration prefix to NonFungibleToken::new.

Check yourself

3 questions · progress saved in this browser

  1. 1.Which JSON args correctly request the second page of 50 tokens for alice.near?
  2. 2.Why should a client avoid requesting thousands of tokens in one nft_tokens call?
  3. 3.What does NEP-181 add that NEP-171 alone does not provide?