Token standards
NFTs (NEP-171)
NEP-171 is the ERC-721 of NEAR. Token ids are strings, transfers use the same transfer-and-call pattern, and metadata lives on-chain by default.
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.
// 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);
}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 atomically
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>;
}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; near-contract-standards bundles all of them behind a NonFungibleToken you embed.