Integration Guide
Looking for the SDK? If you want to integrate zERC20 programmatically (private sends, scanning, wrap/unwrap), see the SDK Guide. This page covers lower-level contract integration and oracle usage.
This guide covers how to integrate zERC20 into your application.
Overview
This guide covers three tips for integrating zERC20:
Token Integration: Use zERC20 as a standard ERC-20 token in your DeFi protocols, wallets, or dApps
Oracle Integration: Leverage zERC20's Transfer Merkle Tree as an on-chain oracle to verify transfer history with ZK proofs
Self-Hosted Indexer: Run your own indexer node for maximum privacy, avoiding sender-recipient linkage exposure
Creating a New zERC20 Token
Want to launch a zERC20 version of your token (e.g., zUNI, zUSDT)? Visit zerc20.io/integrate to get in touch with the zERC20 team. We'll work with you to deploy and configure everything.
Token Integration
zERC20 is fully ERC-20 compatible. Any application that supports ERC-20 tokens can use zERC20.
Contract Addresses
See Contract Addresses for deployment addresses on each chain.
Standard ERC-20 Interface
Wrapping and Unwrapping
Use the LiquidityManager contract to convert between the underlying token and zERC20.
Example Usage:
See Fees and Rewards for details on wrap rewards and unwrap fees.
zERC20 as Oracle
zERC20 maintains a complete history of all transfers as a Merkle tree, using a ZK-friendly Poseidon hash function. External developers can leverage this Transfer Merkle Tree as an on-chain oracle for transfer history verification.
Leaf Structure
Each leaf in the Transfer Merkle Tree represents a single transfer:
from
address
Sender address (converted to field element)
to
address
Recipient address (converted to field element)
value
uint256
Transfer amount (converted to field element)
Tree Structure
There are two types of Merkle roots:
Local Transfer Root
Per-chain transfer Merkle root
40
Global Transfer Root
Cross-chain aggregated Merkle root
46 (40 + 6)
The Global Transfer Tree is constructed by aggregating Local Transfer Roots from all chains using an Aggregation Tree (height 6, supporting up to 64 chains).
Reading Merkle Roots from Contract
External contracts can query the proven Merkle roots from the Verifier contract:
Example Usage:
Merkle proof verification can be performed either on-chain in a smart contract or off-chain using ZKP circuits. Choose the approach that best fits your use case:
On-chain verification: Suitable for simple membership proofs where gas costs are acceptable
ZKP verification: Ideal for privacy-preserving applications or complex logic that would be expensive on-chain
Poseidon Hash Compatibility
The Poseidon hash used in zERC20 is fully compatible with circomlib's Poseidon library:
Leaf hash
Poseidon(3)
Poseidon(from, to, value)
Node hash
Poseidon(2)
Poseidon(left, right)
This compatibility allows developers to build custom ZK circuits using circomlib that can verify membership proofs against zERC20's Transfer Merkle Tree.
Obtaining Merkle Proofs
Local Transfer Merkle Proof
Query the indexer node to obtain Local Transfer Merkle proofs:
Endpoint: POST /proofs
Request:
Response:
target_index
The tree index (snapshot) to prove against
leaf_index
The position of the leaf in the tree
root
The Merkle root at the target index
hash_chain
The hash chain value at the target index
siblings
Array of 40 sibling hashes for the proof path
Supporting Endpoint - Get Tree Index:
GET /tree-index?chain_id={chainId}&token_address={address}&transfer_root={root}
Returns the tree index for a given Merkle root.
Global Transfer Merkle Proof
Global Merkle proofs are constructed by concatenating:
Local Transfer Merkle Proof (40 siblings)
Aggregation Tree Proof (6 siblings)
Construction Algorithm:
Aggregation State from Hub Contract:
The AggregationRootUpdated event from the Hub contract provides the snapshot of all local roots:
Running Your Own Indexer
For maximum privacy, run your own indexer instance to avoid sender-recipient linkage leaks.
Docker Compose
Configuration
Set environment variables:
See docker-compose.yml in the repository root for the full service configuration.
Last updated