ZTP20

Introduction

What is the Token Standard

A token standard is a set of rules and specifications that define how a particular type of token should be created, transferred, and managed on a blockchain platform. These standards ensure interoperability, consistency, and security across different tokens built on the same blockchain.

These token standards provide a foundation for developers to create tokens that are compatible with various decentralized applications (dApps), decentralized finance (DeFi) platforms, and other blockchain-based projects. By adhering to these standards, tokens can be easily integrated, traded, and managed across different platforms and ecosystems.

What is ZTP-20

ZTP-20 is the standard interface for Fungible tokens. This interface enables users to perform actions such as minting and transferring tokens. By adopting this default standard, contracts utilizing ZTP-20 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

Example:

function init() {
  let paramObj;
  paramObj.name = "Global Coin";
  paramObj.symbol = "GCN";
  paramObj.describe = "Global coin token issued by XYZ";
  paramObj.version = "1";
  paramObj.decimals = 8;
  paramObj.protocol = "ztp20"; // To define that this contract is ZTP-20 standards

  Chain.store("contract_info", JSON.stringify(paramObj));
}

Function implementation:

balanceOf

Example:

function balanceOf(address) {

  let value = Chain.load(address);
  return value === false ? "0": value;
}

approve

Example:

function approve(spender, value) {
  
  let key = makeAllowanceKey(Chain.msg.sender, spender);
  Chain.store(key, value);

  Chain.tlog('Approve', Chain.msg.sender, spender, value);

  return true;
}

allowance

Example:

function allowance(owner, spender) {
  
  let key = makeAllowanceKey(owner, spender);
  let value = Chain.load(key);

  return value;
}

transfer

Example:

function transfer(to, value) {
 
  let toValue = Chain.load(to);
  toValue = (toValue === false) ? value: Utils.int64Add(toValue, value);
  Chain.store(to, toValue);

  senderValue = Utils.int64Sub(senderValue, value);
  Chain.store(Chain.msg.sender, senderValue);

  Chain.tlog('Transfer', Chain.msg.sender, to, value);

  return true;
}

transferFrom

Example:

function transferFrom(from, to, value) {

  let fromValue = Chain.load(from);
  let allowValue = allowance(from, Chain.msg.sender);
  let toValue = Chain.load(to);
  toValue = (toValue === false) ? value: Utils.int64Add(toValue, value);
  Chain.store(to, toValue);

  fromValue = Utils.int64Sub(fromValue, value);
  Chain.store(from, fromValue);

  let allowKey = makeAllowanceKey(from, Chain.msg.sender);
  allowValue = Utils.int64Sub(allowValue, value);
  Chain.store(allowKey, allowValue);

  Chain.tlog('Transfer', from, to, value);

  return true;
}

query

Example:

function query(input_str) {
  let result = {};
  let input = JSON.parse(input_str);

  if (input.method === 'contractInfo') {
    result = JSON.parse(Chain.load(CONTRACT_PRE));
    result.supply = Chain.getBalance(Chain.thisAddress);
  } else if (input.method === 'allowance') {
    result.allowance = allowance(input.params.owner, input.params.spender);
  } else if (input.method === 'balanceOf') {
    result.balance = balanceOf(input.params.address);
  } else {
    throw '';
  }
  return JSON.stringify(result);
}

deposit

Example:

function deposit(value) {

  let senderValue = Chain.load(Chain.msg.sender);
  senderValue = (senderValue === false) ? value: Utils.int64Add(senderValue, value);
  Chain.store(Chain.msg.sender, senderValue);
  Chain.tlog('Deposit', Chain.msg.sender, value);
  Chain.tlog('Transfer', "0x", Chain.msg.sender, value);
  return true;
}

withdrawal

function withdrawal(value) {
 
  let senderValue = Chain.load(Chain.msg.sender);
  senderValue = Utils.int64Sub(senderValue, value);
  Chain.store(Chain.msg.sender, senderValue);

  Chain.tlog('Withdrawal', Chain.msg.sender, value);
  Chain.tlog('Transfer', Chain.msg.sender, "0x", value);

  Chain.payCoin(Chain.msg.sender, value);
  return true;
}

Full ZTP-20 contract example:

'use strict';

const CONTRACT_PRE = 'contract_info';
const FIXED_DECIMALS = '6';
const PROTOCOL = 'ztp20';

function makeAllowanceKey(owner, spender) {
  return 'allow_' + owner + '_to_' + spender;
}

