Multi-Token Standard revolutionizes Zetrix blockchain in asset management. Unlike prior ZTP standards where fungible (like currency) and non-fungible (unique items) tokens required separate contracts, ZTP-1155 allows a single contract to handle both. This flexibility, combined with batch transactions and support for various asset types (digital collectibles, in-game items, even real-world ownership), makes ZTP-1155 a powerful tool, offering a more efficient and versatile way to manage all sorts of digital assets on Zetrix.
What is ZTP-1155
The ZTP1155 token standard is a multi-token standard developed for the ZETRIX blockchain. It was proposed as an improvement to the existing token standards to address some of their limitations and to provide more flexibility and efficiency for creating and managing both fungible and non-fungible tokens (NFTs) within a single smart contract.
Here are some key characteristics and features of the ZTP1155 token standard:
Efficiency: Unlike traditional token standards, which require separate smart contracts for each token type (fungible or non-fungible), ZTP1155 allows for the creation of both fungible and non-fungible tokens within a single smart contract. This results in reduced gas costs and improved scalability, as multiple token types can be managed and transferred more efficiently.
Batch Transfers: ZTP1155 supports batch transfers, enabling multiple tokens to be sent in a single transaction. This feature is particularly beneficial for applications like gaming, where players may need to transfer multiple in-game items or assets simultaneously.
Flexible Token Types: ZTP1155 supports a wide range of token types, including both fungible tokens (similar to ZTP-20) and non-fungible tokens (similar to ZTP-721). This flexibility allows developers to create diverse and complex token ecosystems with various token classes and properties.
Reduced Contract Complexity: By consolidating multiple token types into a single smart contract, ZTP1155 simplifies the development and management of token contracts, reducing the complexity and potential risks associated with deploying and maintaining multiple contracts.
Interoperability: ZTP1155 tokens are fully compatible with existing ZETRIX infrastructure, wallets, and decentralized applications (dApps), allowing for seamless integration and interoperability within the broader ZETRIX ecosystem.
Innovative Use Cases: The flexibility and efficiency of ZTP1155 have enabled the development of innovative use cases and applications across various industries, including gaming, decentralized finance (DeFi), digital collectibles, tokenized assets, and more.
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 ="ztp1155"; // To define that this contract is ZTP-1155 standardsChain.store("contract_info",JSON.stringify(paramObj));}
safeTranferForm
Method
Description
safeTransferFrom(paramObj);
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 safeBatchTransferFrom used for transferring multiple types of tokens (both fungible and non-fungible) from one address to another in a single atomic transaction.
Example:
functionsafeBatchTransferFrom(paramObj) {//Transfer assets and keep recordslet i =0;for (i =0; i <paramObj.ids.length; i +=1) {_transFrom(paramObj.ids[i],paramObj.from,paramObj.to,paramObj.values[i],paramObj.datas[i]); }//trigger event Chain.tlog('TransferBatch', Chain.msg.sender, paramObj.from, paramObj.to, JSON.stringify(paramObj.ids), JSON.stringify(paramObj.values));
}
setURI
Method
Description
setURI(paramObj)
The setURI function is typically used in the context of the ZTP-1155 token standard (or similar token standards) to set or update the base URI for the token metadata.
The mint function in a smart contract is used to create or generate new tokens and add them to the total token supply.
Example:
functionmint(paramObj) {//Issue additional assets and keep records_mint(paramObj.id,paramObj.to,paramObj.uri,paramObj.value);//trigger eventChain.tlog('TransferSingle',Chain.msg.sender,'0x',paramObj.to,paramObj.id,paramObj.value);}
burn
Method
Description
burn(paramObj);
The burn function in a smart contract is used to permanently remove a specific number of tokens from circulation, reducing the total token supply.
Example:
functionburn(paramObj) {//Destruction of assets_burn(paramObj.id,paramObj.from,paramObj.value);//trigger eventChain.tlog('TransferSingle',Chain.msg.sender,paramObj.from,'0x',paramObj.id,paramObj.value);}
balanceOf
Method
Description
balanceOf(paramObj);
The balanceOf function in a smart contract is used to retrieve the token balance of a specific address.
Example:
functionbalanceOf(paramObj) {let result = {};result.balance =getBalance(paramObj.id,paramObj.owner);return result;}
isApprovedForAll
Method
Description
isApprovedForAll(paramObj);
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.
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 URI 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.
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';constBALANCE_PRE='balance';constAPPROVE_PRE='approve';constCONTRACT_PRE='contract_info';constZTP_PROTOCOL='ztp1155';function_isHexStr64(str) {let a = /^[A-Fa-f0-9]{64,64}$/;returna.test(str);}functiongetKey(first, second, third ='') {return (third ==='') ? (first +'_'+ second) : (first +'_'+ second +'_'+ third);}function_isCreator(address, ID) {returnID.substr(0,32) ===Utils.sha256(address,1).substr(0,32);}function_maxSupply(ID) {let result =Utils.hexToDec(ID.substr(48,16));Utils.assert(result !==false,'Hex to dec error:'+ID.substr(48,16));Utils.assert(Utils.stoI64Check(result) ===true,'Hex to dec, check int64 error:'+ID.substr(48,16));returnUtils.int64Add(result,"0");}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));}functiongetBalance(id, owner) {let data =Chain.load(getKey(BALANCE_PRE, id, owner));if (data ===false) {return"0"; }returnJSON.parse(data).value;}functionsaveBalance(id, owner, value) {let result =Utils.int64Compare(value,"0");Utils.assert(value >=0,'Value must gt or equal 0.');if (result ===0) {Chain.del(getKey(BALANCE_PRE, id, owner));return; }let balanceObj = {};balanceObj.value = value;saveObj(getKey(BALANCE_PRE, id, owner), balanceObj);}functiongetApproved(owner, operator) {let data =Chain.load(getKey(APPROVE_PRE, owner, operator));if (data ===false) {returnfalse; }returnJSON.parse(data).approved;}functionsaveApproved(owner, operator, approved) {let approvedObj = {};approvedObj.approved = approved;saveObj(getKey(APPROVE_PRE, owner, operator), approvedObj);}functionsaveAsset(id, issuer, uri, value, freezed) {let nftObj = {};nftObj.id = id;nftObj.issuer = issuer;nftObj.uri = uri;nftObj.value = value;nftObj.freezed = freezed;saveObj(getKey(ASSET_PRE, id), nftObj);}functiongetAsset(id) {returnloadObj(getKey(ASSET_PRE, id));}functioncheckAssetExsit(id) {let data =Chain.load(getKey(ASSET_PRE, id));if (data ===false) {returnfalse; }returntrue;}function_mint(id, to, uri, value) {Utils.assert(_isHexStr64(id) ===true,'Id must be 64 length hex str.');Utils.assert(Utils.stoI64Check(value) ===true,'Param value error.');Utils.assert(Utils.int64Compare(value,0) >0,'Param value error.');Utils.assert(uri !==undefined&&uri.length>0,'Param obj has no uri.');Utils.assert(checkAssetExsit(id) ===false,'Check nft already exist.');Utils.assert(_isCreator(to, id) ===true,'Not creator.');Utils.assert(Utils.int64Compare(_maxSupply(id), value) ===0,'Id supply must equal value.');saveAsset(id, to, uri, value,false);saveBalance(id, to, value);}function_transFrom(id, from, to, value, data) {//If it doesn't exist, make lazy castingif (checkAssetExsit(id) ===false) {Utils.assert(data !==undefined&&data.length>0,'Need to mint, but param obj has no data(uri).');Utils.assert(Utils.int64Compare(_maxSupply(id), value) >=0,'Id supply must larger than value.');_mint(id, from, data,_maxSupply(id)); }//Check if your assets are owned or approvedUtils.assert(_isHexStr64(id) ===true,'Id must be 64 length hex str.');Utils.assert(checkAssetExsit(id) ===true,'Check nft not exist.');let approved =getApproved(from,Chain.msg.sender);Utils.assert(Chain.msg.sender === from || approved ===true,'No privilege to trans.');let rawFromValue =getBalance(id, from);let rawToValue =getBalance(id, to); Utils.assert(Utils.int64Compare(rawFromValue, value) >= 0, 'Balance:' + rawFromValue + ' of sender:' + Chain.msg.sender + ' < transfer value:' + value + '.');
let fromValue =Utils.int64Sub(rawFromValue, value);let toValue =Utils.int64Add(rawToValue, value);//Check if your assets are owned or approvedsaveBalance(id, to, toValue);saveBalance(id, from, fromValue);//TODOO triggers contract execution if it is a contract}functionsafeTransferFrom(paramObj) {//Checking parameter ValidityUtils.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(paramObj.value !==undefined&¶mObj.value.length>0,'Param obj has no value.');Utils.assert(paramObj.data !==undefined&¶mObj.data.length>=0,'Param obj has no data.');Utils.assert(Utils.addressCheck(paramObj.from),'From address is invalid.');Utils.assert(Utils.addressCheck(paramObj.to),'To address is invalid.');Utils.assert(paramObj.from !==paramObj.to,'From cannot equal to address.');Utils.assert(Utils.int64Compare(paramObj.value,0) >0,'Value must greater than 0.');_transFrom(paramObj.id,paramObj.from,paramObj.to,paramObj.value,paramObj.data);//trigger eventChain.tlog('TransferSingle',Chain.msg.sender,paramObj.from,paramObj.to,paramObj.id,paramObj.value);}functionsafeBatchTransferFrom(paramObj) {//Checking parameter ValidityUtils.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.ids !==undefined&¶mObj.ids.length>0,'Param obj has no ids.');Utils.assert(paramObj.values !==undefined&¶mObj.values.length>0,'Param obj has no values.');Utils.assert(paramObj.datas !==undefined&¶mObj.datas.length>0,'Param obj has no datas.');Utils.assert(Utils.addressCheck(paramObj.from),'From address is invalid.');Utils.assert(Utils.addressCheck(paramObj.to),'To address is invalid.');Utils.assert(paramObj.from !==paramObj.to,'From cannot equal to address.');Utils.assert(paramObj.ids.length===paramObj.values.length,'Ids not equal values with length.');Utils.assert(paramObj.ids.length===paramObj.datas.length,'Ids not equal data with length.');Utils.assert(paramObj.values.length===paramObj.datas.length,'Values not equal data with length.');//Transfer assets and keep recordslet i =0;for (i =0; i <paramObj.ids.length; i +=1) {Utils.assert(Utils.int64Compare(paramObj.values[i],0) >0,'Value must greater than 0.');_transFrom(paramObj.ids[i],paramObj.from,paramObj.to,paramObj.values[i],paramObj.datas[i]); }//trigger event Chain.tlog('TransferBatch', Chain.msg.sender, paramObj.from, paramObj.to, JSON.stringify(paramObj.ids), JSON.stringify(paramObj.values));
}functionsetApprovalForAll(paramObj) {//Checking parameter ValidityUtils.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.');//state of preservationsaveApproved(Chain.msg.sender,paramObj.operator,paramObj.approved);//Trigger logChain.tlog('ApprovalForAll',Chain.msg.sender,paramObj.operator,paramObj.approved);}functionsetURI(paramObj) {Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');Utils.assert(_isHexStr64(paramObj.id) ===true,'Id must be 64 length hex str.');Utils.assert(checkAssetExsit(paramObj.id) ===true,'Check nft not exist.');Utils.assert(_isCreator(Chain.msg.sender,paramObj.id) ===true,'Not creator.');Utils.assert(paramObj.uri !==undefined&¶mObj.uri.length>0,'Param obj has no uri.');Utils.assert(paramObj.uri.trim() !=="","Param obj uri is empty."); Utils.assert(paramObj.freezed !== undefined && (paramObj.freezed === true || paramObj.freezed === false), 'Param obj freezed error.');
let asset =getAsset(paramObj.id);Utils.assert(asset.freezed ===false,'Nft uri is freezed.'); Utils.assert(Utils.int64Compare(getBalance(paramObj.id, Chain.msg.sender), getAsset(paramObj.id).value) === 0, 'Must be hold all assets.');
saveAsset(asset.id,asset.issuer,paramObj.uri,asset.value,paramObj.freezed);Chain.tlog('URI',paramObj.uri,paramObj.id);if (paramObj.freezed ===true) {Chain.tlog('Freezed',paramObj.uri,paramObj.id); }return;}functionmint(paramObj) {//Checking parameter ValidityUtils.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(paramObj.value !==undefined&¶mObj.value.length>0,'Param obj has no value.');Utils.assert(paramObj.uri !==undefined&¶mObj.uri.length>0,'Param obj has no uri.');Utils.assert(Utils.addressCheck(paramObj.to),'To address is invalid.');//Issue additional assets and keep records_mint(paramObj.id,paramObj.to,paramObj.uri,paramObj.value);//trigger eventChain.tlog('TransferSingle',Chain.msg.sender,'0x',paramObj.to,paramObj.id,paramObj.value);}function_burn(id, from, value) {Utils.assert(_isHexStr64(id) ===true,'Id must be 64 length hex str.');Utils.assert(checkAssetExsit(id) ===true,'Check nft not exist.');Utils.assert(Utils.int64Compare(value,0) >0,'Value must greater than 0.');//Check whether you approve or own assetslet approved =getApproved(from,Chain.msg.sender);Utils.assert(Chain.msg.sender === from || approved ===true,'No privilege to trans.');let rawFromValue =getBalance(id, from); Utils.assert(Utils.int64Compare(rawFromValue, value) >= 0, 'Balance:' + rawFromValue + ' of sender:' + Chain.msg.sender + ' < transfer value:' + value + '.');
let fromValue =Utils.int64Sub(rawFromValue, value);//Transfer assets and keep recordssaveBalance(id, from, fromValue);}functionburn(paramObj) {//Checking parameter ValidityUtils.assert(paramObj.from !==undefined&¶mObj.from.length>0,'Param obj has no from.');Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');Utils.assert(paramObj.value !==undefined&¶mObj.value.length>0,'Param obj has no value.');Utils.assert(Utils.addressCheck(paramObj.from),'From address is invalid.');//Destruction of assets_burn(paramObj.id,paramObj.from,paramObj.value);//trigger eventChain.tlog('TransferSingle',Chain.msg.sender,paramObj.from,'0x',paramObj.id,paramObj.value);}functionbalanceOf(paramObj) {//Checking parameter ValidityUtils.assert(paramObj.owner !==undefined&¶mObj.owner.length>0,'Param obj has no owner');Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id');let result = {};result.balance =getBalance(paramObj.id,paramObj.owner);return result;}functionbalanceOfBatch(paramObj) {//Checking parameter ValidityUtils.assert(paramObj.owners !==undefined&¶mObj.owners.length>0,'Param obj has no owners.');Utils.assert(paramObj.ids !==undefined&¶mObj.ids.length>0,'Param obj has no ids.');Utils.assert(paramObj.ids.length===paramObj.owners.length,'Ids not equal owners with length.');let result = {};result.balances = [];let i =0;for (i =0; i <paramObj.ids.length; i +=1) {result.balances.push(getBalance(paramObj.ids[i],paramObj.owners[i])); }return result;}functionisApprovedForAll(paramObj) {//Checking parameter ValidityUtils.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 approvedObj = {};approvedObj.approved =getApproved(paramObj.owner,paramObj.operator);return approvedObj;}functioncontractInfo() {returnloadObj(CONTRACT_PRE);}functionuri(paramObj) {Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');let uriObj = {};uriObj.uri =getAsset(paramObj.id).uri;return uriObj;}functionfreezed(paramObj) {Utils.assert(paramObj.id !==undefined&¶mObj.id.length>0,'Param obj has no id.');let freezedObj = {};freezedObj.freezed =getAsset(paramObj.id).freezed;return freezedObj;}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 ztp1155.');
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,'safeBatchTransferFrom': safeBatchTransferFrom,'setApprovalForAll': setApprovalForAll,'setURI': setURI,'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,'balanceOfBatch': balanceOfBatch,'isApprovedForAll': isApprovedForAll,'contractInfo': contractInfo,'uri': uri,'freezed': freezed };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
Multi-Item Transactions
In gaming, players often need to manage multiple in-game items, such as weapons, armor, and accessories. ZTP-1155 tokens allow for batch transfers, enabling players to send or receive multiple items in a single transaction, reducing gas costs and improving efficiency.
Collectible Series
Artists and creators can release limited-edition collectible series as ERC-1155 tokens, each representing a unique piece of digital art or collectible. Collectors can buy, sell, and trade these digital assets on various platforms, creating a vibrant and decentralized marketplace for digital collectibles.
Unlockable Content
Content creators can use ERC-1155 tokens to distribute unlockable digital content, such as exclusive artworks, music tracks, or digital publications. Owners of the tokens can access and download the content, providing a novel way to distribute and monetize digital creations.
Supply Chain and Provenance Tracking
ERC-1155 tokens can streamline supply chain operations by automating inventory management, order processing, and logistics tracking, reducing costs, and improving efficiency.