ZRC-20: Fungible Token Standard
Conservation-aware fungible token standard for the Zoo ecosystem with impact tracking and auto-donation extensions
ZIP-0700: ZRC-20 Fungible Token Standard
Abstract
This proposal defines ZRC-20, the canonical fungible token standard for the Zoo L2 chain (Chain ID: 200200). ZRC-20 is fully ERC-20 compatible and extends the Lux LRC-20 standard (LP-3020) with conservation-specific functionality: on-chain impact tracking, configurable auto-donation on transfers, and a burnable-for-conservation mechanism that retires tokens in exchange for verified conservation credits. ZRC-20 also incorporates the LRC-20 extension suite -- burnable, mintable, bridgeable, capped supply, voting, permit (EIP-2612), and flash mint -- adapted for the Zoo ecosystem.
Motivation
The Zoo ecosystem requires a fungible token primitive that:
- ERC-20 Compatibility: Wallets, DEXs, and bridges must work without modification.
- Conservation Alignment: Every token transfer can contribute measurable conservation impact per ZIP-0500 ESG principles.
- Lux Interoperability: Tokens must bridge cleanly to Lux C-Chain via LP-3800 and LP-6000, conforming to ZIP-0013.
- Governance Readiness: Voting weight and delegation must be first-class features for DAO-governed conservation funding.
- Capital Efficiency: Flash minting and permit-based approvals reduce gas costs and improve DeFi composability.
Existing ERC-20 implementations lack any mechanism to track ecological impact or direct value flows toward conservation. ZRC-20 fills this gap while remaining a drop-in replacement for any ERC-20 consumer.
Specification
Core Interface
The ZRC-20 standard implements the full ERC-20 interface plus conservation extensions.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title IZRC20
* @notice Zoo ecosystem fungible token standard with conservation impact tracking.
* @dev Fully ERC-20 compatible. Extends LRC-20 (LP-3020) with impact and donation features.
*/
interface IZRC20 {
// ──────────────────────────────────────────────
// ERC-20 Core (unchanged)
// ──────────────────────────────────────────────
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
// ──────────────────────────────────────────────
// Conservation Extensions
// ──────────────────────────────────────────────
/**
* @notice Returns the conservation fund address that receives auto-donations.
*/
function conservationFund() external view returns (address);
/**
* @notice Returns the auto-donation rate in basis points (0-10000).
* A value of 50 means 0.50% of each transfer is directed to the fund.
*/
function donationBasisPoints() external view returns (uint16);
/**
* @notice Returns cumulative conservation impact credits generated by this token.
*/
function totalImpactCredits() external view returns (uint256);
/**
* @notice Returns impact credits attributed to a specific address.
*/
function impactCreditsOf(address account) external view returns (uint256);
/**
* @notice Burns `amount` tokens from the caller and mints `amount` conservation
* impact credits to the caller, verified against the Zoo Impact Oracle.
*/
function burnForConservation(uint256 amount) external returns (uint256 creditsIssued);
/**
* @notice Emitted when tokens are donated to the conservation fund during a transfer.
*/
event ConservationDonation(
address indexed from,
address indexed to,
uint256 transferAmount,
uint256 donationAmount
);
/**
* @notice Emitted when tokens are burned in exchange for conservation credits.
*/
event BurnedForConservation(
address indexed burner,
uint256 amountBurned,
uint256 creditsIssued
);
/**
* @notice Emitted when the donation rate is updated by governance.
*/
event DonationRateUpdated(uint16 oldRate, uint16 newRate);
}
Extension Interfaces
ZRC-20 tokens MAY implement any combination of the following extensions, each derived from the LRC-20 extension suite (LP-3020).
/**
* @title IZRC20Burnable
*/
interface IZRC20Burnable is IZRC20 {
function burn(uint256 amount) external;
function burnFrom(address account, uint256 amount) external;
}
/**
* @title IZRC20Mintable
*/
interface IZRC20Mintable is IZRC20 {
function mint(address to, uint256 amount) external;
event Minted(address indexed to, uint256 amount, address indexed minter);
}
/**
* @title IZRC20Capped
*/
interface IZRC20Capped is IZRC20 {
function cap() external view returns (uint256);
}
/**
* @title IZRC20Votes
* @dev Compatible with OpenZeppelin Governor and Zoo DAO governance (ZIP-0100).
*/
interface IZRC20Votes is IZRC20 {
function getVotes(address account) external view returns (uint256);
function getPastVotes(address account, uint256 timepoint) external view returns (uint256);
function getPastTotalSupply(uint256 timepoint) external view returns (uint256);
function delegates(address account) external view returns (address);
function delegate(address delegatee) external;
function delegateBySig(
address delegatee,
uint256 nonce,
uint256 expiry,
uint8 v, bytes32 r, bytes32 s
) external;
}
/**
* @title IZRC20Permit
* @dev EIP-2612 gasless approvals.
*/
interface IZRC20Permit is IZRC20 {
function permit(
address owner, address spender, uint256 value,
uint256 deadline, uint8 v, bytes32 r, bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
/**
* @title IZRC20FlashMint
* @dev EIP-3156 flash loan support.
*/
interface IZRC20FlashMint is IZRC20 {
function maxFlashLoan(address token) external view returns (uint256);
function flashFee(address token, uint256 amount) external view returns (uint256);
function flashLoan(
address receiver, address token, uint256 amount, bytes calldata data
) external returns (bool);
}
/**
* @title IZRC20Bridgeable
* @dev LP-3800 / LP-6000 bridgeable asset interface for Lux cross-chain transfers.
*/
interface IZRC20Bridgeable is IZRC20 {
function bridge() external view returns (address);
function mintFromBridge(address to, uint256 amount, bytes32 sourceChainTxHash) external;
function burnForBridge(uint256 amount, bytes32 destinationChainId) external;
event BridgeMint(address indexed to, uint256 amount, bytes32 indexed sourceTxHash);
event BridgeBurn(address indexed from, uint256 amount, bytes32 indexed destChainId);
}
Auto-Donation Mechanism
On every transfer and transferFrom call where donationBasisPoints > 0:
- Compute
donationAmount = amount * donationBasisPoints / 10000. - Transfer
donationAmounttoconservationFund(). - Transfer
amount - donationAmountto the recipient. - Emit
ConservationDonation(from, to, amount, donationAmount).
The donation rate is updatable only by the token's governance (typically a ZooGovernor timelock). The rate MUST NOT exceed 500 basis points (5%).
Burn-for-Conservation
The burnForConservation function:
- Burns
amounttokens frommsg.sender, reducingtotalSupply. - Queries the Zoo Impact Oracle (a trusted on-chain oracle registered in ZIP-0100) for the current conservation credit exchange rate.
- Mints
creditsIssuedimpact credits to the caller's impact balance. - Emits
BurnedForConservation(msg.sender, amount, creditsIssued).
Impact credits are non-transferable soulbound records. They serve as on-chain proof of conservation contribution per ZIP-0501.
Token Registration
All ZRC-20 tokens deployed on Zoo L2 MUST register in the Zoo Contract Registry (ZIP-0100) with the following metadata:
| Field | Type | Description |
|---|---|---|
standard | string | "ZRC-20" |
extensions | string[] | List of implemented extension names |
conservationFund | address | Fund receiving auto-donations |
donationBasisPoints | uint16 | Current donation rate |
impactOracleAddress | address | Oracle used for credit pricing |
Rationale
Why extend ERC-20 rather than create a new standard? Compatibility. Every wallet, DEX, aggregator, and bridge that speaks ERC-20 works with ZRC-20 out of the box. The conservation extensions are additive -- contracts unaware of them simply see a standard ERC-20.
Why auto-donation instead of a transfer tax? Transparency. A tax is hidden in the transfer amount. Auto-donation explicitly splits the transfer into a user amount and a donation amount, with a dedicated event. Users and UIs can clearly display both.
Why soulbound impact credits? Impact credits represent verified conservation contributions. Making them transferable would create a speculative market that decouples price from actual ecological impact, undermining the mission per ZIP-0500.
Why cap the donation rate at 5%? To prevent governance capture where a majority votes to extract excessive value from transfers. The 5% ceiling protects minority holders and DeFi composability.
Backwards Compatibility
ZRC-20 is fully backwards compatible with ERC-20 (EIP-20). Any contract or off-chain tool that interacts with standard ERC-20 functions will work without modification. The conservation extensions use separate function selectors that do not conflict with ERC-20, ERC-165, or any standard Ethereum interface.
Tokens bridged from Lux C-Chain via LP-6000 are wrapped as ZRC-20 with donationBasisPoints = 0 by default, preserving exact transfer semantics for bridged assets.
Security Considerations
- Reentrancy: The
burnForConservationfunction interacts with an external oracle. Implementations MUST follow checks-effects-interactions and use reentrancy guards. - Oracle Manipulation: The Impact Oracle must be resistant to flash loan manipulation. Implementations SHOULD use time-weighted average pricing (TWAP) or multi-source aggregation.
- Donation Rate Governance: Changes to
donationBasisPointsMUST go through a timelock (minimum 48 hours) to prevent flash governance attacks. - Bridge Security:
mintFromBridgeMUST only be callable by the registered bridge contract. Implementations MUST verify the source chain transaction hash has not been replayed. - Flash Mint Caps: Flash minting MUST NOT exceed
totalSupplyto prevent economic attacks on downstream protocols. - Rounding: Donation amount calculation must round down to prevent dust accumulation or transfer failures on small amounts.
References
- EIP-20: Token Standard
- EIP-2612: Permit
- EIP-3156: Flash Loans
- LP-3020: LRC-20 Fungible Token Standard
- LP-3800: Bridged Asset Standard
- LP-6000: B-Chain Bridge Specification
- ZIP-0015: Zoo L2 Chain Architecture
- ZIP-0100: Zoo Contract Registry
- ZIP-0500: ESG Principles for Conservation Impact
- ZIP-0501: Conservation Impact Measurement
- ZIP-0013: LP Standards Conformance
Copyright
Copyright and related rights waived via CC0.