Skip to main content

Token Standards

ZENIQ Smart Chain inherits the token model of its smartBCH ancestor. Two things matter for developers:

  1. ZEN-20 — the fungible-token interface (the ZENIQ Smart Chain equivalent of ERC-20 / SEP-20). Any standard ERC-20 contract is a valid ZEN-20 token.
  2. The native ZENIQ token exposed through a ZEN-20 interface — a built-in pseudo-contract that lets you treat the native coin exactly like a token.
EVM version

Compile token contracts with Solidity 0.8.19 (London EVM). See JSON-RPC → Gas & fees.

ZEN-20 fungible tokens

ZEN-20 is derived from ERC-20 (it is the same interface smartBCH calls SEP-20). A conformant token implements:

// Metadata
function name() external view returns (string);
function symbol() external view returns (string);
function decimals() external view returns (uint8);

// Core
function totalSupply() external view returns (uint256);
function balanceOf(address _owner) external view returns (uint256);
function transfer(address _to, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
function approve(address _spender, uint256 _value) external returns (bool);
function allowance(address _owner, address _spender) external view returns (uint256);

// Convenience (recommended, guards against the approve race)
function increaseAllowance(address _spender, uint256 _delta) external returns (bool);
function decreaseAllowance(address _spender, uint256 _delta) external returns (bool);

event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);

Notes that differ from bare ERC-20:

  • symbol() and decimals() are required, not optional.
  • increaseAllowance / decreaseAllowance are recommended to avoid the well-known approve front-running race; callers should still set an allowance to 0 before changing it to a new non-zero value.
  • Callers must check the returned bool — do not assume transfer / transferFrom never return false.

The easiest way to ship a compliant token is to inherit OpenZeppelin's ERC20 (and ERC20Burnable / Ownable as needed) — it satisfies the ZEN-20 interface unchanged. Use the OpenZeppelin Contracts Wizard to generate one.

The native ZENIQ token as ZEN-20

On Ethereum, ETH is not an ERC-20 token, so DApps must special-case the native coin (or wrap it, à la WETH). ZENIQ Smart Chain solves this with a built-in pseudo-contract that presents the native ZENIQ coin through the full ZEN-20 interface — the same idea smartBCH ships as SEP-206.

It is pseudo because it is implemented natively in zeniqsmartd, not as EVM bytecode, which lets it do something no ordinary contract can: move the native coin. You call it exactly as you would any ZEN-20 token.

Native-token precompile address
0x000000000000000000000000005a454e49510002

(The bytes 5a 45 4e 49 51 spell ZENIQ in ASCII.) This is the ZENIQ address — it is not smartBCH's 0x…2711.

Behavioural specifics:

  • balanceOf(addr) returns the same value as eth_getBalance(addr).
  • transfer moves the native coin directly.
  • The methods must be invoked via call / staticcall; delegatecall and callcode are not allowed.
  • Allowances are stored natively; their slot is not readable with eth_getStorageAt.
  • Calls cost a fixed amount of gas per method (e.g. transfer 32000, balanceOf 20000, approve 25000).

Example — read your native balance through the token interface with ethers.js:

const { ethers } = require("ethers");

const provider = new ethers.providers.JsonRpcProvider("https://api.zeniq.network");
const ZEN20_ABI = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function decimals() view returns (uint8)",
"function balanceOf(address) view returns (uint256)",
"function transfer(address,uint256) returns (bool)",
];

const native = new ethers.Contract(
"0x000000000000000000000000005a454e49510002",
ZEN20_ABI,
provider
);

const bal = await native.balanceOf("0x<your-address>");
console.log(ethers.utils.formatEther(bal), "ZENIQ");

Because this pseudo-contract satisfies the ZEN-20 interface, a DApp can handle the native coin and ordinary ZEN-20 tokens through a single code path.