Near Learn

NFTs (NEP-171)

NEP-171 is the ERC-721 of NEAR. Token ids are strings, transfers use the same transfer-and-call pattern, and NEP-177 metadata fields are stored on-chain (though the media field is usually a URL to off-chain content).

Beginner4 min read3-question check

NEP-171 is NEAR’s non-fungible-token standard — ERC-721’s counterpart. Same core idea, a few NEAR-isms: token ids are strings, transfers use the transfer-and-call pattern, and rich metadata (NEP-177) is part of the standard rather than an off-chain convention.

The core interface
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IERC721 {
    function ownerOf(uint256 tokenId) external view returns (address);
    function balanceOf(address owner) external view returns (uint256);

    function transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
    function getApproved(uint256 tokenId) external view returns (address);
}
Rust
use near_sdk::{AccountId, PromiseOrValue};

// NEP-171 core (near-contract-standards: NonFungibleTokenCore)
pub trait NonFungibleTokenCore {
    fn nft_transfer(
        &mut self,
        receiver_id: AccountId,
        token_id: String,           // ids are strings, not uint256
        approval_id: Option<u64>,
        memo: Option<String>,
    );

    // transfer to a CONTRACT and call its nft_on_transfer
    // (async: if the receiver rejects, nft_resolve_transfer returns the token)
    fn nft_transfer_call(
        &mut self,
        receiver_id: AccountId,
        token_id: String,
        approval_id: Option<u64>,
        memo: Option<String>,
        msg: String,
    ) -> PromiseOrValue<bool>;

    fn nft_token(&self, token_id: String) -> Option<Token>;
}
Coming from EVM

tokenId is a `String`, not a uint256 — often a slug or a running counter rendered as text. ownerOf ↔ reading nft_token(id) (which bundles owner + metadata).

Like fungible tokens, nft_transfer_call moves the token to a contract and calls its nft_on_transfer in one step. Per-token approvals are an optional extension (NEP-178), not baked into the core the way ERC-721 approve is.

Metadata (NEP-177) and enumeration (NEP-181) are companion standards. NEP-177 fields (title, description, copies, …) are stored on-chain in contract state, but media / reference are usually URLs (IPFS, Arweave, HTTP) pointing at off-chain content, with optional media_hash / reference_hash to pin it; near-contract-standards bundles all of them behind a NonFungibleToken you embed.

Check yourself

3 questions · progress saved in this browser

  1. 1.How are NEP-171 token ids typed?
  2. 2.What is the NEAR counterpart of ERC-721's ownerOf(tokenId)?
  3. 3.Where do per-token approvals, ERC-721's approve, live in NEAR's NFT standards?