Build on X Layer
Bridge to X Layer

Bridge to X Layer#

You can use ZKEVM Bridge Contract to bridge assets like ETH, OKB, and ERC-20 tokens between layer 1 and layer 2. However, there are a few points to note:

  • ETH is the native token of layer 1 (Ethereum). After bridging to layer 2 X Layer, it becomes a Wrapped ETH (WETH) ERC-20 token.
  • OKB is an ERC-20 token of layer 1 (Ethereum). After bridging to layer 2 X Layer, it becomes a native token.
  • Other ERC-20 tokens, which are bridged to the other side, remain ERC-20 tokens. You can calculate the new ERC-20 address by using the precalculatedWrapperAddress method.
  • There are no restrictions if you are bridging ERC-20 tokens from layer 1 to layer 2. However, there is a whitelist restriction when bridging assets from layer 2 to layer 1.
  • NetworkId and ChainId are not the same. NetworkId is a property that is unique to Polygon's Agglayer whereas ChainId is typically a transaction replay protection mechanism across EVM chains. The relevant information for Ethereum and X Layer are shown below.
Network (Mainnet)ChainIdNetworkId
Ethereum10
X Layer1963
Network (Testnet)ChainIdNetworkId
Sepolia111551110
X Layer testnet1951
Note
If you want to request layer 2 to layer 1 token whitelisting, you can join our X Layer Discord server, and navigate to #support-tickets to raise whitelising request.

The mapping list for OKB and ETH is as follows:

ERC-20 TokenTestnetMainnet
OKB0x3F4B6664338F23d2397c953f2AB4Ce8031663f80 (Layer 1 Ethereum Sepolia)0x75231f58b43240c9718dd58b4967c5114342a86c (Layer 1 Ethereum)
WETH0xBec7859BC3d0603BeC454F7194173E36BF2Aa5C8 (Layer 2 X Layer testnet)0x5a77f1443d16ee5761d310e38b62f77f726bc71c (Layer 2 X Layer mainnet)

Deposit OKB and ERC-20 tokens from layer 1#

The ZKEVM bridge contract allows ETH and ERC-20 token bridging from layer 1 to layer 2 using the bridgeAsset function. Notice that ERC-20 tokens will have a different address on layer 2. You can use the precalculatedWrapperAddress function to query the new address in ZkEVMBridgeContract.sol contract

If the asset you bridged is USDC, please refer to this document.

bridgeAsset#

Deposit and add a new leaf to the Merkle tree:

function bridgeAsset(
    uint32 destinationNetwork,
    address destinationAddress,
    uint256 amount,
    address token,
    bool forceUpdateGlobalExitRoot,
    bytes permitData
  ) public

Parameters

NameTypeDescription
destinationNetworkuint32Network destination. 3 for X Layer
destinationAddressaddressAddress destination
amountuint256Amount of tokens
tokenaddressToken address, 0 address is reserved for ETH
forceUpdateGlobalExitRootboolIndicates if the new global exit root is updated or not
permitDatabytesRaw data of the call permit of the token

precalculatedWrapperAddress#

This returns the precalculated address of a wrapper using the token information. Note: Updating the metadata of a token is not supported. Since the metadata has relevance in the address deployed, this function will not return a valid wrapped address if the metadata provided is not the original one.

function precalculatedWrapperAddress(
    uint32 originNetwork,
    address originTokenAddress,
    string name,
    string symbol,
    uint8 decimals
  ) external returns (address)

Parameters

NameTypeDescription
originNetworkuint32Origin network
originTokenAddressaddressOrigin token address, 0 address is reserved for ETH
namestringName of the token
symbolstringSymbol of the token
decimalsuint8Decimals of the token

Withdrawing OKB and ERC-20 tokens from layer 2#

You can use the claimAsset method to retrieve assets on layer 2. Calling this method on layer 2 incurs no Gas fees, so the official bridge service will be provided to automatically invoke the claimAsset method for you.

If the asset you withdrew is native USDC, please refer to this document.

claimAsset#

function claimAsset(
    bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProofLocalExitRoot,
    bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProofRollupExitRoot,
    uint256 globalIndex,
    bytes32 mainnetExitRoot,
    bytes32 rollupExitRoot,
    uint32 originNetwork,
    address originTokenAddress,
    uint32 destinationNetwork,
    address destinationAddress,
    uint256 amount,
    bytes calldata metadata
  ) external

Parameters

NameTypeDescription
smtProofLocalExitRootbytes32[32]Smt proof to proof the leaf against the network exit root
smtProofRollupExitRootbytes32[32]Smt proof to proof the rollupLocalExitRoot against the rollups exit root
globalIndexuint32Index of the leaf
mainnetExitRootbytes32Mainnet exit root
rollupExitRootbytes32Rollup exit root
originNetworkuint32Origin network. 0 for assets from originating from layer 1
originTokenAddressaddressOrigin token address, 0 address is reserved for ETH
destinationNetworkuint32Network destination. 0 for layer 1
destinationAddressaddressAddress destination
amountuint256`Amount of tokens
metadatabytesAbi encoded metadata if any, empty otherwise

Bridge to Ethereum#

Creating an ERC-20 token with custom logic on X Layer (layer 2)#

ERC-20 is the technical standard for fungible tokens created using the Ethereum blockchain. A fungible token is interchangeable with another token — whereas the well-known non-fungible tokens (NFTs) are not interchangeable. You can read more information about ERC-20 here. Deploy a simple ERC-20 contract with any of the tools like Truffle, Hardhat, or Foundry. This contract is implemented based on the open-source library, which can be found at OpenZeppelin.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts@4.9.3/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {
    constructor() ERC20("MyToken", "MTK") {}
}

Depositing OKB and ERC-20 tokens from layer 2#

The same bridge contract is used on both layer 1 and layer 2, so the interface definition is identical.

Note
Claiming assets on Ethereum will consume gas fees and is not free.