I'm trying to implement my own NFT-contract, following this tutorial on NEAR, and the Non-Fungible Token (NEP-171) specifications. The tutorial is in Rust, but I'm trying to do something similar with AssemblyScript. I thought if I implemented the methods with the correct names and signature, it would be possible for the NEAR wallet to call the respective methods (e.g. nft_tokens_for_owner) on my NFT-contract. I'm just wondering if I'm missing something, or if I have the wrong understanding on how it's suppose to work.
I have minted one NFT-token, using the nft_mint method in my contract, using my own testnet account. The transaction can be found here. However, the NFT is not displayed in the "Collectibles" tab in my testnet wallet.
My contract (index.ts) looks like this:
import { PersistentMap } from 'near-sdk-as';
import { Token, TokenMetadata } from './reit-token';
@nearBindgen
class NFTContractMetadata {
spec: string; // required, essentially a version like "nft-1.0.0"
name: string; // required, ex. "Mochi Rising — Digital Edition" or "Metaverse 3"
symbol: string; // required, ex. "MOCHI"
icon: string | null; // Data URL
base_uri: string | null; // Centralized gateway known to have reliable access to decentralized storage assets referenced by `reference` or `media` URLs
reference: string | null; // URL to a JSON file with more info
reference_hash: string | null; // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included.
}
@nearBindgen
export class Contract {
//keeps track of the metadata for the contract
metadata: NFTContractMetadata = {
spec: 'reit-token-0.0.0',
name: 'Reit Token',
symbol: 'REIT',
icon: null,
base_uri: null,
reference: null,
reference_hash: null,
};
//contract owner
owner_id: string; // Not sure if this is relevant for the contract itself
//keeps track of all the token IDs for a given account
tokens_per_owner: PersistentMap<string, Array<string>> = new PersistentMap<
string,
Array<string>
>('tokens_pr_owner');
//keeps track of the token struct for a given token ID
tokens_by_id: PersistentMap<string, Token> = new PersistentMap<string, Token>(
'tokens_by_id'
);
//keeps track of the token metadata for a given token ID
token_metadata_by_id: PersistentMap<string, TokenMetadata> =
new PersistentMap<string, TokenMetadata>('token_metadata_by_id');
nft_tokens_for_owner(account_id: string): Array<Token> {
const tokenIds: string[] = this.tokens_per_owner.getSome(account_id);
const tokens: Array<Token> = new Array<Token>();
for (let i = 0; i < tokenIds.length; ++i) {
const token: Token = this.tokens_by_id.getSome(tokenIds[i]);
tokens.push(token);
}
return tokens;
}
nft_mint(
token_id: string,
metadata: TokenMetadata,
receiver_id: string
): void {
// assert(
// this.tokens_by_id.contains(token_id),
// 'ID is already taken, create new ID'
// );
const token = new Token(token_id, metadata, receiver_id);
const tokens: Array<string> = new Array<string>();
tokens.push(token_id);
this.tokens_per_owner.set(receiver_id, tokens);
this.tokens_by_id.set(token_id, token);
this.token_metadata_by_id.set(token_id, token.metadata);
}
Inside reit-token.ts
import { ContractPromise } from 'near-sdk-as';
// implementation based on NEP-171
// https://nomicon.io/Standards/NonFungibleToken/Core.html
@nearBindgen
export class TokenMetadata {
title: string; // ex. "Arch Nemesis: Mail Carrier" or "Parcel #5055"
description: string; // free-form description
media: string; // URL to associated media, preferably to decentralized, content-addressed storage
media_hash: string; // Base64-encoded sha256 hash of content referenced by the `media` field. Required if `media` is included.
copies: number; // number of copies of this set of metadata in existence when token was minted.
issued_at: number; // When token was issued or minted, Unix epoch in milliseconds
expires_at: number; // When token expires, Unix epoch in milliseconds
starts_at: number; // When token starts being valid, Unix epoch in milliseconds
updated_at: number; // When token was last updated, Unix epoch in milliseconds
extra: string; // anything extra the NFT wants to store on-chain. Can be stringified JSON.
reference: string; // URL to an off-chain JSON file with more info.
reference_hash: string; // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included.
}
@nearBindgen
export class Token {
id: string;
owner_id: string;
metadata: TokenMetadata;
constructor(token_id: string, metadata: TokenMetadata, receiver_id: string) {
this.id = token_id;
this.metadata = metadata;
this.owner_id = receiver_id;
}
}
@nearBindgen
export class ReitToken {
token: Token;
constructor(token_id: string, metadata: TokenMetadata, receiver_id: string) {
this.token = { id: token_id, metadata: metadata, owner_id: receiver_id };
}
// --- view methods --- //
nft_token(): Token {
return this.token;
}
// --- change methods --- //
nft_transfer(
receiver_id: string,
token_id: string,
approval_id: number,
memo: string
): void {
// assert(false, 'nft_transfer not implemented');
}
nft_transfer_call(
receiver_id: string,
token_id: string,
approval_id: number,
memo: string,
msg: string
): ContractPromise {
// assert(false, 'nft_transfer_call not implemented');
return ContractPromise.create('', '', {}, 1);
}
}
