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:

  1. Token Integration: Use zERC20 as a standard ERC-20 token in your DeFi protocols, wallets, or dApps

  2. Oracle Integration: Leverage zERC20's Transfer Merkle Tree as an on-chain oracle to verify transfer history with ZK proofs

  3. 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/integratearrow-up-right 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:

Field
Type
Description

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:

Root Type
Description
Tree Height

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 libraryarrow-up-right:

Usage
circomlib Template
Description

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:

Field
Description

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:

  1. Local Transfer Merkle Proof (40 siblings)

  2. 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