A non-fungible token (NFT) is a unique cryptographic token that represents ownership of specific digital or physical assets on a blockchain. Unlike cryptocurrencies such as Bitcoin, NFTs are indivisible and cannot be exchanged on a one-to-one basis due to their distinctive characteristics. Each NFT is one-of-a-kind and includes specific metadata that sets it apart from other tokens. NFTs are utilized across various industries, enabling creators to tokenize and monetize their work, including art, music, videos, virtual real estate, collectibles, and more. Ownership and transaction records of NFTs are securely stored on the blockchain using smart contracts, ensuring transparency and authenticity. NFT marketplaces facilitate the buying, selling, and trading of these tokens, allowing collectors and investors to participate in the growing digital asset ecosystem.
What is ZTP-721
ZTP-721 is the standard interface for Non-Fungible Tokens (NFTs). This interface enables users to perform actions such as minting, transferring, and burning tokens. By adopting this default standard, contracts utilizing ZTP-721 can seamlessly integrate with various services across the network.
Methods
The list below outlines the required methods to be implemented in the contract:
Token definition:
Notes: Token definitions are defined during the init procedure.
init
Method
Description
name
Token Name. Example: “Global Coin”
symbol
Token Symbol. Example: GCN
describe
Token description: “Global coin token issued by XYZ”
decimals
Token decimal places.
version
Token version
Example:
functioninit() {let paramObj;paramObj.name ="Global NFT";paramObj.symbol ="GCN";paramObj.describe ="Global coin token issued by XYZ";paramObj.version ="1";paramObj.protocol ="ztp721"; // To define that this contract is ZTP-721 standardsChain.store("contract_info",JSON.stringify(paramObj));}
transferFrom
Method
Description
transFrom(id, from, to);
This function allows a designated address, often referred to as the "spender," to transfer a specified amount of tokens from the owner's address to another address.
The safeTransferFrom function in a smart contract is used to securely transfer tokens between addresses, ensuring that the recipient address can handle the received tokens.
The approve function in a smart contract is used to authorize a designated spender to transfer a specified amount of tokens on behalf of the token owner.
The setApproveForAll function in a smart contract is used to grant approval to a third-party operator to manage all tokens on behalf of the token owner.
The balanceOf function in a smart contract is used to retrieve the token balance of a specific address.
Example:
functionbalanceOf(paramObj) {let result = {};result.count =getAssetUserCount(paramObj.owner);return result;}
ownerOf
Method
Description
ownerOf(paramObj);
The ownerOf function in a smart contract is used to determine the current owner of a specific non-fungible token (NFT).
Example:
functionownerOf(paramObj) {let result = {};result.address =getAssetOwner(paramObj.id);return result;}
getApproved
Method
Description
getApproved(paramObj);
The getApprove function in a smart contract is used to check the amount of tokens that a designated spender is authorized to transfer on behalf of the token owner.
The isApproveForAll function in a smart contract is used to check if a specific operator is approved to manage all tokens on behalf of the token owner.
Example:
functionisApprovedForAll(paramObj) {let result = {};result.approved =getApproveAll(paramObj.owner,paramObj.operator);return result;}
contractInfo
Method
Description
contractInfo();
The contractInfo function in a smart contract is used to retrieve general information or details about the contract, such as its name, symbol, and other metadata.
The tokenURI function in a smart contract is used to retrieve the metadata URI associated with a specific non-fungible token (NFT), which provides detailed information and attributes about the token.
Example:
functiontokenURI(paramObj) {let result = {};result.uri =loadObj(getKey(ASSET_PRE,paramObj.id)).uri;return result;}
totalSupply
Method
Description
totalSupply(paramObj);
The totalSupply function in a smart contract is used to retrieve the total number of tokens that have been minted or created in the token contract.
Example:
functiontotalSupply(paramObj) {let result = {};result.count =getAssetSupply();return result;}
query
Method
Description
function query(input_str)
The query function defines a set of common functions for managing and interacting with non-fungible tokens (NFTs), such as balanceOf, ownerOf, approve, getApproved, setApprovalForAll, isApprovedForAll, transferFrom, and safeTransferFrom.
'use strict';constASSET_PRE='asset';constASSET_USER_COUNT_PRE='asset_user_count';constASSET_OWNER_PRE='asset_owner';constAPPROVE_SINGLE_PRE='approve_single';constAPPROVE_ALL_PRE='approve_all';constCONTRACT_PRE='contract_info';constASSET_SUPPLY='asset_supply';constZTP_PROTOCOL='ztp721';functiongetKey(first, second, third ='') {return (third ==='') ? (first +'_'+ second) : (first +'_'+ second +'_'+ third);}functionloadObj(key) {let data =Chain.load(key);Utils.assert(data !==false,'Failed to get storage data, key:'+ key);returnJSON.parse(data);}functionsaveObj(key, value) {Chain.store(key,JSON.stringify(value));}functioncheckAssetExsit(id) {let data =Chain.load(getKey(ASSET_PRE, id));if (data ===false) {returnfalse; }returntrue;}functionsaveAsset(id, issuer, uri) {let nftObj = {};nftObj.id = id;nftObj.issuer = issuer;nftObj.uri = uri;saveObj(getKey(ASSET_PRE, id), nftObj);}functiongetAssetOwner(id) {let data =Chain.load(getKey(ASSET_OWNER_PRE, id));if (data ===false) {return''; }returnJSON.parse(data).owner;}functionsaveAssetOwner(id, owner) {let obj = {};obj.owner = owner;saveObj(getKey(ASSET_OWNER_PRE, id), obj);}functiongetAssetUserCount(user) {let data =Chain.load(getKey(ASSET_USER_COUNT_PRE, user));if (data ===false) {return'0'; }returnJSON.parse(data).count;}functionsaveAssetUserCount(user, count) {let key =getKey(ASSET_USER_COUNT_PRE, user);if (Utils.int64Compare(count,'0') !==0) {let obj = {};obj.count = count;saveObj(key, obj);return; }let data =Chain.load(key);if (data !==false) {Chain.del(key); }}functiongetApproveSingle(id) {let data =Chain.load(getKey(APPROVE_SINGLE_PRE, id));if (data ===false) {return''; }returnJSON.parse(data).operator;}functionsaveApproveSingle(id, operator) {let obj = {};obj.operator = operator;saveObj(getKey(APPROVE_SINGLE_PRE, id), obj);}functiondelApproveSingle(id) {let key =getKey(APPROVE_SINGLE_PRE, id);let data =Chain.load(key);if (data ===false) {returnfalse; }Chain.del(key);returntrue;}functiongetApproveAll(owner, operator) {let data =Chain.load(getKey(APPROVE_ALL_PRE, owner, operator));if (data ===false) {returnfalse; }returnJSON.parse(data).approved;}functionsaveApproveAll(owner, operator, approved) {let key =getKey(APPROVE_ALL_PRE, owner, operator);if (approved) {let approvedObj = {};approvedObj.approved = approved;saveObj(key, approvedObj);return; }let data =Chain.load(key);if (data !==false) {Chain.del(key); }}functiongetAssetSupply() {let data =Chain.load(ASSET_SUPPLY);if (data ===false) {return'0'; }returnJSON.parse(data).count;}functionsaveAssetSupply(count) {let supplyObj = {};supplyObj.count = count;saveObj(ASSET_SUPPLY, supplyObj);}function_approve(owner, id, approved) {if (approved !=='') {saveApproveSingle(id, approved);Chain.tlog('Approval', owner, approved, id);return; }if (delApproveSingle(id)) {Chain.tlog('Approval', owner,'0x', id);return; }}function_transFrom(id, from, to) {Utils.assert(checkAssetExsit(id),'Check nft not exist.');let owner =getAssetOwner(id);Utils.assert(owner === from,'Nft owner not equal from.'); Utils.assert(owner === Chain.msg.sender || getApproveSingle(id) === Chain.msg.sender || getApproveAll(owner, Chain.msg.sender), 'No privilege to trans.');
saveAssetUserCount(from,Utils.int64Sub(getAssetUserCount(from),'1'));saveAssetUserCount(to,Utils.int64Add(getAssetUserCount(to),'1'));saveAssetOwner(id, to);_approve(owner, id,'');Chain.tlog('Transfer', owner, to, id);return;}functionsafeTransferFrom(paramObj) {Utils.assert(paramObj.from !==undefined&¶mObj.from.length>0,'Param obj has no from.');Utils.assert(paramObj.to !==undefined&¶mObj.to.length>0,'Param obj has no to.');Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');Utils.assert(Utils.addressCheck(paramObj.from),'From address is invalid.');Utils.assert(Utils.addressCheck(paramObj.to),'To address is invalid.');_transFrom(paramObj.id,paramObj.from,paramObj.to);return;}functiontransferFrom(paramObj) {Utils.assert(paramObj.from !==undefined&¶mObj.from.length>0,'Param obj has no from.');Utils.assert(paramObj.to !==undefined&¶mObj.to.length>0,'Param obj has no to.');Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');Utils.assert(Utils.addressCheck(paramObj.from),'From address is invalid.');Utils.assert(Utils.addressCheck(paramObj.to),'To address is invalid.');_transFrom(paramObj.id,paramObj.from,paramObj.to);return;}functionapprove(paramObj) {Utils.assert(paramObj.approved !==undefined&¶mObj.approved.length>=0,'Param obj has no approved.');Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');Utils.assert(Utils.addressCheck(paramObj.approved) ||paramObj.approved ==='','Approved address is invalid.');Utils.assert(Chain.msg.sender !==paramObj.approved,'Approved cannot equal msg sender.');Utils.assert(checkAssetExsit(paramObj.id),'Check nft not exist.');let owner =getAssetOwner(paramObj.id);Utils.assert(owner ===Chain.msg.sender,'No privilege to trans.');_approve(owner,paramObj.id,paramObj.approved);return;}functionsetApprovalForAll(paramObj) {Utils.assert(paramObj.operator !==undefined&¶mObj.operator.length>0,'Param obj has no operator.');Utils.assert(paramObj.approved !==undefined,'Param obj has no approved.');Utils.assert(paramObj.approved ===true||paramObj.approved ===false,'Approved must be true or false.');Utils.assert(Utils.addressCheck(paramObj.operator),'Operator address is invalid.');Utils.assert(Chain.msg.sender !==paramObj.operator,'Operator cannot equal msg sender.');saveApproveAll(Chain.msg.sender,paramObj.operator,paramObj.approved);Chain.tlog('ApprovalForAll',Chain.msg.sender,paramObj.operator,paramObj.approved);return;}functionmint(paramObj) {Utils.assert(paramObj.to !==undefined&¶mObj.to.length>0,'Param obj has no to.');Utils.assert(paramObj.uri !==undefined&¶mObj.uri.length>0,'Param obj has no uri.');Utils.assert(Utils.addressCheck(paramObj.to),'To address is invalid.');let newId =Utils.int64Add(getAssetSupply(),'1');let newUserCount =Utils.int64Add(getAssetUserCount(paramObj.to),'1');saveAsset(newId,Chain.msg.sender,paramObj.uri);saveAssetOwner(newId,paramObj.to);saveAssetUserCount(paramObj.to, newUserCount);saveAssetSupply(newId);Chain.tlog('Transfer','0x',paramObj.to, newId);return;}functionburn(paramObj) {Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');Utils.assert(checkAssetExsit(paramObj.id),'Check nft not exist.');let owner =getAssetOwner(paramObj.id); Utils.assert(owner === Chain.msg.sender || getApproveSingle(paramObj.id) === Chain.msg.sender || getApproveAll(owner, Chain.msg.sender), 'No privilege to burn.');
saveAssetUserCount(owner,Utils.int64Sub(getAssetUserCount(owner),'1'));saveAssetOwner(paramObj.id,'');_approve(owner,paramObj.id,'');Chain.tlog('Transfer', owner,'0x',paramObj.id);return;}functionbalanceOf(paramObj) {Utils.assert(paramObj.owner !==undefined&¶mObj.owner.length>0,'Param obj has no owner');let result = {};result.count =getAssetUserCount(paramObj.owner);return result;}functionownerOf(paramObj) {Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');let result = {};result.address =getAssetOwner(paramObj.id);return result;}functiongetApproved(paramObj) {Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');let result = {};result.address =getApproveSingle(paramObj.id);return result;}functionisApprovedForAll(paramObj) {Utils.assert(paramObj.owner !==undefined&¶mObj.owner.length>0,'Param obj has no owner.');Utils.assert(paramObj.operator !==undefined&¶mObj.operator.length>0,'Param obj has no operator.');let result = {};result.approved =getApproveAll(paramObj.owner,paramObj.operator);return result;}functioncontractInfo() {returnloadObj(CONTRACT_PRE);}functiontokenURI(paramObj) {Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');let result = {};result.uri =loadObj(getKey(ASSET_PRE,paramObj.id)).uri;return result;}functiontotalSupply(paramObj) {let result = {};result.count =getAssetSupply();return result;}functioninit(input_str) {let paramObj =JSON.parse(input_str).params;Utils.assert(paramObj.name !==undefined&¶mObj.name.length>0,'Param obj has no name.');Utils.assert(paramObj.symbol !==undefined&¶mObj.symbol.length>0,'Param obj has no symbol.');Utils.assert(paramObj.describe !==undefined&¶mObj.describe.length>0,'Param obj has no describe.'); Utils.assert(paramObj.protocol !== undefined && paramObj.protocol.length > 0 && paramObj.protocol.toLowerCase() === ZTP_PROTOCOL, 'Param obj protocol must be ZTP721.');
Utils.assert(paramObj.version !==undefined&¶mObj.version.length>0,'Param obj has no version.');Utils.assert(paramObj.url !==undefined&¶mObj.url.length>0,'Param obj has no url.');saveObj(CONTRACT_PRE, paramObj);return;}functionmain(input_str) {let funcList = {'safeTransferFrom': safeTransferFrom,'transferFrom': transferFrom,'approve': approve,'setApprovalForAll': setApprovalForAll,'mint': mint,'burn': burn };let inputObj =JSON.parse(input_str); Utils.assert(funcList.hasOwnProperty(inputObj.method) && typeof funcList[inputObj.method] === 'function', 'Cannot find func:' + inputObj.method);
funcList[inputObj.method](inputObj.params);}functionquery(input_str) {let funcList = {'balanceOf': balanceOf,'ownerOf': ownerOf,'getApproved': getApproved,'isApprovedForAll': isApprovedForAll,'contractInfo': contractInfo,'tokenURI': tokenURI,'totalSupply': totalSupply };let inputObj =JSON.parse(input_str); Utils.assert(funcList.hasOwnProperty(inputObj.method) && typeof funcList[inputObj.method] === 'function', 'Cannot find func:' + inputObj.method);
returnJSON.stringify(funcList[inputObj.method](inputObj.params));}
Use Case
Digital Art and Collectibles
Artists and creators could tokenize their artwork and collectibles as ZTP721 tokens, allowing them to prove ownership, authenticity, and scarcity. Platforms on the ZETRIX blockchain could facilitate the buying, selling, and trading of digital art and collectibles as ZTP721 tokens.
Gaming and Virtual Real Estate
ZTP721 tokens could be used in the gaming industry to represent in-game assets, characters, and virtual real estate. Games built on the ZETRIX blockchain could leverage ZTP721 tokens to create unique, tradable assets within their virtual worlds, allowing players to buy, sell, and trade in-game items and properties.
Tokenized Real-World Assets
The ZTP721 standard could be used to tokenize real-world assets such as real estate, luxury goods, and collectible items on the ZETRIX blockchain. By representing these assets as NFTs (ZTP721 tokens), ownership could be easily transferred, fractional ownership could be established, and the provenance of the asset could be verified, leading to increased liquidity and accessibility to traditional asset classes.
Lending and Borrowing Platforms
TP721 tokens could be used to create digital event tickets and access passes on the ZETRIX blockchain, providing a secure and transparent way to manage ticket sales, prevent counterfeiting, and verify attendance. This could streamline the ticketing process, reduce fraud, and enhance the overall event experience for organizers and attendees.
Digital Identity and Credentials
The ZTP721 standard could be used to create digital identities and credentials, such as academic diplomas, professional certifications, and personal identification documents on the ZETRIX blockchain. By storing these credentials as NFTs (ZTP721 tokens), individuals could have greater control over their personal data, share verifiable proofs of their achievements and qualifications, and easily transfer ownership of their credentials when necessary.
Supply Chain and Provenance Tracking
ZTP721 tokens could be used to track the provenance and authenticity of goods throughout the supply chain on the ZETRIX blockchain. By creating NFTs for each product or batch of products, companies could monitor the movement and ownership of their goods, verify the authenticity of products, and combat counterfeit products effectively.
Music and Intellectual Property Rights
Musicians, artists, and content creators could tokenize their music, artworks, and intellectual property rights as ZTP721 tokens on the ZETRIX blockchain. This would enable them to monetize their creations through the sale of NFTs, establish ownership and copyright protections, and receive royalties automatically whenever their works are resold or used by others.