NEP-171: Non-fungible tokens
NEP-171 non-fungible token (NFT) core standard on NEAR: nft_transfer, nft_transfer_call, nft_on_transfer, nft_resolve_transfer and nft_token vs ERC-721.
Intermediate5 min read3-question check
NEP-171 is NEAR’s NFT core — the ERC-721 counterpart. A token is identified by a string token_id inside one contract, has exactly one owner_id, and can be transferred directly or with a transfer-and-call that notifies a receiving contract.
The core is deliberately small. Display data is NEP-177, marketplace approvals are NEP-178, listing tokens is NEP-181 and royalties are NEP-199. A typical NFT contract implements all five plus NEP-297 events (nft_mint, nft_transfer, nft_burn).
Methods#
| Method | Kind | What it does | Deposit |
|---|---|---|---|
nft_transfer(receiver_id, token_id, approval_id?, memo?) | change | Transfer a token. Callable by the owner, or by an approved account passing its approval_id (NEP-178). | exactly 1 yoctoNEAR |
nft_transfer_call(receiver_id, token_id, approval_id?, memo?, msg) | change | Transfer, then call receiver_id.nft_on_transfer(...). Resolves to true if the token ended up with the receiver. | exactly 1 yoctoNEAR |
nft_token(token_id) | view | The Token (token_id, owner_id, plus metadata / approved_account_ids when those extensions are implemented), or null. | — |
nft_on_transfer(sender_id, previous_owner_id, token_id, msg) | change (on the receiver) | Implemented by contracts that accept NFTs. Return true to send the token back. | — |
nft_resolve_transfer(previous_owner_id, receiver_id, token_id, approved_account_ids?) | private callback | Runs after nft_on_transfer. Returns the token to the previous owner if the receiver asked for it or panicked. | — |
How nft_transfer_call works#
- Alice calls
nft.nft_transfer_call(receiver_id: "escrow.near", token_id: "42", msg)with 1 yoctoNEAR. - The NFT contract makes
escrow.nearthe owner immediately and clears the token’s approvals. - It calls
escrow.near.nft_on_transfer(sender_id: "alice.near", previous_owner_id: "alice.near", token_id: "42", msg)(sender_iddiffers from the previous owner when an approved account initiated the transfer). nft_resolve_transferruns. If the receiver returnedtrue, or panicked, the token goes back to Alice and her old approvals are restored; otherwise the receiver keeps it.
Rust
use near_contract_standards::non_fungible_token::core::NonFungibleTokenReceiver;
use near_contract_standards::non_fungible_token::TokenId;
use near_sdk::store::LookupMap;
use near_sdk::{env, near, AccountId, PanicOnDefault, PromiseOrValue};
#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct Escrow {
collection: AccountId,
deposited_by: LookupMap<TokenId, AccountId>,
}
#[near]
impl NonFungibleTokenReceiver for Escrow {
fn nft_on_transfer(
&mut self,
sender_id: AccountId,
previous_owner_id: AccountId,
token_id: TokenId,
msg: String,
) -> PromiseOrValue<bool> {
let _ = (sender_id, msg);
// anyone can call this — reject NFTs from other contracts
if env::predecessor_account_id() != self.collection {
return PromiseOrValue::Value(true); // "send it back"
}
self.deposited_by.insert(token_id, previous_owner_id);
PromiseOrValue::Value(false) // keep it
}
}Check yourself
3 questions · progress saved in this browser