function approve(spender, value) {
  Utils.assert(Utils.addressCheck(spender) === true, 'Arg-spender is not a valid address.');
  Utils.assert(Utils.stoI64Check(value) === true, 'Arg-value must be alphanumeric.');
  Utils.assert(Utils.int64Compare(value, '0') >= 0, 'Arg-value of spender ' + spender + ' must greater or equal to 0.');

  let key = makeAllowanceKey(Chain.msg.sender, spender);
  Chain.store(key, value);

  Chain.tlog('Approve', Chain.msg.sender, spender, value);

  return true;
}

function allowance(owner, spender) {
  Utils.assert(Utils.addressCheck(owner) === true, 'Arg-owner is not a valid address.');
  Utils.assert(Utils.addressCheck(spender) === true, 'Arg-spender is not a valid address.');

  let key = makeAllowanceKey(owner, spender);
  let value = Chain.load(key);
  Utils.assert(value !== false, 'Failed to get the allowance given to ' + spender + ' by ' + owner + ' from metadata.');

  return value;
}

function transfer(to, value) {
  Utils.assert(Utils.addressCheck(to) === true, 'Arg-to is not a valid address.');
  Utils.assert(Utils.stoI64Check(value) === true, 'Arg-value must be alphanumeric.');
  Utils.assert(Utils.int64Compare(value, '0') > 0, 'Arg-value must be greater than 0.');
  Utils.assert(Chain.msg.sender !== to, 'From cannot equal to address.');

  let senderValue = Chain.load(Chain.msg.sender);
  Utils.assert(senderValue !== false, 'Failed to get the balance of ' + Chain.msg.sender + ' from metadata.');
  Utils.assert(Utils.int64Compare(senderValue, value) >= 0, 'Balance:' + senderValue + ' of sender:' + Chain.msg.sender + ' < transfer value:' + value + '.');

  let toValue = Chain.load(to);
  toValue = (toValue === false) ? value: Utils.int64Add(toValue, value);
  Chain.store(to, toValue);

  senderValue = Utils.int64Sub(senderValue, value);
  Chain.store(Chain.msg.sender, senderValue);

  Chain.tlog('Transfer', Chain.msg.sender, to, value);

  return true;
}

function transferFrom(from, to, value) {
  Utils.assert(Utils.addressCheck(from) === true, 'Arg-from is not a valid address.');
  Utils.assert(Utils.addressCheck(to) === true, 'Arg-to is not a valid address.');
  Utils.assert(Utils.stoI64Check(value) === true, 'Arg-value must be alphanumeric.');
  Utils.assert(Utils.int64Compare(value, '0') > 0, 'Arg-value must be greater than 0.');
  Utils.assert(from !== to, 'From cannot equal to address.');

  let fromValue = Chain.load(from);
  Utils.assert(fromValue !== false, 'Failed to get the value, probably because ' + from + ' has no value.');
  Utils.assert(Utils.int64Compare(fromValue, value) >= 0, from + ' Balance:' + fromValue + ' < transfer value:' + value + '.');

  let allowValue = allowance(from, Chain.msg.sender);
  Utils.assert(Utils.int64Compare(allowValue, value) >= 0, 'Allowance value:' + allowValue + ' < transfer value:' + value + ' from ' + from + ' to ' + to + '.');

  let toValue = Chain.load(to);
  toValue = (toValue === false) ? value: Utils.int64Add(toValue, value);
  Chain.store(to, toValue);

  fromValue = Utils.int64Sub(fromValue, value);
  Chain.store(from, fromValue);

  let allowKey = makeAllowanceKey(from, Chain.msg.sender);
  allowValue = Utils.int64Sub(allowValue, value);
  Chain.store(allowKey, allowValue);

  Chain.tlog('Transfer', from, to, value);

  return true;
}

function deposit(value) {
  Utils.assert(Utils.stoI64Check(value) === true, 'Arg-value must be alphanumeric.');
  Utils.assert(Utils.int64Compare(value, Chain.msg.coinAmount) === 0, 'Arg-value must equal pay coin amount.');
  Utils.assert(Utils.int64Compare(value, "0") > 0, 'Arg-value must > 0.');
  let senderValue = Chain.load(Chain.msg.sender);
  senderValue = (senderValue === false) ? value: Utils.int64Add(senderValue, value);
  Chain.store(Chain.msg.sender, senderValue);
  Chain.tlog('Deposit', Chain.msg.sender, value);
  Chain.tlog('Transfer', "0x", Chain.msg.sender, value);
  return true;
}

function withdrawal(value) {
  Utils.assert(Utils.stoI64Check(value) === true, 'Arg-value must be alphanumeric.');
  Utils.assert(Utils.int64Compare(Chain.msg.coinAmount, "0") === 0, 'Pay coin amount must be 0.');
  Utils.assert(Utils.int64Compare(value, "0") > 0, 'Arg-value must > 0.');
  let senderValue = Chain.load(Chain.msg.sender);
  Utils.assert(senderValue !== false, 'Failed to get the balance of ' + Chain.msg.sender + ' from metadata.');
  Utils.assert(Utils.int64Compare(senderValue, value) >= 0, 'Balance:' + senderValue + ' of sender:' + Chain.msg.sender + ' < transfer value:' + value + '.');

  senderValue = Utils.int64Sub(senderValue, value);
  Chain.store(Chain.msg.sender, senderValue);

  Chain.tlog('Withdrawal', Chain.msg.sender, value);
  Chain.tlog('Transfer', Chain.msg.sender, "0x", value);

  Chain.payCoin(Chain.msg.sender, value);
  return true;
}

function balanceOf(address) {
  Utils.assert(Utils.addressCheck(address) === true, 'Arg-address is not a valid address.');

  let value = Chain.load(address);
  return value === false ? "0": value;
}

function init(input_str) {
  
  let paramObj = JSON.parse(input_str).params;
  Utils.assert(paramObj.name !== undefined && paramObj.name.length > 0, 'Param obj has no name.');
  Utils.assert(paramObj.symbol !== undefined && paramObj.symbol.length > 0, 'Param obj has no symbol.');
  Utils.assert(paramObj.describe !== undefined && paramObj.describe.length > 0, 'Param obj has no describe.');
  Utils.assert(paramObj.version !== undefined && paramObj.version.length > 0, 'Param obj has no version.');

  paramObj.decimals = FIXED_DECIMALS;
  paramObj.protocol = PROTOCOL;
  Chain.store(CONTRACT_PRE, JSON.stringify(paramObj));
}

function main(input_str) {
  let input = JSON.parse(input_str);

  if (input.method === 'transfer') {
    transfer(input.params.to, input.params.value);
  } else if (input.method === 'transferFrom') {
    transferFrom(input.params.from, input.params.to, input.params.value);
  } else if (input.method === 'approve') {
    approve(input.params.spender, input.params.value);
  } else if (input.method === 'deposit') {
    deposit(input.params.value);
  } else if (input.method === 'withdrawal') {
    withdrawal(input.params.value);
  } else {
    throw '
';
  }
}

function query(input_str) {
  let result = {};
  let input = JSON.parse(input_str);

  if (input.method === 'contractInfo') {
    result = JSON.parse(Chain.load(CONTRACT_PRE));
    result.supply = Chain.getBalance(Chain.thisAddress);
  } else if (input.method === 'allowance') {
    result.allowance = allowance(input.params.owner, input.params.spender);
  } else if (input.method === 'balanceOf') {
    result.balance = balanceOf(input.params.address);
  } else {
    throw '';
  }
  return JSON.stringify(result);
}

Use Case

Utility Tokens

Projects could issue ZTP20 tokens as utility tokens to provide access to specific services or features within their platforms. For example, a decentralized application (dApp) could use ZTP20 tokens to grant users access to premium features or content.

Stablecoins

Stablecoins like ZTether (ZUSD) or ZCoin (ZC) could be created as ZTP20 tokens pegged to the value of fiat currencies or other assets. These stablecoins could be used for trading, remittances, and as a store of value within the ZETRIX ecosystem due to their price stability.

Decentralized Exchanges (DEXs)

ZTP20 tokens could be the primary assets traded on decentralized exchanges built on the ZETRIX blockchain. Users could trade ZTP20 tokens directly from their ZETRIX wallets without the need for intermediaries, providing greater control and privacy over their assets.

Lending and Borrowing Platforms

DeFi platforms on the ZETRIX blockchain could allow users to lend and borrow ZTP20 tokens using smart contracts. These platforms could utilize ZTP20 tokens as collateral for loans, enabling users to earn interest on their deposits or borrow assets against their holdings.

Tokenized Assets and Securities

ZTP20 tokens could be used to represent tokenized assets, such as real estate, commodities, and traditional securities like stocks and bonds. These tokenized assets could be traded and transferred on the ZETRIX blockchain, providing increased liquidity and accessibility to traditional asset classes.

Gaming and Collectibles

Non-fungible tokens (NFTs) based on the ZTP20 standard could be used in the gaming industry to represent in-game items, characters, and collectibles. Games built on the ZETRIX blockchain could use ZTP20-based NFTs as unique, tradable assets within their virtual worlds.

Reward and Loyalty Programs

Companies and organizations could issue ZTP20 tokens as part of reward and loyalty programs to incentivize customer engagement and retention. These tokens could be redeemed for discounts, products, or exclusive services, enhancing customer loyalty and brand engagement within the ZETRIX ecosystem.

Last updated